Python 표준 라이브러리(
inspect)만으로 만든 학습용 미니 DI(Dependency Injection) 컨테이너. Spring·FastAPI·Dagger 같은 DI 프레임워크가 내부에서 어떻게 동작하는지 따라치며 익히는 프로젝트.
@injectable데코레이터 — 클래스를 임포트 시점에 자동 등록.- 타입 힌트 기반 의존성 주입 —
__init__시그니처를inspect로 읽어 자동 해결. - 두 가지 스코프 —
Scope.SINGLETON(기본, 캐싱) /Scope.PROTOTYPE(매번 새 인스턴스). - 순환 의존성 검출 — 경로 추적 + 위상정렬 두 단계로 안전 검증.
- 위상정렬(Kahn's algorithm) — 의존성 그래프의 인스턴스화 순서 결정.
- 패키지 자동 스캔 —
bootstrap("my_app")한 줄로 패키지 트리를 임포트해 등록.
외부 의존성 0개 (Python 3.10+ 표준 라이브러리만 사용).
git clone https://github.com/junhyeong9812/python-di-container.git
cd python-di-container
pip install -e .from mini_di import injectable, Container, Scope
@injectable
class UserRepository:
def find(self, user_id: int) -> dict:
return {"id": user_id, "name": "alice"}
@injectable
class UserService:
def __init__(self, repo: UserRepository) -> None:
self.repo = repo
def get_user(self, user_id: int) -> dict:
return self.repo.find(user_id)
container = Container()
service = container.resolve(UserService) # UserRepository 자동 주입
print(service.get_user(1)) # {'id': 1, 'name': 'alice'}from mini_di import bootstrap
container = bootstrap("my_app") # my_app 패키지 트리 전체를 스캔 → 자동 등록
service = container.resolve(MyService)python-di-container/
├── docs/
│ ├── notes.md ← inspect 모듈과 DI 컨테이너 동작 원리 정리
│ ├── design.md ← 설계 결정 (요구/제약/구조/API)
│ ├── phases.md ← Phase 0~8 따라치기 가이드
│ ├── progress.md ← 진행 기록
│ └── study/ ← Python 문법/패턴 주제별 심화 학습 문서
│ ├── decorator-syntax.md
│ ├── inspect-module.md
│ ├── type-hints-runtime.md
│ ├── enum-class.md
│ ├── dataclass-frozen.md
│ ├── keyword-only-args.md
│ ├── dynamic-import.md
│ └── relative-import.md
├── src/
│ └── mini_di/
│ ├── __init__.py ← 공개 API 재노출
│ ├── scopes.py ← Scope enum
│ ├── exceptions.py ← DIError, NotRegisteredError 등
│ ├── registry.py ← 전역 등록소 + ComponentMeta
│ ├── decorators.py ← @injectable
│ ├── scanner.py ← 패키지 자동 스캔
│ └── container.py ← Container, resolve, topological_order, bootstrap
├── examples/
│ └── simple_app/
│ ├── repositories.py
│ ├── services.py
│ └── main.py
├── tests/
│ ├── conftest.py ← pytest 격리 fixture
│ ├── test_resolve.py ← 의존성 주입 동작
│ ├── test_scope.py ← SINGLETON/PROTOTYPE
│ ├── test_cycle.py ← 순환 의존성 검출
│ └── test_scanner.py ← bootstrap end-to-end
└── pyproject.toml
[등록] → [의존성 그래프 분석] → [위상 정렬] → [인스턴스화] → [컨테이너 보관] → [주입]
@injectable inspect.signature Kahn's algorithm 재귀 resolve dict 캐시 타입 키로 조회
- 등록:
@injectable이 임포트 시점에 클래스를 전역_registry에 추가. - 그래프 분석: 각 클래스의
__init__타입 힌트를typing.get_type_hints로 읽어 의존 관계 파악. - 위상정렬: Kahn's algorithm으로 인스턴스화 순서 결정 + 사이클 검출.
- 인스턴스화: SINGLETON은 한 번 만들어 캐시, PROTOTYPE은 매번 새로 생성.
- 주입: 같은 타입을 다시 요청하면 캐시된 동일 참조 반환.
python -m examples.simple_app.main
# {'id': 1, 'name': 'alice'}pip install pytest
python -m pytest tests/ -v
# 18 passed| 테스트 파일 | 검증 항목 |
|---|---|
test_resolve.py |
의존성 없는 resolve, 자동 주입, 2-depth 체인, 미등록/타입힌트 누락/기본값 (6 cases) |
test_scope.py |
SINGLETON 캐싱, PROTOTYPE 매번 새, 두 소비자 간 공유, default scope (4 cases) |
test_cycle.py |
self-loop, A↔B 사이클(resolve/topological), 정상 위상순서 (4 cases) |
test_scanner.py |
bootstrap 반환, end-to-end resolve, eager/lazy 차이 (4 cases) |
이 프로젝트는 따라치며 익히는 학습 목적으로 만들어졌다.
docs/notes.md—inspect모듈과 DI 컨테이너의 일반 동작 원리.docs/design.md— 설계 결정 (왜 이렇게 만들었는지).docs/phases.md— Phase 0~8 따라치기 가이드. 각 줄에 "이 코드가 파이썬에서 어떤 역할인지" 1줄 주석.docs/study/—@데코레이터, MRO, Enum 등 사용된 Python 문법/표준 라이브러리 기능을 주제별로 심화 정리. CPython 내부 동작까지 포함.
학습 목적상 단순함을 우선하여 다음은 다루지 않는다:
- HTTP request/session 스코프
Optional[X],Union[X, Y],list[X]등 제네릭/유니온 타입 해석- 인터페이스(추상 클래스) → 구현체 바인딩
- 비동기 팩토리 (
async def __init__) - 설정 파일/환경 변수 주입
- 메서드 인젝션 (FastAPI
Depends스타일) - 멀티스레드 락
확장 아이디어는 docs/phases.md 부록 C 참조.
- Python 3.10+ (PEP 604
X | None문법,dataclass(kw_only=True))
학습 목적의 개인 프로젝트. 자유롭게 참고/수정 가능.