0Pricing
AI Engineering Academy · 강의

스키마 진화와 이전 버전 호환성

스키마 버전 관리, 과거 추출 결과 마이그레이션, 전환 중 병렬 검증을 통해 장기 실행 추출 파이프라인의 호환성을 깨뜨리는 스키마 변경을 관리합니다.

스키마 진화와 이전 버전 호환성은(는) CoddyKit의 무료 AI Engineering Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Engineering Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

스키마는 시간이 지나면서 왜 변경되는가

추출 스키마는 고정되어 있지 않습니다. 비즈니스 요구 사항은 발전하고, 새로운 문서 유형이 등장하며, 처음부터 수집했어야 할 필드를 나중에 발견하기도 합니다. 운영 중인 파이프라인에서 스키마를 변경하면 이전 버전과의 호환성 문제가 발생합니다. 기존에 추출된 레코드는 이전 스키마를 사용하고 새 레코드는 새 스키마를 사용하기 때문입니다. 이러한 전환을 안전하게 관리하는 것이 스키마 발전의 핵심입니다.

스키마 버전 관리하기

각 스키마에 버전 번호를 지정하고 모든 추출 레코드와 함께 저장하십시오. 스키마를 변경할 때마다 버전을 증가시키십시오. 그러면 스키마 버전별로 레코드를 조회하고, 이전 레코드에 마이그레이션을 실행하며, 버전별로 별도의 유효성 검사 로직을 유지할 수 있습니다. 모든 출력 모델에 schema_version이라는 간단한 문자열 필드를 추가하는 것으로 충분합니다.

from pydantic import BaseModel
from typing import Literal

class InvoiceV1(BaseModel):
    schema_version: Literal['1.0'] = '1.0'
    vendor: str
    total_amount: float

class InvoiceV2(BaseModel):
    schema_version: Literal['2.0'] = '2.0'
    vendor: str
    vendor_tax_id: str | None = None  # new field
    total_amount: float
    currency: str = 'USD'  # new field with default

추가 변경과 호환성을 깨는 변경

추가 변경은 안전합니다. Optional 필드나 기본값이 있는 필드를 추가해도 기존 추출 코드나 기존 레코드가 손상되지 않습니다. 호환성을 깨는 변경은 위험합니다. 필드 이름을 바꾸거나, 문자열에서 정수로 형식을 변경하거나, 필드를 삭제하면 후속 시스템이 손상됩니다. 항상 추가 변경을 우선하십시오. 호환성을 깨는 변경이 불가피하다면 새로운 주요 스키마 버전을 만들고 통제된 방식으로 마이그레이션하십시오.

# Safe: additive change - add optional field
class ProductV2(BaseModel):
    name: str
    price: float
    sku: str | None = None   # NEW optional field - backward safe
    category: str = 'general'  # NEW with default - backward safe

# Risky: breaking change - rename or retype
# class ProductV2(BaseModel):
#     product_name: str  # RENAMED from name - breaks consumers
#     price_cents: int   # RETYPED from float - breaks data

데이터베이스에 스키마 버전 저장하기

추출 결과 테이블에 스키마 버전을 포함하여 각 레코드를 어떤 버전이 생성했는지 항상 알 수 있도록 하십시오. 추출된 전체 데이터를 저장하는 jsonb 열과 schema_version 텍스트 열을 함께 사용하는 방식이 일반적입니다. 이렇게 하면 버전을 고려한 쿼리를 작성하고, 트래픽이 적은 시간대에 이전 레코드 중 필요한 것만 선택적으로 마이그레이션할 수 있습니다.

-- PostgreSQL table design
CREATE TABLE extractions (
    doc_id TEXT PRIMARY KEY,
    schema_version TEXT NOT NULL,
    extracted_data JSONB NOT NULL,
    extracted_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_schema_version ON extractions(schema_version);

-- Query old records needing migration
SELECT doc_id, extracted_data
FROM extractions
WHERE schema_version = '1.0'
LIMIT 1000;

마이그레이션 스크립트 작성하기

각 스키마 버전 전환마다 마이그레이션 스크립트를 작성하십시오. 이 스크립트는 이전 레코드를 읽고, 새 형식으로 변환한 다음, 새 버전과 함께 다시 기록해야 합니다. 실패해도 데이터베이스가 일부만 마이그레이션된 상태로 남지 않도록 트랜잭션을 사용하여 작은 묶음으로 마이그레이션을 실행하십시오. 마이그레이션이 완료되었음을 확인할 때까지는 항상 이전 스키마를 사용할 수 있게 유지하십시오.

import asyncpg
import json

async def migrate_v1_to_v2(pool, batch_size=100):
    async with pool.acquire() as conn:
        rows = await conn.fetch(
            'SELECT doc_id, extracted_data FROM extractions WHERE schema_version=$1 LIMIT $2',
            '1.0', batch_size
        )
        for row in rows:
            old = row['extracted_data']
            new_data = {
                'schema_version': '2.0',
                'vendor': old['vendor'],
                'vendor_tax_id': None,  # unknown for old records
                'total_amount': old['total_amount'],
                'currency': 'USD'  # assume USD for old records
            }
            await conn.execute(
                'UPDATE extractions SET extracted_data=$1, schema_version=$2 WHERE doc_id=$3',
                json.dumps(new_data), '2.0', row['doc_id']
            )

전환 중 병렬 유효성 검사

스키마를 마이그레이션하는 동안 병렬 유효성 검사를 실행하십시오. 들어오는 문서 중 일부를 대상으로 이전 스키마와 새 스키마를 동시에 사용해 추출합니다. 결과를 비교하여 새 스키마가 이전 스키마가 수집하던 모든 항목과 새 필드를 모두 수집하는지 확인하십시오. 통계적으로 유의미한 표본에서 병렬 유효성 검사를 통해 안정적인 동등성이 확인된 후에만 이전 스키마를 폐기하십시오.

async def parallel_validate(text: str) -> dict:
    v1_result, v2_result = await asyncio.gather(
        extract_with_schema(text, InvoiceV1),
        extract_with_schema(text, InvoiceV2)
    )
    discrepancy = (
        v1_result.vendor != v2_result.vendor or
        abs(v1_result.total_amount - v2_result.total_amount) > 0.01
    )
    if discrepancy:
        log_discrepancy(text, v1_result, v2_result)
    return {'v1': v1_result, 'v2': v2_result, 'discrepancy': discrepancy}

스키마 출시를 위한 기능 플래그

기능 플래그를 사용하여 파이프라인이 이전 스키마에서 새 스키마로 전환되는 시점을 제어하십시오. 그러면 트래픽의 일정 비율에 새 스키마를 점진적으로 적용하고 오류율을 모니터링하며, 문제가 발생하면 코드를 다시 배포하지 않고 즉시 되돌릴 수 있습니다. LaunchDarkly 같은 기능 플래그 서비스나 간단한 데이터베이스 행 모두 사용할 수 있습니다.

import os

def get_active_schema():
    version = os.environ.get('EXTRACTION_SCHEMA_VERSION', '1.0')
    schemas = {
        '1.0': InvoiceV1,
        '2.0': InvoiceV2,
    }
    return schemas.get(version, InvoiceV1)

async def extract_document(text: str):
    SchemaClass = get_active_schema()
    return await extract_with_schema(text, SchemaClass)

유니온 형식으로 소비자 호환성 유지하기

추출된 데이터를 읽는 후속 시스템은 여러 스키마 버전을 적절히 처리해야 합니다. 소비자 코드에서 판별 유니온을 사용하여 schema_version 필드에 따라 올바른 구문 분석 로직을 선택하십시오. 이는 조건문을 여러 겹 작성하는 것보다 견고하고, 버전 3이 추가될 때 확장하기도 쉽습니다.

from pydantic import BaseModel
from typing import Union, Annotated
from typing import Literal

def parse_extraction(raw: dict) -> Union[InvoiceV1, InvoiceV2]:
    version = raw.get('schema_version', '1.0')
    if version == '1.0':
        return InvoiceV1(**raw)
    elif version == '2.0':
        return InvoiceV2(**raw)
    else:
        raise ValueError(f'Unknown schema version: {version}')

배포 전에 스키마 변경 테스트하기

새 스키마를 배포하기 전에 전체 회귀 테스트 세트에 대해 실행하십시오. 회귀 테스트 세트는 예상 출력이 알려진 대표 문서의 선별된 모음입니다. 이전 스키마와 새 스키마에서 각 필드의 F1 점수를 비교하십시오. 어느 필드에서든 F1이 하락하면 새 스키마 설명이 모델을 혼란스럽게 만든 것이므로, 배포하기 전에 해당 필드의 설명을 수정하십시오.

def eval_schema_on_test_set(test_cases: list, SchemaClass) -> dict:
    field_f1 = {}
    for case in test_cases:
        result = extract_with_schema(case['text'], SchemaClass)
        for field in case['expected']:
            expected = case['expected'][field]
            actual = getattr(result, field, None)
            # Update precision/recall counters
            update_metrics(field_f1, field, expected, actual)
    return {k: compute_f1(v) for k, v in field_f1.items()}

스키마 지원 중단 처리하기

새로운 추출에 더 이상 사용하지 않는 스키마 버전은 지원을 중단할 수 있습니다. 지원 중단이란 해당 버전의 새 레코드 수용을 중지하고, 기존 레코드는 계속 읽을 수 있게 유지하며, 기존 레코드를 마이그레이션하거나 보관할 종료 일정을 정하는 것을 의미합니다. 모든 소비자가 구문 분석 코드를 업그레이드해야 한다는 사실을 알 수 있도록 변경 기록에 지원 중단 내용을 문서화하십시오.

DEPRECATED_VERSIONS = {'1.0'}
SUNSET_DATE = '2026-09-01'

def warn_if_deprecated(version: str):
    if version in DEPRECATED_VERSIONS:
        import warnings
        warnings.warn(
            f'Schema version {version} is deprecated. '
            f'It will be removed after {SUNSET_DATE}. '
            'Migrate consumers to version 2.0.',
            DeprecationWarning,
            stacklevel=2
        )

변경 기록과 소통

모든 스키마 변경에는 변경 내용, 변경 이유, 마이그레이션 방법, 예상 영향을 설명하는 변경 기록 항목이 함께 있어야 합니다. 변경 사항을 배포하기 전에 추출된 데이터를 사용하는 모든 팀과 변경 기록 항목을 공유하십시오. 많은 스키마 마이그레이션 사고는 기술적 실패가 아니라 변경 예정 사실을 전달받지 못한 소비자 때문에 발생합니다.

# CHANGELOG.md entry format:
# ## Schema v2.0 (2026-07-01)
# ### Changes
# - ADDED: vendor_tax_id (Optional[str]) - VAT/EIN extracted from header
# - ADDED: currency (str, default='USD') - detected from symbol/code
# ### Migration
# Run: python scripts/migrate_v1_to_v2.py --batch-size=500
# ### Consumers
# - billing-service: update parse_extraction() to handle v2
# - audit-service: query now supports currency filter

빠른 확인

추출 파이프라인의 스키마 발전과 이전 버전과의 호환성에 대한 이해도를 확인하십시오.

레슨 요약

이 레슨에서는 다음을 배웠습니다. 스키마 버전 관리를 사용하면 모든 추출 레코드와 함께 버전 식별자를 저장하여 필요한 레코드만 선택적으로 마이그레이션할 수 있고, 추가 변경은 안전하지만 필드 이름 변경이나 형식 변경에는 신중한 마이그레이션이 필요하며, 병렬 유효성 검사를 통해 이전 스키마를 폐기하기 전에 새 스키마를 검증할 수 있습니다. 다음으로 TTFT 및 TPOT 지표를 사용하여 LLM 지연 시간을 측정하는 방법을 알아보겠습니다.

자주 묻는 질문

“스키마 진화와 이전 버전 호환성” 강의는 무료인가요?

네 — “스키마 진화와 이전 버전 호환성” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“스키마 진화와 이전 버전 호환성”에서 뭘 배우나요?

스키마 버전 관리, 과거 추출 결과 마이그레이션, 전환 중 병렬 검증을 통해 장기 실행 추출 파이프라인의 호환성을 깨뜨리는 스키마 변경을 관리합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Engineering Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Engineering Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Engineering Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“스키마 진화와 이전 버전 호환성” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Engineering Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Engineering Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Instructor: Pydantic을 활용한 타입 지정 추출
  2. 부분 데이터와 누락 데이터 처리
  3. 비동기 처리와 대기열을 활용한 일괄 처리
  4. 스키마 진화와 이전 버전 호환성
← AI Engineering Academy(으)로 돌아가기