Spring에서 의존성은 어떻게 주입되고, 왜 생성자 주입을 권장할까?

EunBeen Noh·2025년 5월 8일

SpringAdvanced

목록 보기
12/16

1. DI(Dependency Injection)란?

DI, 즉 의존성 주입은
객체가 직접 의존 객체를 생성하지 않고 외부에서 주입받는 설계 방식이다.
Spring은 DI를 핵심 원리로 삼고 있으며,
이를 통해 객체 간의 결합도를 낮추고 테스트와 유연성을 높이는 구조를 지향한다.

즉, 객체 간의 관계(의존성)를 코드 내부에서 만들지 않고,
외부 설정이나 컨테이너를 통해 주입받는 것이다.

2. Spring의 DI 방식 – 세 가지

Spring은 DI를 세 가지 방식으로 제공하고 있다.

방식설명권장 여부
생성자 주입생성자를 통해 의존 객체를 주입가장 권장
필드 주입필드에 직접 주입 (@Autowired)지양
Setter 주입setter 메서드를 통해 주입상황에 따라 허용

1. 생성자 주입 (Constructor Injection)

// @Autowired 생략 가능 (생성자가 1개일 경우)
public Injection(InjectionService injectionService) {
    this.injectionService = injectionService;
}
@RequiredArgsConstructor // lombok 어노테이션을 사용한 생성자 주입
public class Injection {
    private final InjectionService injectionService;
}
  • final 키워드로 불변성(immutability) 확보

    • 객체 생성 시 반드시 주입되어야 하며, 이후 값이 변경될 수 없기 때문에
      객체의 상태가 중간에 변질되지 않아 안전하고 예측 가능하다.

    • 불변성은 특히 멀티스레드 환경이나 도메인 객체 설계에서 중요한 품질임

      불변성 면에서의 생성자 주입

      대부분의 의존성은 애플리케이션 시작 시점에 한 번만 주입되고, 이후 변경되면 안 되는 값이 많다.
      즉, 불변성이 중요한데,
      오직 생성자 주입만이 final을 사용할 수 있어 불변 객체 설계가 가능하다.
      Setter 주입은 public 메서드로 열려 있기 때문에,
      외부 클래스가 의도치 않게 의존 객체를 바꿔버릴 수 있다.

  • 주입 시점이 명확하고, NPE(NullPointerException) 방지

    • 생성자 호출 시점에 의존성이 주입되므로,
      주입되지 않은 상태에서 메서드를 호출하는 일이 없어 런타임 NPE 발생 가능성을 원천 차단할 수 있다.
    • 반면, 필드나 setter 주입은 객체 생성 후 주입되므로, 그 사이 null 상태가 생길 수 있다.
  • 테스트하기 용이하고, DI 누락 시 컴파일 시점에 오류 발생

    • 생성자 파라미터로 의존 객체를 받기 때문에, 의존성이 누락되면 컴파일 시점에 에러가 발생한다. (오류 발생 시점이 컴파일 시점이기 때문에 빠른 디버깅이 가능하다.)
    • 이는 테스트 코드에서도 명확히 드러나고, Mock 객체를 생성자에 주입해 단위 테스트 작성도 편리하다.
    • 필드 주입은 리플렉션(런타임 시점에 오류 발생) 기반으로 작동해 테스트 시 의존성 주입이 더 복잡하고 불안정하다.

Spring 4.3부터는 생성자가 1개일 경우 @Autowired 생략 가능
Spring 공식 문서에서도 생성자 주입을 가장 안전하고 유지보수하기 좋은 방식으로 권장한다.

2. 필드 주입 (Field Injection)


public class Injection {
    @Autowired
    private InjectionService injectionService;
}
  • 코드가 간결하지만, 테스트가 어렵고
  • 순환 참조 발생 시 디버깅이 어려움
  • final 사용 불가 → 불변성 불가

과거에는 흔히 사용되었지만, 현재는 테스트 불가능한 코드, 유지보수 어려움으로 인해 지양한다.

3. Setter 주입 (Setter Injection)

public class Injection {
    private InjectionService injectionService;

    @Autowired
    public void setInjectionService(InjectionService injectionService) {
        this.injectionService = injectionService;
    }
}
  • 선택적으로 의존성을 주입하고 싶은 경우 사용
  • 메서드를 public으로 열어야 하므로 외부에서 의도치 않은 변경 위험 존재
  • 생성자 주입보다 안정성이 떨어짐

결론

  • Spring에서 DI는 객체 간 결합을 줄이고, 테스트 가능한 구조를 만들기 위한 핵심 원칙이다.
  • 필드 주입은 더 이상 권장되지 않으며, 생성자 주입이 기본 전략이다.
  • 생성자 주입은 불변 객체 설계, 명확한 책임, 테스트 용이성 모두를 만족시킨다.
  • Setter 주입은 선택적 의존성이 필요한 경우에만 제한적으로 사용하자.
  • “무조건 생성자 주입”이 아니라, 상황에 따라 조절하지만 기본은 생성자 주입이다.

0개의 댓글