Spring @Autowired 필드 주입이 권장되지 않는 이유
문제 발생
필드 주입으로 작성한 서비스 클래스를 순수 단위 테스트(Spring 컨텍스트 없이)하려는데, 의존성을 주입할 방법이 마땅치 않아 결국 무거운 통합 테스트로만 검증하게 됐습니다.
@Service
public class OrderService {
@Autowired
private PaymentClient paymentClient; // 필드 주입
}원인 분석
필드 주입은 Spring 컨테이너가 리플렉션으로 private 필드에 직접 값을 꽂아 넣는 방식입니다. 코드가 짧아 보이지만 여러 구조적 문제가 있습니다.
- 테스트 어려움: 순수 Java 코드로
new OrderService()를 만들면paymentClient가null입니다. Mockito 같은 도구로 리플렉션을 써서 강제로 주입하지 않는 한, Spring 컨텍스트를 통째로 띄우는 통합 테스트 없이는 이 클래스를 테스트하기 어렵습니다. - 불변성 불가: 필드 주입은 필드를
final로 선언할 수 없습니다. 생성자 주입은final필드로 선언 가능해 "생성된 이후 절대 바뀌지 않는다"는 것을 컴파일러가 보장합니다. - 순환 의존성이 늦게 발견됨: 생성자 주입은 애플리케이션 시작 시점에 순환 의존성이 있으면 즉시 실패합니다. 필드 주입은 객체 생성 자체는 성공하고 나중에 리플렉션으로 채워 넣기 때문에, Spring이 순환 의존성을 다른 방식(임시 프록시 등)으로 우회하려다 예상치 못한 동작을 낼 수 있습니다.
- 필수 의존성이 다 채워졌는지 컴파일 타임에 알 수 없습니다 —
@Autowired필드가null인 채로 빈이 만들어질 수 있는 경로가 실수로 생기기 쉽습니다.
해결 방안
생성자 주입을 기본으로 씁니다. Spring 4.3부터는 생성자가 하나뿐이면 @Autowired를 생략해도 자동으로 주입됩니다.
@Service
public class OrderService {
private final PaymentClient paymentClient;
public OrderService(PaymentClient paymentClient) {
this.paymentClient = paymentClient;
}
}이렇게 하면 순수 Java 테스트에서 new OrderService(mockPaymentClient)로 바로 의존성을 주입할 수 있어 Spring 컨텍스트 없이도 단위 테스트가 가능합니다. 롬복(Lombok)을 쓴다면 @RequiredArgsConstructor로 final 필드에 대한 생성자를 자동 생성할 수 있습니다.
공식 문서
- Spring Framework Docs: Dependency Injection — "The Spring team generally advocates constructor injection"이라고 명시합니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.