---
id: wiki-2026-0508-dependency-injection
title: Dependency Injection (DI)
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [DI, IoC, dependency injection, constructor injection, DIP, container, factory]
duplicate_of: none
source_trust_level: A
confidence_score: 0.95
verification_status: applied
tags: [dependency-injection, ioc, dip, solid, design-pattern, testing, container]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: TypeScript / Python / Java
  framework: Spring / NestJS / FastAPI / Awilix / tsyringe
---

# Dependency Injection (DI)

## 매 한 줄
> **"매 class 내부 의 new X — 매 외부 의 inject"**. 매 IoC (Inversion of Control) 의 implementation. 매 SOLID 의 D (Dependency Inversion Principle). 매 testability + 매 swap 의 enable. 매 modern: 매 minimalist (functional injection > heavy container).

## 매 핵심

### Without DI (tight coupling)
```ts
class UserService {
  private db = new PostgresDatabase();  // 매 매 directly create
  
  save(user) { this.db.save(user); }
}
```

### With DI
```ts
class UserService {
  constructor(private db: Database) {}  // 매 외부 inject
  save(user) { this.db.save(user); }
}

// 매 wiring
const service = new UserService(new PostgresDatabase());
```

### 매 3 type
1. **Constructor injection** (recommended).
2. **Setter injection**.
3. **Interface injection**.

### 매 lifetime
- **Singleton**: 매 1 instance (e.g., DB connection pool).
- **Scoped / Request**: 매 매 request.
- **Transient**: 매 매 inject 의 새 instance.

→ 매 wrong choice 의 memory leak / state share 의 cause.

### 매 framework

#### Java / Kotlin
- **Spring**: 매 enterprise standard, 매 annotation.
- **Guice**: 매 lighter.
- **Dagger**: 매 compile-time.

#### .NET
- 매 built-in IServiceCollection.

#### TypeScript / JS
- **NestJS**: 매 Spring-like.
- **Awilix / tsyringe**: 매 lightweight.
- **InversifyJS**: 매 decorator-based.

#### Python
- **FastAPI**: 매 Depends() function.
- **Dependency-injector**.
- **manual** (most common in Python).

#### Go
- **Wire** (Google): 매 compile-time.
- 매 manual (idiomatic).

### 매 modern critique
- 매 heavy DI container 의 over-engineering.
- 매 "manual DI" / "functional DI" 의 simpler.
- 매 small project 의 anti-pattern (over-abstraction).

### 매 응용
1. **Testing**: 매 mock 의 swap.
2. **Multi-implementation**: 매 dev / prod / test.
3. **Plugin architecture**.
4. **Hexagonal / Clean Architecture**.

## 💻 패턴

### Constructor injection (TypeScript)
```ts
interface Database {
  save(user: User): Promise<void>;
}

class PostgresDB implements Database {
  async save(user: User) { /* ... */ }
}

class InMemoryDB implements Database {  // 매 testing
  private store = new Map();
  async save(user: User) { this.store.set(user.id, user); }
}

class UserService {
  constructor(private readonly db: Database) {}
  async save(user: User) { await this.db.save(user); }
}

// 매 wiring
const prodService = new UserService(new PostgresDB());
const testService = new UserService(new InMemoryDB());
```

### NestJS
```ts
@Injectable()
export class UserService {
  constructor(
    @Inject('DATABASE') private db: Database,
    private logger: Logger,
  ) {}
}

@Module({
  providers: [
    UserService,
    { provide: 'DATABASE', useClass: PostgresDB },
  ],
})
export class UserModule {}
```

### FastAPI (functional DI)
```python
from fastapi import Depends, FastAPI

app = FastAPI()

def get_db() -> Database:
    return PostgresDB()

def get_user_service(db: Database = Depends(get_db)) -> UserService:
    return UserService(db)

@app.post('/users')
async def create_user(user: User, service: UserService = Depends(get_user_service)):
    return await service.save(user)
```

### Spring (annotation)
```java
@Service
public class UserService {
    private final Database db;
    
    @Autowired  // 매 constructor 의 default 의 optional
    public UserService(Database db) {
        this.db = db;
    }
}

@Configuration
public class AppConfig {
    @Bean
    public Database database() {
        return new PostgresDB();
    }
}
```

### Manual DI (Python — common idiom)
```python
class UserService:
    def __init__(self, db: Database, logger: Logger):
        self.db = db
        self.logger = logger
    
    def save(self, user):
        self.db.save(user)

# 매 main.py
def main():
    db = PostgresDB(connection_string=os.getenv('DB_URL'))
    logger = Logger()
    service = UserService(db, logger)
    
    app = create_app(service)
    app.run()
```

### Dependency Inversion Principle (DIP)
```ts
// 매 ❌ Concrete dependency
class OrderService {
  notifyUser(user: User) {
    new EmailSender().send(user.email, 'Order confirmed');
  }
}

// 매 ✅ Abstraction
interface Notifier {
  notify(user: User, message: string): Promise<void>;
}

class OrderService {
  constructor(private notifier: Notifier) {}
  async notifyUser(user: User) {
    await this.notifier.notify(user, 'Order confirmed');
  }
}

// 매 implementations
class EmailNotifier implements Notifier { ... }
class SMSNotifier implements Notifier { ... }
class SlackNotifier implements Notifier { ... }
```

### Lifetime example (NestJS)
```ts
// Singleton (default)
@Injectable()
class CacheService {}

// Request-scoped
@Injectable({ scope: Scope.REQUEST })
class RequestContext {
  constructor(@Inject(REQUEST) private req: Request) {}
}

// Transient
@Injectable({ scope: Scope.TRANSIENT })
class IDGenerator {}
```

### Test with mock
```ts
import { MockProxy, mock } from 'jest-mock-extended';

describe('UserService', () => {
  let service: UserService;
  let mockDb: MockProxy<Database>;
  
  beforeEach(() => {
    mockDb = mock<Database>();
    service = new UserService(mockDb);
  });
  
  it('saves to db', async () => {
    const user = { id: '1', email: 'x@y.z' };
    await service.save(user);
    expect(mockDb.save).toHaveBeenCalledWith(user);
  });
});
```

### Functional DI (modern minimalist)
```ts
// 매 매 high-order function
type Deps = { db: Database; logger: Logger };

const createUserService = (deps: Deps) => ({
  save: (user: User) => deps.db.save(user),
  delete: (id: string) => deps.db.delete(id),
});

// 매 use
const service = createUserService({ db, logger });
```

### Wire (Go)
```go
// wire.go
//go:build wireinject

package main

import "github.com/google/wire"

func InitializeUserService() *UserService {
    wire.Build(NewPostgresDB, NewLogger, NewUserService)
    return &UserService{}
}
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| Enterprise Java | Spring |
| Modern TS backend | NestJS |
| Python web | FastAPI Depends |
| Small Python | Manual DI |
| Testable code | DI (구조 무관) |
| Plugin system | DI container |
| Single-use script | No DI (overkill) |

**기본값**: Constructor DI + 매 manual wiring at composition root.

## 🔗 Graph
- 부모: [[Design-Patterns]] · [[SOLID]] · [[Clean-Architecture]]
- 변형: [[Constructor Injection]]
- 응용: [[Hexagonal Architecture]] · [[Bounded Contexts (DDD)]]
- Adjacent: [[Software Architecture Styles]] · [[Architecture Anti-patterns]] · [[Anaemic Domain Model]] · [[Clean-Code-Principles]]

## 🤖 LLM 활용
**언제**: 매 architecture review. 매 testability improve. 매 multi-impl swap. 매 onboarding code.
**언제 X**: 매 single function utility (overkill). 매 prototype.

## ❌ 안티패턴
- **Service Locator** (anti-pattern in many views): 매 hidden dependency.
- **Magic injection** (annotation overuse): 매 trace 어려움.
- **Wrong lifetime**: 매 singleton 가 매 request state 의 hold.
- **DI 의 small project 의 force**: 매 over-engineering.
- **Field injection**: 매 immutability lose.
- **Circular dependency**: 매 design smell.

## 🧪 검증 / 중복
- Verified (Fowler IoC, Spring docs, NestJS docs).
- 신뢰도 A.
- Related: [[Software Architecture Styles]] · [[Clean-Code-Principles]] · [[Code_Smells]] · [[Bounded Contexts (DDD)]] · [[Anaemic Domain Model]].

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — types + lifetime + 매 NestJS / FastAPI / Spring / Go Wire / functional code |
