5 minute read

개요

FastAPI에서 API는 Client의 Request를 처리한 뒤 HTTP Response를 반환합니다. 이번에는 FastAPI에서 Response의 구조를 정의하는 방법과 Status Code, Error Handling 방법을 정리하였습니다.


1. HTTP Response

HTTP Response는 Server가 Client의 Request를 처리한 뒤 반환하는 HTTP 메시지입니다. HTTP Response는 크게 다음과 같이 구성됩니다.

HTTP Response
├─ Status Code
├─ Headers
└─ Body
  • Status Code: 요청 처리 결과를 나타낸다.
  • Headers: Response에 대한 부가 정보를 전달한다.
  • Response Body: Client에게 실제로 전달할 데이터를 담는다.

그러므로 Response와 Response Body는 같은 의미가 아니라는 것을 잘 기억해야 합니다.


2. Response Model

‘Response Model`은 API가 Client에게 반환할 데이터의 구조와 타입을 정의하는 모델입니다.

Request Model이 Client가 Server로 전달해야 할 데이터의 구조를 정의한다면, Response Model은 반대로 Server가 Client에게 반환해야 할 데이터의 구조를 정의합니다.

FastAPI에서는 Pydnatic Model을 Response Model로 사용할 수 있습니다.

from pydantic import BaseModel


class Item(BaseModel):
    name: str
    price: float


@app.get("/items/{item_id}")
async def read_item(item_id: int) -> Item:
    return Item(name="Keyboard", price=50000)

위 코드에서 -> Item은 Path Operation Function의 Return Type이며, FastAPI는 이를 Response Model로 활용합니다.

Response Model은 다음과 같은 역할을 합니다.

  • 반환 데이터 Validation
  • 반환 데이터 Serialization
  • 출력 데이터 Filtering
  • OpenAPI Schema 및 API 문서 생성

Response Validation

Request Valiation이 Client가 보낸 데이터를 검증하는 과정이라면, Response Validation은 Server가 반환하려는 데이터가 API가 정의한 구조에 맞는지 검증하는 과정입니다.

Response Model과 맞지 않는 데이터가 반환된다면 이는 Client의 문제가 아니라 Server 구현의 문제로 볼 수 있습니다.


3. Serialization

Serialization은 프로그램 내부의 객체를 전송하거나 저장할 수 있는 데이터 형태로 변환하는 과정입니다. FastAPI에서는 Python 객체를 JSON Response로 변환하는 과정에서 Serialization이 사용됩니다.

Python Object
      ↓
Serialization
      ↓
JSON
      ↓
HTTP Response Body

반대로 JSON과 같은 외부 데이터를 Python 객체로 변환하는 과정은 일반적으로 Deserialization 또는 Parsing이라고 볼 수 있습니다.


4. Response Model을 이용한 출력 데이터 Filtering

Server 내부에서 사용하는 데이터와 Client에게 공개해야 할 데이터가 항상 같은 것은 아닙니다. 예를 들어 Server 내부 데이터에 다음 정보가 있다고 하겠습니다.

username
email
password

Client에게는 password를 제외한 데이터만 반환해야 한다면 별도의 Response Model을 정의할 수 있습니다.

class UserOut(BaseModel):
    username: str
    email: str

FastAPI는 Response Model에 정의된 Filed를 기준으로 Response 데이터를 생성하므로, Client에게 노출하지 않아야 할 데이터를 제외할 수 있습니다. 따라서 Response Model은 단순한 타입 정보가 아니라 API가 외부에 공개할 데이터의 범위를 제한하는 역할도 할 수 있습니다.


5. Return Type과 response_model

FastAPI에서는 Response Model을 주로 두 가지 방식으로 지정할 수 있습니다.

Return Type

Python의 Return Type을 이용해 Reponse Model을 정의할 수 있습니다.

async def read_item() -> Item:

response_model

Path Operation Decorator의 response_model을 이용할 수도 있습니다. 일반적인 경우에는 Return Type을 사용할 수 있으며, 함수가 실제로 반환하는 객체와 Client에게 공개할 Response 구조를 다르게 지정해야 하는 경우에는 response_model을 사용할 수 있습니다.

@app.get("/items/", response_model=Item)

6. HTTP Status Code

HTTP Status Code는 Server가 Client의 Request를 어떻게 처리했는지를 나타내는 세 자리 숫자입니다.

Status code는 첫 번째 숫자를 기준으로 크게 다음과 같이 분류됩니다.

범위 의미
1xx 정보성 응답
2xx 성공
3xx Redirection
4xx Client Error
5xx Server Error

Backend API에서 자주 사용하는 Status Code는 다음과 같습니다.

Status Code 의미
200 OK 요청을 정상적으로 처리
201 Created 새로운 Resource 생성 성공
204 No Content 요청은 성공했지만 Response Body 없음
400 Bad Request 잘못된 Request
401 Unauthorized 인증 필요 또는 인증 실패
403 Forbidden 접근 권한 없음
404 Not Found 요청한 Resource가 존재하지 않음
422 Request 데이터 Validation 실패
500 Internal Server Error Server 내부 오류

Status Code는 Response Body를 확인하지 않아도 요청 처리 결과의 의미를 Client에게 전달하는 역할을 합니다.

FastAPI에서는 다음과 같이 Response Status Code를 선언할 수 있습니다.

from fastapi import status


@app.post(
    "/items/",
    status_code=status.HTTP_201_CREATED,
)
async def create_item():
    ...

숫자를 직접 사용할 수도 있지만 status.HTTP_201_CREATED와 같은 상수를 사용하면 코드의 의미가 더 명확해집니다.


7. Error Hnadling

Error Handling은 API 요쳥을 정상적으로 처리할 수 없는 상황을 적절한 Status Code와 Response로 Client에게 전달하는 과정입니다. 예를 들어 Client가 존재하지 않는 Resource를 요청했다면 정상적인 200 OK대신 404 Not Found를 반환하는 것이 적절합니다.

FastAPI에서는 HTTPException을 이용하여 HTTP Error Response를 발생시킬 수 있습니다.

from fastapi import HTTPException


if item_id not in items:
    raise HTTPException(
        status_code=404,
        detail="Item not found",
    )

HTTPException으로 raise 하는 이유

return은 함수의 정상적인 실행 결과를 반환하고 종료합니다. 반면 raise는 예외를 발생시키고 현재의 정상적인 실행 흐름을 즉시 중단합니다. 따라서 HTTPException은 return하는 것이 아니라 raise하여 사용합니다.


8. Validation Error와 HTTPException

FastAPI에서는 Request 데이터가 정의된 타입이나 Validatino 조건을 만족하지 못하면 자동으로 Validation Error를 처리합니다. 예를 들어 item_id: int인데 문자열이 전달되면 개발자가 직접 오류 처리를 작성하지 않아도 FastAPI가 Error Response를 생성합니다.

반면 다음과 같은 상황은 데이터 타입 자체에는 문제가 없습니다.

GET /items/100

100은 정상적인 정수이지만 실제 item_id=100인 데이터가 존재하지 않을 수 있습니다. 이 경우에는 Application의 상태와 Business Logic을 확인해야 하므로 개발자가 직접 HTTPException을 발생시킬 수 있습니다.


9. 실습

Response Model, Status Code, Error Handling을 하나의 간단한 API를 통해 확인해 보도록 하겠습니다.

from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


class ItemCreate(BaseModel):
    name: str
    price: float
    secret: str


items = {}


@app.post(
    "/items/{item_id}",
    response_model=Item,
    status_code=status.HTTP_201_CREATED,
)
async def create_item(item_id: int, item: ItemCreate):
    items[item_id] = item
    return item


@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int):
    if item_id not in items:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Item not found",
        )

    return items[item_id]

이 실습에서는 다음 세가지를 확인합니다. 확인에는 http://127.0.0.1:8000/docs를 이용해 확인합니다. 아래 이미지와 같이 작성합니다.


  1. POST 요청에 secret Field를 함께 전달해도 Response Model에 포함되지 않았기 때문에 Response에서는 제외되는지 확인한다.
  2. Item 생성에 성공했을 때 201 Created가 반환되는지 확인한다.


    위 이미지를 보면 Response Code로 201을 받은 것과 Response Body에는 secret 정보가 없이 온 것을 확인할 수 있습니다.

  3. 존재하지 않는 Item을 조회했을 때 404 Not Found와 Error Response가 반환되는지 확인한다.



    위 두 이미지를 보면 item_id의 값을 존재하지 않는 2로 get요청을 보내면 404 Not Found와 Error Response가 반환되는지 확인할 수 있습니다.


10. 정리

이번 학습에서 기억해야 할 핵심은 다음과 같이 정리 하였습니다.

  • Response는 Status Code, Headers, Body 등을 포함하는 HTTP 응답 전체이다.
  • Response Model은 Client에게 반환할 데이터의 구조를 정의하며 Validation, Serialization, Filtering 등에 사용된다.
  • Serialization은 Python 객체를 JSON과 같이 전송 가능한 형태로 변환하는 과정이다.
  • Status Code는 Request 처리 결과의 의미를 Client에게 전달한다.
  • 4xx는 주로 Client Request의 문제, 5xx는 Server 처리 과정의 문제를 의미한다.
  • HTTPException은 HTTP Error Response를 발생시키기 위해 raise하여 사용한다.
  • Request Validation Error는 FastAPI가 자동으로 처리할 수 있으며, Application의 상태에 따른 Error는 개발자가 HTTPException 등을 이용해 처리할 수 있다.

참조

  • FastAPI 공식 문서 - Response Model - Return Type
    https://fastapi.tiangolo.com/tutorial/response-model/

  • FastAPI 공식 문서 - Response Status Code
    https://fastapi.tiangolo.com/tutorial/response-status-code/

  • FastAPI 공식 문서 - Handling Errors
    https://fastapi.tiangolo.com/tutorial/handling-errors/

  • RFC 9110 - HTTP Semantics
    https://www.rfc-editor.org/rfc/rfc9110.html

  • MDN Web Docs - HTTP Messages
    https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Messages

Comments