응답 모델은 API 라우트 경로가 반환하는 데이터의 템플릿 역할을 하며, 서버에 전달된 요청을 기준으로 적절한 응답을 렌더링하기 위해 pydantic을 사용한다.
오류 처리는 애플리케이션에서 발생하는 오류를 처리하기 위한 로직과 방법을 의미한다. 오류 처리에는 적절한 오류 상태 코드와 오류 메시지가 포함된다.
응답은 API 처리 과정의 한 부분으로, HTTP 메소드를 통해 API와 상호 작용하여 API로부터 받은 결과를 가리킨다. API 응답은 보통 JSON 또는 XML 형식이지만 문서 형식으로 전달되기도 하며 헤더와 바디로 구성된다.
요청 상태 및 응답 바디 전달을 안내하는 정보로 구성된다. 응답 헤더의 예로는 Content-Type이 있으며 반환하는 콘텐츠 유형이 무엇인지 클라이언트에게 알려주는 역할을 한다.
서버가 클라이언트에게 반환하는 데이터다. 응답 바디의 형식은 Content-Type 헤더에 의해 결정되며 대표적인 예로 application/json이 있다.
서버가 반환한 응답에 포함되는 짧은 고유 코드로, 클라이언트가 보낸 요청의 상태를 나타낸다.
크게 다섯 개의 그룹으로 분류할 수 있으며 각 그룹은 다음과 같은 상태를 의미한다.
상태 코드의 첫 번째 숫자는 상태 그룹을 의미한다.
대표적인 상태 코드
응답 모델도 pydantic을 사용해 작성하지만 목적은 전혀 다르다.
작성한 라우트 경로의 정의:
@app.get("/todo")
async def retrieve_todo() -> dict:
return{
"todos": todo_list
}
이 라우트는 현재 데이터베이스에 있는 모든 todo 아이템의 목록을 반환한다.
이 라우트는 todo 배열에 저장된 모든 값을 반환한다. 특정한 값만 반환하게 하려면 별도의 데이터로 저장하거나 추가 로직을 작성해야 한다. 원하는 필드만 반환하도록 모델을 만들 수 있다. 이 모델을 response_model 인수로 라우트 정의에 추가하면 된다.
모든 todo를 추출해서 배열로 반환하는 라우트를 ID 없이 todo 아이템만 반환하도록 변경:
model.py
from typing import List
class TodoItems(BaseModel):
todos: List[TodoItem]
class Config:
schema_extra = {
"example": {
"todos": [
{
"item": "Example Schema 1!"
},
{
"item": "Example Schema 2!"
}
]
}
}
TodoItems라는 새로운 모델을 정의해서 TodoItem 모델에 정의된 변수 목록을 반환한다.
todo.py에 있는 라우트에 응답 모델 추가:
from model import Todo, TodoItem, TodoItems
...
@todo_router.get("/todo", response_model=TodoItems)
async def retireve_todos() -> dict:
return{
"todos": todo_list
}
요청이 알 수 없는 오류 메시지를 그대로 노출하면 클라이언트 입장에서는 오류의 원인을 파악하기 어려울 수 있다.
존재하지 않는 리소스나 권한이 없는 페이지에 접근하는 경우 요청 시 오류가 발생하며 서버 자체에서 오류가 발생하기도 한다. FastAPI에서 오류는 FastAPI의 HTTPException 클래스를 사용해 예외를 발생시켜 처리한다.
HTTP 예외: 요청 흐름상에 발생하는 오류나 문제를 가리키는 이벤트다.
HTTPException 클래스는 다음 세 개의 인수를 받는다.
현재 todo 라우트 경로 정의에서는 todo가 존재하지 않으면 메시지를 반환한다. HTTPException을 사용해 예외를 발생시키도록 수정:
지금은 존재하지 않는 todo를 추출하면 404가 아닌 200 응답이 반환된다.
HTTPException 클래스를 사용해 라우트를 변경하면 적절한 응답 코드와 함께 상태 메시지도 반환할 수 있다. todo.py에서 추출, 변경, 삭제 라우트를 변경:
from fastapi import APIRouter, Path, HTTPException, status
...
@todo_router.get("/todo/{todo_id}")
async def get_single_todo(todo_id: int = Path(..., title="The ID of the todo to retrieve.")) -> dict:
for todo in todo_list:
if todo.id == todo.id:
return{
"todo": todo
}
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Todo with supplied ID doesn't exist",
)
@todo_router.put("/todo/{todo_id}")
async def update_todo(todo_data: TodoItem, todo_id: int = Path(..., title="The ID of the todo to be updated.")) -> dict:
for todo in todo_list:
if todo.id == todo_id:
todo.item = todo_data.item
return {
"message": "Todo updated successfully."
}
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Todo with supplied ID doesn't exist",
)
@todo_router.delete("/todo/{todo_id}")
async def delete_single_todo(todo_id: int) -> dict:
for index in range(len(todo_list)):
todo = todo_list[index]
if todo.id == todo_id:
todo_list.pop(index)
return{
"message": "Todo deleted successfully."
}
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Todo with supplied ID doesn't exist",
)
존재하지 않는 todo를 추출하려고 하면 404 코드와 메시지가 반환된다.
마지막으로 데코레이터 함수에 status_code 인수를 추가해서 기본 응답 코드(200)를 다른 코드로 변경:
@todo_router.post("/todo", status_code=201)
async def add_todo(todo: Todo) -> dict:
todo_list.append(todo)
return{
"message": "Todo added successfully"
}