본문 바로가기
코딩/FastAPI

FastAPI 배우기 - Query Parameters and String Validations

by 형큐 2023. 6. 27.
SMALL

공식문서를 번역한 내용입니다.

https://fastapi.tiangolo.com/ko/tutorial/query-params-str-validations/

 

Query Parameters and String Validations - FastAPI

FastAPI framework, high performance, easy to learn, fast to code, ready for production

fastapi.tiangolo.com


쿼리 매개변수와 문자열 유효성 검사

FastAPI를 사용하면 매개변수에 대한 추가 정보 및 유효성 검사를 선언할 수 있습니다.

이 애플리케이션을 예로 들어 보겠습니다:

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(q: str | None = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

쿼리 매개변수 q는 Union[str, None](또는 파이썬 3.10에서는 str | None) 유형으로, 이는 str 유형이지만 None일 수도 있음을 의미하며 실제로 기본값은 None이므로 FastAPI는 이 매개변수가 필요하지 않다는 것을 알 수 있습니다.

🍳Note

FastAPI는 기본값이 없음이므로 q 값이 필요하지 않다는 것을 알 수 있습니다.

Union[str, None]의 Union을 사용하면 편집기에서 더 나은 지원을 제공하고 오류를 감지할 수 있습니다.

추가적인 유효성 검사

q는 선택 사항이지만 제공될 때마다 길이가 50자를 초과하지 않도록 강제할 것입니다.

Import Query

이를 위해서는 먼저 가져오기를 수행합니다:

  • Query from fastapi
  • 타이핑(또는 3.9 미만 파이썬의 타이핑_확장)을 통해 주석 달기

Python 3.9 이상에서 주석은 표준 라이브러리의 일부이므로 입력할 때 가져올 수 있습니다.

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳INFO

FastAPI는 버전 0.95.0에서 Annotated에 대한 지원을 추가하고 이를 권장하기 시작했습니다.

이전 버전을 사용하는 경우 주석 기능을 사용하려고 할 때 오류가 발생할 수 있습니다.

주석 달기를 사용하기 전에 FastAPI 버전을 0.95.1 이상으로 업그레이드해야 합니다.

q 매개변수의 유형에 주석을 사용하세요.

이전에 파이썬 타입 인트로에서 주석이 달린 매개변수에 메타데이터를 추가하는 데 사용할 수 있다고 말씀드린 것을 기억하시나요?

이제 FastAPI와 함께 사용할 차례입니다. 🚀

이 타입 어노테이션이 있습니다:

q: str | None = None

우리가 할 일은 그것을 주석으로 감싸서 이렇게 만드는 것입니다:

q: Annotated[str | None] = None

두 버전 모두 동일한 의미를 가지며, q는 문자열 또는 None일 수 있는 매개변수이며 기본값은 None입니다.

이제 재미있는 부분으로 넘어가 보겠습니다. 🎉

q 매개변수의 주석에 쿼리를 추가합니다.

이제 더 많은 메타데이터를 넣을 수 있는 곳에 주석을 달았으므로 여기에 Query를 추가하고 매개변수 max_length를 50으로 설정합니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

기본값은 여전히 없음이므로 매개 변수는 여전히 선택 사항입니다.

하지만 이제 Annotated 안에 Query(max_length=50)를 넣으면, 쿼리 매개변수에서 이 값을 추출하고(어차피 기본값이었을 것입니다 🤷), 이 값에 대한 추가 유효성 검사를 수행하기를 원한다고 FastAPI에 알려줍니다(추가 유효성 검사를 수행하기 위해 이렇게 하는 것입니다). 😎

이제 FastAPI가

  • 최대 길이가 50자인지 확인하여 데이터의 유효성을 검사합니다.
  • 데이터가 유효하지 않은 경우 클라이언트에게 명확한 오류를 표시합니다.
  • OpenAPI 스키마 경로 작업에서 매개변수를 문서화합니다(자동 문서 UI에 표시되도록).

기본값으로 대체 (이전) 쿼리 사용

이전 버전의 FastAPI(0.95.0 이전 버전)에서는 매개변수의 기본값으로 Query를 주석에 넣는 대신 Annotated를 사용해야 했는데, 이를 사용하는 코드를 주변에서 볼 가능성이 높기 때문에 설명해 드리겠습니다.

🍳Tip

새 코드의 경우 가능하면 위에서 설명한 대로 주석을 사용하세요. 여러 가지 장점(아래 설명)이 있으며 단점은 없습니다. 🍰

Query()를 함수 매개변수의 기본값으로 사용하고 매개변수 max_length를 50으로 설정하는 방식입니다:

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

이 경우 (Annotated를 사용하지 않고) 함수의 기본값 None을 Query()로 대체해야 하므로 이제 매개변수 Query(default=None)로 기본값을 설정해야 하며, 이는 기본값을 정의하는 동일한 목적을 수행합니다(적어도 FastAPI의 경우).

So:

q: Union[str, None] = Query(default=None)

...매개 변수를 선택 사항으로 설정하며 기본값은 없음과 동일합니다:

q: Union[str, None] = None

그리고 Python 3.10 이상에서:

q: str | None = Query(default=None)

...매개 변수를 선택 사항으로 설정하며 기본값은 없음과 동일합니다:

q: str | None = None

하지만 쿼리 매개변수로 명시적으로 선언합니다.


🍳Info

매개변수를 선택 사항으로 만드는 데 가장 중요한 부분은 부품이라는 점을 명심하세요:

= None 또는

= Query(default=None) 를 사용하면 None을 기본값으로 사용하므로 매개변수가 필요하지 않게 됩니다.

Union[str, None] 부분을 사용하면 편집기에서 더 나은 지원을 제공할 수 있지만, 이 매개변수가 필요하지 않다는 것을 FastAPI에 알리는 것은 아닙니다.


그런 다음 쿼리에 더 많은 매개변수를 전달할 수 있습니다. 이 경우 문자열에 적용되는 최대 길이 매개변수입니다:

q: Union[str, None] = Query(default=None, max_length=50)

이렇게 하면 데이터의 유효성을 검사하고, 데이터가 유효하지 않은 경우 명확한 오류를 표시하며, OpenAPI 스키마 경로 작업에서 매개 변수를 문서화합니다.

쿼리를 기본값으로 사용하거나 주석이 달린

Annotated 내에서 쿼리를 사용할 때는 쿼리의 기본 매개 변수를 사용할 수 없다는 점에 유의하세요.

대신 함수 매개변수의 실제 기본값을 사용하세요. 그렇지 않으면 일관성이 떨어집니다.

예를 들어, 이것은 허용되지 않습니다:

예를 들어, 이것은 허용되지 않습니다:

q: Annotated[str, Query(default="rick")] = "morty"

...기본값이 '릭'인지 '모티'인지 명확하지 않기 때문입니다.

따라서 (가급적):

q: Annotated[str, Query()] = "rick"

...또는 이전 코드베이스에서 찾을 수 있습니다:

q: str = Query(default="rick")

주석 처리의 장점

함수 매개변수의 기본값 대신 주석이 달린 것을 사용하는 것이 여러 가지 이유로 더 좋습니다. 🤓

함수 매개변수의 기본값은 실제 기본값이며, 일반적으로 파이썬에서는 이 기본값이 더 직관적입니다. 😌

FastAPI 없이도 다른 곳에서 동일한 함수를 호출할 수 있으며 예상대로 작동합니다. 기본값이 없는 필수 매개변수가 있는 경우 편집기에서 오류를 통해 알려주며, 필수 매개변수를 전달하지 않고 실행하면 Python에서도 오류가 발생합니다.

Annotated를 사용하지 않고 (이전) 기본값 스타일을 사용하는 경우 다른 곳에서 FastAPI 없이 해당 함수를 호출하는 경우 함수가 올바르게 작동하려면 함수에 인수를 전달해야 하며, 그렇지 않으면 값이 예상과 다를 수 있습니다(예: str 대신 QueryInfo 또는 이와 유사한 것). 그러면 에디터도 불평하지 않으며, 파이썬도 내부 연산에 오류가 발생할 때만 해당 함수를 실행해도 불평하지 않습니다.

Annotated는 메타데이터 주석을 두 개 이상 가질 수 있으므로 이제 Typer와 같은 다른 도구에서도 동일한 함수를 사용할 수 있습니다. 🚀

더 많은 유효성 검사 추가

min_length 매개변수를 추가할 수도 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(min_length=3, max_length=50)] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

정규식 추가

매개변수가 일치해야 하는 정규식 패턴을 정의할 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")
    ] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

이 특정 정규식 패턴은 수신된 매개변수 값이 맞는지 확인합니다:

  • ^: 다음 문자로 시작하며, 이전에는 문자가 없습니다.
  • 고정 쿼리: 정확한 값 고정 쿼리를 가집니다.
  • $: 거기서 끝나고, 고정 쿼리 뒤에 더 이상 문자가 없습니다.

이 모든 '정규식' 아이디어가 막막하게 느껴지더라도 걱정하지 마세요. 정규식은 많은 사람들에게 어려운 주제입니다. 아직 정규식을 사용하지 않고도 많은 작업을 수행할 수 있습니다.

하지만 정규식이 필요할 때 언제든 가서 배우면 이미 FastAPI에서 직접 사용할 수 있다는 것을 알아두세요.

패턴 대신 Pydantic v1 정규식 사용

Pydantic 버전 2 이전과 FastAPI 0.100.0 이전에는 이 매개 변수를 패턴 대신 정규식이라고 불렀지만 이제 더 이상 사용되지 않습니다.

여전히 이 매개변수를 사용하는 코드를 볼 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None, Query(min_length=3, max_length=50, regex="^fixedquery$")
    ] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

하지만 이 기능은 더 이상 사용되지 않으며 새 매개변수 패턴을 사용하려면 업데이트해야 합니다. 🤓

기본값

물론 없음이 아닌 기본값을 사용할 수도 있습니다.

q 쿼리 매개 변수의 최소 길이를 3으로, 기본값을 "fixedquery"로 선언하고 싶다고 가정해 보겠습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳Note

기본값이 None(없음)을 포함한 모든 유형이면 매개변수는 선택 사항(필수가 아님)이 됩니다.


필수 설정

더 많은 유효성 검사나 메타데이터를 선언할 필요가 없는 경우에는 다음과 같이 기본값을 선언하지 않는 것만으로도 쿼리 매개변수를 필수로 설정할 수 있습니다:

q: str

대신에:

q: Union[str, None] = None

하지만 이제는 다음과 같이 쿼리로 선언하고 있습니다:

q: Annotated[Union[str, None], Query(min_length=3)] = None

따라서 쿼리를 사용하는 동안 필요에 따라 값을 선언해야 하는 경우 기본값을 선언하지 않을 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

줄임표(...)와 함께 사용해야 합니다.

값이 필요하다는 것을 명시적으로 선언하는 다른 방법이 있습니다. 기본값을 리터럴 값으로 설정할 수 있습니다...:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳Info

이전에 본 적이 없다면... 특별한 단일 값으로, 파이썬의 일부이며 "줄임표"라고 불립니다.

Pydantic과 FastAPI에서 값이 필요하다는 것을 명시적으로 선언하는 데 사용됩니다.


이렇게 하면 FastAPI가 이 매개변수가 필요하다는 것을 알 수 있습니다.

Required with None

매개변수가 None을 허용할 수 있지만 여전히 필수라고 선언할 수 있습니다. 이렇게 하면 값이 None인 경우에도 클라이언트가 강제로 값을 전송하게 됩니다.

이렇게 하려면 None이 유효한 유형이지만 여전히 ...을 기본값으로 사용한다고 선언하면 됩니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳Tip

FastAPI의 모든 데이터 유효성 검사 및 직렬화를 지원하는 Pydantic은 기본값 없이 Optional 또는 Union[Something, None]을 사용할 때 특별한 동작이 있으며, 이에 대한 자세한 내용은 필수 옵션 필드에 대한 Pydantic 문서에서 확인할 수 있습니다.


줄임표(...) 대신 Pydantic의 필수 사용

...사용이 불편하다면 Pydantic에서 Required를 가져와서 사용할 수도 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query
from pydantic import Required

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = Required):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳Tip

대부분의 경우 필수 항목이 필요한 경우 기본값을 생략할 수 있으므로 일반적으로 ... 또는 Required를 사용할 필요가 없다는 점을 기억하세요.


쿼리 매개변수 목록/복수 값

쿼리를 사용하여 쿼리 매개변수를 명시적으로 정의할 때 값 목록을 받도록 선언하거나 다른 방식으로 여러 값을 받도록 선언할 수도 있습니다.

예를 들어 URL에 여러 번 나타날 수 있는 쿼리 매개변수 q를 선언하려면 다음과 같이 작성할 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

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

그런 다음 다음과 같은 URL을 입력합니다:

http://localhost:8000/items/?q=foo&q=bar

를 사용하면 경로 연산 함수 내부의 함수 매개변수 q에서 여러 쿼리 매개변수 값(foo 및 bar)을 Python 목록으로 받을 수 있습니다.

따라서 해당 URL에 대한 응답은 다음과 같습니다:

{
  "q": [
    "foo",
    "bar"
  ]
}

🍳Tip

위의 예와 같이 목록 유형으로 쿼리 매개변수를 선언하려면 명시적으로 Query를 사용해야 하며, 그렇지 않으면 요청 본문으로 해석됩니다.


대화형 API 문서가 그에 따라 업데이트되어 여러 값을 사용할 수 있습니다:

쿼리 매개변수 목록 / 기본값이 있는 여러 값

기본값 목록이 제공되지 않는 경우 기본값 list를 정의할 수도 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[list[str], Query()] = ["foo", "bar"]):
    query_items = {"q": q}
    return query_items

아래로 이동하면:

http://localhost:8000/items/

의 기본값은 q입니다: ["foo", "bar"]가 되고 응답은 다음과 같습니다:

{
  "q": [
    "foo",
    "bar"
  ]
}

list 사용

List[str](또는 파이썬 3.9 이상에서는 list[str]) 대신 list를 직접 사용할 수도 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
    query_items = {"q": q}
    return query_items

🍳Note

이 경우 FastAPI는 목록의 내용을 확인하지 않는다는 점에 유의하세요.

예를 들어 List[int]는 목록의 내용이 정수인지 확인하고 문서화합니다. 하지만 목록만으로는 그렇지 않습니다.


메타데이터 추가 선언

매개변수에 대한 자세한 정보를 추가할 수 있습니다.

이 정보는 생성된 OpenAPI에 포함되며 문서 사용자 인터페이스 및 외부 도구에서 사용됩니다.

🍳Note

도구마다 OpenAPI 지원 수준이 다를 수 있다는 점에 유의하세요.

일부 도구는 아직 선언된 추가 정보를 모두 표시하지 않을 수도 있지만, 대부분의 경우 누락된 기능은 이미 개발 계획이 있는 기능입니다.

title을 추가할 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(title="Query string", min_length=3)] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

그리고 설명:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None,
        Query(
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
        ),
    ] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

별칭 매개변수

매개 변수가 item-query라고 가정해 보겠습니다.

아래처럼::

http://127.0.0.1:8000/items/?item-query=foobaritems

하지만 item-query는 유효한 Python 변수 이름이 아닙니다.

가장 가까운 것은 item_query입니다.

하지만 여전히 정확히 item-query여야 합니다...

그런 다음 별칭을 선언할 수 있으며, 이 별칭은 매개변수 값을 찾는 데 사용됩니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(alias="item-query")] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

매개변수 사용 중단

이제 이 매개변수가 더 이상 마음에 들지 않는다고 가정해 보겠습니다.

이 기능을 사용하는 클라이언트가 있기 때문에 당분간 그대로 두어야 하지만 문서에 사용 중단된 것으로 명확하게 표시하고 싶을 것입니다.

그런 다음 deprecated=True 매개 변수를 Query에 전달합니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None,
        Query(
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            pattern="^fixedquery$",
            deprecated=True,
        ),
    ] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

문서에는 다음과 같이 표시됩니다:

OpenAPI에서 제외

생성된 OpenAPI 스키마에서 쿼리 매개 변수를 제외하려면(따라서 자동 문서화 시스템에서 제외하려면) query의 include_in_schema 매개 변수를 False로 설정합니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    hidden_query: Annotated[str | None, Query(include_in_schema=False)] = None
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}

요약

매개변수에 대한 추가 유효성 검사 및 메타데이터를 선언할 수 있습니다.

일반 유효성 검사 및 메타데이터:

  • 별칭
  • title
  • 설명
  • 사용 중단됨

문자열에 특정한 유효성 검사:

  • min_length
  • max_length
  • regex

이 예제에서는 문자열 값에 대한 유효성 검사를 선언하는 방법을 살펴보았습니다.

숫자와 같은 다른 유형에 대한 유효성 검사를 선언하는 방법은 다음 챕터를 참조하세요.

쿼리 매개변수와 문자열 유효성 검사

FastAPI를 사용하면 매개변수에 대한 추가 정보 및 유효성 검사를 선언할 수 있습니다.

이 애플리케이션을 예로 들어 보겠습니다:

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(q: str | None = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

쿼리 매개변수 q는 Union[str, None](또는 파이썬 3.10에서는 str | None) 유형으로, 이는 str 유형이지만 None일 수도 있음을 의미하며 실제로 기본값은 None이므로 FastAPI는 이 매개변수가 필요하지 않다는 것을 알 수 있습니다.

🍳Note

FastAPI는 기본값이 없음이므로 q 값이 필요하지 않다는 것을 알 수 있습니다.

Union[str, None]의 Union을 사용하면 편집기에서 더 나은 지원을 제공하고 오류를 감지할 수 있습니다.

추가적인 유효성 검사

q는 선택 사항이지만 제공될 때마다 길이가 50자를 초과하지 않도록 강제할 것입니다.

Import Query

이를 위해서는 먼저 가져오기를 수행합니다:

  • Query from fastapi
  • 타이핑(또는 3.9 미만 파이썬의 타이핑_확장)을 통해 주석 달기

Python 3.9 이상에서 주석은 표준 라이브러리의 일부이므로 입력할 때 가져올 수 있습니다.

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳INFO

FastAPI는 버전 0.95.0에서 Annotated에 대한 지원을 추가하고 이를 권장하기 시작했습니다.

이전 버전을 사용하는 경우 주석 기능을 사용하려고 할 때 오류가 발생할 수 있습니다.

주석 달기를 사용하기 전에 FastAPI 버전을 0.95.1 이상으로 업그레이드해야 합니다.

q 매개변수의 유형에 주석을 사용하세요.

이전에 파이썬 타입 인트로에서 주석이 달린 매개변수에 메타데이터를 추가하는 데 사용할 수 있다고 말씀드린 것을 기억하시나요?

이제 FastAPI와 함께 사용할 차례입니다. 🚀

이 타입 어노테이션이 있습니다:

q: str | None = None

우리가 할 일은 그것을 주석으로 감싸서 이렇게 만드는 것입니다:

q: Annotated[str | None] = None

두 버전 모두 동일한 의미를 가지며, q는 문자열 또는 None일 수 있는 매개변수이며 기본값은 None입니다.

이제 재미있는 부분으로 넘어가 보겠습니다. 🎉

q 매개변수의 주석에 쿼리를 추가합니다.

이제 더 많은 메타데이터를 넣을 수 있는 곳에 주석을 달았으므로 여기에 Query를 추가하고 매개변수 max_length를 50으로 설정합니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

기본값은 여전히 없음이므로 매개 변수는 여전히 선택 사항입니다.

하지만 이제 Annotated 안에 Query(max_length=50)를 넣으면, 쿼리 매개변수에서 이 값을 추출하고(어차피 기본값이었을 것입니다 🤷), 이 값에 대한 추가 유효성 검사를 수행하기를 원한다고 FastAPI에 알려줍니다(추가 유효성 검사를 수행하기 위해 이렇게 하는 것입니다). 😎

이제 FastAPI가

  • 최대 길이가 50자인지 확인하여 데이터의 유효성을 검사합니다.
  • 데이터가 유효하지 않은 경우 클라이언트에게 명확한 오류를 표시합니다.
  • OpenAPI 스키마 경로 작업에서 매개변수를 문서화합니다(자동 문서 UI에 표시되도록).

기본값으로 대체 (이전) 쿼리 사용

이전 버전의 FastAPI(0.95.0 이전 버전)에서는 매개변수의 기본값으로 Query를 주석에 넣는 대신 Annotated를 사용해야 했는데, 이를 사용하는 코드를 주변에서 볼 가능성이 높기 때문에 설명해 드리겠습니다.

🍳Tip

새 코드의 경우 가능하면 위에서 설명한 대로 주석을 사용하세요. 여러 가지 장점(아래 설명)이 있으며 단점은 없습니다. 🍰

Query()를 함수 매개변수의 기본값으로 사용하고 매개변수 max_length를 50으로 설정하는 방식입니다:

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

이 경우 (Annotated를 사용하지 않고) 함수의 기본값 None을 Query()로 대체해야 하므로 이제 매개변수 Query(default=None)로 기본값을 설정해야 하며, 이는 기본값을 정의하는 동일한 목적을 수행합니다(적어도 FastAPI의 경우).

So:

q: Union[str, None] = Query(default=None)

...매개 변수를 선택 사항으로 설정하며 기본값은 없음과 동일합니다:

q: Union[str, None] = None

그리고 Python 3.10 이상에서:

q: str | None = Query(default=None)

...매개 변수를 선택 사항으로 설정하며 기본값은 없음과 동일합니다:

q: str | None = None

하지만 쿼리 매개변수로 명시적으로 선언합니다.


🍳Info

매개변수를 선택 사항으로 만드는 데 가장 중요한 부분은 부품이라는 점을 명심하세요:

= None 또는

= Query(default=None) 를 사용하면 None을 기본값으로 사용하므로 매개변수가 필요하지 않게 됩니다.

Union[str, None] 부분을 사용하면 편집기에서 더 나은 지원을 제공할 수 있지만, 이 매개변수가 필요하지 않다는 것을 FastAPI에 알리는 것은 아닙니다.


그런 다음 쿼리에 더 많은 매개변수를 전달할 수 있습니다. 이 경우 문자열에 적용되는 최대 길이 매개변수입니다:

q: Union[str, None] = Query(default=None, max_length=50)

이렇게 하면 데이터의 유효성을 검사하고, 데이터가 유효하지 않은 경우 명확한 오류를 표시하며, OpenAPI 스키마 경로 작업에서 매개 변수를 문서화합니다.

쿼리를 기본값으로 사용하거나 주석이 달린

Annotated 내에서 쿼리를 사용할 때는 쿼리의 기본 매개 변수를 사용할 수 없다는 점에 유의하세요.

대신 함수 매개변수의 실제 기본값을 사용하세요. 그렇지 않으면 일관성이 떨어집니다.

예를 들어, 이것은 허용되지 않습니다:

예를 들어, 이것은 허용되지 않습니다:

q: Annotated[str, Query(default="rick")] = "morty"

...기본값이 '릭'인지 '모티'인지 명확하지 않기 때문입니다.

따라서 (가급적):

q: Annotated[str, Query()] = "rick"

...또는 이전 코드베이스에서 찾을 수 있습니다:

q: str = Query(default="rick")

주석 처리의 장점

함수 매개변수의 기본값 대신 주석이 달린 것을 사용하는 것이 여러 가지 이유로 더 좋습니다. 🤓

함수 매개변수의 기본값은 실제 기본값이며, 일반적으로 파이썬에서는 이 기본값이 더 직관적입니다. 😌

FastAPI 없이도 다른 곳에서 동일한 함수를 호출할 수 있으며 예상대로 작동합니다. 기본값이 없는 필수 매개변수가 있는 경우 편집기에서 오류를 통해 알려주며, 필수 매개변수를 전달하지 않고 실행하면 Python에서도 오류가 발생합니다.

Annotated를 사용하지 않고 (이전) 기본값 스타일을 사용하는 경우 다른 곳에서 FastAPI 없이 해당 함수를 호출하는 경우 함수가 올바르게 작동하려면 함수에 인수를 전달해야 하며, 그렇지 않으면 값이 예상과 다를 수 있습니다(예: str 대신 QueryInfo 또는 이와 유사한 것). 그러면 에디터도 불평하지 않으며, 파이썬도 내부 연산에 오류가 발생할 때만 해당 함수를 실행해도 불평하지 않습니다.

Annotated는 메타데이터 주석을 두 개 이상 가질 수 있으므로 이제 Typer와 같은 다른 도구에서도 동일한 함수를 사용할 수 있습니다. 🚀

더 많은 유효성 검사 추가

min_length 매개변수를 추가할 수도 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(min_length=3, max_length=50)] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

정규식 추가

매개변수가 일치해야 하는 정규식 패턴을 정의할 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")
    ] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

이 특정 정규식 패턴은 수신된 매개변수 값이 맞는지 확인합니다:

  • ^: 다음 문자로 시작하며, 이전에는 문자가 없습니다.
  • 고정 쿼리: 정확한 값 고정 쿼리를 가집니다.
  • $: 거기서 끝나고, 고정 쿼리 뒤에 더 이상 문자가 없습니다.

이 모든 '정규식' 아이디어가 막막하게 느껴지더라도 걱정하지 마세요. 정규식은 많은 사람들에게 어려운 주제입니다. 아직 정규식을 사용하지 않고도 많은 작업을 수행할 수 있습니다.

하지만 정규식이 필요할 때 언제든 가서 배우면 이미 FastAPI에서 직접 사용할 수 있다는 것을 알아두세요.

패턴 대신 Pydantic v1 정규식 사용

Pydantic 버전 2 이전과 FastAPI 0.100.0 이전에는 이 매개 변수를 패턴 대신 정규식이라고 불렀지만 이제 더 이상 사용되지 않습니다.

여전히 이 매개변수를 사용하는 코드를 볼 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None, Query(min_length=3, max_length=50, regex="^fixedquery$")
    ] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

하지만 이 기능은 더 이상 사용되지 않으며 새 매개변수 패턴을 사용하려면 업데이트해야 합니다. 🤓

기본값

물론 없음이 아닌 기본값을 사용할 수도 있습니다.

q 쿼리 매개 변수의 최소 길이를 3으로, 기본값을 "fixedquery"로 선언하고 싶다고 가정해 보겠습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳Note

기본값이 None(없음)을 포함한 모든 유형이면 매개변수는 선택 사항(필수가 아님)이 됩니다.


필수 설정

더 많은 유효성 검사나 메타데이터를 선언할 필요가 없는 경우에는 다음과 같이 기본값을 선언하지 않는 것만으로도 쿼리 매개변수를 필수로 설정할 수 있습니다:

q: str

대신에:

q: Union[str, None] = None

하지만 이제는 다음과 같이 쿼리로 선언하고 있습니다:

q: Annotated[Union[str, None], Query(min_length=3)] = None

따라서 쿼리를 사용하는 동안 필요에 따라 값을 선언해야 하는 경우 기본값을 선언하지 않을 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

줄임표(...)와 함께 사용해야 합니다.

값이 필요하다는 것을 명시적으로 선언하는 다른 방법이 있습니다. 기본값을 리터럴 값으로 설정할 수 있습니다...:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳Info

이전에 본 적이 없다면... 특별한 단일 값으로, 파이썬의 일부이며 "줄임표"라고 불립니다.

Pydantic과 FastAPI에서 값이 필요하다는 것을 명시적으로 선언하는 데 사용됩니다.


이렇게 하면 FastAPI가 이 매개변수가 필요하다는 것을 알 수 있습니다.

Required with None

매개변수가 None을 허용할 수 있지만 여전히 필수라고 선언할 수 있습니다. 이렇게 하면 값이 None인 경우에도 클라이언트가 강제로 값을 전송하게 됩니다.

이렇게 하려면 None이 유효한 유형이지만 여전히 ...을 기본값으로 사용한다고 선언하면 됩니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳Tip

FastAPI의 모든 데이터 유효성 검사 및 직렬화를 지원하는 Pydantic은 기본값 없이 Optional 또는 Union[Something, None]을 사용할 때 특별한 동작이 있으며, 이에 대한 자세한 내용은 필수 옵션 필드에 대한 Pydantic 문서에서 확인할 수 있습니다.


줄임표(...) 대신 Pydantic의 필수 사용

...사용이 불편하다면 Pydantic에서 Required를 가져와서 사용할 수도 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query
from pydantic import Required

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = Required):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

🍳Tip

대부분의 경우 필수 항목이 필요한 경우 기본값을 생략할 수 있으므로 일반적으로 ... 또는 Required를 사용할 필요가 없다는 점을 기억하세요.


쿼리 매개변수 목록/복수 값

쿼리를 사용하여 쿼리 매개변수를 명시적으로 정의할 때 값 목록을 받도록 선언하거나 다른 방식으로 여러 값을 받도록 선언할 수도 있습니다.

예를 들어 URL에 여러 번 나타날 수 있는 쿼리 매개변수 q를 선언하려면 다음과 같이 작성할 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

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

그런 다음 다음과 같은 URL을 입력합니다:

http://localhost:8000/items/?q=foo&q=bar

를 사용하면 경로 연산 함수 내부의 함수 매개변수 q에서 여러 쿼리 매개변수 값(foo 및 bar)을 Python 목록으로 받을 수 있습니다.

따라서 해당 URL에 대한 응답은 다음과 같습니다:

{
  "q": [
    "foo",
    "bar"
  ]
}

🍳Tip

위의 예와 같이 목록 유형으로 쿼리 매개변수를 선언하려면 명시적으로 Query를 사용해야 하며, 그렇지 않으면 요청 본문으로 해석됩니다.


대화형 API 문서가 그에 따라 업데이트되어 여러 값을 사용할 수 있습니다:

쿼리 매개변수 목록 / 기본값이 있는 여러 값

기본값 목록이 제공되지 않는 경우 기본값 list를 정의할 수도 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[list[str], Query()] = ["foo", "bar"]):
    query_items = {"q": q}
    return query_items

아래로 이동하면:

http://localhost:8000/items/

의 기본값은 q입니다: ["foo", "bar"]가 되고 응답은 다음과 같습니다:

{
  "q": [
    "foo",
    "bar"
  ]
}

list 사용

List[str](또는 파이썬 3.9 이상에서는 list[str]) 대신 list를 직접 사용할 수도 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
    query_items = {"q": q}
    return query_items

🍳Note

이 경우 FastAPI는 목록의 내용을 확인하지 않는다는 점에 유의하세요.

예를 들어 List[int]는 목록의 내용이 정수인지 확인하고 문서화합니다. 하지만 목록만으로는 그렇지 않습니다.


메타데이터 추가 선언

매개변수에 대한 자세한 정보를 추가할 수 있습니다.

이 정보는 생성된 OpenAPI에 포함되며 문서 사용자 인터페이스 및 외부 도구에서 사용됩니다.

🍳Note

도구마다 OpenAPI 지원 수준이 다를 수 있다는 점에 유의하세요.

일부 도구는 아직 선언된 추가 정보를 모두 표시하지 않을 수도 있지만, 대부분의 경우 누락된 기능은 이미 개발 계획이 있는 기능입니다.

title을 추가할 수 있습니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(title="Query string", min_length=3)] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

그리고 설명:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None,
        Query(
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
        ),
    ] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

별칭 매개변수

매개 변수가 item-query라고 가정해 보겠습니다.

아래처럼::

http://127.0.0.1:8000/items/?item-query=foobaritems

하지만 item-query는 유효한 Python 변수 이름이 아닙니다.

가장 가까운 것은 item_query입니다.

하지만 여전히 정확히 item-query여야 합니다...

그런 다음 별칭을 선언할 수 있으며, 이 별칭은 매개변수 값을 찾는 데 사용됩니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(alias="item-query")] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

매개변수 사용 중단

이제 이 매개변수가 더 이상 마음에 들지 않는다고 가정해 보겠습니다.

이 기능을 사용하는 클라이언트가 있기 때문에 당분간 그대로 두어야 하지만 문서에 사용 중단된 것으로 명확하게 표시하고 싶을 것입니다.

그런 다음 deprecated=True 매개 변수를 Query에 전달합니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None,
        Query(
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            pattern="^fixedquery$",
            deprecated=True,
        ),
    ] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

문서에는 다음과 같이 표시됩니다:

OpenAPI에서 제외

생성된 OpenAPI 스키마에서 쿼리 매개 변수를 제외하려면(따라서 자동 문서화 시스템에서 제외하려면) query의 include_in_schema 매개 변수를 False로 설정합니다:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    hidden_query: Annotated[str | None, Query(include_in_schema=False)] = None
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}

요약

매개변수에 대한 추가 유효성 검사 및 메타데이터를 선언할 수 있습니다.

일반 유효성 검사 및 메타데이터:

  • 별칭
  • title
  • 설명
  • 사용 중단됨

문자열에 특정한 유효성 검사:

  • min_length
  • max_length
  • regex

이 예제에서는 문자열 값에 대한 유효성 검사를 선언하는 방법을 살펴보았습니다.

숫자와 같은 다른 유형에 대한 유효성 검사를 선언하는 방법은 다음 챕터를 참조하세요.

반응형

 

반응형
LIST