기본 Depends를 넘어, FastAPI의 dependency injection은 고급 패턴을 지원합니다: 하위 의존성(sub-dependencies), 매개변수가 있는 의존성(클래스 기반 또는 팩토리를 통해), 스코프 리소스(yield), 전역/라우터 의존성, 그리고 테스트를 위한 의존성 재정의. 실제 애플리케이션을 구조화하는 다재다능한 도구로 만듭니다.
def get_token(authorization: str = Header()):
return parse(authorization)
def get_current_user(token: str = Depends(get_token)): # 다른 의존성에 의존
return decode(token)
def get_admin(user: User = Depends(get_current_user)): # 다시 그것에 의존
if not user.is_admin: raise HTTPException(403)
return user
# FastAPI가 전체 체인을 해결하며, 요청 내에서 공유 의존성을 캐시함
의존성은 다른 의존성에 의존하여 FastAPI가 자동으로 해결하는 트리를 형성합니다(그리고 공유 의존성을 요청별로 캐시하므로, get_token은 여러 의존성에서 사용되어도 한 번만 실행됩니다).
class RateLimiter:
def __init__(self, calls: int): # 의존성 구성
self.calls = calls
def __call__(self, request: Request): # 호출 가능 → 의존성으로 동작
check_rate_limit(request, self.calls)
@app.get("/items", dependencies=[Depends(RateLimiter(calls=100))]) # 라우트별 구성
def items(): ...
__call__이 있는 클래스는 구성 가능한 의존성입니다 — 사용 시점에 매개변수화합니다(예: 라우트마다 다른 속도 제한).
@app.get("/admin", dependencies=[Depends(verify_admin)]) # 효과를 위해 실행 (인증 검사)
def admin(): ... # 반환값이 필요 없음
app = FastAPI(dependencies=[Depends(verify_api_key)]) # 모든 라우트에 적용
router = APIRouter(dependencies=[Depends(get_current_user)]) # 라우터의 모든 라우트에
횡단 요구사항(인증, 속도 제한)을 위해 앱 전역 또는 라우터별로 의존성을 적용합니다.
app.dependency_overrides[get_db] = get_test_db # 테스트에서 모든 의존성 교체
FastAPI의 dependency injection은 가장 강력한 기능 중 하나이며, 이러한 고급 패턴은 실제의 복잡한 애플리케이션을 우아하게 구조화하게 합니다.
이를 이해하는 것은 가치 있는 시니어 수준 지식입니다: 하위 의존성은 자동 해결과 요청별 캐싱(중복 작업 방지)으로 재사용 가능한 계층(토큰 → 사용자 → admin)을 구축하고; 매개변수화된/클래스 기반 의존성은 구성 가능하고 재사용 가능한 컴포넌트(라우트마다 다른 설정의 속도 제한기, 권한 검사)를 만들며; 부수 효과로서의 의존성은 함수 시그니처를 어지럽히지 않고 요구사항(인증)을 깔끔하게 강제하고; 전역/라우터 수준 의존성은 횡단 관심사(인증, 속도 제한)를 전체 라우트 그룹에 한 곳에서 적용합니다.
결정적으로, 의존성 재정의는 복잡한 의존성 그래프조차 손쉽게 테스트 가능하게 만듭니다.
이러한 패턴을 익히는 것—의존성을 조합하고, 매개변수화하며, 전역 또는 라우터별로 스코프를 지정하고, 테스트에서 재정의하는 것—은 사소하지 않은 FastAPI 애플리케이션에서 깔끔하고 DRY하며 테스트 가능한 아키텍처를 가능하게 합니다. 프레임워크의 DI 시스템을 완전히 활용할 수 있는 개발자를 기본 Depends만 사용하는 사람과 구별합니다.
잘 구조화되고 유지보수 가능하며 대규모인 FastAPI 앱을 구축하기 위한 중요한 주제입니다.