나는 웹 프레임워크를 사용할 때 거의 Python의 Fast API를 사용한다. 사내에서 많이 쓰기 때문이다.
그러나 아무래도 국내에서는 스프링을 많이 사용하는데, 스프링에서 많이 사용되는 어노테이션
을 FastAPI에 적용한 사례가 있어 공유하고자 한다.
예시
만약 아래와 같은 스마트 스토어 서비스가 있다고 가정해보자.

이 스마트 스토어는 편의점,약국,백화점,서점등 각종 서비스의 아이템을 하나의 웹 사이트에서 다 이용할 수 있는 서비스이다.
사용자 입장에서?

사용자는 하나의 웹 사이트에서 각종 마켓에 있는 물품 구매할 수 있다. 사용자 입장에서는 불편하게 다른 사이트를 왔다갔다 할 필요 없이 하나의 서비스에서 모든 것을 상호작용 할 수 있다.
개발자 입장에서?

개발자 입장에서는 난감할 수 있다. 서버에 등록된 서비스가 많을경우 구현 & 관리해야할 엔드포인트가 많아질 수 있다.
이 때, 개발자의 생산성을 늘리기 위해 각 API마다 내부에 중복된 기능들을 어노테이션으로 관리할 수 있다.
아래는 어노테이션처럼 쓸 수 있는 파이썬 데코레이터(wraps)를 FastAPI와 함께 사용하는 예시이다.
1. 데코레이터 사용 예시
모든 API의 요청은 맨 최초에
의존하는 DB 및 서비스의 상태를 먼저 체크하고 이후 모든 상호작용
이 이루어져야 한다고 가정한다.
from fastapi import FastAPI
from functools import wraps
from service_checker import DB_상태_체크, 서비스_상태_체크
app = FastAPI()
# 서버 상태 체크 데코레이터
def dependency_service_health_check(func):
@wraps(func)
async def wrapper(*args, **kwargs):
await DB_상태_체크()
await 서비스_상태_체크()
# ... 이하 다른 것도 추가 가능
return func
@app.get("/items")
@dependency_service_health_check
async def get_items():
# @dependency_service_health_check 내부 메소드가 먼저 작동된다.
# 이후 상호작용들
return {}
dependency_service_health_check(func)
부분이 Spring의@어노테이션
역할을 한다.- 실제로는 파이썬의 데코레이터 이며, 어노테이션과 거의 동일한 개념으로 이해하셔도 무방하다.
wrapper
내부에서 필요한 작업(로그, 권한검사, 공통 전처리/후처리 등)을 수행할 수 있다.
2. 추가 데코레이터 예시 (인증/인가)
아래 예시에서는 인증/인가 처리를 위한 데코레이터를 간단히 구현해보았다. 서비스 호출 시 유효한 토큰인지 판별하고, 권한을 확인하는 과정을 공통 로직으로 처리할 수 있다.
from fastapi import FastAPI, HTTPException, Request
def auth_required(func):
@wraps(func)
async def wrapper(*args, **kwargs):
request: Request = kwargs["request"] # FastAPI에서는 request 객체를 인자로 받을 수 있음
token = request.headers.get("Authorization")
if not token or token != "valid_token":
raise HTTPException(status_code=401, detail="Unauthorized")
# 권한 체크
user_role = "admin"
if user_role not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Forbidden")
return await func(*args, **kwargs)
return wrapper
@app.get("/secret-data")
@auth_required
async def get_secret_data(request: Request):
return {"secret": "data"}
위와 같이
@auth_required
데코레이터를 통해 인증/인가(권한) 로직을 중앙 집중적으로 관리할 수 있다.
3. 데코레이터의 확장 (파라미터 받기)
어노테이션(데코레이터) 사용 중, 상황에 따라 파라미터를 데코레이터에 넘겨야 할 경우가 생길 수 있다.
예를 들어, 특정 그룹이나 롤(Role)만 접근 가능하도록 설정하고 싶을 때가 그렇다.
def role_required(role_name: str):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
request: Request = kwargs["request"]
user_role = request.headers.get("User-Role", None)
if user_role != role_name:
raise HTTPException(status_code=403, detail="Forbidden")
return await func(*args, **kwargs)
return wrapper
return decorator
@app.get("/admin-only")
@role_required("admin")
async def admin_only_api(request: Request):
return {"message": "관리자 전용 API인데?"}
- 파라미터를 받는 데코레이터의 구조:
- 바깥에 파라미터를 받은 후(
role_required("admin")
), - 내부에서 실제 작업을 수행하는 wrapper 함수를 구성한다.
- 바깥에 파라미터를 받은 후(
4. 마치며
- 파이썬의 데코레이터는 Spring의 어노테이션과 유사한 목적(공통 로직 관리, 가독성 향상, 중복 코드 제거)으로 사용 가능하다.
- 데코레이터 내부에서 전처리/후처리, 예외 처리, 인증/인가, 로깅 등 필요한 작업을 일괄 관리하면, 유지보수가 더욱 용이해진다.
- 규모가 큰 프로젝트에서도 이처럼 데코레이터를 적극 활용하면, 협업 시에도 로직이 분산되지 않고 체계적이며 일관된 방식으로 관리할 수 있다.
추가로, 데코레이터 작성 시에는 비동기 함수(async)를 사용하는 FastAPI 특성을 고려해
await
키워드를 적절히 활용해야 한다는 점에 유의 ! ! !