모델 (Pydantic Models)
모델 (Pydantic Models)
클라이언트(예: 브라우저)에서 API로 데이터를 보내야 하는 상황을 생각해 볼까요. 그 데이터는 request body(요청 본문)로 보냅니다.
request body는 클라이언트가 API에 보내는 데이터이고, response body는 API가 클라이언트에게 돌려보내는 데이터예요. API는 거의 항상 response body를 보내야 하지만, 클라이언트가 항상 request body를 보내야 하는 건 아니에요. 가끔은 경로만 요청하고, 쿼리 파라미터 몇 개만 붙여서 본문 없이 요청하는 경우도 있습니다.
request body를 선언할 때는 Pydantic 모델의 모든 기능과 장점을 그대로 사용합니다.
참고: 데이터를 보낼 때는
POST(가장 흔함),PUT,DELETE,PATCH중 하나를 사용하세요.GET요청에 본문을 보내는 것은 스펙에서 동작이 정의되어 있지 않지만, FastAPI는 아주 복잡하거나 극단적인 경우에 한해 지원합니다. 권장되지 않기 때문에,GET에서 본문을 쓰면 Swagger UI의 인터랙티브 문서에 본문 문서가 표시되지 않고 중간의 프록시가 지원하지 않을 수도 있어요.
Pydantic의 BaseModel 가져오기
먼저 pydantic에서 BaseModel을 import 해야 합니다.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
데이터 모델 만들기
그다음 데이터 모델을 BaseModel을 상속받는 클래스로 선언합니다. 모든 속성에는 표준 파이썬 타입을 사용합니다.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
쿼리 파라미터를 선언할 때와 마찬가지로, 모델 속성에 기본값이 있으면 필수가 아니고, 없으면 필수예요. None을 쓰면 그냥 선택적(optional)으로 만들 수 있습니다.
예를 들어 위 모델은 이런 JSON object(즉 파이썬 dict)를 선언하는 셈입니다.
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
여기서 description과 tax는 선택적 속성(None 기본값)이라, 이렇게 본문에서 빠져도 됩니다.
{
"name": "Foo",
"price": 45.2
}
파라미터로 선언하기
path operation 에 추가하려면, 경로 파라미터나 쿼리 파라미터를 선언했던 것과 똑같은 방식으로 선언하면 됩니다.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
이렇게 item: Item처럼 선언해 두면 FastAPI가 처리해 줍니다.
동작 결과
- 요청 본문을 JSON으로 읽습니다.
- 필요하면 해당 타입으로 변환합니다.
- 데이터를 검증(validate)합니다.
- 데이터가 유효하지 않으면, 정확히 어디서 무엇이 잘못됐는지를 알려 주는 깔끔한 오류를 반환합니다.
- 받은 데이터를 파라미터
item에 넣어 줍니다.- 함수에서
Item타입으로 선언했기 때문에, 모든 속성과 그 타입에 대해 에디터 지원(자동 완성 등)도 그대로 받을 수 있어요.
- 함수에서
- 모델에 대한 JSON Schema 정의를 생성합니다. 프로젝트에 맞다면 이 스키마를 다른 곳에서도 자유롭게 사용할 수 있습니다.
자동 문서
모델의 JSON Schema는 생성된 OpenAPI 스키마에 포함되고, 인터랙티브 API 문서(Swagger UI)에도 표시됩니다. 그리고 이 스키마를 필요로 하는 각 path operation의 API 문서에서도 사용됩니다.
에디터 지원
Item 타입으로 선언했기 때문에, 속성과 타입에 대한 자동 완성, 오타 검사 같은 에디터 지원을 받을 수 있어요. 특히 item. 뒤에 .을 치면 name, description, price, tax가 모두 나오는 식이죠.
모델 사용하기
경로 작업 안에서 모델을 직접 사용할 수도 있습니다.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
item_dict = item.model_dump()
if item.tax is not None:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
이 예시에서는 item.model_dump()로 모델을 dict로 바꾼 뒤, tax가 있으면 price_with_tax 값을 계산해서 dict에 추가하고 반환합니다.
request body + 경로 파라미터
경로 파라미터와 request body를 동시에 선언할 수도 있습니다.
FastAPI는 경로 파라미터와 일치하는 함수 파라미터는 경로에서 가져오고, Pydantic 모델로 선언된 함수 파라미터는 request body에서 가져온다는 걸 알아서 처리합니다.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.model_dump()}
request body + 경로 + 쿼리 파라미터
body, path, query 파라미터를 모두 동시에 선언할 수도 있습니다. FastAPI가 각각을 구분해서 올바른 위치에서 데이터를 가져옵니다.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
result = {"item_id": item_id, **item.model_dump()}
if q:
result.update({"q": q})
return result
함수 파라미터는 다음과 같이 해석됩니다.
- 파라미터가 경로(path)에도 선언되어 있으면 → 경로 파라미터
- 파라미터가 단일 타입(
int,float,str,bool등)이면 → 쿼리 파라미터 - 파라미터가 Pydantic 모델 타입으로 선언되었으면 → request body
참고: FastAPI는
q의 기본값이= None이기 때문에 필수 값이 아니라는 걸 압니다.str | None타입 표기 자체로 필수 여부를 판단하지는 않아요. 다만 타입 어노테이션을 붙여 두면 에디터가 더 나은 지원을 해 주고 오류도 잡아낼 수 있습니다.
Pydantic 없이
Pydantic 모델을 쓰고 싶지 않다면 Body 파라미터를 사용할 수도 있습니다. 자세한 내용은 Body - Multiple Parameters 문서의 "Singular values in body" 부분을 참고하세요.