경로와 쿼리 파라미터만으로는 구조화된 데이터를 보내기 어렵습니다. 요청 본문(body)은 JSON 같은 형식으로 여러 필드를 한 번에 전달하는 공간입니다. Pydantic은 Python 타입 힌트를 활용해 데이터를 검증해 주는 라이브러리입니다. 3편에서 URL 설계를 정리했다면, 이제는 JSON 본문을 받아 Pydantic 모델로 검증하는 기본 패턴을 배울 차례입니다.
이번 글에서 새로 나오는 용어
- 요청 본문:
POST·PUT요청이 실어 나르는 JSON 덩어리로, 여러 필드를 한 번에 전달하기 위해 이번 글에서 집중해서 다룹니다. - Pydantic: 타입 힌트만 적어도 자동으로 파싱·검증을 도와주는 라이브러리로, FastAPI가 요청 본문을 읽을 때 기본 엔진으로 사용합니다.
- BaseModel: Pydantic 모델의 부모 클래스로, 상속만 하면 필드 정의와 검증 로직을 한 번에 확보할 수 있습니다.
실습 카드
- 예상 소요 시간: 40분
- 사전 준비: 3편 예제 코드, Python 3.12,
uv run fastapi dev사용 경험- 실습 목표: Pydantic 모델을 요청 본문과 연결해 검증 흐름을 익힌다
이 글에서 할 것
- Pydantic
BaseModel로 요청 스키마 정의하기 - JSON 본문을 받아 자동 변환 확인하기
- 예외 상황에서 반환되는 422 메시지 살펴보기
Pydantic 모델 만들기
FastAPI는 Pydantic 모델을 기본으로 사용합니다. 모델을 만들고 엔드포인트 인자로 넣으면 자동으로 파싱과 검증이 동작합니다.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
in_stock: bool = True
tags: list[str] = []
POST 요청으로 본문 받기
Item 모델을 인자로 추가하면 FastAPI가 요청 본문을 읽고 Item 인스턴스로 만들어 줍니다.
@app.post("/items")
def create_item(item: Item):
return {"message": "created", "data": item}
Swagger UI에서 POST /items를 클릭하면 자동으로 JSON 예시가 채워지고, 모델 필드 설명도 함께 표시됩니다.
실전 예제: 상담 예약 접수
학생 상담 앱에서 요청 본문을 이렇게 정의하면, 누가 언제 어떤 주제로 상담을 신청했는지 명확히 저장할 수 있습니다.
class CounselingRequest(BaseModel):
student_id: int
topic: str
preferred_slots: list[str]
@app.post("/counseling")
def create_request(payload: CounselingRequest):
return {"status": "queued", "student_id": payload.student_id}
프런트엔드가 다른 언어로 만들어져도, 모델 정의만으로 정확한 JSON 구조를 공유할 수 있습니다.
uv 기반으로 테스트하기
uv run fastapi dev main.py
터미널에서 실행한 뒤 curl로 테스트할 수도 있습니다.
curl -X POST http://127.0.0.1:8000/items \
-H "Content-Type: application/json" \
-d '{"name": "notebook", "price": 3.5, "tags": ["study"]}'
응답 본문에는 Item 모델 구조 그대로 JSON이 반환됩니다.
검증 실패 시 응답
필수 필드를 생략하거나 잘못된 타입을 보내면 FastAPI가 422 응답을 줍니다.
{
"detail": [
{
"type": "missing",
"loc": ["body", "item", "name"],
"msg": "Field required"
}
]
}
이 정보만으로도 어떤 필드가 빠졌는지 바로 확인할 수 있습니다. Swagger UI에서도 같은 메시지가 표시됩니다.
중첩 모델과 기본값
Pydantic 모델은 중첩 구조도 자연스럽게 지원합니다.
class Supplier(BaseModel):
name: str
email: str
class Item(BaseModel):
name: str
price: float
supplier: Supplier | None = None
요청 본문에서 supplier를 내려보내면 그대로 구조화된 객체로 받을 수 있고, 생략하면 None으로 처리됩니다.
실습
- 따라 하기:
Item모델을 만들고/itemsPOST 요청으로 JSON을 전송해 200 응답을 확인한다. - 확장하기:
Supplier중첩 모델을 추가하고 Swagger UI에서 예시 JSON을 수정해 본다. - 디버깅: 필수 필드를 빼거나 잘못된 타입을 보내 422 응답을 확인하고 메시지를 해석한다.
- 완료 기준: POST 요청이 성공/실패 케이스 모두 의도대로 작동하고, 스키마 차이를 설명할 수 있다.
마무리
요청 본문에 구조화된 데이터를 받으려면 Pydantic 모델로 스키마를 선언하고, FastAPI가 자동으로 파싱·검증을 수행하도록 맡기면 됩니다. 다음 글에서는 이 모델을 활용해 간단한 CRUD 흐름을 만들어 보고 상태를 메모리 리스트에 저장해 보겠습니다.
💬 댓글
이 글에 대한 의견을 남겨주세요