IT/CS 공부

[CS] 의존성 주입(DI : Dependency Injection)

박소민 2025. 6. 23. 10:33

의존성 주입(Dependency Injection)이란?

  • 객체 간의 의존 관계를 외부에서 설정해주는 설계 방식.
  • 객체 A가 작업 수행을 위해 객체 B가 필요 할 때, B를 A 내부에서 생성하는 것이 아니라 외부(C)에서 생성한 후 A에 주입한다.
  • 이를 통해 A와 B 간의 결합도를 낮춰 유연하고 테스트 가능한 구조를 만든다.

의존성 구성 요소

구성 요소 설명
의존성 (Dependency) A가 사용하려는 객체 = B (예: OrderRepository)
의존성 소비자 (Dependent) 의존성을 사용하는 객체 = A(예: OrderService)
주입자 (Injector) 의존성을 생성하고 소비자에게 주입하는 외부 객체 =C (예: Spring 컨테이너)

의존성 주입이 필요한 이유

  • 변경에 유연한 구조를 만들기 위함 (OCP 원칙 - 확장에는 열려 있고 변경에는 닫혀야 함)
  • 테스트 가능성 향상 (Mock 객체 주입 등으로 단위 테스트 용이)
  • 재사용성과 유지보수성 향상 (객체 생성 책임을 분리)

주입 방식

1. 생성자 주입 (Constructor Injection)

  • 존재 이유
    • 필수 의존성을 반드시 설정하도록 강제하기 위함
    • 객체가 항상 유효한 상태로 생성되도록 보장
  • 사용되는 이유
    • 불변성(immutable)이 중요한 객체 설계에 적합
    • 객체 생성 시점에 모든 의존성이 준비되어야 할 때
    • 필수 의존성을 빠뜨리는 실수를 컴파일 시점에 방지할 수 있음

 

예제와 구성요소 설명
// 의존성 소비자 (Dependent)
public class OrderService {
    private final OrderRepository orderRepository;       // 의존성 1
    private final NotificationService notificationService; // 의존성 2

    // 주입자(Injector)가 두 의존성을 생성자에 주입
    public OrderService(OrderRepository orderRepository,
                        NotificationService notificationService) {
        this.orderRepository = orderRepository;
        this.notificationService = notificationService;
    }

    public void placeOrder(Long userId, String item) {
        orderRepository.saveOrder(userId, item);
        notificationService.send("Order placed for item: " + item);
    }
}
  • 의존성 (Dependency): OrderRepository, NotificationService
  • 의존성 소비자 (Dependent): OrderService
  • 주입자 (Injector): 스프링 컨테이너가 생성자 호출 시 의존성 주입

2. Setter 주입 (Setter Injection)

  • 존재 이유
    • 선택적 의존성을 주입할 수 있도록 하기 위함
    • 필요에 따라 일부 의존성만 설정할 수 있도록 유연한 구조 제공
  • 사용되는 이유
    • 초기화 이후 변경이 가능해야 할 때
    • 객체가 반드시 특정 의존성을 필요로 하진 않는 경우
    • 테스트 시에 특정 의존성만 따로 설정할 수 있도록 하기 위함

 

예제와 구성요소 설명
// 의존성 소비자 (Dependent)
public class OrderService {
    private OrderRepository orderRepository;          // 의존성 1
    private NotificationService notificationService;  // 의존성 2

    // 기본 생성자
    public OrderService() {}

    // 주입자(Injector)가 setter 메서드를 통해 각각 주입
    public void setOrderRepository(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public void setNotificationService(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    public void placeOrder(Long userId, String item) {
        orderRepository.saveOrder(userId, item);
        notificationService.send("Order placed for item: " + item);
    }
}
  • 의존성: OrderRepository, NotificationService
  • 소비자: OrderService
  • 주입자: 스프링 컨테이너 또는 테스트 코드에서 setter 호출

3. 메서드 주입 (Method Injection)

  • 존재 이유
    • 일시적, 동적인 의존성을 주입하기 위함
    • 메서드 실행 시점마다 다른 의존성이 필요할 수 있음
  • 사용되는 이유
    • 요청마다 다른 Formatter, 전략(Strategy) 등을 주입해야 할 때
    • 상태를 갖지 않는, 단발성 의존성 주입에 적합

 

예제와 구성요소 설명
public class ReportService {
    public void generateReport(ReportData data, ReportFormatter formatter) {
        formatter.format(data);
    }
}
  • 의존성: ReportFormatter
  • 소비자: ReportService
  • 주입자: 메서드를 호출하는 클라이언트 코드 (직접 인자로 전달)