Back_End/Spring

스프링 의존성 주입(Dependency Injection, DI) 완전 정복

10Biliion 2025. 5. 21. 08:41

스프링의 핵심 철학은 IoC(Inversion of Control) 입니다. 철학을 구현하는 가장 대표적인 기술이 바로 의존성 주입(Dependency Injection) 입니다.

 

1. 의존성 주입이란?

의존성(Dependency): 객체가 다른 객체를 사용할 때, 관계를 ‘의존성’이라고 합니다.
주입(Injection): 필요한 의존 객체를 직접 생성하지 않고 외부에서 넣어주는 입니다.

전통적인 방식 (의존 객체를 직접 생성)

public class OrderService {
    private OrderRepository orderRepository = new OrderRepository(); // 직접 생성
}

DI 적용 방식 (외부에서 주입)

public class OrderService {
    private final OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) { // 생성자를 통해 주입
        this.orderRepository = orderRepository;
    }
}

2. 스프링에서 DI중요한 이유

구분 설명
유연성 구현체 변경이 쉬움 (ex: 메모리 DB → MySQL)
테스트 용이성 가짜 객체(Mock)주입하여 단위 테스트 가능
결합도 감소 객체 간의 강한 결합을 피하고 느슨한 연결을 유지
SOLID 원칙 준수 특히 DIP(Dependency Inversion Principle)지킬 있음
 

3. 스프링에서 의존성 주입하는 방법 3가지

생성자 주입 (Constructor Injection) ⭐️ 가장 권장되는 방식

@Component
public class OrderService {

    private final OrderRepository orderRepository;

    @Autowired // 생략 가능 (생성자가 1개일 경우)
    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }
}

장점

  • final 키워드로 불변 보장
  • 주입받지 못하면 컴파일 시점에 에러 발생
  • 테스트 시에도 명확한 의존성 요구

필드 주입 (Field Injection)

@Component
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;
}

단점

  • 테스트 주입 불가능 (Reflection 사용해야 함)
  • DI 프레임워크가 없으면 객체 생성 자체가
  • 외부에서 변경 불가능 (private)

🚫 실무에서는 테스트/유지보수 측면에서 비권장 방식입니다.


세터 주입 (Setter Injection)

@Component
public class OrderService {

    private OrderRepository orderRepository;

    @Autowired
    public void setOrderRepository(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }
}

장점

  • 선택적 의존성 주입에 적합
  • DI 프레임워크 없이도 기본 생성자로 객체 생성 가능

단점

  • 불변성 보장이
  • 의존성 누락 가능성이 있음

4. 컴포넌트 스캔과 등록

자동 등록 (@Component 계열 사용)

@Component
public class OrderService {
    ...
}
  • @Component 외에도 @Service, @Repository, @Controller 사용 가능
  • @SpringBootApplication 아래 패키지부터 스캔

수동 등록 (@Configuration + @Bean)

@Configuration
public class AppConfig {

    @Bean
    public OrderService orderService() {
        return new OrderService(orderRepository());
    }

    @Bean
    public OrderRepository orderRepository() {
        return new MemoryOrderRepository();
    }
}
  • 테스트나 특정 설정이 필요할 유용
  • XML 없이 Java 코드로 구성 가능

5. 사용법

생성자 주입을 기본으로 사용
필요한 경우에만 필드/세터 주입 사용
@RequiredArgsConstructor사용하면 생성자 주입을 간편하게 구현 가능

@Service
@RequiredArgsConstructor
public class OrderService {
    private final OrderRepository orderRepository; // 생성자 자동 생성됨
}

 

주입  방식 장점 단점 권장 여부
생성자 주입 불변성, 명확한 의존성 코드가 약간 길어짐 매우 권장
필드 주입 코드 간단 테스트 어려움 비권장
세터 주입 선택적 의존성 불변성 보장 불가 🔸조건부 사용