Spring 을 쓰다가 FastAPI 로 전환하면서 많은 것들이 의문이었지만 그중 가장 큰 것은 "Python 은 빌드되는게 아닌데 의존성 주입을 사용할 수 있나?" 였다. 그리고 그런 나에게 보란듯이 FastAPI 공식 문서에는 의존성 주입 파트가 있다. 이 글은 공식문서를 정독하고 정리하는 글 정도가 되겠다. 이미 어느정도 Depends 를 알고 있고, Depends 의 내부동작을 파악하고 싶다면 다음 글이 도움이 될 수도 있다.
FastAPI 의존성 주입(Dependency Injection) 정리
FastAPI는 의존성 주입(Dependency Injection, DI)을 통해 코드의 재사용성, 유지보수성, 테스트 용이성을 향상시킨다.
1. 기본 의존성 주입
FastAPI에서 기본적인 의존성을 정의하고 사용하는 방법은 다음과 같다.
1.1. 의존성 함수 정의하기
from fastapi import Depends, FastAPI
app = FastAPI()
def get_db():
db = connect_to_db()
try:
yield db
finally:
db.close()
위 예제에서는 데이터베이스 세션을 관리하는 의존성 함수를 정의했다. yield
를 사용하여 세션을 반환하고, 요청이 완료된 후 세션을 닫는 동작을 한다.
1.2. 의존성 주입 사용하기
from fastapi import Depends, FastAPI
from sqlalchemy.orm import Session
app = FastAPI()
@app.get("/items/")
def read_items(db: Session = Depends(get_db)):
items = db.query(Item).all()
return items
경로 작업 함수(path operation function)에서 Depends
를 사용하여 get_db
의존성을 주입받는다. 이를 통해 데이터베이스 세션을 쉽게 사용할 수 있다.
1.3. 기본 사용방법 정리
일단 나는 여기까지만 파악했을 때 혼란스러웠다. 이게 뭐지? 하는 기분이었다. 그런데 놀랍게도 의존성으로 주입되는 대상이 호출 가능한 (Callable) 객체라는 점에서 Spring 과 유사점이 있다. 다만 다른 점이 있다면 FastAPI 의 의존성은 경로 작업 함수(FastAPI 인스턴스로 정의된 경로)에서만 유효하다는 점이다.
아직까지 많은 것들이 이해되지 않지만 나같은 범재는 학습할때 우선 받아들이고 넘어가는 과정이 필요하다. 우선 팩트를 그대로 받아들이고 넘어가자.
그리고 이번 예제에서는 yield 를 사용하고 있다. 의존성 주입시 yield 를 이용하여 리소스를 안전하게 관리할 수 있다는 것도 장점이다.
2. 클래스를 사용한 의존성
클래스를 사용하여 의존성을 정의하면 상태를 유지하거나 여러 메서드를 포함할 수 있다.
from fastapi import Depends, FastAPI
app = FastAPI()
class CommonQueryParams:
def __init__(self, q: str = None, limit: int = 10):
self.q = q
self.limit = limit
@app.get("/items/")
def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
return {"q": commons.q, "limit": commons.limit}
위 예제에서는 CommonQueryParams
클래스를 의존성으로 사용하여 공통 쿼리 매개변수를 처리한다.
2.1 클래스를 사용한 의존성 정리
Python 에서 클래스는 Callable 하다는 것을 명심하자. Java 도 클래스를 new 키워드로 호출하면 기본 생성자가 호출되지 않는가? Python 도 똑같다. CommonQueryParams 클래스의 __init__ 함수가 호출되는것이다.
3. 서브-의존성
의존성은 다른 의존성을 포함할 수 있습니다. 이를 서브-의존성이라고 한다.
from fastapi import Depends, FastAPI, HTTPException, Header
from datetime import datetime
from typing import Optional
app = FastAPI()
def get_token_header(authorization: Optional[str] = Header(None)):
"""
Authorization 헤더에서 토큰을 추출하고 검증합니다.
헤더 형식은 'Bearer <token>'이어야 합니다.
"""
if authorization is None:
raise HTTPException(status_code=400, detail="Authorization header missing")
try:
scheme, token = authorization.split()
if scheme.lower() != "bearer":
raise HTTPException(status_code=400, detail="Invalid authentication scheme")
except ValueError:
raise HTTPException(status_code=400, detail="Invalid Authorization header format")
if token != "expected_token":
raise HTTPException(status_code=400, detail="Invalid Token")
return datetime.now()
def get_current_user(now: datetime = Depends(get_token_header)):
"""
현재 사용자를 반환합니다. 여기서는 단순히 요청 시각을 반환합니다.
"""
return {"time": f"{now}"}
@app.get("/users/me")
def read_current_user(current_user: dict = Depends(get_current_user)):
"""
현재 사용자의 정보를 반환하는 엔드포인트입니다.
"""
return current_user
여기서 get_current_user
는 get_token_header
를 서브-의존성으로 사용하여 토큰 검증 후 현재 시간을 반환한다.
3.1 서브-의존성 정리
API 문서를 보자, header 로 Authorization 을 요구함을 알 수 있다.

이 요청을 보내보자
curl -X 'GET' \
'http://127.0.0.1:8000/users/me' \
-H 'accept: application/json' \
-H 'Authorization: bearer expected_token'
정상적으로 처리됨을 알 수 있다.
이를 통해 서브-의존성을 통핸 의존성 체인 구성은 결국 의존성 Stack(가정이다...) 의 최상단에 있는 의존성부터 순차적으로 처리해나가고 결국 API 에서는 최상단에서 요청하는 값을 요구하게 된다. 그리고 각 의존성 함수가 수행되면서 자신을 호출한 호출자에게 반환값을 전달한다.
이정도면 의존성 체이닝이라고 부를 수도 있지 않을까?
4. 전역 의존성
전역 의존성은 애플리케이션 전체에 걸쳐 모든 경로 연산자에 적용되는 의존성이다.
from fastapi import Depends, FastAPI
app = FastAPI()
def verify_token(token: str):
if token != "secret-token":
raise HTTPException(status_code=400, detail="Invalid Token")
return token
app = FastAPI(dependencies=[Depends(verify_token)])
@app.get("/protected-route/")
def protected_route():
return {"message": "Access granted"}
위 예제에서는 verify_token
의존성을 전역 의존성으로 설정하여 모든 경로 연산자에 적용된다.
이러한 전역 의존성은 FastAPI 구현체 혹은 APIRouter 구현체에 적용할 수 있다.
4.1 전역의존성 정리
전역 의존성의 역할이 미들웨어와 다른점이 무엇인지 고민이 생기는 지점이다. 그래서 GTP 에게 물어봤다.

'탐구 생활 > FastAPI' 카테고리의 다른 글
오픈소스 초보자의 FastAPI 기여하기 (2) | 2024.12.03 |
---|---|
FastAPI: fastapi-permissions 를 이용한 접근제어 (0) | 2024.12.01 |