C.W.K.
Stream
Lesson 05 of 05 · published

Pydantic 과 런타임 검증 — 타입이 진짜가 될 때

~18 min · pydantic, validation, fastapi, runtime

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

Pydantic — 진짜 체크하는 타입

Python 빌트인 타입 힌트는 정적 — 타입 체커가 보고, 인터프리터가 무시. Pydantic 이 같은 힌트 읽고 런타임에 검증 — 잘못된 데이터로 Pydantic 모델 인스턴스화하면 유용한 메시지 가진 ValidationError raise. FastAPI 가 요청/응답 검증에 사용, config 라이브러리, "parse, don't validate" 중요한 모든 곳.

BaseModel — 기반

BaseModel 서브클래스, 타입 힌트로 필드 선언. kwargs 로 인스턴스화하면 모든 거 검증 — 타입 강제 (필드가 int 면 문자열 "42" 가 int 42 됨), 누락 필수 필드 raise, 추가 필드 무시 또는 거부 (config 따라). 모델 인스턴스가 속성 접근 가진 일반 Python 객체.

디폴트 값과 Field()

직접 디폴트가 Python 처럼 작동. Field() 가 메타데이터 추가 — Field(default=10, ge=0, le=100) 가 필드 0 과 100 사이 검증. 문자열엔 Field(min_length=1, max_length=50). mutable 디폴트엔 Field(default_factory=list) — Python mutable 디폴트와 같은 함정, 같은 해결.

Pydantic v2 vs v1

Pydantic v2 (2023 릴리스) 가 메이저 재작성, ~10x 빠름, v1 에서 breaking 변경. 야생 대부분 코드가 이제 v2. 모양 비슷 — 같은 BaseModel, 비슷한 필드 — 근데 v1 은 .dict(), v2 는 .model_dump(). 항상 어느 버전인지 체크, 섞으면 고통.

Pippa 가 어디 사용

Pippa 의 FastAPI 라우트가 요청/응답 모양에 Pydantic 모델. 모든 chat 메시지, council pick, 설정 업데이트 — Pydantic 모델. 검증이 API 경계에서, 애플리케이션 안에선 파싱 쓰레기 없는 멋진 타입 객체.

Code

Pydantic 기본·python
# pip install pydantic
from pydantic import BaseModel, Field

class User(BaseModel):
    name: str
    age: int = Field(ge=0, le=150)
    email: str | None = None
    tags: list[str] = Field(default_factory=list)

# dict 에서 생성
u = User(name="alice", age=30)
print(u)                              # name='alice' age=30 email=None tags=[]
print(u.name, u.age)

# 생성 시 검증
try:
    bad = User(name="alice", age=200)        # age 범위 밖
except Exception as e:
    print("ValidationError:", str(e)[:200])

# 타입 강제 — '30' 이 30 됨
u2 = User(name="bob", age="30")
print(u2.age, type(u2.age))           # 30 <class 'int'>
JSON round-trip·python
from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int
    tags: list[str] = []

u = User(name="alice", age=30, tags=["admin", "user"])

# Pydantic v2
print(u.model_dump())                 # dict
print(u.model_dump_json())            # JSON 문자열

# Round-trip
from_json = User.model_validate_json(u.model_dump_json())
print(from_json == u)                 # True

# Pydantic v1 은 .dict() 와 .json() — 다른 메서드 이름
검증 메시지 — 진짜 유용·python
from pydantic import BaseModel, ValidationError, Field

class Order(BaseModel):
    item: str = Field(min_length=1)
    quantity: int = Field(gt=0)
    price: float = Field(ge=0.0)

try:
    Order(item="", quantity=-1, price=-5)
except ValidationError as e:
    for err in e.errors():
        print(err["loc"], err["msg"])

# 출력:
# ('item',)        String should have at least 1 character
# ('quantity',)    Input should be greater than 0
# ('price',)       Input should be greater than or equal to 0
중첩 모델 — 합성·python
from pydantic import BaseModel

class Address(BaseModel):
    street: str
    city: str
    zip: str

class User(BaseModel):
    name: str
    addresses: list[Address] = []

u = User(
    name="alice",
    addresses=[
        {"street": "123 Main", "city": "Seoul", "zip": "01234"},
        {"street": "456 Oak", "city": "Busan", "zip": "56789"},
    ],
)
# 중첩 dict 자동 검증 + Address 인스턴스로 변환
print(u.addresses[0].city)            # 'Seoul'
print(type(u.addresses[0]))           # <class '...Address'>
FastAPI 라우트 — Pippa 가 실제로 하는 거·python
# Pippa 의 chat 라우트가 Pydantic 어떻게 쓰는지 sketch
from pydantic import BaseModel

class ChatMessage(BaseModel):
    role: str  # 'user' | 'assistant' (더 빡빡엔 Literal)
    content: str
    timestamp: float | None = None

class ChatRequest(BaseModel):
    conversation_id: str
    message: ChatMessage
    brain: str = "claude"
    extended_thinking: bool = False

class ChatResponse(BaseModel):
    conversation_id: str
    message_id: str
    content: str
    brain: str

# FastAPI 에서:
# @router.post('/api/chat', response_model=ChatResponse)
# async def chat(req: ChatRequest) -> ChatResponse:
#     ...
# FastAPI 가 요청 body 자동 검증, 응답 자동 직렬화.

External links

Exercise

Pydantic BaseModel Quest 정의 — name: str (min length 1), tracks: list[str] (디폴트 빈), difficulty: Literal['easy', 'medium', 'hard'], estimated_hours: float = Field(gt=0). 유효 Quest 생성. 무효 거 (예 — 빈 name, 음수 hours, 알 수 없는 difficulty) 만들고 ValidationError 잡아서 각 에러의 locmsg 출력. .model_dump_json().model_validate_json() 으로 round-trip.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.