type: source status: seedling title: Pydantic 마스터하기: 데이터 검증 필수 사항 tags: [“python”, “pydantic”, “data-validation”, “fastapi”, “basemodel”] created: 2026-04-26 url: https://velog.io/@jk01019/pydantic harvested: 2026-04-26 site: Velog source_count: 1 source_type: article updated: 2026-04-26 valid_as_of: 2026-04-26
Pydantic: 데이터 검증 마스터하기
학습 목표 매핑
SKALA 3기 Module 1 — Python AI Native (Learning Objective 1-4)
- Objective: Pydantic을 이용하여 데이터 검증 모델을 정의하고, JSON 입력 데이터에 대해 타입·범위·필수 필드를 자동 검증 (Bloom L2-L3)
- Evaluation: 스키마 검증 테스트 (유효/무효 데이터 모두 처리)
Pydantic이란?
정의
Pydantic은 Python의 강력한 데이터 유효성 검사 및 파싱 라이브러리입니다.
핵심 역할:
- ✅ 입력 데이터의 유효성 자동 검사
- ✅ 타입 불일치 시 자동 변환
- ✅ 잘못된 데이터 방지 (런타임 에러 감소)
- ✅ FastAPI와 통합으로 API 요청 검증
특징
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
email: str자동 기능:
- 타입 검증: id가 정수가 아니면 오류
- 타입 변환: 문자열 “123” → 정수 123
- 필드 검증: 이메일 형식 검증 (추가 설정)
- 기본값 관리: 선택 필드 처리
선행 개념
이 개념을 배우기 전에 필수로 알아야 할 것:
- Module 1-1 → python-type-hints-fastapi: Python 타입 힌트 문법
- 왜?: Pydantic의
BaseModel클래스 정의 시 각 필드에 타입 힌트를 명시해야 함 - 예:
class User(BaseModel): id: int←int가 타입 힌트
- 왜?: Pydantic의
후속 개념 (이 개념이 선행)
이 개념을 배운 후 다음 단계:
- Module 4-1 → fastapi-docker-official-deployment: FastAPI에서 Pydantic 모델을 request/response 스키마로 사용
- 예:
@app.post("/users/")엔드포인트에서 Pydantic 모델로 자동 검증 및 Swagger 문서 생성
- 예:
BaseModel 기초
1. 기본 모델 정의
from pydantic import BaseModel
from datetime import datetime
class User(BaseModel):
id: int
name: str
email: str
is_active: bool = True # 기본값 설정
signup_ts: datetime | None = None # Optional2. 데이터 검증 (자동)
# ✅ 유효한 데이터
user = User(
id=1,
name="John",
email="john@example.com"
)
print(user)
# User(id=1, name='John', email='john@example.com', is_active=True, signup_ts=None)
# ❌ 타입 불일치 → 자동 오류
try:
user = User(
id="invalid", # 문자열이 아니고 정수 기대
name="John",
email="john@example.com"
)
except ValidationError as e:
print(e)
# value_error.integer - id는 정수여야 함3. 자동 타입 변환
# "123" → 123 (자동 변환)
user = User(
id="123", # 문자열 입력
name="John",
email="john@example.com"
)
print(user.id) # 123 (정수)
print(type(user.id)) # <class 'int'>Field로 상세 검증
1. 기본 제약
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str = Field(
..., # 필수 필드
min_length=3, # 최소 3자
max_length=100, # 최대 100자
description="상품명"
)
price: float = Field(
...,
gt=0, # Greater Than 0
description="가격 (0보다 커야 함)"
)
quantity: int = Field(
default=0, # 기본값
ge=0, # Greater or Equal 0
le=1000, # Less or Equal 1000
description="재고 수량"
)
description: str = Field(
default=None, # Optional
description="상품 설명"
)2. 검증 예시
# ✅ 유효한 데이터
product = Product(
name="Laptop",
price=999.99,
quantity=10
)
# ❌ 검증 실패 - 가격이 0
try:
product = Product(name="TV", price=0, quantity=5)
except ValidationError as e:
print(e)
# value_error - price는 0보다 커야 함
# ❌ 검증 실패 - 이름이 너무 짧음
try:
product = Product(name="A", price=100, quantity=5)
except ValidationError as e:
print(e)
# string_too_short - name은 최소 3자 이상Validator로 커스텀 검증
필드 검증
from pydantic import BaseModel, validator
class User(BaseModel):
name: str
age: int
email: str
@validator('age')
def check_age(cls, v):
if v < 0 or v > 150:
raise ValueError('Age must be between 0 and 150')
return v
@validator('email')
def check_email(cls, v):
if '@' not in v:
raise ValueError('Invalid email format')
return v모델 검증
from pydantic import validator, root_validator
class Product(BaseModel):
name: str
price: float
discount_price: float | None = None
@root_validator
def check_discount(cls, values):
price = values.get('price')
discount = values.get('discount_price')
if discount and discount >= price:
raise ValueError('Discount price must be less than regular price')
return values중첩 구조 (Nested Models)
1. 중첩 모델 정의
class Address(BaseModel):
street: str
city: str
postal_code: str
class User(BaseModel):
name: str
age: int
address: Address # 중첩 모델2. 중첩 데이터 자동 변환
# 중첩된 딕셔너리를 자동으로 Address 모델로 변환
user = User(
name="John",
age=30,
address={
"street": "123 Main St",
"city": "New York",
"postal_code": "10001"
}
)
print(user.address.city) # "New York"
print(type(user.address)) # <class '__main__.Address'>3. 복잡한 구조
class Tag(BaseModel):
name: str
class Post(BaseModel):
title: str
content: str
author: User # User 모델
tags: list[Tag] # Tag 리스트
comments: list[str] = [] # 기본값 빈 리스트데이터 내보내기 & 변환
1. JSON 변환
user = User(name="John", age=30)
# JSON 문자열로 변환
user_json = user.json()
print(user_json)
# '{"name": "John", "age": 30}'
# 원본 데이터로부터 파싱
user_from_json = User.parse_raw('{"name": "Alice", "age": 25}')2. 딕셔너리 변환
# 딕셔너리로 변환
user_dict = user.dict()
print(user_dict)
# {'name': 'John', 'age': 30}
# None 값 제외
user_partial = user.dict(exclude_none=True)
# 특정 필드만 포함
user_subset = user.dict(include={'name'})
# {'name': 'John'}
# 특정 필드 제외
user_exclude = user.dict(exclude={'age'})
# {'name': 'John'}FastAPI와의 연동
1. 요청 검증
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
description: str | None = None
@app.post("/items/")
async def create_item(item: Item):
# item은 자동으로 검증됨
return item자동 기능:
- ✅ JSON 요청 → Item 모델로 자동 변환
- ✅ 타입 검증 (price가 숫자 아니면 오류)
- ✅ OpenAPI 문서 자동 생성 (Swagger UI)
- ✅ 유효하지 않은 요청 시 상세 오류 메시지
2. 응답 검증
@app.get("/items/{item_id}", response_model=Item)
async def get_item(item_id: int):
# 반환값이 Item 모델 검증됨
return {"name": "Laptop", "price": 999.99}주요 특징 & 메서드
| 기능 | 메서드 | 설명 |
|---|---|---|
| JSON 변환 | .json() | 모델 → JSON 문자열 |
| JSON 파싱 | .parse_raw() | JSON 문자열 → 모델 |
| 딕셔너리 변환 | .dict() | 모델 → 딕셔너리 |
| 딕셔너리 파싱 | Model(**dict) | 딕셔너리 → 모델 |
| 복사 | .copy() | 모델 얕은 복사 |
| 검증 무시 | construct() | 검증 스킵 (위험) |
에러 처리
ValidationError
from pydantic import ValidationError, BaseModel
class User(BaseModel):
id: int
name: str
age: int
try:
user = User(id="invalid", name="John", age="thirty")
except ValidationError as e:
print(e.json())
# [
# {"loc": ["id"], "msg": "value is not a valid integer", "type": "type_error.integer"},
# {"loc": ["age"], "msg": "value is not a valid integer", "type": "type_error.integer"}
# ]설정 커스터마이징
모델 설정
from pydantic import BaseModel, Extra
class User(BaseModel):
name: str
age: int
class Config:
# 추가 필드 거부
extra = Extra.forbid # {"name": "John", "age": 30, "email": "..."} → 오류
# 문자열 자동 trim
anystr_strip_whitespace = True
# JSON 인코딩
json_encoders = {
datetime: lambda v: v.isoformat()
}학습 설계 포인트
Cognitive Level (Bloom)
- L2 (Understand): BaseModel 정의, 기본 검증 이해
- L3 (Apply): Field, Validator를 이용한 커스텀 검증
권장 실습
- 기초: BaseModel 정의 + 자동 타입 검증
- 심화: Field 제약 조건 추가 (min_length, gt 등)
- 커스텀: Validator로 비즈니스 로직 검증
- FastAPI: 요청 모델로 검증 자동화
참고 자료
- Pydantic 공식: https://pydantic-docs.helpmanual.io/
- FastAPI 데이터 검증: https://fastapi.tiangolo.com/tutorial/body/
- Pydantic GitHub: https://github.com/pydantic/pydantic
타 소스와의 연계
python-type-hints-fastapi (타입 힌트 기초) python-venv-poetry-conda-leapcell (Pydantic 라이브러리 설치)