Files
2nd/10_Wiki/Topics/AI_and_ML/Dependency_Injection_(DI).md
koriweb d8a80f6272 chore(wiki): dangling 링크 canonical 정규화 (768파일/1200건)
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해
끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은
과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업.
도구: Datacollect/scripts/link_reconcile_apply.mjs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:24:15 +09:00

8.0 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-dependency-injection Dependency Injection (DI) 10_Wiki/Topics verified self
DI
IoC
dependency injection
constructor injection
DIP
container
factory
none A 0.95 applied
dependency-injection
ioc
dip
solid
design-pattern
testing
container
2026-05-10 pending
language framework
TypeScript / Python / Java 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)

class UserService {
  private db = new PostgresDatabase();  // 매 매 directly create
  
  save(user) { this.db.save(user); }
}

With DI

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)

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

@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)

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)

@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)

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)

// 매 ❌ 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)

// 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

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)

// 매 매 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)

// 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

🤖 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.

🧪 검증 / 중복

🕓 Changelog

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