[FastAPI 공식문서] FastAPI - (10) 데이터

이영락·2024년 10월 14일

개발자 기본기

목록 보기
45/53

🏖️ 요청 예제 데이터 선언

FastAPI를 사용하면 API의 입력 데이터를 쉽게 설명하고 예제 데이터를 추가할 수 있습니다. 이를 통해 API 문서화가 직관적이고 효율적으로 이루어집니다. 특히, Pydantic 모델을 사용해 예제를 선언할 수 있으며, JSON 스키마의 일부로 추가됩니다.


📑 Pydantic 모델에 추가하는 JSON 스키마 예제

Pydantic 모델을 정의할 때, model_config 속성을 사용해 JSON 스키마의 예제를 선언할 수 있습니다. 예를 들어, 아래와 같은 방식으로 Pydantic v2 이상에서 모델의 예제 데이터를 정의할 수 있습니다.

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

    model_config = {
        "json_schema_extra": {
            "examples": [
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ]
        }
    }

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, "item": item}

위 코드에서 model_config 내에 json_schema_extra를 사용하여 예제 데이터를 선언했습니다. API 문서에는 이 예제 데이터가 표시됩니다.


📋 Field 추가 인자를 사용한 예제 선언

Pydantic의 Field를 이용해 개별 필드마다 예제 데이터를 선언할 수 있습니다. 아래와 같은 방식으로 필드에 예제를 포함할 수 있습니다.

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class Item(BaseModel):
    name: str = Field(examples=["Foo"])
    description: str | None = Field(default=None, examples=["A very nice Item"])
    price: float = Field(examples=[35.4])
    tax: float | None = Field(default=None, examples=[3.2])

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, "item": item}

Field를 사용하면 각 필드에 예제를 직접 제공할 수 있습니다. 이 방식은 데이터 스키마 문서화가 더욱 상세하게 이루어지도록 도와줍니다.


🛠️ Body를 사용한 예제 데이터 선언

Body 파라미터를 사용해 전체 요청 본문에 대한 예제를 정의할 수 있습니다. examples 매개변수를 사용하여 여러 예제도 추가할 수 있습니다.

from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Annotated[
        Item,
        Body(
            examples=[
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ],
        ),
    ],
):
    return {"item_id": item_id, "item": item}

이 코드를 사용하면 예제가 /docs 문서 UI에 자동으로 나타납니다.


📝 여러 예제 데이터 추가

다양한 상황에 맞춘 여러 예제 데이터를 동시에 제공할 수도 있습니다. 이렇게 하면 API 사용자가 입력할 수 있는 다양한 경우의 수를 쉽게 이해할 수 있습니다.

from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Annotated[
        Item,
        Body(
            examples=[
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                },
                {
                    "name": "Bar",
                    "price": "35.4",
                },
                {
                    "name": "Baz",
                    "price": "thirty five point four",
                },
            ],
        ),
    ],
):
    return {"item_id": item_id, "item": item}

이 코드는 서로 다른 3가지 예제를 제공합니다:

  • 첫 번째는 모든 필드가 완전한 예제.
  • 두 번째는 가격이 문자열로 제공됨.
  • 세 번째는 유효하지 않은 데이터(문자열로 된 가격).

🗂️ OpenAPI 특화 예제 데이터 선언

OpenAPI에서 지원하는 특화된 examples 필드를 사용하여 여러 예제를 제공할 수 있습니다. 각 예제에는 설명, 요약, 실제 데이터를 포함할 수 있습니다.

from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Annotated[
        Item,
        Body(
            openapi_examples={
                "normal": {
                    "summary": "A normal example",
                    "description": "A **normal** item works correctly.",
                    "value": {
                        "name": "Foo",
                        "description": "A very nice Item",
                        "price": 35.4,
                        "tax": 3.2,
                    },
                },
                "converted": {
                    "summary": "An example with converted data",
                    "description": "FastAPI can convert price `strings` to actual `numbers` automatically",
                    "value": {
                        "name": "Bar",
                        "price": "35.4",
                    },
                },
                "invalid": {
                    "summary": "Invalid data is rejected with an error",
                    "value": {
                        "name": "Baz",
                        "price": "thirty five point four",
                    },
                },
            },
        ),
    ],
):
    return {"item_id": item_id, "item": item}

각 예제에는:

  • summary: 간단한 요약
  • description: 설명
  • value: 실제 예제 데이터

이 예제는 Swagger UI와 같은 문서화 도구에서 아주 유용하게 사용됩니다.


요약

FastAPI를 사용하면 API가 처리할 수 있는 데이터의 예제를 쉽게 정의할 수 있습니다. Pydantic 모델 속에 예제를 추가하거나, FieldBody를 사용해 각 필드 또는 전체 요청에 대한 예제를 선언할 수 있습니다. 이렇게 하면 자동 문서화가 가능해지며, API 사용자는 더 직관적으로 데이터를 입력할 수 있게 됩니다.


이렇게 FastAPI에서 예제 데이터를 선언하는 방법을 자세히 정리해봤습니다. 추가로 궁금한 사항이 있다면 언제든지 알려주세요! 😊


🏖️ 추가 데이터 자료형

FastAPI에서는 복잡한 데이터 자료형도 기본적으로 지원하며, 이를 활용해 더 유연하고 강력한 API를 개발할 수 있습니다. 이러한 자료형들은 데이터 검증, 변환, 문서화를 자동으로 처리하여 개발 생산성을 극대화시킵니다.


📑 복잡한 자료형 개요

아래는 FastAPI에서 사용할 수 있는 추가적인 복잡한 자료형입니다:

  1. UUID (범용 고유 식별자):

    • UUID는 여러 데이터베이스나 시스템에서 고유한 식별자로 사용됩니다.
    • FastAPI에서는 str로 표현되며, 주로 객체의 ID로 많이 사용됩니다.
    from uuid import UUID
    
    @app.get("/items/{item_id}")
    async def get_item(item_id: UUID):
        return {"item_id": item_id}
  2. datetime.datetime (날짜 및 시간):

    • datetime.datetime날짜와 시간을 함께 다루는 자료형입니다.
    • 요청 및 응답에서 ISO 8601 형식(예: 2024-10-14T15:53:00+05:00)으로 문자열 변환됩니다.
    from datetime import datetime
    
    @app.post("/event/")
    async def create_event(start_time: datetime):
        return {"start_time": start_time}
  3. datetime.date (날짜):

    • 날짜만을 표현하는 자료형으로, ISO 8601 형식(예: 2024-10-14)으로 요청과 응답이 처리됩니다.
  4. datetime.time (시간):

    • 시간만을 표현하는 자료형입니다. ISO 8601 형식(예: 14:23:55.003)으로 변환됩니다.
  5. datetime.timedelta (시간 차이):

    • 시간 간격을 나타내는 자료형입니다.
    • 전체 초를 나타내는 float 형식으로 변환되며, ISO 8601 시차 인코딩도 지원합니다.
    from datetime import timedelta
    
    @app.post("/process/")
    async def process_data(delay: timedelta):
        return {"process_delay": delay}
  6. frozenset (고유한 값의 집합):

    • 집합 자료형으로, 중복 값을 허용하지 않습니다.
    • 요청 시 리스트를 받고 중복 값을 제거하여 set로 변환하며, 응답 시에는 리스트로 변환됩니다.
  7. bytes (바이너리 데이터):

    • 바이너리 데이터를 처리하는 자료형입니다.
    • 요청 및 응답에서 str로 처리되며, 스키마에는 binary 형식으로 명시됩니다.
    @app.post("/upload/")
    async def upload_file(file_data: bytes):
        return {"file_size": len(file_data)}
  8. Decimal (정확한 소수점 숫자):

    • Decimal정확한 소수점 연산을 제공하는 자료형입니다.
    • float와 비슷하게 처리되며, 금융 데이터처럼 정밀한 연산이 필요한 경우에 사용됩니다.

📋 추가 자료형 사용 예시

아래 예시는 여러 복잡한 자료형을 동시에 사용하는 예시입니다. UUID, datetime, timedelta 등을 활용해 다양한 데이터 타입을 처리하는 API를 구성할 수 있습니다.

from datetime import datetime, time, timedelta
from typing import Annotated
from uuid import UUID

from fastapi import Body, FastAPI

app = FastAPI()

@app.put("/items/{item_id}")
async def read_items(
    item_id: UUID,
    start_datetime: Annotated[datetime, Body()],
    end_datetime: Annotated[datetime, Body()],
    process_after: Annotated[timedelta, Body()],
    repeat_at: Annotated[time | None, Body()] = None,
):
    start_process = start_datetime + process_after
    duration = end_datetime - start_process
    return {
        "item_id": item_id,
        "start_datetime": start_datetime,
        "end_datetime": end_datetime,
        "process_after": process_after,
        "repeat_at": repeat_at,
        "start_process": start_process,
        "duration": duration,
    }

📝 설명

  1. item_id: UUID 형식을 사용해 고유 식별자로 정의.
  2. start_datetime: 시작 시간을 받는 datetime 형식.
  3. end_datetime: 종료 시간을 받는 datetime 형식.
  4. process_after: 작업을 지연시키는 시간 차이(timedelta).
  5. repeat_at: 선택적인 시간(time)을 받는 매개변수.

위 함수는 UUID로 항목을 고유하게 식별하고, 시작 및 종료 시간작업 지연 시간을 계산하여 처리합니다.


📌 데이터 변환 및 검증

위와 같이 복잡한 자료형을 사용하면 FastAPI는 다음과 같은 이점을 제공합니다:

  • 편집기 자동완성 및 타입 지원: 사용되는 자료형에 대해 명확한 타입 힌트를 제공하여, IDE에서 자동완성을 지원받을 수 있습니다.
  • 데이터 검증 및 변환: 요청 데이터를 정의한 자료형에 맞춰 자동으로 검증하고, 필요한 경우 데이터 변환까지 처리합니다.
  • 문서화: FastAPI는 OpenAPI 문서 생성을 자동화하여, 개발자가 별도로 문서를 작성할 필요가 없습니다.

🛠️ 다양한 복잡한 자료형 활용

  • 금융 및 정밀 계산: Decimal을 활용해 정확한 소수점 계산을 보장할 수 있습니다.
  • 고유 식별자 관리: UUID를 사용해 데이터베이스 간 충돌 없는 고유 식별자를 쉽게 관리합니다.
  • 시간 기반 작업: datetimetimedelta를 사용해 시간 관련 작업을 쉽게 처리합니다.
  • 파일 처리: bytes 자료형을 사용해 파일 업로드바이너리 데이터 처리가 가능합니다.

요약

FastAPI는 기본적인 자료형 외에도 UUID, datetime, Decimal과 같은 복잡한 자료형을 지원하며, 이를 통해 더욱 정교한 API를 구축할 수 있습니다. Pydantic과의 강력한 통합 덕분에 데이터 검증, 변환, 문서화 등이 자동으로 이루어져 개발 생산성이 높아집니다.

복잡한 자료형을 통해 여러분의 API를 정확하게 관리하고 유연하게 설계해보세요!


🏖️ 쿠키 매개변수

쿠키 매개변수FastAPI에서 QueryPath 매개변수처럼 간편하게 선언할 수 있습니다. 웹 애플리케이션에서 클라이언트가 서버에 전송하는 쿠키 데이터를 처리하는 데에 사용됩니다.


쿠키를 처리하기 위해서는 FastAPI에서 Cookie 클래스를 임포트해야 합니다. CookiePathQuery처럼 매개변수의 값을 쿠키에서 가져올 수 있도록 해줍니다.

예시:

from typing import Annotated
from fastapi import Cookie, FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
    return {"ads_id": ads_id}

위 코드에서 ads_id는 쿠키에 저장된 값을 읽어옵니다. None 값을 기본값으로 설정하여 쿠키가 없을 때에도 정상적으로 작동하도록 합니다.


쿠키 매개변수는 PathQuery와 마찬가지로 선언됩니다. 쿠키의 기본값과 추가 검증어노테이션도 사용할 수 있습니다.

예시:

from typing import Annotated
from fastapi import Cookie, FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
    return {"ads_id": ads_id}

여기에서 쿠키 이름이 ads_id로 선언되며, 이 쿠키가 클라이언트로부터 요청에 포함되어 전달됩니다.


💡 기술 세부사항

CookiePathQuery 클래스처럼 Param 클래스를 상속합니다. 이로 인해 쿠키 매개변수도 동일한 방식으로 처리됩니다.

  • Query, Path, Cookie 모두 FastAPI에서 특수 함수처럼 동작하며, 이들은 실제로 매개변수 값을 처리하는 클래스 객체를 반환합니다.
  • 쿠키를 명확하게 선언하기 위해 Cookie 클래스를 사용해야 합니다. Cookie를 사용하지 않으면 쿼리 매개변수로 해석되기 때문입니다.

요약

  • 쿠키 매개변수는 FastAPI에서 QueryPath 매개변수와 같은 방식으로 선언됩니다.
  • 쿠키의 값을 읽어오려면 Cookie 클래스를 사용하여 매개변수를 정의해야 합니다.
  • 쿠키 매개변수를 정의할 때 기본값을 설정하거나 추가적인 검증 로직을 적용할 수 있습니다.

쿠키 매개변수는 웹 애플리케이션에서 사용자 세션이나 설정 정보를 유지하는 데 유용하게 사용될 수 있습니다. FastAPI에서는 간편하게 쿠키를 읽고 처리할 수 있으니, 이를 적절히 활용해보세요!


🏖️ 헤더 매개변수

헤더 매개변수FastAPI에서 Query, Path, 그리고 Cookie 매개변수와 비슷하게 정의할 수 있으며, 클라이언트가 보낸 HTTP 헤더 값을 처리하는 데 사용됩니다.


📥 Header 임포트

헤더 값을 처리하기 위해서는 FastAPI에서 Header 클래스를 임포트해야 합니다.

예시:

from typing import Union
from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(user_agent: Union[str, None] = Header(default=None)):
    return {"User-Agent": user_agent}

위 예시에서, user_agent는 HTTP 요청의 헤더 값을 읽어옵니다. 기본값을 None으로 설정하여 헤더가 존재하지 않을 경우에도 오류 없이 처리됩니다.


🍪 Header 매개변수 선언

헤더 매개변수는 Query, Path, Cookie와 마찬가지로 선언할 수 있습니다. 첫 번째 값은 기본값이며, 추가 검증어노테이션도 사용할 수 있습니다.

예시:

from typing import Union
from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(user_agent: Union[str, None] = Header(default=None)):
    return {"User-Agent": user_agent}

위 코드에서, user_agent는 클라이언트가 보낸 User-Agent 헤더 값을 받아 처리합니다.


💡 기술 세부사항

  • HeaderPath, Query, Cookie와 동일한 Param 클래스를 상속받은 자매 클래스입니다.
  • 이를 이용해 헤더 값을 받아오며, 쿼리 매개변수로 잘못 해석되지 않도록 Header를 사용해야 합니다.

🔄 자동 변환

Header는 추가적인 기능을 제공합니다. 주로 헤더는 하이픈(-)으로 구분되지만, 파이썬 변수에는 하이픈을 사용할 수 없으므로 자동으로 언더스코어(_)로 변환됩니다.

예시:

from typing import Union
from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(user_agent: Union[str, None] = Header(default=None)):
    return {"User-Agent": user_agent}
  • 파이썬에서는 user_agent로 선언하지만, FastAPI는 자동으로 이를 User-Agent로 변환해줍니다.
  • 대소문자를 구분하지 않으며, user_agent로 선언한 값은 헤더의 User-Agent 값을 처리하게 됩니다.

언더스코어 변환 비활성화:

만약 언더스코어 변환을 비활성화하고 싶다면, convert_underscores=False를 설정할 수 있습니다.

from typing import Union
from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(
    strange_header: Union[str, None] = Header(default=None, convert_underscores=False),
):
    return {"strange_header": strange_header}

주의: 일부 프록시나 서버는 언더스코어가 포함된 헤더를 지원하지 않을 수 있습니다.


📑 중복 헤더

때로는 중복된 헤더 값을 처리할 수 있습니다. 동일한 헤더가 여러 번 포함될 수 있으며, 이때는 list로 선언하여 모든 값을 받아올 수 있습니다.

예시:

from typing import List, Union
from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(x_token: Union[List[str], None] = Header(default=None)):
    return {"X-Token values": x_token}

만약 요청 헤더에 X-Token: fooX-Token: bar가 두 번 들어오면, 응답은 다음과 같이 리스트 형태로 반환됩니다:

{
  "X-Token values": [
    "foo",
    "bar"
  ]
}

🏖️ 요약

  • Header 매개변수Query, Path, Cookie와 동일한 방식으로 선언됩니다.
  • 하이픈(-)을 포함한 헤더를 자동으로 언더스코어(_)로 변환해 처리하며, 이를 비활성화할 수도 있습니다.
  • 중복된 헤더 값을 리스트로 처리할 수 있습니다.

헤더 매개변수를 활용해 다양한 HTTP 헤더 값을 처리하고, 이를 통해 클라이언트 정보를 더욱 유연하게 다룰 수 있습니다!

profile
AI Engineer / 의료인공지능

0개의 댓글