파이썬/Fast API
Pydantic 상속과 타입 어노테이션 활용
코샵
2025. 1. 27. 10:41
반응형
소개
Pydantic은 Python의 타입 어노테이션을 활용하여 데이터 검증을 제공하는 강력한 라이브러리입니다. 이번 글에서는 Pydantic의 다양한 기능과 실전 활용법을 자세히 알아보겠습니다.
기본 모델 구조
BaseModel 상속과 기본 필드 정의
from typing import Annotated
from pydantic import BaseModel, Field
from datetime import datetime
from bson import ObjectId
class BaseItem(BaseModel):
"""
모든 모델의 기본이 되는 베이스 클래스입니다.
모든 모델에 공통적으로 필요한 필드를 정의합니다.
"""
id: Annotated[ObjectId, Field(description="기본 ID")]
created_at: datetime = Field(
default_factory=datetime.now,
description="생성 시간"
)
updated_at: datetime = Field(
default_factory=datetime.now,
description="수정 시간"
)
타입 어노테이션과 Field 활용
Field 파라미터의 상세 활용
class ProductItem(BaseItem):
"""
상품 정보를 나타내는 모델입니다.
기본 정보 외에 상품 특화 필드를 포함합니다.
"""
id: Annotated[ObjectId, Field(
description="상품 고유 식별자",
alias="product_id" # API 응답시 필드명 변경
)]
name: str = Field(
min_length=2,
max_length=50,
description="상품명 (2-50자)"
)
price: float = Field(
gt=0,
description="상품 가격 (0보다 큰 값)"
)
description: str | None = Field(
default=None,
max_length=1000,
description="상품 설명 (최대 1000자)"
)
category: str = Field(
regex="^[A-Z]{2,10}$",
description="상품 카테고리 (대문자 2-10자)"
)
모델 구성과 검증 로직
복잡한 데이터 구조 처리
class Address(BaseModel):
"""
주소 정보를 나타내는 내부 모델입니다.
"""
street: str = Field(..., description="도로명 주소")
city: str = Field(..., description="도시")
postal_code: str = Field(
regex="^\d{5}$",
description="우편번호 (5자리)"
)
is_default: bool = Field(
default=False,
description="기본 주소 여부"
)
class UserProfile(BaseModel):
"""
사용자 프로필을 나타내는 모델입니다.
주소와 같은 복잡한 중첩 데이터를 포함합니다.
"""
id: Annotated[ObjectId, Field(alias="user_id")]
username: str = Field(
min_length=3,
max_length=20,
regex="^[a-zA-Z0-9_-]+$"
)
email: str = Field(
regex="^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
)
addresses: list[Address] = Field(
default_factory=list,
max_items=5,
description="최대 5개의 주소 저장 가능"
)
@property
def default_address(self) -> Address | None:
"""기본 주소를 반환하는 프로퍼티"""
return next(
(addr for addr in self.addresses if addr.is_default),
None
)
검증과 변환 로직 추가
사용자 정의 검증기
from pydantic import validator
from decimal import Decimal
class OrderItem(BaseModel):
"""
주문 항목을 나타내는 모델입니다.
가격 계산과 검증 로직을 포함합니다.
"""
product_id: ObjectId
quantity: int = Field(gt=0)
unit_price: Decimal = Field(ge=0)
total_price: Decimal | None = None
@validator("quantity")
def validate_quantity(cls, v: int) -> int:
if v > 100:
raise ValueError("한 번에 최대 100개까지만 주문 가능합니다")
return v
@validator("total_price", always=True)
def calculate_total_price(
cls,
v: Decimal | None,
values: dict
) -> Decimal:
"""수량과 단가를 기반으로 총 가격을 계산합니다"""
if "quantity" not in values or "unit_price" not in values:
raise ValueError("수량과 단가가 필요합니다")
return values["quantity"] * values["unit_price"]
모델 설정과 메타데이터
Config 클래스 활용
class Product(BaseModel):
"""
상품 정보 모델입니다.
상세한 설정과 메타데이터를 포함합니다.
"""
id: ObjectId
name: str
price: Decimal
stock: int
class Config:
# MongoDB ObjectId 사용 허용
arbitrary_types_allowed = True
# JSON 변환 규칙 정의
json_encoders = {
ObjectId: str,
Decimal: str
}
# 스키마 예제 추가
schema_extra = {
"example": {
"id": "507f1f77bcf86cd799439011",
"name": "Premium Product",
"price": "29.99",
"stock": 100
}
}
# 필드 할당 시 검증 활성화
validate_assignment = True
실전 사용 예제
# 모델 사용 예시
try:
user = UserProfile(
username="john_doe",
email="john@example.com",
addresses=[
Address(
street="123 Main St",
city="Seoul",
postal_code="12345",
is_default=True
)
]
)
# 데이터 검증
print(user.dict()) # 딕셔너리로 변환
print(user.json()) # JSON으로 변환
print(user.default_address) # 기본 주소 접근
except ValueError as e:
print(f"유효성 검사 실패: {e}")
마치며
Pydantic의 강력한 타입 시스템과 검증 기능을 활용하면, 안전하고 유지보수하기 쉬운 데이터 모델을 구축할 수 있습니다. 상속과 검증 로직을 적절히 활용하여 재사용 가능한 컴포넌트를 만들어보세요.