개요
필자는 주로 FastAPI를 사용해서 백엔드 개발을 한다.
FastAPI의 자유로움과 성능에 반해 FastAPI를 주로 사용하고 있지만 아직 1.x.x 번대 버전이 출시되지도 않은 만큼 자잘한 버그들이 있는 편이다.
해당 포스트에서는 FastAPI의 버그 중 하나인 매개변수에 별칭(alias) 사용 시 실제 요청에는 해당 매개변수를 포함했음에도 불구하고 매개변수를 찾을 수 없다는 오류를 반환하는 문제에 대해 설명하고자 한다.
버전
- FastAPI: 0.115.14
- Pydantic: 2.11.7
본론
FastAPI는 Pydantic을 기반으로 타입 체크와 타입 힌트를 제공한다.
따라서, 요청에 Pydantic의 BaseModel를 상속받는 클래스를 사용해서 매개변수를 관리(타입 체킹, 포함 여부 등) 할 수 있다.
FastAPI Github의 fastapi/dependencies/utils.py의 _validate_value_with_model_field 함수를 보면 ModelField를 인자로 받아 검증(validate, required)하는 코드가 포함된 것을 확인할 수 있다.
따라서 요청의 인자에 Annotated와 BaseModel을 상속받은 클래스, FastAPI에서 제공하는 요청 매개변수 클래스(Form, Body 등)을 적절히 조합하면 어떤 데이터를 어떤 방법으로 받을 지 간단하게 표현할 수 있다.
from pydantic import BaseModel, Field
from typing import Annotated
from fastapi import FastAPI, Query
USERS = [
{"id": 1, "name": "Alice", "type": "admin"},
{"id": 2, "name": "Bob", "type": "user"},
{"id": 3, "name": "Charlie", "type": "guest"},
]
class SearchModel(BaseModel):
type: str = Field(...) # type은 필수로 입력되어야 한다.
app = FastAPI()
@app.get("/param/query")
def get_users_with_query(
# SearchModel을 Url Query 형식으로 받는다.
data: Annotated[SearchModel, Query(...)]
):
return list(filter(lambda user: user["type"] == data.type, USERS))

Pydantic의 Field는 기본적으로 해당 필드의 변수명으로 유효성 검사와 직렬화하지만,
alias 속성을 사용하면 개발자가 지정한 다른 이름으로 유효성 검사 또는 직렬화할 때 사용할 필드의 이름을 지정할 수 있다.
Pydantic의 공식 문서에서 Field의 alias 인자에 대한 설명을 확인해 보면 다음과 같이 설명되어 있다.
The name to use for the attribute when validating or serializing by alias.
This is often used for things like converting between snake and camel case.
별칭으로 유효성을 검사하거나 직렬화할 때 속성에 사용할 이름입니다.
스네이크 케이스와 카멜 케이스를 변환하는 등의 작업에 자주 사용됩니다.
따라서 FastAPI에서도 BaseModel 내의 Field에 alias를 지정하면 지정한 alias로 유효성 검사와 직렬화해줄 것이라고 생각하는것이 당연하지만 실제로 매개변수 필드에 alias를 지정하면 "[422] Field Required" 에러가 반환된다.
from pydantic import BaseModel, Field
from typing import Annotated
from fastapi import FastAPI, Query
USERS = [
{"id": 1, "name": "Alice", "type": "admin"},
{"id": 2, "name": "Bob", "type": "user"},
{"id": 3, "name": "Charlie", "type": "guest"},
]
class SearchModel(BaseModel):
type: str = Field(..., alias="user_type")
app = FastAPI()
@app.get("/param/query")
def get_users_with_query(
data: Annotated[SearchModel, Query(...)]
):
return list(filter(lambda user: user["type"] == data.type, USERS))

alias를 추가해 "type"필드의 이름을 "user_type"으로 지정했는데 openapi.json에는 "user_type"으로 정상적으로 표기되지만 실제로 값을 넣어 API 호출 시 에러가 발생하는 것을 확인할 수 있다.
이는 FastAPI에서 요청 매개변수를 전달 인자(arguments)로 변환하는 함수(request_query_to_args)에서 alias를 찾았음에도 params_to_process 딕셔너리의 key로 alias가 아니라 필드의 기본 name을 넣기 때문에 발생한다.
fastapi/fastapi/dependencies/utils.py at ebdeda2de6e17036e3048940b7a9e725ef6a95b7 · fastapi/fastapi
FastAPI framework, high performance, easy to learn, fast to code, ready for production - fastapi/fastapi
github.com
이 문제는 Query, Cookie, Header를 사용할 시 발생하며 Body, Form 사용 시에는 별개의 변환 함수인 request_body_to_args를 사용하기 때문에 발생하지 않는다. (이 함수에서는 정상적으로 alias가 딕셔너리의 key로 지정되어 있다.)
fastapi/fastapi/dependencies/utils.py at ebdeda2de6e17036e3048940b7a9e725ef6a95b7 · fastapi/fastapi
FastAPI framework, high performance, easy to learn, fast to code, ready for production - fastapi/fastapi
github.com
from pydantic import BaseModel, Field
from typing import Annotated
from fastapi import FastAPI, Body
USERS = [
{"id": 1, "name": "Alice", "type": "admin"},
{"id": 2, "name": "Bob", "type": "user"},
{"id": 3, "name": "Charlie", "type": "guest"},
]
class SearchModel(BaseModel):
type: str = Field(..., alias="user_type")
app = FastAPI()
@app.post("/param/body")
def get_users_with_body(
data: Annotated[SearchModel, Body(...)]
):
return list(filter(lambda user: user["type"] == data.type, USERS))

따라서, 우리가 쿼리, 쿠키, 헤더 매개변수의 유효성 검증, 직렬화 시에 사용할 이름을 변경하고 싶다면 request_query_to_args의 코드를 다음과 같이 변경해야 한다.
As-Is
params_to_process[field.name] = value
To-Be
params_to_process[field.alias] = value
추가
Pydantic의 Field는 alias 말고도 validation_alias, serialization_alias라는 인자들이 존재한다.
validation_alias는 유효성 검증, serialization_alias는 직렬화 시 사용할 이름으로 alias가 담당하던 두 역할을 분리한 인자들이다.
따라서 Field의 validation_alias를 지정하면 FastAPI에서 생성한 openapi.json에서 해당 필드의 이름도 변경되어야 할 거라고 생각되지만 실제로 확인해보면 그대로인 것을 확인할 수 있다.
from pydantic import BaseModel, Field
from typing import Annotated
from fastapi import FastAPI, Query
USERS = [
{"id": 1, "name": "Alice", "type": "admin"},
{"id": 2, "name": "Bob", "type": "user"},
{"id": 3, "name": "Charlie", "type": "guest"},
]
class SearchModel(BaseModel):
type: str = Field(..., validation_alias="user_type")
app = FastAPI()
@app.get("/param/query")
def get_users_with_query(
data: Annotated[SearchModel, Query(...)]
):
return list(filter(lambda user: user["type"] == data.type, USERS))

이 상태로 Swagger에서 서버에 요청을 보내보면 "type"으로 요청을 보내기 때문에 422 Unprocessable Content 에러가 발생한다.

이는 FastAPI의 ModelField에서 alias 속성을 mode와 상관 없이 alias가 지정되어 있다면 alias, 아니면 name을 반환하도록 하기 때문이다.
@dataclass
class ModelField:
field_info: FieldInfo
name: str
mode: Literal["validation", "serialization"] = "validation"
@property
def alias(self) -> str:
a = self.field_info.alias
return a if a is not None else self.name
따라서 위 코드를 다음과 같이 수정하면 정상적으로 생성된 openapi.json을 받아볼 수 있다.
@dataclass
class ModelField:
field_info: FieldInfo
name: str
mode: Literal["validation", "serialization"] = "validation"
@property
def alias(self) -> str:
if (
self.mode == "validation"
and self.field_info.validation_alias is not None
):
a = self.field_info.validation_alias
elif (
self.mode == "serialization"
and self.field_info.serialization_alias is not None
):
a = self.field_info.serialization_alias
else:
a = self.field_info.alias
return a if a is not None else self.name
첫번째 문제는 이미 요청된 PR이 있지만 두번째 문제에 대한 PR은 아직 없는 것 같아 현재 작성 중에 있다.
https://github.com/fastapi/fastapi/pull/13833
Fix ModelField alias property by pressogh · Pull Request #13833 · fastapi/fastapi
Hello! I've encountered an issue in FastAPI's openapi.json generation for parameter inputs (Query, Header, Cookie). When using alias for field names in Pydantic models, the alias appears co...
github.com
Reference
https://fastapi.tiangolo.com/tutorial/query-params/
Query Parameters - FastAPI
FastAPI framework, high performance, easy to learn, fast to code, ready for production
fastapi.tiangolo.com
https://github.com/fastapi/fastapi/pull/13203
🐛 Fix parameter models with alias by AMBase · Pull Request #13203 · fastapi/fastapi
Hello! My PR fixes a problem related to the use of aliases inside parameter models. Here is an example of how I caught this bug. from fastapi import FastAPI, Cookie from pydantic import BaseModel, ...
github.com
'Language > Python' 카테고리의 다른 글
| [SQLAlchemy] 동시성 제어 (Optimistic Lock / Pessimistic Lock) (0) | 2025.06.28 |
|---|