파이썬/Fast API

[Pydantic] Field로 데이터 유효성 검사 마스터하기

코샵 2025. 1. 9. 10:27
반응형

소개

FastAPI와 함께 자주 사용되는 Pydantic의 Field를 통해 데이터 유효성 검사를 구현하는 방법을 알아보겠습니다.

설치 방법

# pip 사용
pip install pydantic

# poetry 사용
poetry add pydantic

# pipenv 사용
pipenv install pydantic

기본 사용법

간단한 모델 정의

from pydantic import BaseModel, Field

class User(BaseModel):
    name: str = Field(..., min_length=2, max_length=50)
    age: int = Field(gt=0, lt=150)
    email: str = Field(..., pattern=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")

Field의 주요 검증 옵션

옵션 설명 예시
default 기본값 설정 Field(default=0)
gt/lt 크기 비교 Field(gt=0, lt=100)
ge/le 이상/이하 Field(ge=0, le=100)
min_length/max_length 문자열 길이 Field(min_length=2)
regex/pattern 정규식 패턴 Field(pattern=r"\d+")
description 필드 설명 Field(description="사용자 이름")

고급 사용법

복잡한 검증 규칙

from typing import List
from pydantic import BaseModel, Field, validator

class Product(BaseModel):
    name: str = Field(..., min_length=1)
    price: float = Field(gt=0)
    tags: List[str] = Field(default_factory=list, max_items=5)

    @validator('price')
    def validate_price(cls, value):
        if not value.is_integer() and value * 100 % 10 != 0:
            raise ValueError('가격은 1원 단위까지만 허용됩니다')
        return value

중첩 모델

class Address(BaseModel):
    street: str = Field(..., min_length=5)
    city: str = Field(..., min_length=2)
    country: str = Field(..., min_length=2)

class User(BaseModel):
    name: str = Field(..., min_length=2)
    addresses: List[Address] = Field(default_factory=list, max_items=3)

상수 및 제약 조건

from enum import Enum
from typing import Optional

class UserType(str, Enum):
    ADMIN = "admin"
    USER = "user"
    GUEST = "guest"

class User(BaseModel):
    name: str = Field(..., min_length=2)
    type: UserType = Field(default=UserType.GUEST)
    status: str = Field(
        default="active",
        pattern="^(active|inactive|suspended)$"
    )

실전 예제

회원가입 검증

from datetime import date
from typing import Optional
from pydantic import BaseModel, Field, EmailStr, validator

class UserRegistration(BaseModel):
    email: EmailStr = Field(
        ...,
        description="사용자 이메일"
    )
    password: str = Field(
        ...,
        min_length=8,
        pattern=r"^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$",
        description="최소 8자, 하나의 문자, 숫자, 특수문자 포함"
    )
    name: str = Field(
        ...,
        min_length=2,
        max_length=50,
        description="사용자 이름"
    )
    birth_date: date = Field(
        ...,
        description="생년월일"
    )
    phone: Optional[str] = Field(
        None,
        pattern=r"^\d{3}-\d{4}-\d{4}$",
        description="전화번호 (000-0000-0000 형식)"
    )

    @validator('birth_date')
    def validate_birth_date(cls, v):
        if v > date.today():
            raise ValueError('생년월일은 현재 날짜보다 이후일 수 없습니다')
        return v

상품 정보 검증

from decimal import Decimal
from typing import List, Optional

class ProductCreate(BaseModel):
    name: str = Field(
        ...,
        min_length=2,
        max_length=100,
        description="상품명"
    )
    price: Decimal = Field(
        ...,
        ge=Decimal('0'),
        decimal_places=2,
        description="상품 가격 (최대 소수점 2자리)"
    )
    description: Optional[str] = Field(
        None,
        max_length=1000,
        description="상품 설명"
    )
    categories: List[str] = Field(
        default_factory=list,
        min_items=1,
        max_items=5,
        description="상품 카테고리 (1-5개)"
    )
    stock: int = Field(
        default=0,
        ge=0,
        description="재고 수량"
    )

    @validator('categories')
    def validate_categories(cls, v):
        if len(set(v)) != len(v):
            raise ValueError('중복된 카테고리가 있습니다')
        return v

에러 처리

from pydantic import ValidationError

try:
    user = User(
        name="J",  # 최소 길이 위반
        age=200,   # 최대값 위반
        email="invalid-email"  # 이메일 형식 위반
    )
except ValidationError as e:
    print(e.errors())

성능 최적화

Config 클래스 활용

class User(BaseModel):
    name: str = Field(...)
    age: int = Field(...)

    class Config:
        validate_assignment = True  # 할당 시에도 검증
        frozen = True  # 불변 객체로 만들기
        validate_all = True  # 모든 필드 검증

캐시 활용

from functools import lru_cache

@lru_cache
def get_user_model():
    return User

마치며

Pydantic의 Field는 데이터 유효성 검사를 매우 직관적이고 강력하게 구현할 수 있게 해줍니다. FastAPI와 함께 사용하면 API의 입력 데이터를 안전하게 처리할 수 있습니다.