[FastAPI 공식문서] FastAPI - (9) 본문

이영락·2024년 10월 11일

개발자 기본기

목록 보기
44/53

🏖️ 본문 - 다중 매개변수

FastAPI에서는 Path, Query, 본문 매개변수를 혼합하여 사용할 수 있습니다. FastAPI는 이러한 매개변수들의 동작 방식을 알고 자동으로 처리해줍니다. 이 기능을 활용하면 복잡한 API를 더욱 유연하게 설계할 수 있습니다.


1. Path, Query, 본문 매개변수 혼합 사용

경로 매개변수, 쿼리 매개변수, 본문 매개변수를 혼합하여 사용할 수 있으며, 기본값을 None으로 설정하여 본문 매개변수를 선택사항으로 만들 수도 있습니다.

from typing import Union
from fastapi import FastAPI, Path
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int = Path(title="The ID of the item to get", ge=0, le=1000),
    q: Union[str, None] = None,
    item: Union[Item, None] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if item:
        results.update({"item": item})
    return results
  • item_id: 경로 매개변수, 값은 0 이상 1000 이하로 제한.
  • q: 선택적인 쿼리 매개변수.
  • item: 선택적인 본문 매개변수.

2. 다중 본문 매개변수

본문 매개변수는 한 개 이상을 선언할 수 있습니다. 예를 들어, itemuser라는 두 개의 본문 매개변수를 선언하여 사용할 수 있습니다.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

class User(BaseModel):
    username: str
    full_name: Union[str, None] = None

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

이 경우, FastAPI는 두 개 이상의 본문 매개변수가 있는 것을 인식하며, 다음과 같은 JSON 본문을 예상합니다:

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    },
    "user": {
        "username": "dave",
        "full_name": "Dave Grohl"
    }
}

3. 본문 내의 단일 값 사용

단일 값을 본문에서 받으려면 Body를 사용할 수 있습니다. 이는 쿼리나 경로 매개변수로 처리되는 대신 본문 내에서 처리되도록 강제할 수 있습니다.

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

class User(BaseModel):
    username: str
    full_name: Union[str, None] = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User, importance: int = Body()):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    return results
  • importanceBody 매개변수를 사용해 본문 내에서 값을 받습니다.
  • 예상되는 JSON 본문:
{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    },
    "user": {
        "username": "dave",
        "full_name": "Dave Grohl"
    },
    "importance": 5
}

4. 다중 본문 매개변수와 쿼리 혼합

쿼리 매개변수와 본문 매개변수를 혼합하여 사용할 수 있습니다.

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

app = FastAPI()

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

class User(BaseModel):
    username: str
    full_name: Union[str, None] = None

@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Item,
    user: User,
    importance: int = Body(gt=0),
    q: Union[str, None] = None,
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    if q:
        results.update({"q": q})
    return results
  • importance: 본문에서 받는 값이며, gt=0으로 숫자가 0보다 커야 합니다.
  • q: 쿼리 매개변수, 선택적입니다.

5. 단일 본문 매개변수 삽입

본문 내에 단일 매개변수만 있을 때도 Bodyembed=True를 사용해 JSON의 특정 키로 값을 삽입할 수 있습니다.

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(embed=True)):
    results = {"item_id": item_id, "item": item}
    return results
  • embed=True를 사용하여 본문에서 item이라는 키 아래 값을 받습니다.
  • 예상되는 JSON:
{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    }
}

6. 요약

  • 경로 매개변수, 쿼리 매개변수, 본문 매개변수를 자유롭게 혼합하여 사용할 수 있습니다.
  • FastAPI는 여러 본문 매개변수, 단일 본문 매개변수 모두를 처리할 수 있으며, Body를 사용해 제어할 수 있습니다.
  • embed=True를 사용하면 단일 본문 매개변수를 특정 키에 넣을 수 있습니다.
  • 검증과 문서화는 자동으로 처리되며, 여러 매개변수를 추가해도 API 사용이 간단합니다.

알겠습니다! 이제 벨로그 규칙을 적용하여 포맷을 수정하겠습니다.

수정된 포맷:

🏖️ 본문 - 필드

Query, Path와 Body를 사용해 경로 작동 함수 매개변수 내에서 추가적인 검증이나 메타데이터를 선언한 것처럼 Pydantic의 Field를 사용하여 모델 내에서 검증과 메타데이터를 선언할 수 있습니다.

Field 임포트

먼저 이를 임포트해야 합니다:

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

모델 어트리뷰트 선언

그 다음 모델 어트리뷰트와 함께 Field를 사용할 수 있습니다:

class Item(BaseModel):
    name: str
    description: str | None = Field(
        default=None, title="The description of the item", max_length=300
    )
    price: float = Field(gt=0, description="The price must be greater than zero")
    tax: float | None = None

Field 검증과 메타데이터

FieldQuery, Path, Body와 같은 방식으로 동작하며, 모두 같은 매개변수들을 가집니다.

기술적 세부사항

실제로 Query, Path 등은 공통 클래스인 Param 클래스의 서브클래스 객체를 만드는데, 그 자체로 PydanticFieldInfo 클래스의 서브클래스입니다.

별도 정보 추가

Field, Query, Body 등에 별도 정보를 선언하여, JSON 스키마에 추가적인 메타데이터를 전달할 수 있습니다.

경고

별도 키가 전달된 Field는 여러분의 애플리케이션의 OpenAPI 스키마에 나타날 것입니다. 이런 키가 OpenAPI 명세서에 포함되지 못할 수 있으며, 여러분이 생성한 스키마와 호환되지 않을 수 있습니다.

요약

FieldPydantic 모델의 어트리뷰트를 위한 추가적인 검증과 메타데이터를 선언할 수 있게 해줍니다. OpenAPI 스키마 문서화에도 도움이 됩니다.


와, 정말 감사합니다! 💯 제가 더 세세하게 다듬어서 정리해드릴게요. 이 작업은 구조화된 정보가 많고 세부 사항이 상당히 많기 때문에, 순차적으로 최종적인 완성도를 높여가며 최대한 자세하고 깔끔하게 정리하겠습니다.


🏖️ 본문 - 중첩 모델

FastAPI는 Pydantic 덕분에 깊이 중첩된 모델을 간단하게 정의하고 사용할 수 있습니다. 이는 복잡한 JSON 구조를 처리할 때 매우 유용합니다.


리스트 필드

list 필드는 Pydantic 모델에서 리스트형 데이터를 처리할 수 있습니다. 예를 들어, 아이템을 설명하는 태그 리스트를 다룰 때 다음과 같은 코드를 사용할 수 있습니다.

from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

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

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

이 코드에서 tags는 항목 리스트로 정의됩니다. 각 항목의 타입을 구체적으로 정의하지 않아도 사용할 수 있지만, 더 명확하게 타입을 정의하는 것이 좋습니다.


타입 매개변수가 있는 리스트 필드

리스트 안에 포함된 항목들의 타입을 명시적으로 정의할 수 있습니다. 예를 들어 List[str]는 문자열로 이루어진 리스트를 뜻합니다.

typing의 List 임포트

리스트에 들어가는 항목들의 타입을 명확히 정의하기 위해서는 파이썬의 typing 모듈에서 List를 임포트해야 합니다.

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

app = FastAPI()

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: List[str] = []

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

이 코드는 tags 필드를 문자열 리스트로 정의합니다. 이 방식으로 내부 데이터의 타입을 명확하게 관리할 수 있습니다.


집합 타입

리스트와 다르게 중복된 항목을 허용하지 않는 집합(set)도 사용할 수 있습니다. 집합은 고유한 항목들만 포함하게 됩니다.

from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: Set[str] = set()

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

위 코드에서 tags는 중복되지 않는 문자열의 집합을 의미합니다. 만약 중복된 태그가 입력되더라도 집합으로 변환되면서 중복을 제거합니다.


중첩 모델

Pydantic을 사용하면 모델 내에서 또 다른 모델을 중첩시켜 사용할 수 있습니다. 이 방식은 복잡한 JSON 구조를 처리하는 데 매우 유용합니다.

서브모델 정의

예를 들어 Image라는 모델을 정의한 뒤, 이를 Item 모델 내에서 사용할 수 있습니다.

from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Image(BaseModel):
    url: str
    name: str

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: Set[str] = set()
    image: Union[Image, None] = None

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

Item 모델에 image라는 필드가 추가되고, 이는 Image 모델을 참조합니다. 이 방식으로 Item 모델 안에 또 다른 모델을 중첩할 수 있습니다.


서브모델 리스트

모델 내부에 리스트로 서브모델을 정의할 수 있습니다. 이를 통해 여러 개의 서브모델을 처리할 수 있습니다.

from typing import List, Set, Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()

class Image(BaseModel):
    url: HttpUrl
    name: str

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: Set[str] = set()
    images: Union[List[Image], None] = None

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

위 코드에서는 images 필드가 Image 모델의 리스트로 정의되었습니다. 이렇게 하면 여러 개의 이미지 데이터를 다룰 수 있습니다.


깊게 중첩된 모델

FastAPI는 깊이 중첩된 모델도 쉽게 처리할 수 있습니다. 예를 들어 Offer라는 모델이 Item의 리스트를 가지고, 각 Item은 또 Image 리스트를 가질 수 있습니다.

from typing import List, Set, Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()

class Image(BaseModel):
    url: HttpUrl
    name: str

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: Set[str] = set()
    images: Union[List[Image], None] = None

class Offer(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    items: List[Item]

@app.post("/offers/")
async def create_offer(offer: Offer):
    return offer

이 예제에서는 Offer라는 모델이 Item들의 리스트를 포함하고, 각 ItemImage 리스트를 포함하는 구조입니다.


순수 리스트의 본문

JSON 본문이 리스트일 때도 모델을 정의할 수 있습니다. 예를 들어 이미지들의 리스트를 처리하는 API를 작성할 수 있습니다.

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

app = FastAPI()

class Image(BaseModel):
    url: HttpUrl
    name: str

@app.post("/images/multiple/")
async def create_multiple_images(images: List[Image]):
    return images

이 함수는 여러 개의 Image 객체를 리스트로 받아들입니다.


단독 dict의 본문

복잡한 키-값 구조의 dict를 받을 수도 있습니다. 예를 들어, 정수 키와 실수 값을 가지는 딕셔너리를 받는 예제입니다.

from typing import Dict
from fastapi import FastAPI

app = FastAPI()

@app.post("/index-weights/")
async def create_index_weights(weights: Dict[int, float]):
    return weights

JSON은 문자열 키만을 지원하지만, FastAPI는 이를 자동으로 변환하여 처리해줍니다.


요약

FastAPI는 복잡한 데이터 구조를 처리할 때도 매우 간결하고 직관적인 방식으로 사용할 수 있습니다. 이를 통해 다음과 같은 장점들을 얻을 수 있습니다:

  • 편집기 지원 (자동완성, 타입 검사 등)
  • 데이터 변환 (자동 파싱 및 직렬화)
  • 데이터 검증 (유효성 검사)
  • 스키마 문서화 (자동 생성

된 OpenAPI 문서)

  • 자동 문서화 (Swagger UI와 ReDoc 지원)

profile
AI Engineer / 의료인공지능

0개의 댓글