Skip to content

3.3장: Pydantic을 사용한 데이터 모델

학습 시간: 50분


1. Pydantic: 우주선의 "디지털 청사진"

우주선을 구축한다고 상상해 보세요. 무작정 금속 조각들을 용접할 수는 없습니다. 다음과 같은 상세한 청사진이 필요합니다:

  • 선박명 (타입: 문자열, 최대 길이: 50 문자)
  • 발사 연도 (타입: 정수)
  • 초광속 엔진 여부 (타입: 부울)

Pydantic은 Python에서 데이터에 대한 이러한 "디지털 청사진"을 생성할 수 있게 해주는 라이브러리입니다. FastAPI에서는 세 가지 주요 기능을 수행합니다:

  1. 구조 선언: 데이터가 어떤 필드로 구성되어 있는지 명확하게 설명합니다.
  2. 데이터 유효성 검사: 들어오는 데이터가 청사진에 일치하는지 자동으로 확인합니다.
  3. 문서화: FastAPI는 이 청사진들을 사용하여 상세하고 인터랙티브한 문서를 생성합니다.

💡 우주 비유: Pydantic 모델은 객체의 기술 사양서입니다. 스테이션에 도착하는 모든 "화물"(데이터)은 사양서의 사양과 일치해야 합니다. 그렇지 않으면 온보드 컴퓨터(Pydantic)가 이를 거부합니다.


2. 첫 번째 청사진 생성: Spaceship 모델

이제 우리 우주선을 설명할 모델을 만들어 봅시다.

1단계: Pydantic에서 BaseModel 임포트 Pydantic은 fastapi[all]과 함께 이미 설치되어 있습니다. 우리는 모델의 기본 클래스만 임포트하면 됩니다.

main.py의 맨 위에 다른 임포트 옆에 다음을 추가하세요:

# main.py
from fastapi import FastAPI
from pydantic import BaseModel

2단계: Spaceship 모델 설명 BaseModel로부터 상속받는 클래스를 생성합니다. 클래스 내부에 표준 Python 타입 힌트를 사용하여 필드와 그 타입을 정의합니다.

main.py에 다음 코드를 추가하세요 (임포트 후에 추가 가능):

class Spaceship(BaseModel):
    """
    우주선의 기술 사양서(모델).
    """
    name: str
    type: str
    launch_year: int
    status: str
이것으로 끝입니다! 방금 "청사진"을 만들었습니다. Pydantic은 이제 Spaceship 타입의 모든 객체는 지정된 타입의 네 가지 필드를 가져야 한다는 것을 알고 있습니다.


3. 모델 적용: 엔드포인트 개선

이제 새 모델을 사용하여 API를 "더 스마트하게" 만들어 봅시다.

A. 응답 모델로서의 모델 (Response Model) FastAPI에 우리의 엔드포인트가 Spaceship 모델과 일치하는 데이터를 반환해야 한다고 지정할 수 있습니다. 이는 응답이 항상 올바른 구조를 가질 것이라는 점을 보장합니다.

/spaceships/{ship_id} 엔드포인트를 다음과 같이 변경하세요:

# main.py

# ... db_spaceships 및 Spaceship 모델 코드 ...

# `response_model`을 사용하여 응답의 "청사진"을 지정합니다.
@app.get("/spaceships/{ship_id}", response_model=Spaceship)
def get_spaceship(ship_id: int):
    """
    Spaceship 모델과 일치하는 선박 데이터를 반환합니다.
    """
    ship = db_spaceships.get(ship_id)
    return ship
- response_model=Spaceship: 우리는 FastAPI에 말합니다: "이 함수의 응답은 Spaceship 구조와 일치해야 합니다. 모든 불필요한 필드를 필터링하고 타입이 올바른지 확인해."

이것이 제공하는 것은 무엇입니까?

  • 데이터 필터링: 만약 db_spaceships에 불필요한 필드(예: "secret_code")가 있었다면, 최종 JSON에 포함되지 않았을 것입니다.
  • 구조 보장: API 클라이언트는 항상 예상된 형식으로 응답을 받을 것이라는 점을 확신할 수 있습니다.
  • 문서화: /docs에서는 이제 정확한 응답 예시(Example Value)가 표시됩니다.

B. 컬렉션을 위한 모델 선박 목록을 반환하는 /spaceships 엔드포인트는 어떻게 될까요? 이를 위해 typing 모듈에서 list를 사용해야 합니다.

임포트와 /spaceships 엔드포인트를 변경하세요:

# main.py 상단
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List  # <-- List 임포트

# ... 코드 ...

# 응답이 Spaceship 타입 객체의 목록(List)임을 지정합니다.
@app.get("/spaceships", response_model=List[Spaceship])
def get_spaceships():
    """
    선박 목록을 반환합니다. 목록의 각 요소는
    Spaceship 모델에 따라 검사됩니다.
    """
    # Pydantic은 키가 ID인 딕셔너리와 함께 작동할 수 없습니다.
    # 우리의 딕셔너리를 간단한 목록으로 변환합니다.
    return list(db_spaceships.values())

  • response_model=List[Spaceship]: 우리는 응답이 Spaceship 모델과 일치하는 객체로 구성된 목록임을 지정합니다.
  • return list(db_spaceships.values()): 중요한 변경 사항입니다! Pydantic은 키가 ID인 딕셔너리가 아닌, 반복 가능한 객체(목록)를 예상합니다. 우리는 "DB 시뮬레이터"의 값들을 목록으로 변환합니다.

4. 개선된 API 확인

uvicorn 서버가 --reload 옵션으로 실행 중인지 확인하세요.

  1. http://127.0.0.1:8000/spaceships 확인하세요: 이제 응답은 객체가 아닌 JSON 배열입니다. 이는 컬렉션에 대해 더 올바르고 표준적인 구조입니다.
    [
      { "name": "Voyager-1", "type": "Зонд", ... },
      { "name": "Hubble Space Telescope", ... }
    ]
    
  2. http://127.0.0.1:8000/spaceships/1 확인하세요: 응답은 변경되지 않았지만, 이제 모델에 보장된 일치를 보여줍니다.
  3. /docs 확인하세요: 페이지 하단의 "Schemas" 섹션에 Spaceship 모델이 나타났습니다. 그리고 엔드포인트의 응답 예시에는 이제 아름답고 구조화된 데이터 스키마가 표시됩니다.

5. 고급 유효성 검사: "온보드 컴퓨터" 작동 중

Pydantic은 단순히 타입을 확인하는 것보다 훨씬 더 많은 것을 할 수 있습니다.

Spaceship 모델에 유효성 검사를 추가해 봅시다:

from pydantic import BaseModel, Field

class Spaceship(BaseModel):
    name: str = Field(..., min_length=3, max_length=50, description="선박명")
    type: str
    launch_year: int = Field(..., gt=1950, description="발사 연도는 1950년 이후여야 합니다.")
    status: str

  • Field(...): 추가 유효성 검사 규칙을 추가하는 데 사용됩니다.
  • ... (Ellipsis): 필드가 필수임을 의미합니다.
  • min_length, max_length: 문자열에 대한 제한입니다.
  • gt: "Greater Than" (초과).

아직 새 선박을 생성하지는 않지만 (이것은 다음 장에서 다룰 것입니다), 이러한 규칙은 이미 문서에 반영될 것이고 우리가 POST 요청을 구현할 때 작동할 것입니다.


복습 퀴즈

1. FastAPI에서 Pydantic은 다음 용도로 사용됩니다...

2. 데이터 모델을 생성하려면 다음 클래스로부터 상속받아야 합니다...

3. `@app.get` 데코레이터의 `response_model` 매개변수는 다음을 위해 필요합니다...

4. 엔드포인트가 *목록* 객체 타입 `Item`을 반환한다고 어떻게 지정합니까?

5. Pydantic 모델에서 `Field(..., gt=0)`는 필드가...


🚀 장 요약:

API 데이터에 대한 "디지털 청사진"을 설계했습니다. 이제 단순히 작동하는 것을 넘어, 예측 가능하고 안정적으로 작동합니다.

  • 📝 Pydantic을 사용하여 Spaceship 모델을 생성했습니다.
  • 🛡️ 이제 API는 response_model을 사용하여 나가는 데이터를 검증하고 필터링합니다.
  • 📊 문서는 정확한 데이터 스키마를 보여주면서 훨씬 더 유익해졌습니다.

청사진이 준비되고 승인되었습니다! 다음 장에서는 데이터 읽기에서 데이터 생성으로 넘어갈 것입니다. 우리 함대를 위한 완전한 CRUD 작업을 구현할 것입니다.

📌 확인:

  • /docsSpaceship 모델 스키마가 나타났는지 확인하십시오.
  • /spaceships 엔드포인트가 이제 객체 ({...})가 아닌 JSON 배열 ([...])을 반환하는지 확인하십시오.
  • 모델 추가 후 코드에 구문 오류가 없는지 확인하십시오.

⚠️ 오류 발생 시:

  • NameError: name 'BaseModel' is not defined: BaseModelpydantic에서 임포트했는지 확인하십시오.
  • NameError: name 'List' is not defined: Listtyping에서 임포트했는지 확인하십시오.
  • /spaceships에 대한 응답이 비어 있음 ([]): return db_spaceshipsreturn list(db_spaceships.values())로 변경했는지 확인하십시오. ```