176 lines
5.0 KiB
Markdown
176 lines
5.0 KiB
Markdown
---
|
|
id: wiki-2026-0508-dependency-injection-의존성-주입
|
|
title: Dependency Injection (의존성 주입)
|
|
category: 10_Wiki/Topics
|
|
status: verified
|
|
canonical_id: self
|
|
aliases: [DI, Dependency Injection, IoC, Inversion of Control]
|
|
duplicate_of: none
|
|
source_trust_level: A
|
|
confidence_score: 0.93
|
|
verification_status: applied
|
|
tags: [di, ioc, design-pattern, spring, nestjs]
|
|
raw_sources: []
|
|
last_reinforced: 2026-05-10
|
|
github_commit: pending
|
|
tech_stack:
|
|
language: typescript
|
|
framework: nestjs
|
|
---
|
|
|
|
# Dependency Injection (의존성 주입)
|
|
|
|
## 매 한 줄
|
|
> **"매 의존성 매 외부 주입, 직접 생성 X"**. IoC 의 가장 흔한 구현. Martin Fowler (2004) 가 명명. 2026 현재 Spring (Java), NestJS (Node), Angular, dagger (Android), wire (Go) 매 mainstream; testability + decoupling 의 backbone.
|
|
|
|
## 매 핵심
|
|
|
|
### 매 형태 (3 종류)
|
|
- **Constructor injection** — 생성자 매 dependency 받음. 매 immutable, 매 권장.
|
|
- **Setter injection** — setter 매 dependency 받음. optional dep 에 적합.
|
|
- **Field injection** — 매 framework 의 reflection. simple 하지만 testability ↓.
|
|
|
|
### 매 benefit
|
|
- **Testability** — mock 매 쉽게 inject.
|
|
- **Decoupling** — concrete impl 의 unaware.
|
|
- **Lifecycle 관리** — singleton, request-scoped, transient.
|
|
- **Configuration centralization** — DI container 의 wiring.
|
|
|
|
### 매 응용
|
|
1. Spring `@Autowired`, `@Component`.
|
|
2. NestJS `@Injectable()` + module providers.
|
|
3. Angular `providedIn: 'root'`.
|
|
4. Go: 매 manual 또는 google/wire codegen.
|
|
|
|
## 💻 패턴
|
|
|
|
### NestJS — constructor injection
|
|
```typescript
|
|
@Injectable()
|
|
export class UserService {
|
|
constructor(
|
|
private readonly repo: UserRepository,
|
|
private readonly logger: Logger,
|
|
) {}
|
|
|
|
async find(id: string) {
|
|
this.logger.log(`finding ${id}`);
|
|
return this.repo.findById(id);
|
|
}
|
|
}
|
|
|
|
@Module({
|
|
providers: [UserService, UserRepository, Logger],
|
|
exports: [UserService],
|
|
})
|
|
export class UserModule {}
|
|
```
|
|
|
|
### Spring Boot — constructor injection (no `@Autowired` needed)
|
|
```java
|
|
@Service
|
|
public class OrderService {
|
|
private final PaymentGateway gateway;
|
|
private final OrderRepository repo;
|
|
|
|
public OrderService(PaymentGateway gateway, OrderRepository repo) {
|
|
this.gateway = gateway;
|
|
this.repo = repo;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Token / interface binding (NestJS)
|
|
```typescript
|
|
export const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');
|
|
|
|
@Module({
|
|
providers: [
|
|
{ provide: PAYMENT_GATEWAY, useClass: StripeGateway },
|
|
],
|
|
exports: [PAYMENT_GATEWAY],
|
|
})
|
|
export class PaymentModule {}
|
|
|
|
@Injectable()
|
|
class CheckoutService {
|
|
constructor(@Inject(PAYMENT_GATEWAY) private gw: PaymentGateway) {}
|
|
}
|
|
```
|
|
|
|
### Factory provider (async, e.g. DB connection)
|
|
```typescript
|
|
{
|
|
provide: 'DATABASE_CONNECTION',
|
|
useFactory: async (config: ConfigService) => {
|
|
return await createPool({ url: config.get('DB_URL') });
|
|
},
|
|
inject: [ConfigService],
|
|
}
|
|
```
|
|
|
|
### Manual DI (Go, wire-style)
|
|
```go
|
|
// wire.go (codegen)
|
|
func InitializeApp() *App {
|
|
repo := NewUserRepo(NewDB())
|
|
svc := NewUserService(repo)
|
|
return NewApp(svc)
|
|
}
|
|
```
|
|
|
|
### Test with mock (vitest)
|
|
```typescript
|
|
const mockRepo: UserRepository = {
|
|
findById: vi.fn().mockResolvedValue({ id: '1', name: 'A' }),
|
|
};
|
|
const service = new UserService(mockRepo, mockLogger);
|
|
expect(await service.find('1')).toEqual({ id: '1', name: 'A' });
|
|
```
|
|
|
|
### Modern: TC39 decorators + Reflect.metadata (2026)
|
|
```typescript
|
|
@injectable()
|
|
class EmailService {
|
|
@inject(SMTP_CLIENT) private smtp!: SmtpClient;
|
|
}
|
|
```
|
|
|
|
## 매 결정 기준
|
|
| 상황 | Approach |
|
|
|---|---|
|
|
| Spring/Java | `@Component` + constructor injection |
|
|
| Node.js/TS, full framework | NestJS module providers |
|
|
| Frontend (Angular) | `providedIn: 'root'` |
|
|
| Go | manual or `google/wire` |
|
|
| Tiny app, no framework | manual constructor — DI 불필요 |
|
|
|
|
**기본값**: constructor injection, framework-managed singleton. Field injection 매 피하기.
|
|
|
|
## 🔗 Graph
|
|
- 부모: [[Inversion of Control]] · [[SOLID]] (D)
|
|
- 변형: [[Service Locator]] · [[Factory Pattern]]
|
|
- 응용: [[Spring Framework]] · [[NestJS]] · [[Angular]]
|
|
- Adjacent: [[Cross-Cutting Concerns]] · [[Aspect-Oriented Programming (AOP)]]
|
|
|
|
## 🤖 LLM 활용
|
|
**언제**: provider wiring 생성, mock 매 test 자동 생성, refactor field→constructor injection.
|
|
**언제 X**: lifecycle / scope decision 매 architectural taste 필요.
|
|
|
|
## ❌ 안티패턴
|
|
- **Service Locator** — DI 와 혼동, dep 매 hidden.
|
|
- **Field injection 남용** — test 시 reflection 필요, 매 fragile.
|
|
- **Circular dep** — A→B→A. forwardRef 로 hack 보다 redesign.
|
|
- **God container** — singleton 매 200개. module boundary 무시.
|
|
- **`new` in business logic** — DI 의 의미 없음.
|
|
|
|
## 🧪 검증 / 중복
|
|
- Verified (Fowler 2004 *Inversion of Control Containers and the Dependency Injection Pattern*, NestJS docs 2026).
|
|
- 신뢰도 A.
|
|
|
|
## 🕓 Changelog
|
|
| 날짜 | 변경 |
|
|
|---|---|
|
|
| 2026-05-08 | Phase 1 |
|
|
| 2026-05-10 | Manual cleanup — full content with NestJS/Spring/Go patterns |
|