dependency-injector

dependency-injector 4.46 버전이 얼마 전(2025-02-28)에 릴리즈되었습니다. dependency-injector는 의존성 주입 패턴(Dependency Injection, 이하 DI)을 사용하도록 도와주는 python 라이브러리입니다. 한동안 유지보수가 끊긴 관계로 최근에는 잘 사용하고 있지 않지만 Python DI 라이브러리 중에서는 제일 좋다고 생각합니다. 2년 전에 저는 이 라이브러리에 PR을 올렸었습니다. 그리고 이 PR이 4.46 버전에 포함되었습니다.

PR 소개

이번에 머지된 제 PR은 FastAPI의 typing.Annotated를 사용한 fastapi.Depends기능과 dependency-injector의 호환이 가능하도록 한 PR입니다.

fastapi.Depends는 DI을 위한 핵심 기능입니다. FastAPI에서는 엔드포인트가 필요로 하는 의존성을 Depends를 통해 선언적으로 정의할 수 있습니다. 주로 데이터베이스 세션, 인증된 사용자 정보, 공통 파라미터 등을 주입하는 데에 사용합니다. Depends는 크게 두 가지 방식으로 사용할 수 있습니다.

  1. 함수 파라미터에 직접 Depends 사용
  2. Annotated로 Depends를 래핑해서 사용
"""
공식 문서의 코드를 변형해 가져왔습니다.
https://fastapi.tiangolo.com/tutorial/dependencies/#share-annotated-dependencies
"""
from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100) -> dict:
    return {"q": q, "skip": skip, "limit": limit}

# Annotated로 Depends를 래핑
CommonsDep = typing.Annotated[dict, Depends(common_parameters)]

# 1. 함수 파라미터에 직접 Depends 사용
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons

# Annotated로 래핑한 Depends 사용
@app.get("/users/")
async def read_users(commons: CommonsDep):
    return commons

1번 방법을 사용하면 같은 함수를 래핑한 fastapi.Depends를 사용하면 코드 중복이 일어납니다. 코드 중복을 막기 위한 방법이 2번 Annotated를 활용한 방법입니다. 뿐만 아니라 typing.Annotated를 사용한 의존성 주입은 타입 안정성을 향상해줍니다. fastapi.Depends를 사용할 때는 결과 타입이 주입된 함수의 리턴으로 하나, 파라미터에 붙는 타입으로 하나 총 두 가지 타입이 존재하는데 이 타입을 모두 수동으로 동일하게 맞춰주어야 합니다. 2번 방법으로 타입을 이 작업을 한 번만 함으로서 타입 안정성이 강화됩니다.

2번 방법은 FastAPI 0.95버전에 추가되었고, 추가되었을 당시에는 dependency-injector가 2번 방식과는 충돌이 일어나는 문제가 있었습니다. 최근에는 typing.Annotated가 라이브러리에 활발하게 활용되고 있지만 당시에는 그렇지 않았습니다. 다른 DI 라이브러리가 영 시원찮아서 별로 쓰고 싶지 않았던 저는 dependency-injector 코드를 뒤져서 typing.Annotated로 덮여 있어도 사용할 수 있게 만드는 wrapper를 만들었습니다. 헌데 라이브러리 자체에 이 기능이 병합되면 좋을 것 같아서 올린 PR이 이 PR입니다.

PR 좀 봐주세요…

처음에는 PR을 바로 올리면 거부당할 것 같아서 소심하게 이슈에 버그 리포트만 올렸습니다. PR을 받는다면 내가 작성한 코드 수정해서 올린다고 적었죠. 두 달 뒤에 구현한 코드를 PR로 만들어달라는 댓글이 달렸습니다. 처음에는 메인테이너인 줄 알아서 드디어 왔다!!! 싶었는데 일반 유저 댓글이었습니다. 그렇지만 약간의 아쉬움을 뒤로 하고 PR을 작성해서 올렸습니다.

comment-about-new-dependency-injector-fork

라이브러리를 포크한 사람이 댓글을 달았습니다.

야심차게 PR을 올렸는데 금방 하트를 제법 받았습니다. 한 달 동안 스무 개 정도 받아서 기분이 좋았습니다. 하지만 정작 가장 중요한 메인테이너의 답변은 오지 않았습니다. 나중에 알고 보니 제가 PR을 올리기 한 달 전부터 메인테이너가 활동을 잠수 중인 상태였습니다. 다른 개발자들도 비슷한 생각이었는지 프로젝트를 포크해서 직접 관리하겠다는 의견도 나왔지만, 그마저도 오래가지는 못했습니다.

그렇게 PR은 자연스레 잊혀져가는 듯했습니다. 그러다 올해 1월, 오랜만에 알림이 왔습니다. 새로운 메인테이너가 black 포맷팅을 적용해주시면 좋을 것 같아요. 라고 댓글을 달았습니다. 2년 만에 받은 첫 피드백이었습니다.

comment-about-new-dependency-injector-fork

드디어 머지될 수 있어요!!

새로 코어 메인테이너를 맡게 된 분의 댓글이었습니다. 댓글 알림 이메일을 보자마자 설렜습니다. 누구에게나 첫 경험은 특별하고 이건 제 첫 오픈소스 PR이었으니까요. 하지만 black 설정이 따로 저장소에 없어서 최신 black을 기본으로 적용해도 되냐고 물어봤습니다. 메인테이너가 동의했고 black을 후다닥 적용해서 수정 커밋을 올렸습니다.

comment-about-new-dependency-injector-fork

black 설정이 없어서 물어봤습니다

그리고 승인받았습니다!! :)

comment-about-merged

메인테이너 두 분이 승인해주셨습니다.

4.46 릴리즈에서 만나요

4.46버전에서부터 fastapi.Depends와 함께 dependency-injector를 사용할 수 있습니다. PyPI를 통해다운받을 수 있습니다. 사용 예시는 여기에서 확인할 수 있습니다.