
DI, 즉 의존성 주입은
객체가 직접 의존 객체를 생성하지 않고 외부에서 주입받는 설계 방식이다.
Spring은 DI를 핵심 원리로 삼고 있으며,
이를 통해 객체 간의 결합도를 낮추고 테스트와 유연성을 높이는 구조를 지향한다.
즉, 객체 간의 관계(의존성)를 코드 내부에서 만들지 않고,
외부 설정이나 컨테이너를 통해 주입받는 것이다.
Spring은 DI를 세 가지 방식으로 제공하고 있다.
| 방식 | 설명 | 권장 여부 |
|---|---|---|
| 생성자 주입 | 생성자를 통해 의존 객체를 주입 | 가장 권장 |
| 필드 주입 | 필드에 직접 주입 (@Autowired) | 지양 |
| Setter 주입 | setter 메서드를 통해 주입 | 상황에 따라 허용 |
// @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) 방지
테스트하기 용이하고, DI 누락 시 컴파일 시점에 오류 발생
Spring 4.3부터는 생성자가 1개일 경우 @Autowired 생략 가능
Spring 공식 문서에서도 생성자 주입을 가장 안전하고 유지보수하기 좋은 방식으로 권장한다.
public class Injection {
@Autowired
private InjectionService injectionService;
}
과거에는 흔히 사용되었지만, 현재는 테스트 불가능한 코드, 유지보수 어려움으로 인해 지양한다.
public class Injection {
private InjectionService injectionService;
@Autowired
public void setInjectionService(InjectionService injectionService) {
this.injectionService = injectionService;
}
}
결론
- Spring에서 DI는 객체 간 결합을 줄이고, 테스트 가능한 구조를 만들기 위한 핵심 원칙이다.
- 필드 주입은 더 이상 권장되지 않으며, 생성자 주입이 기본 전략이다.
- 생성자 주입은 불변 객체 설계, 명확한 책임, 테스트 용이성 모두를 만족시킨다.
- Setter 주입은 선택적 의존성이 필요한 경우에만 제한적으로 사용하자.
- “무조건 생성자 주입”이 아니라, 상황에 따라 조절하지만 기본은 생성자 주입이다.