Response를 직접 돌려주기

Response를 직접 돌려주기 (Return a Response Directly)

FastAPI _path operation_을 만들면 보통 어떤 데이터든 돌려줄 수 있어요. dict, list, Pydantic 모델, 데이터베이스 모델 등이죠.

Response Model을 선언하면 FastAPI가 Pydantic으로 그 데이터를 JSON으로 직렬화해요.

response model을 선언하지 않으면 FastAPI는 JSON Compatible Encoder에 설명된 jsonable_encoder를 사용해 JSONResponse에 넣습니다.

JSONResponse를 직접 만들어 돌려줄 수도 있어요.

팁: 보통은 JSONResponse를 직접 돌려주는 것보다 Response Model을 쓰는 게 성능이 훨씬 좋아요. 그 경우 데이터를 Pydantic으로, Rust 쪽에서 직렬화하거든요.

출처: 공식문서

Response 돌려주기

Response나 그 하위 클래스를 돌려줄 수 있어요.

참고: JSONResponse 자체도 Response의 하위 클래스예요.

그리고 Response를 돌려주면 FastAPI는 그걸 그대로 전달합니다.

Pydantic 모델로 데이터 변환을 하지 않고, 내용을 어떤 타입으로 바꾸지도 않아요.

그래서 유연성이 아주 크죠. 어떤 데이터 타입이든 돌려줄 수 있고, 어떤 데이터 선언·검증도 재정의할 수 있어요.

하지만 책임도 커집니다. 돌려주는 데이터가 올바르고, 올바른 형식이며, 직렬화 가능한지 직접 확인해야 해요.

Response에서 jsonable_encoder 사용하기

FastAPI는 돌려주는 Response를 전혀 바꾸지 않기 때문에, 그 내용이 그대로 쓸 준비가 되어 있어야 해요.

예를 들어, 모든 데이터 타입(datetime, UUID 등)을 JSON 호환 타입으로 변환한 dict로 먼저 바꾸지 않으면 Pydantic 모델을 JSONResponse에 넣을 수 없어요.

그럴 때는 jsonable_encoder를 사용해 데이터를 변환한 뒤 response에 넘기면 됩니다:

from datetime import datetime

from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from pydantic import BaseModel


class Item(BaseModel):
    title: str
    timestamp: datetime
    description: str | None = None


app = FastAPI()


@app.put("/items/{id}")
def update_item(id: str, item: Item):
    json_compatible_item_data = jsonable_encoder(item)
    return JSONResponse(content=json_compatible_item_data)

기술적 세부사항: from starlette.responses import JSONResponse를 쓸 수도 있어요. FastAPI는 개발자인 여러분의 편의를 위해 starlette.responses와 같은 것을 fastapi.responses로 제공하지만, 사용 가능한 response 대부분은 Starlette에서 직접 옵니다.

커스텀 Response 돌려주기

위 예시는 필요한 모든 부분을 보여주지만 아직 쓸모는 없어요. 그냥 item을 직접 돌려줘도 FastAPI가 알아서 JSONResponse에 넣고 dict로 변환해 주니까요. 전부 기본 동작이에요.

이제 그걸 사용해 커스텀 response를 돌려주는 방법을 볼게요.

XML response를 돌려주고 싶다고 해 봐요.

XML 내용을 문자열에 넣고, 그걸 Response에 담아 돌려주면 됩니다:

from fastapi import FastAPI, Response

app = FastAPI()


@app.get("/legacy/")
def get_legacy_data():
    data = """<?xml version="1.0"?>
    <shampoo>
    <Header>
        Apply shampoo here.
    </Header>
    <Body>
        You'll have to use soap here.
    </Body>
    </shampoo>
    """
    return Response(content=data, media_type="application/xml")

Response Model이 작동하는 방식

_path operation_에 Response Model - Return Type을 선언하면 FastAPI는 Pydantic으로 그 데이터를 JSON으로 직렬화해요.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]

이 작업이 Rust 쪽에서 일어나기 때문에, 일반 파이썬과 JSONResponse 클래스로 처리하는 것보다 성능이 훨씬 좋아요.

response_model이나 return type을 쓰면 FastAPI는 데이터 변환에 jsonable_encoder(더 느리죠)를 쓰지 않고 JSONResponse 클래스도 쓰지 않아요.

대신 response model(또는 return type)로 Pydantic이 생성한 JSON 바이트를 그대로 가져와서, JSON에 맞는 미디어 타입(application/json)의 Response를 직접 돌려줍니다.

주의사항 (Notes)

Response를 직접 돌려주면 그 데이터는 자동으로 검증·변환(직렬화)·문서화되지 않아요.

하지만 OpenAPI의 추가 응답에 설명된 대로 문서화할 수는 있습니다.

이후 섹션들에서 커스텀 Response를 사용하면서도 자동 데이터 변환·문서화 등을 유지하는 방법을 볼 수 있어요.

더 알아보기 (Learn more)