🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Dependency Injection 핵심 개념 완벽 정리 - 슬라이드 1/11
A

AI Generated

2025. 11. 3. · 40 Views

Dependency Injection 핵심 개념 완벽 정리

의존성 주입(DI)은 객체 간의 결합도를 낮추고 테스트 가능한 코드를 작성하는 핵심 디자인 패턴입니다. 실제 동작하는 예제로 DI의 기본 개념부터 다양한 주입 방식까지 초급 개발자를 위해 쉽게 설명합니다.


카테고리:TypeScript
언어:TypeScript
메인 태그:#TypeScript
서브 태그:
#DependencyInjection#DesignPattern#SOLID#Testing

들어가며

이 글에서는 Dependency Injection 핵심 개념 완벽 정리에 대해 상세히 알아보겠습니다. 총 10가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.

목차

  1. DI_없이_작성한_코드의_문제점
  2. 생성자_주입_기본_패턴
  3. 인터페이스를_활용한_추상화
  4. 세터_주입_패턴
  5. 메서드_주입_패턴
  6. DI_컨테이너_기본_개념
  7. 싱글톤_패턴과_DI
  8. 팩토리_패턴과_DI_결합
  9. 테스트를_위한_Mock_주입
  10. 순환_의존성_문제_해결

1. DI 없이 작성한 코드의 문제점

개요

클래스 내부에서 직접 의존성을 생성하면 결합도가 높아지고 테스트가 어려워집니다. 이는 유지보수성을 떨어뜨리는 주요 원인입니다.

코드 예제

class UserService {
  private db = new Database(); // 강한 결합

  getUser(id: string) {
    return this.db.findUser(id);
  }
}
// Database 변경 시 UserService도 수정 필요

설명

UserService가 Database 객체를 직접 생성하므로 두 클래스가 강하게 결합됩니다. Database를 테스트용으로 교체하거나 다른 구현체로 변경하기 어렵습니다.


2. 생성자 주입 기본 패턴

개요

생성자를 통해 의존성을 외부에서 주입받으면 결합도가 낮아지고 테스트가 쉬워집니다. 가장 권장되는 DI 방식입니다.

코드 예제

class UserService {
  constructor(private db: IDatabase) {}

  getUser(id: string) {
    return this.db.findUser(id);
  }
}

const service = new UserService(new Database());

설명

Database 인스턴스를 외부에서 주입받으므로 UserService는 IDatabase 인터페이스에만 의존합니다. 테스트 시 MockDatabase로 쉽게 교체할 수 있습니다.


3. 인터페이스를 활용한 추상화

개요

인터페이스를 사용하면 구체적인 구현이 아닌 추상화에 의존하게 되어 더욱 유연한 코드가 됩니다.

코드 예제

interface IDatabase {
  findUser(id: string): User;
}

class MockDB implements IDatabase {
  findUser(id: string): User {
    return { id, name: 'Test User' };
  }
}

설명

IDatabase 인터페이스를 정의하면 실제 DB, MockDB, TestDB 등 다양한 구현체를 자유롭게 교체할 수 있습니다.


4. 세터 주입 패턴

개요

세터 메서드를 통해 의존성을 주입하는 방식으로, 선택적 의존성에 적합합니다. 생성 후에도 의존성 변경이 가능합니다.

코드 예제

class EmailService {
  private logger?: ILogger;

  setLogger(logger: ILogger) {
    this.logger = logger;
  }

  send(email: string) {
    this.logger?.log(`Sending to ${email}`);
  }
}

설명

setLogger를 통해 필요할 때만 Logger를 주입할 수 있습니다. 선택적 의존성이므로 Logger 없이도 EmailService가 동작합니다.


5. 메서드 주입 패턴

개요

특정 메서드 실행 시에만 필요한 의존성을 매개변수로 전달받는 방식입니다. 일회성 의존성에 유용합니다.

코드 예제

class ReportGenerator {
  generate(data: Data, formatter: IFormatter) {
    const formatted = formatter.format(data);
    return formatted;
  }
}

const report = generator.generate(data, new PDFFormatter());

설명

generate 메서드 실행 시에만 Formatter를 주입받습니다. 매번 다른 포맷터를 사용할 수 있어 유연성이 높습니다.


6. DI 컨테이너 기본 개념

개요

DI 컨테이너는 의존성 생성과 주입을 자동으로 관리해주는 도구입니다. 수동 주입의 번거로움을 해결합니다.

코드 예제

class Container {
  private services = new Map();

  register(name: string, instance: any) {
    this.services.set(name, instance);
  }

  get(name: string) {
    return this.services.get(name);
  }
}

설명

Container에 서비스를 등록하고 필요할 때 꺼내 쓸 수 있습니다. 의존성 관리를 중앙화하여 코드가 간결해집니다.


7. 싱글톤 패턴과 DI

개요

DI 컨테이너를 사용하면 싱글톤 인스턴스 관리가 쉬워집니다. 애플리케이션 전체에서 하나의 인스턴스만 사용합니다.

코드 예제

class DIContainer {
  private singletons = new Map();

  singleton(name: string, factory: () => any) {
    if (!this.singletons.has(name)) {
      this.singletons.set(name, factory());
    }
    return this.singletons.get(name);
  }
}

설명

singleton 메서드는 처음 호출 시에만 인스턴스를 생성하고 이후에는 같은 인스턴스를 반환합니다.


8. 팩토리 패턴과 DI 결합

개요

팩토리 함수를 주입하면 필요할 때마다 새로운 인스턴스를 생성할 수 있습니다. 매번 새 객체가 필요한 경우 유용합니다.

코드 예제

type Factory<T> = () => T;

class TaskManager {
  constructor(private taskFactory: Factory<Task>) {}

  createTask(name: string) {
    const task = this.taskFactory();
    task.name = name;
    return task;
  }
}

설명

팩토리 함수를 주입받아 createTask 호출마다 새로운 Task 인스턴스를 생성합니다. 인스턴스 생성 로직을 외부에서 제어할 수 있습니다.


9. 테스트를 위한 Mock 주입

개요

DI를 사용하면 테스트 시 실제 객체 대신 Mock 객체를 쉽게 주입할 수 있습니다. 단위 테스트가 간편해집니다.

코드 예제

class MockLogger implements ILogger {
  logs: string[] = [];
  log(msg: string) { this.logs.push(msg); }
}

const mockLogger = new MockLogger();
const service = new UserService(mockLogger);
service.doSomething();
console.log(mockLogger.logs); // 로그 확인

설명

MockLogger를 주입하여 실제 로깅 없이 테스트할 수 있습니다. logs 배열로 메서드 호출 여부를 쉽게 검증할 수 있습니다.


10. 순환 의존성 문제 해결

개요

A가 B를 의존하고 B가 A를 의존하는 순환 참조는 DI에서 흔한 문제입니다. 인터페이스와 지연 주입으로 해결합니다.

코드 예제

class ServiceA {
  private serviceB?: ServiceB;

  setServiceB(b: ServiceB) {
    this.serviceB = b;
  }
}

class ServiceB {
  constructor(private serviceA: ServiceA) {}
}

설명

ServiceA는 세터 주입으로 ServiceB를 나중에 받습니다. 생성자 주입만 사용하면 순환 참조가 발생하므로 세터 주입으로 해결합니다.


마치며

이번 글에서는 Dependency Injection 핵심 개념 완벽 정리에 대해 알아보았습니다. 총 10가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.

관련 태그

#TypeScript #DependencyInjection #DesignPattern #SOLID #Testing

#TypeScript#DependencyInjection#DesignPattern#SOLID#Testing

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스

마이크로서비스 배포 완벽 가이드

Kubernetes를 활용한 마이크로서비스 배포의 핵심 개념부터 실전 운영까지, 초급 개발자도 쉽게 따라할 수 있는 완벽 가이드입니다. 실무에서 바로 적용 가능한 배포 전략과 노하우를 담았습니다.

Spring Boot 상품 서비스 구축 완벽 가이드

실무 RESTful API 설계부터 테스트, 배포까지 Spring Boot로 상품 서비스를 만드는 전 과정을 다룹니다. JPA 엔티티 설계, OpenAPI 문서화, Docker Compose 배포 전략을 초급 개발자도 쉽게 따라할 수 있도록 스토리텔링으로 풀어냅니다.

단위 테스트와 통합 테스트 완벽 가이드

테스트 코드 작성이 처음이라면 이 가이드로 시작하세요. JUnit 5 기초부터 Mockito, MockMvc, SpringBootTest, Testcontainers까지 실무에서 바로 쓸 수 있는 테스트 기법을 단계별로 배웁니다.

Application Load Balancer 완벽 가이드

AWS의 Application Load Balancer를 처음 배우는 개발자를 위한 실전 가이드입니다. ALB 생성부터 ECS 연동, 헬스 체크, HTTPS 설정까지 실무에 필요한 모든 내용을 다룹니다. 초급 개발자도 쉽게 따라할 수 있도록 단계별로 설명합니다.

고객 상담 AI 시스템 완벽 구축 가이드

AWS Bedrock Agent와 Knowledge Base를 활용하여 실시간 고객 상담 AI 시스템을 구축하는 방법을 단계별로 학습합니다. RAG 기반 지식 검색부터 Guardrails 안전 장치, 프론트엔드 연동까지 실무에 바로 적용 가능한 완전한 시스템을 만들어봅니다.