본문 바로가기
IT

스프링 의존성 주입: @Autowired vs @RequiredArgsConstructor + 단위 테스트 예제

by urosie 2025. 9. 24.
반응형

스프링에서는 객체 간 의존성을 주입할 때 여러 방법이 있지만, 대표적으로 **필드 주입(@Autowired)**과 **생성자 주입(@RequiredArgsConstructor)**이 있습니다. 겉으로는 어노테이션 하나 차이처럼 보이지만, 동작과 장단점은 크게 다릅니다.


1. 필드 주입: @Autowired

 
@Service
public class UserService {
    @Autowired
    private UserRepository repository;

    public String getUserName(Long id) {
        return repository.findNameById(id);
    }
}
  • 스프링이 리플렉션으로 필드에 값 주입
  • final 필드 불가능 → 값 변경 가능
  • 단위 테스트 어려움: 스프링 컨테이너 없이는 new UserService() 사용 불가
  • 순환 참조가 있어도 런타임에야 발견 가능

단위 테스트 문제

 
class UserServiceTest {
    @Test
    void testGetUserName() {
        UserService service = new UserService(); // ❌ repository가 null
        service.getUserName(1L); // NullPointerException 발생
    }
}
  • 결과: 스프링 컨테이너 없이는 테스트 자체가 불가능

2. 생성자 주입: @RequiredArgsConstructor (Lombok)

 
@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository repository;

    public String getUserName(Long id) {
        return repository.findNameById(id);
    }
}
  • Lombok이 final 필드를 대상으로 생성자 자동 생성
  • 스프링이 생성자를 통해 의존성 주입
  • final 필드 → 불변성 보장
  • 단위 테스트 용이: 스프링 없이도 new UserService(mockRepo)로 테스트 가능
  • 순환 참조 시 앱 시작 단계에서 바로 감지 가능

단위 테스트 예제

 
class UserServiceTest {
    @Test
    void testGetUserName() {
        // 스프링 컨테이너 없이 가짜 객체 주입
        UserRepository fakeRepo = id -> "mock-user";
        UserService service = new UserService(fakeRepo);

        assertEquals("mock-user", service.getUserName(1L));
    }
}
  • 결과: 스프링 컨테이너 없이도 단위 테스트 가능, 빠르고 가볍다

3. 핵심 비교

항목@Autowired (필드 주입)@RequiredArgsConstructor (생성자 주입)
final 사용
단위 테스트 스프링 필요, 느림 스프링 없이 가능, 빠름
불변성 낮음 높음
순환참조 감지 런타임 애플리케이션 시작 시
코드 간결성 약간 짧음 Lombok 사용 시 간결
권장 여부 비권장 권장

✅ 결론

  • **생성자 주입(@RequiredArgsConstructor)**은 테스트 편의, 안전성, 유지보수 측면에서 훨씬 유리합니다.
  • 필드 주입(@Autowired)은 간단하지만 장기적인 관점에서는 권장되지 않습니다.
  • 특히 단위 테스트를 많이 하는 프로젝트라면, 스프링 컨테이너 없이 객체를 직접 생성해서 테스트할 수 있는 생성자 주입이 필수라고 할 수 있습니다.
반응형

댓글