[FastAPI] add_api_route로 동적 라우팅 구현
FastAPI를 사용하면서 동적으로 라우트를 추가해야 하는 경우가 종종 있습니다. 이럴 때 사용할 수 있는 것이 바로 add_api_route
메서드입니다. 일반적으로 데코레이터를 사용하는 방식과는 다르게, 프로그래밍 방식으로 라우트를 추가할 수 있어 유연한 API 설계가 가능합니다.
add_api_route 기본 사용법
가장 기본적인 사용법부터 살펴보겠습니다. 데코레이터 방식과 비교하면 다음과 같습니다.
from fastapi import FastAPI
app = FastAPI()
# 데코레이터 방식
@app.get("/users/{user_id}")
async def get_user(user_id: int):
return {"id": user_id}
# add_api_route 방식
async def get_user(user_id: int):
return {"id": user_id}
app.add_api_route("/users/{user_id}", get_user, methods=["GET"])
언뜻 보면 큰 차이가 없어 보이지만, add_api_route는 동적 라우팅이 필요한 상황에서 그 진가를 발휘합니다.
동적 CRUD API 구현
실제 프로젝트에서는 여러 모델에 대해 비슷한 CRUD 작업을 반복해서 구현해야 하는 경우가 많습니다. 이런 상황에서 add_api_route를 활용하면 코드 중복을 효과적으로 줄일 수 있습니다.
from fastapi import FastAPI, HTTPException
from typing import Dict, Any
app = FastAPI()
class CRUDRouter:
def __init__(self, prefix: str):
self.prefix = prefix
def register_routes(self, app: FastAPI):
async def get_item(item_id: int):
# 데이터베이스 조회 로직
return {"id": item_id}
async def create_item(data: Dict[str, Any]):
# 데이터 생성 로직
return {"id": 1, **data}
async def update_item(item_id: int, data: Dict[str, Any]):
# 데이터 수정 로직
return {"id": item_id, **data}
# 라우트 등록
app.add_api_route(
f"{self.prefix}/{{item_id}}",
get_item,
methods=["GET"]
)
app.add_api_route(
self.prefix,
create_item,
methods=["POST"]
)
app.add_api_route(
f"{self.prefix}/{{item_id}}",
update_item,
methods=["PUT"]
)
# 사용 예시
user_router = CRUDRouter("/users")
user_router.register_routes(app)
고급 에러 처리와 응답 설정
프로덕션 환경에서는 적절한 에러 처리와 응답 설정이 필수적입니다. add_api_route는 이러한 설정을 세밀하게 제어할 수 있습니다.
from fastapi import HTTPException
from typing import Optional
async def get_user(user_id: int):
if user_id <= 0:
raise HTTPException(status_code=400, detail="Invalid user ID")
return {"id": user_id}
app.add_api_route(
"/users/{user_id}",
get_user,
methods=["GET"],
response_model=dict,
responses={
400: {"description": "Invalid user ID provided"},
404: {"description": "User not found"}
},
tags=["users"],
summary="Get user by ID"
)
미들웨어 적용
특정 엔드포인트에만 미들웨어를 적용해야 하는 경우가 있습니다. add_api_route를 사용하면 이러한 요구사항도 쉽게 구현할 수 있습니다.
from fastapi import Request
from functools import wraps
def auth_middleware(endpoint):
@wraps(endpoint)
async def wrapper(*args, request: Request, **kwargs):
# 인증 로직
auth_header = request.headers.get("Authorization")
if not auth_header:
raise HTTPException(status_code=401)
return await endpoint(*args, request=request, **kwargs)
return wrapper
async def protected_endpoint(request: Request):
return {"message": "Protected data"}
app.add_api_route(
"/protected",
auth_middleware(protected_endpoint),
methods=["GET"]
)
이처럼 add_api_route는 단순한 라우팅 이상의 기능을 제공합니다. 특히 대규모 애플리케이션에서 반복적인 패턴을 추상화하거나, 동적으로 API를 생성해야 하는 경우에 매우 유용합니다.
다만 너무 많은 동적 라우트 생성은 코드의 가독성을 해칠 수 있으므로, 적절한 상황에서만 사용하는 것이 좋습니다. 또한 문서화를 철저히 하여 다른 개발자들도 쉽게 이해할 수 있도록 하는 것이 중요합니다.