티스토리 뷰



[FastAPI] 데이터 무결성을 위한 Pydantic 스키마 설계 가이드
FastAPI로 API를 개발할 때 가장 빈번하게 마주치는 과제는 "외부에서 들어오는 데이터가 내가 정의한 구조와 정말 일치하는가?"입니다. 필드가 몇 개 없을 때는 간단하지만, 수십 개의 필드와 복잡한 데이터 타입이 섞이기 시작하면 체계적인 정리가 필요합니다.
1. 필수의 유무: Optional과 Default 처리
가장 먼저 고민해야 할 것은 "이 데이터가 반드시 들어와야 하는가?"입니다.
• 값이 없을 때 (Null/None 처리): Optional (Python 3.10+ 에서는 | None)을 사용하여 해당 필드가 비어있을 수 있음을 명시해야 합니다.
• 기본값 설정: 클라이언트가 값을 보내지 않았을 때 null로 둘지, 아니면 특정 기본값(예: 0, "")을 채울지 정의합니다.

from pydantic import BaseModel, Field
from typing import Optional

class ProductSchema(BaseModel):
    name: str  # 필수 값
    description: Optional[str] = None  # 값이 없으면 None (null)
    price: float = 0.0  # 값이 없으면 0.0 기본값 사용
    is_active: bool = Field(default=True, description="활성화 상태 여부")


엄격한 타입 검증 (정수, 실수, 문자열 등)
Pydantic은 기본적으로 **데이터 타입 변환(Coercion)**을 시도합니다. 예를 들어, 정수 필드에 "123"이라는 문자열이 들어오면 자동으로 123으로 변환합니다. 하지만 더 엄격한 검증이 필요할 때가 있습니다.
• 숫자 범위 제한: gt(greater than), lt(less than), ge(greater or equal) 등을 사용하여 값의 범위를 제한할 수 있습니다.
• 문자열 길이 및 패턴: min_length, max_length 또는 정규표현식(pattern)을 활용합니다.

class UserProfile(BaseModel):
    age: int = Field(ge=0, le=120)  # 0세~120세 사이 정수만 허용
    score: float = Field(default=0.0, gt=-1.0) # -1.0보다 큰 실수
    username: str = Field(min_length=2, max_length=20) # 글자수 제한


필드가 많고 복잡할 때: 중첩 모델(Nested Models)
필드가 수십 개가 넘어간다면 하나의 클래스에 모두 정의하는 것은 유지보수에 치명적입니다. 연관된 데이터끼리 그룹화하여 중첩 모델로 설계하세요.

class Address(BaseModel):
    city: str
    zip_code: str

class UserDetail(BaseModel):
    id: int
    info: UserProfile  # 위에서 정의한 모델 재사용
    address: Optional[Address] = None # 중첩된 구조로 가독성 향상


실제 데이터 검토를 위한 Custom Validator
단순히 타입만 맞는다고 끝이 아닙니다. "시작일이 종료일보다 빨라야 한다"와 같은 비즈니스 로직 검증은 @field_validator를 사용합니다.

from pydantic import field_validator

class EventSchema(BaseModel):
    start_date: str
    end_date: str

    @field_validator('end_date')
    @classmethod
    def check_date_order(cls, v, info):
        if 'start_date' in info.data and v < info.data['start_date']:
            raise ValueError('종료일은 시작일보다 빨라야 합니다.')
        return v

5. 종합적인 설계 전략
많은 필드를 처리해야 하는 대규모 프로젝트라면 다음 전략을 참고하세요.
1. 공통 필드 분리: 생성일, 수정일 등 반복되는 필드는 BaseSchema를 만들어 상속받아 사용합니다.
2. Extra 필드 제어: 정의되지 않은 데이터가 들어오는 것을 막으려면 model_config에서 extra='forbid' 설정을 추가하세요.
3. Alias 사용: DB 필드명과 API 노출 필드명이 다를 경우 Field(alias="...")를 통해 매핑합니다.
마치며
스키마를 정의하는 것은 단순히 에러를 막는 것이 아니라, API의 문서(Swagger)를 만드는 과정이기도 합니다. 처음 설계할 때 Null 처리와 타입 범위를 꼼꼼하게 정의해두면, 실제 데이터가 들어왔을 때 발생하는 예외 상황을 90% 이상 사전 차단할 수 있습니다.
데이터의 구조가 복잡해질수록 "작게 나누고(Composition), 명확하게 제약하는(Validation)" 원칙을 잊지 마세요!


댓글
D-DAY
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함