FastAPI에서 Pydantic V2 마이그레이션 후 성능 개선
배경
운영 중인 FastAPI 기반 API 서버가 Pydantic 1.x를 사용하고 있었다. Pydantic V2가 출시된 지 2년이 지났고, Rust 기반 재작성으로 큰 성능 개선이 있다는 이야기를 들어 마이그레이션을 진행했다.
주요 Breaking Changes
1. Config 클래스 방식 변경
# Before (V1)
class User(BaseModel):
name: str
class Config:
orm_mode = True
# After (V2)
from pydantic import ConfigDict
class User(BaseModel):
model_config = ConfigDict(from_attributes=True)
name: str
2. validator 데코레이터 변경
# Before
from pydantic import validator
class Item(BaseModel):
price: int
@validator('price')
def check_price(cls, v):
if v < 0:
raise ValueError('must be positive')
return v
# After
from pydantic import field_validator
class Item(BaseModel):
price: int
@field_validator('price')
@classmethod
def check_price(cls, v):
if v < 0:
raise ValueError('must be positive')
return v
3. json() 메서드 deprecated
# Before
user.json()
# After
user.model_dump_json()
성능 측정
10,000개 객체를 JSON으로 직렬화하는 벤치마크를 돌렸다.
import time
from pydantic import BaseModel
class Product(BaseModel):
id: int
name: str
price: float
tags: list[str]
products = [Product(id=i, name=f"item-{i}", price=i*1.5, tags=["tag1", "tag2"]) for i in range(10000)]
start = time.time()
result = [p.model_dump_json() for p in products]
print(f"Elapsed: {time.time() - start:.3f}s")
- Pydantic V1: 0.847s
- Pydantic V2: 0.456s
약 46% 성능 향상을 확인했다.
마이그레이션 팁
pydantic.v1호환 레이어를 활용하면 점진적 마이그레이션이 가능하다mypy나pyright타입 체커를 돌려서 deprecated 사용을 미리 찾아내는 게 효율적이었다- 테스트 커버리지가 낮은 프로젝트라면 마이그레이션 전 테스트 작성을 권장한다
결론
마이그레이션 작업은 하루 정도 소요됐고, API 응답 시간이 평균 15% 개선됐다. JSON 직렬화가 많은 엔드포인트일수록 효과가 컸다. FastAPI 최신 버전과의 호환성도 좋아져서 만족스러운 작업이었다.