경로 파라미터 (Path Parameters)
경로 파라미터 (Path Parameters)
경로 파라미터(또는 경로 변수)는 Python의 format 문자열과 같은 문법으로 선언할 수 있어요.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
경로 파라미터 item_id의 값은 함수의 인자 item_id로 전달돼요.
이 예제를 실행하고 http://127.0.0.1:8000/items/foo에 접속하면 이런 응답을 볼 수 있어요.
{"item_id":"foo"}
타입이 있는 경로 파라미터
경로 파라미터의 타입은 표준 Python 타입 어노테이션을 사용해 함수에서 선언할 수 있어요.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
이 경우 item_id는 int로 선언됐어요.
팁
이렇게 하면 함수 안에서 오류 검사나 자동 완성 같은 편집기 지원을 받을 수 있어요.
데이터 변환
이 예제를 실행하고 브라우저에서 http://127.0.0.1:8000/items/3을 열면 이런 응답이 보여요.
{"item_id":3}
팁
함수가 받은(그리고 반환한) 값이 문자열
"3"이 아니라 Pythonint인3이라는 점에 주목하세요.
그래서 이 타입 선언만으로 FastAPI는 자동으로 요청을 "파싱"해 줘요.
데이터 검증
하지만 브라우저에서 http://127.0.0.1:8000/items/foo로 접속하면 이렇게 깔끔한 HTTP 오류를 볼 수 있어요.
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
경로 파라미터 item_id의 값이 "foo"였고, 이건 int가 아니기 때문이에요.
int 대신 float를 넣어도 같은 오류가 나요. 예를 들어 http://127.0.0.1:8000/items/4.2처럼요.
팁
그래서 같은 Python 타입 선언으로 FastAPI는 데이터 검증까지 해 줘요.
오류 메시지가 검증이 통과하지 못한 정확한 지점까지 명확히 알려준다는 점도 눈여겨보세요.
이건 API와 상호작용하는 코드를 개발하고 디버깅할 때 엄청나게 도움이 돼요.
문서화
브라우저에서 http://127.0.0.1:8000/docs를 열면 자동으로 만들어지는 대화형 API 문서를 볼 수 있어요.

팁
역시 같은 Python 타입 선언만으로 FastAPI는 (Swagger UI를 통합한) 자동 대화형 문서를 만들어 줘요.
경로 파라미터가 정수로 선언된 것도 확인할 수 있어요.
표준 기반의 이점, 대체 문서
생성된 스키마가 OpenAPI 표준을 따르기 때문에 호환되는 도구가 아주 많아요.
그 덕분에 FastAPI 자체도 (ReDoc을 사용한) 대체 API 문서를 제공해요. http://127.0.0.1:8000/redoc에서 접근할 수 있어요.

마찬가지로 여러 언어용 코드 생성 도구를 포함해 호환 도구가 정말 많아요.
Pydantic
모든 데이터 검증은 내부적으로 Pydantic이 처리해요. 그래서 Pydantic의 모든 이점을 그대로 누릴 수 있고, 안전한 선택이라는 걸 알 수 있어요.
str, float, bool과 그 밖의 여러 복잡한 데이터 타입에도 같은 타입 선언을 사용할 수 있어요.
이 중 몇 가지는 튜토리얼의 다음 장에서 다뤄요.
순서가 중요해요
경로 동작을 만들다 보면 고정된 경로를 가진 상황을 만나게 돼요.
예를 들어 /users/me가 현재 사용자의 데이터를 가져오는 경로라고 해 볼게요.
그리고 특정 사용자의 ID로 데이터를 가져오는 /users/{user_id} 경로도 있을 수 있죠.
경로 동작은 순서대로 평가되기 때문에, /users/me 경로가 /users/{user_id} 경로보다 먼저 선언되도록 해야 해요.
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/me")
async def read_user_me():
return {"user_id": "the current user"}
@app.get("/users/{user_id}")
async def read_user(user_id: str):
return {"user_id": user_id}
그렇지 않으면 /users/{user_id} 경로가 /users/me도 매칭해서, user_id 파라미터에 "me"라는 값이 들어온다고 "생각"하게 돼요.
마찬가지로 경로 동작을 다시 정의할 수는 없어요.
from fastapi import FastAPI
app = FastAPI()
@app.get("/users")
async def read_users():
return ["Rick", "Morty"]
@app.get("/users")
async def read_users2():
return ["Bean", "Elfo"]
경로가 먼저 매칭되기 때문에 첫 번째 동작이 항상 사용돼요.
미리 정의된 값
경로 파라미터를 받는 경로 동작이 있는데, 가능한 유효한 경로 파라미터 값을 미리 정해두고 싶다면 표준 Python Enum을 사용할 수 있어요.
Enum 클래스 만들기
Enum을 import 하고, str과 Enum을 상속하는 서브 클래스를 만들어요.
str을 상속하면 API 문서가 값들이 string 타입이어야 한다는 걸 알 수 있고, 올바르게 렌더링할 수 있어요.
그 다음 고정된 값의 클래스 속성을 만들면, 그 값들이 사용 가능한 유효한 값이 돼요.
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
팁
궁금하다면, "AlexNet", "ResNet", "LeNet"은 머신러닝 모델의 이름일 뿐이에요.
경로 파라미터 선언하기
그 다음 만든 enum 클래스(ModelName)를 타입 어노테이션으로 사용해 경로 파라미터를 만들면 돼요.
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
문서 확인하기
경로 파라미터에 사용 가능한 값이 미리 정의되어 있기 때문에 대화형 문서에서 그 값들을 깔끔하게 보여줄 수 있어요.

Python 열거형 다루기
경로 파라미터의 값은 열거형 멤버가 돼요.
열거형 멤버 비교하기
만든 enum ModelName의 열거형 멤버와 비교할 수 있어요.
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
열거형 값 가져오기
model_name.value를 사용해서 실제 값(여기서는 str)을 가져올 수 있어요. 일반적으로는 your_enum_member.value처럼요.
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
팁
ModelName.lenet.value로"lenet"값에 접근할 수도 있어요.
열거형 멤버 반환하기
경로 동작에서 enum 멤버를 반환할 수 있어요. JSON 본문(예: dict)에 중첩된 경우에도요.
클라이언트에 반환하기 전에 해당 값들(여기서는 문자열)로 변환돼요.
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
클라이언트에서 다음과 같은 JSON 응답을 받게 돼요.
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
경로를 포함하는 경로 파라미터
경로가 /files/{file_path}인 경로 동작이 있다고 해 볼게요.
그런데 file_path 자체에 home/johndoe/myfile.txt 같은 경로가 포함되어야 한다고 해요.
그렇다면 그 파일의 URL은 /files/home/johndoe/myfile.txt 같은 모양이 되겠죠.
OpenAPI 지원
OpenAPI는 경로 파라미터 안에 경로를 포함하도록 선언하는 방법을 지원하지 않아요. 테스트하고 정의하기 어려운 시나리오로 이어질 수 있기 때문이에요.
그럼에도 FastAPI에서는 Starlette의 내부 도구 중 하나를 사용해 여전히 할 수 있어요.
그리고 파라미터가 경로를 포함해야 한다는 문서화를 추가하지 않더라도 문서는 여전히 동작해요.
경로 변환기
Starlette에서 직접 제공하는 옵션을 사용하면 다음과 같은 URL로 경로를 포함하는 경로 파라미터를 선언할 수 있어요.
/files/{file_path:path}
이 경우 파라미터 이름은 file_path이고, 마지막 부분인 :path는 파라미터가 어떤 경로와도 매칭되어야 한다는 뜻이에요.
그래서 이렇게 사용할 수 있어요.
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
팁
파라미터에 앞에 슬래시(
/)가 있는/home/johndoe/myfile.txt가 포함되어야 할 수도 있어요.그 경우 URL은
files와home사이에 슬래시가 두 개(//) 들어간/files//home/johndoe/myfile.txt모양이 돼요.
정리
FastAPI에서 짧고 직관적이며 표준적인 Python 타입 선언만으로 다음을 얻을 수 있어요.
- 편집기 지원: 오류 검사, 자동 완성 등
- 데이터 "파싱"
- 데이터 검증
- API 어노테이션과 자동 문서화
그리고 이걸 한 번만 선언하면 돼요.
이게 아마 (raw 성능 외에) 대체 프레임워크와 비교했을 때 FastAPI의 가장 눈에 띄는 장점일 거예요.