본문으로 건너뛰기
개발 머꼬
개발 노트Spring
hohyeon.dev19

Spring @Autowired 필드 주입이 권장되지 않는 이유

  • #Engineering Note
  • #Spring
  • #Spring Boot
  • #Testing

문제 발생

필드 주입으로 작성한 서비스 클래스를 순수 단위 테스트(Spring 컨텍스트 없이)하려는데, 의존성을 주입할 방법이 마땅치 않아 결국 무거운 통합 테스트로만 검증하게 됐습니다.

@Service
public class OrderService {
    @Autowired
    private PaymentClient paymentClient; // 필드 주입
}

원인 분석

필드 주입은 Spring 컨테이너가 리플렉션으로 private 필드에 직접 값을 꽂아 넣는 방식입니다. 코드가 짧아 보이지만 여러 구조적 문제가 있습니다.

  1. 테스트 어려움: 순수 Java 코드로 new OrderService()를 만들면 paymentClientnull입니다. Mockito 같은 도구로 리플렉션을 써서 강제로 주입하지 않는 한, Spring 컨텍스트를 통째로 띄우는 통합 테스트 없이는 이 클래스를 테스트하기 어렵습니다.
  2. 불변성 불가: 필드 주입은 필드를 final로 선언할 수 없습니다. 생성자 주입은 final 필드로 선언 가능해 "생성된 이후 절대 바뀌지 않는다"는 것을 컴파일러가 보장합니다.
  3. 순환 의존성이 늦게 발견됨: 생성자 주입은 애플리케이션 시작 시점에 순환 의존성이 있으면 즉시 실패합니다. 필드 주입은 객체 생성 자체는 성공하고 나중에 리플렉션으로 채워 넣기 때문에, Spring이 순환 의존성을 다른 방식(임시 프록시 등)으로 우회하려다 예상치 못한 동작을 낼 수 있습니다.
  4. 필수 의존성이 다 채워졌는지 컴파일 타임에 알 수 없습니다 — @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)을 쓴다면 @RequiredArgsConstructorfinal 필드에 대한 생성자를 자동 생성할 수 있습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.