FastAPI + Pydantic V2로 타입 안전성 극대화하기
FastAPI와 Pydantic V2를 활용한 타입 안전한 REST API 개발 가이드입니다. 향상된 성능과 새로운 검증 패턴, 실전 최적화 팁까지 중급 개발자를 위한 실용적인 내용을 담았습니다.
Pydantic V2의 진화, 무엇이 달라졌나?¶
Pydantic V2는 단순한 업데이트가 아닙니다. Rust로 작성된 pydantic-core 덕분에 기존 대비 5~50배 빠른 성능을 자랑하죠. FastAPI와 함께 사용하면 런타임 타입 검증과 자동 문서화를 동시에 얻을 수 있어요. V2의 핵심 변화는 ConfigDict를 통한 설정 방식, Field의 강화된 유효성 검증, 그리고 더 직관적인 serialization입니다. 특히 model_validate(), model_dump() 같은 새로운 메서드명은 코드 가독성을 크게 향상시켰습니다.
기본 모델 정의부터 시작하기¶
실전에서 바로 쓸 수 있는 사용자 등록 API를 만들어볼게요. Pydantic V2의 ConfigDict와 Field를 활용하면 타입 안전성과 유효성 검증을 한 번에 해결할 수 있습니다.
python from pydantic import BaseModel, Field, EmailStr, ConfigDict from datetime import datetime from typing import Optional
class UserCreate(BaseModel): model_config = ConfigDict(str_strip_whitespace=True)
username: str = Field(min_length=3, max_length=20, pattern="^[a-zA-Z0-9_]+$")
email: EmailStr
age: int = Field(ge=18, le=120)
bio: Optional[str] = Field(None, max_length=500)
class UserResponse(BaseModel): model_config = ConfigDict(from_attributes=True)
id: int
username: str
email: EmailStr
created_at: datetime
여기서 핵심은 model_config입니다. V1의 class Config를 대체하며, str_strip_whitespace로 자동 공백 제거, from_attributes로 ORM 객체 변환을 쉽게 처리할 수 있어요.
FastAPI 엔드포인트와 통합하기¶
Pydantic 모델을 FastAPI에 연결하면 자동으로 OpenAPI 문서가 생성되고, 요청 검증이 이루어집니다. 에러 핸들링까지 깔끔하게 처리해볼게요.
python from fastapi import FastAPI, HTTPException, status from pydantic import ValidationError
app = FastAPI()
@app.post("/users/", response_model=UserResponse, status_code=status.HTTP_201_CREATED) async def create_user(user: UserCreate): # 데이터베이스 저장 로직 (여기서는 시뮬레이션) try: # user 객체는 이미 검증되어 안전합니다 new_user = { "id": 1, "username": user.username, "email": user.email, "created_at": datetime.now() } return UserResponse(**new_user) except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="사용자 생성 실패" )
@app.get("/users/{user_id}", response_model=UserResponse) async def get_user(user_id: int): # 실제로는 DB 조회 user_data = {"id": user_id, "username": "testuser", "email": "test@example.com", "created_at": datetime.now()} return user_data
response_model을 지정하면 응답 데이터도 자동 검증되며, 민감한 정보를 필터링할 수 있습니다. 타입 힌트만으로 완벽한 API 문서가 생성되는 것이 FastAPI의 마법이죠.
고급 유효성 검증 패턴¶
Pydantic V2는 field_validator와 model_validator로 커스텀 검증을 더 강력하게 지원합니다. 실무에서 자주 쓰는 패턴을 소개할게요.
python from pydantic import field_validator, model_validator from typing_extensions import Self
class PasswordChange(BaseModel): current_password: str new_password: str = Field(min_length=8) confirm_password: str
@field_validator('new_password')
@classmethod
def validate_password_strength(cls, v: str) -> str:
if not any(c.isupper() for c in v):
raise ValueError('대문자를 최소 1개 포함해야 합니다')
if not any(c.isdigit() for c in v):
raise ValueError('숫자를 최소 1개 포함해야 합니다')
return v
@model_validator(mode='after')
def validate_passwords_match(self) -> Self:
if self.new_password != self.confirm_password:
raise ValueError('새 비밀번호가 일치하지 않습니다')
if self.current_password == self.new_password:
raise ValueError('현재 비밀번호와 달라야 합니다')
return self
V2의 @field_validator는 @classmethod 데코레이터와 함께 사용하며, model_validator의 mode='after'는 모든 필드 검증 후 실행됩니다. 복잡한 비즈니스 로직도 선언적으로 표현할 수 있어요.
실전 팁: 성능과 보안 최적화¶
타입 안전성 극대화 체크리스트:
-
응답 모델 분리:
UserCreate,UserResponse,UserUpdate처럼 용도별로 모델을 나누면 보안과 명확성이 향상됩니다. -
model_dump()활용: JSON 직렬화 시exclude_unset=True로 None 값 제거,by_alias=True로 camelCase 변환이 가능해요. -
성능 모니터링: Pydantic V2는 빠르지만, 복잡한 중첩 모델은
model_config = ConfigDict(validate_assignment=False)로 할당 시 검증을 비활성화할 수 있습니다. -
에러 메시지 커스터마이징:
errors='wrap'모드로 사용자 친화적인 에러를 만들 수 있어요.
FastAPI와 Pydantic V2의 조합은 타입 안전성, 성능, 개발 생산성 모두를 만족시킵니다. 런타임 에러를 개발 단계에서 잡아내고, 자동 생성된 문서로 프론트엔드 팀과의 협업도 원활해지죠. 지금 바로 프로젝트에 적용해보세요!
이 글은 AI가 자동으로 작성했습니다.