python / / 2024. 8. 16. 10:44

[fastapi] 응답 모델(Response Model) 사용하기

1. 응답 모델이란?

응답 모델은 API 엔드포인트가 클라이언트에게 반환할 데이터의 형식을 정의하는 Pydantic 모델이다. 응답 모델을 사용하면 API 응답의 데이터 구조를 명확히 하고, 자동으로 데이터 검증과 문서화를 지원한다.

2. 기본적인 응답 모델 사용법

응답 모델을 정의하려면 Pydantic의 BaseModel을 상속받는 클래스를 생성하고, 이를 FastAPI의 response_model 매개변수에 전달한다.

다음 예제는 간단한 사용자 정보를 반환하는 API 엔드포인트를 정의한다.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    id: int
    name: str
    email: str

@app.get("/user/{user_id}", response_model=User)
def get_user(user_id: int):
    return {"id": user_id, "name": "John Doe", "email": "john.doe@example.com"}

위 코드에서 User 모델은 id, name, email 속성을 갖는 데이터를 정의한다. /user/{user_id} 엔드포인트는 이 모델을 사용하여 응답을 반환한다. 클라이언트는 User 모델에 정의된 속성만을 포함하는 JSON 응답을 받게 된다.

즉, json에서는 id, name, email이 있지만 응답 모델에는 id, name만 있으면 email은 넘어가지 않는다.

아래와 같이 응답 모델에 email을 제거해보자.

class User(BaseModel):
    id: int
    name: str
    #email: str

@response_model_router.get("/user/{user_id}", response_model=User)
def get_user(user_id: int):
    return {"id": user_id, "name": "John Doe", "email": "john.doe@example.com"}

실행결과

{
  "id": 1,
  "name": "John Doe"
}

이와 같이 email을 제외하고 출력이 된다.

3. 응답 모델의 유효성 검사

Pydantic을 사용하여 정의된 응답 모델은 자동으로 유효성 검사를 수행한다. 응답 모델의 필드 타입과 필수 여부를 명시함으로써, 서버에서 클라이언트로 반환되는 데이터의 유효성을 보장할 수 있다.

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

class User(BaseModel):
    id: int
    name: str
    email: EmailStr  # Email validation

@app.get("/user/{user_id}", response_model=User)
def get_user(user_id: int):
    return {"id": user_id, "name": "John Doe", "email": "john.doe@example.com"}

여기서 email 필드는 EmailStr 타입을 사용하여 이메일 주소의 유효성을 자동으로 검사한다.

4. 응답 모델의 선택적 필드

응답 모델의 필드를 선택적으로 정의할 수 있다. 선택적 필드는 기본값을 설정하거나 Optional을 사용하여 지정할 수 있다.

from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    id: int
    name: str
    email: Optional[str] = None  # Optional field

@app.get("/user/{user_id}", response_model=User)
def get_user(user_id: int):
    return {"id": user_id, "name": "John Doe"}

여기서 email 필드는 선택적이며, 응답에는 포함되지 않을 수 있다. 즉, 아래와 같이 응답이 된다.

{
  "id": 1,
  "name": "John Doe",
  "email": null
}

만일 email 값이 없을 경우에 표시하지 않으려면 response_model_exclude_unsetTrue로 설정하면 된다. response_model_exclude_unset를 True로 하면 기본값이 아닌 응답값 중에 값이 설정된 것만 출력이 된다.

{
  "id": 1,
  "name": "John Doe"
}

5. 복합적인 응답 모델

복합적인 응답 모델을 사용하여 여러 모델을 조합할 수 있다. 예를 들어, 기본 응답 모델에 추가 정보를 포함하는 방식으로 설계할 수 있다.

from typing import List
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    id: int
    name: str

class UserWithItems(BaseModel):
    id: int
    name: str
    items: List[Item]

@app.get("/user/{user_id}", response_model=UserWithItems)
def get_user(user_id: int):
    return {
        "id": user_id,
        "name": "John Doe",
        "items": [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]
    }

이 코드에서는 UserWithItems 모델이 User 모델에 items 필드를 추가하여, 사용자와 관련된 아이템 목록을 포함한 응답을 반환한다.

6. 응답 모델과 문서화

FastAPI는 응답 모델을 자동으로 문서화하여, Swagger UI와 ReDoc에서 API 문서를 자동으로 생성한다. 응답 모델을 정의하면, 해당 모델에 대한 정보가 API 문서에 포함되어 클라이언트가 쉽게 이해할 수 있다.

6-1) Field에 추가하는 방법

응답 모델을 문서화하려면 아래와 같이 Field를 사용하면 된다.

class User(BaseModel):
    id: int = Field(description="The unique identifier of the user", example=123)
    name: str = Field(..., description="The name of the user", example="John Doe")
    email: str = Field(
        ..., description="The email address of the user", example="john.doe@example.com"
    )

6-2) json_schema_extra 사용

json_schema_extra를 사용하여 문서화와 example을 추가할 수 있다.

class User(BaseModel):
    id: int
    name: str
    email: str

    model_config = {
        "json_schema_extra": {
            "examples": [
                {"id": 123, "name": "John Doe", "email": "john.doe@example.com"}
            ]
        }
    }

7. 응답 모델의 예외 처리

FastAPI는 응답 모델의 유효성 검사 외에도, 예외를 발생시킬 수 있다. 예를 들어, 응답 모델에 정의되지 않은 필드가 포함된 경우, FastAPI는 이를 자동으로 제거한다.

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    id: int
    name: str

@app.get("/user/{user_id}", response_model=User)
def get_user(user_id: int):
    if user_id < 0:
        raise HTTPException(status_code=400, detail="Invalid user ID")
    return {"id": user_id, "name": "John Doe", "extra_field": "This will be removed"}

위 예제에서 extra_field는 응답 모델에 정의되지 않았으므로, 클라이언트에는 포함되지 않는다.

참고

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유