FastAPI 애플리케이션을 개발하면서 다양한 모듈에서 라우트를 정의해주고 이를 수동으로 선언해주는 것이 귀찮아서, route 파일을 자동으로 읽어서 선언할 수 있도록 구현해보았습니다.
기존의 방법은 다음과 같습니다.
# /app/app.py
from fastapi import FastAPI
from app.api.user import Router as user_router
from app.api.product import Router as product_router
app = FastAPI()
app.include_router(user_router, prefix="/user", tags=["User"])
app.include_router(product_router, prefix="/product", tags=["Product"])
...
다음 Python 코드는 FastAPI 애플리케이션에서 모든 라우터와 웹소켓 엔드포인트를 포함하는 과정을 자동화합니다. 스크립트가 어떻게 동작하는지 살펴보겠습니다.
# /app/api/__init__.py
import os
import importlib
from fastapi import APIRouter
def include_routers(app):
api_router = APIRouter()
api_dir = os.path.dirname(__file__)
for root, dirs, files in os.walk(api_dir):
for file in files:
if file.endswith(".py") and file != "__init__.py":
module_path = os.path.relpath(os.path.join(str(root), str(file)), str(api_dir))
module_path = module_path.replace(os.sep, ".")[:-3]
module_name = f"app.api.{module_path}"
module = importlib.import_module(module_name)
if hasattr(module, "Router"):
path = module_path.split(".")
if len(path) == 2:
tag = module_path.split(".")[1].title()
else:
# 폴더 안에 있다면, 2번째부터 " " 으로 연결
tag = " ".join(module_path.split(".")[1:]).title()
api_router.include_router(
module.Router,
tags=[tag],
prefix=f"/{module_path.replace('.', '/')}"
)
if hasattr(module, "websocket_endpoint") and callable(module.websocket_endpoint):
websocket_path = f"/{module_path.replace('.', '/')}"
app.add_websocket_route(f"{websocket_path}", module.websocket_endpoint)
app.include_router(api_router, prefix="/api")
# /app/app.py
from fastapi import FastAPI
from include_routers import include_routers
app = FastAPI()
include_routers(app)
# 추가적인 앱 설정
APIRouter 초기화: 먼저 모든 라우트를 포함할 APIRouter 인스턴스를 생성합니다.
디렉토리 탐색: os.walk를 사용하여 API 모듈이 위치한 디렉토리를 탐색합니다. 이때 __init__.py(본인)를 제외한 모든 Python 파일을 찾습니다.
모듈 동적 임포트: 찾은 각 Python 파일에 대해 모듈 경로를 구성하고, importlib.import_module을 사용하여 모듈을 동적으로 임포트합니다.
라우터 포함:
웹소켓 엔드포인트 포함: 모듈에 websocket_endpoint 함수가 있고 호출 가능하면, 이를 FastAPI 애플리케이션에 웹소켓 라우트로 추가합니다.
최종 라우터 포함: 마지막으로, 모든 동적으로 발견된 라우트를 포함한 api_router를 /api 프리픽스와 함께 FastAPI 애플리케이션에 포함시킵니다.
위 코드를 메인 FastAPI 애플리케이션 파일(app.py)에서 include_routers(app) 함수를 호출
확장성: 프로젝트가 커져도 새로운 라우트를 수동으로 추가할 필요가 없습니다. 새로운 모듈을 생성하고 Router 또는 웹소켓 엔드포인트를 정의하면, 스크립트가 이를 자동으로 포함시킵니다.
유지보수성: 오류 발생 가능성을 줄이고, 모든 것이 동적으로 처리되므로 라우트를 유지보수하기가 더 쉽습니다.
유연성: 이 접근 방식은 프로젝트의 크기나 구조에 관계없이 잘 동작합니다. 모듈을 폴더에 정리해도 스크립트가 이에 맞게 동작합니다.
FastAPI에서 라우터 선언 과정을 자동화하여 app.py의 코드를 깔끔하게 관리하고, 자동으로 호출할 수 있도록 하여, 시간 절약과 유지 보수에 도움이 되었습니다.
이 글이 FastAPI 프로젝트 구조화에 도움이 되었기를 바랍니다. 질문이나 의견이 있다면 언제든 댓글로 남겨주세요!