요청 본문 (Request Body)

요청 본문 (Request Body)

클라이언트(예: 브라우저)가 여러분의 API로 데이터를 보내야 할 때, 그 데이터는 **요청 본문(request body)**으로 보내져요. 요청 본문은 클라이언트가 여러분의 API에 보내는 데이터이고, **응답 본문(response body)**은 반대로 여러분의 API가 클라이언트에게 보내는 데이터예요.

여러분의 API는 거의 항상 응답 본문을 보내야 해요. 그런데 클라이언트는 매번 요청 본문을 보낼 필요는 없어요. 어떤 때는 경로만 요청하고, 쿼리 파라미터 몇 개만 붙여서 본문 없이 요청할 때도 있죠.

요청 본문을 선언할 때는 Pydantic 모델을 그 모든 기능과 이점과 함께 사용해요.

참고

데이터를 보내려면 POST(가장 흔한 방식), PUT, DELETE, PATCH 중 하나를 사용해야 해요.

GET 요청으로 본문을 보내는 것은 스펙상 정의되지 않은 동작이에요. 그래도 FastAPI는 아주 복잡하거나 극단적인 사례에서만 이를 지원해요.

Pydantic의 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

데이터 모델 만들기

그런 다음 여러분의 데이터 모델을 BaseModel을 상속하는 클래스로 선언해요. 모든 속성에는 표준 Python 타입을 사용해요.

Python 3.10+

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을 사용하면 단순히 선택 사항으로 만들 수 있어요.

예를 들어 위 모델은 JSON "object"(또는 Python dict)처럼 생긴 데이터를 선언해요.

{
    "name": "Foo",
    "description": "An optional description",
    "price": 45.2,
    "tax": 3.5
}

여기서 descriptiontaxNone을 기본값으로 가지니 선택 사항이에요.

파라미터로 선언하기

이 모델을 경로 동작(path operation) 에 추가하려면, 경로 파라미터와 쿼리 파라미터를 선언했던 것과 같은 방식으로 선언하면 돼요.

Python 3.10+

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는 item 파라미터를 요청 본문에서 가져온다고 이해해요.

결과

이런 방식으로 함수에 파라미터를 선언하면, FastAPI는 다음을 처리해요.

  • 요청의 본문을 JSON으로 읽어요.
  • 필요한 경우 해당 타입으로 변환해요.
  • 데이터를 검증해요.
  • 데이터가 유효하지 않으면, 어디서 무엇이 잘못됐는지 정확히 짚어 주는 친절하고 명확한 오류를 반환해요.
  • 받은 데이터를 item 파라미터로 전달해 줘요.
  • 함수에서 Item 타입으로 선언했으므로, 모든 속성과 그 타입에 대한 에디터 지원(자동 완성 등)도 그대로 받게 돼요.
  • 모델에 대한 JSON Schema 정의를 생성해요. 여러분의 프로젝트에 맞다면 이 정의를 다른 곳에서도 자유롭게 쓸 수 있어요.

모델 사용하기

함수 안에서 모델 객체의 속성에 바로 접근할 수 있어요.

Python 3.10+

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):
    results = {"item_id": item_id, "item": item}
    return results

요청 본문 + 경로 파라미터

경로 파라미터와 요청 본문을 동시에 선언할 수도 있어요.

FastAPI는 경로 파라미터와 일치하는 함수 파라미터는 경로에서 가져와야 하고, Pydantic 모델로 선언된 함수 파라미터는 요청 본문에서 가져와야 한다는 것을 인식해요.

Python 3.10+

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):
    results = {"item_id": item_id, "item": item}
    return results

요청 본문에는 item_id 경로 파라미터와 같은 이름을 갖는 값이 필요 없어요. 경로 파라미터는 경로에서 가져오니까요.

요청 본문 + 경로 + 쿼리 파라미터

본문, 경로, 쿼리 파라미터를 세 가지 모두 동시에 선언할 수도 있어요.

FastAPI는 각각의 파라미터를 인식해서 올바른 위치에서 데이터를 가져와요.

Python 3.10+

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):
    results = {"item_id": item_id, **item.model_dump()}
    if q:
        results.update({"q": q})
    return results

함수 파라미터는 다음과 같이 인식돼요.

  • 파라미터가 경로에도 선언되어 있다면, 경로 파라미터로 사용돼요.
  • 파라미터가 단일 타입(int, float, str, bool 등)이라면 쿼리 파라미터로 해석돼요.
  • 파라미터가 Pydantic 모델 타입으로 선언되어 있다면 요청 본문으로 해석돼요.

참고

FastAPI는 q의 값이 = None 기본값 때문에 필수가 아니라는 것을 알게 돼요.

str | None이 값이 필수가 아니라는 판단에 쓰이는 건 아니에요. 기본값이 = None이기 때문에 필수가 아니라는 걸 알게 되는 거죠.

하지만 타입 애너테이션을 붙여 두면 에디터가 더 나은 지원을 해 주고 오류를 감지할 수 있어요.

Pydantic 없이

Pydantic 모델을 사용하고 싶지 않다면, Body 파라미터를 사용할 수도 있어요. 자세한 내용은 Body - Multiple Parameters: Singular values in body 문서를 참고하세요.