final 필드를 사용할 수 있어 불변성을 보장final 키워드를 사용할 수 있음 @Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) { // 생성자를 통한 주입
this.userRepository = userRepository;
}
}
final 필드로 선언된 userRepository는 한 번 할당된 이후 변경 불가능@Autowired가 필요하여, Spring Context를 실행해야 테스트 가능 new 키워드를 이용해 직접 객체를 생성하여 테스트 가능 @Test
void testUserService() {
UserRepository mockRepository = mock(UserRepository.class);
UserService userService = new UserService(mockRepository); // 직접 주입 가능
// 테스트 수행...
}
✅ Spring 없이도 객체 생성 및 주입이 가능하여 빠른 단위 테스트 가능
@SpringBootTest
class UserServiceTest {
@Autowired // Spring이 주입해야 함
private UserService userService;
@Test
void testUserService() {
// userService가 주입되려면 Spring Context가 실행되어야 함
}
}
⚠ 단점: @Autowired를 사용하면, 테스트 실행 시 Spring Context가 필요하여 속도가 느려지고, 독립적인 단위 테스트가 어려움
// 생성자 주입 방식
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) { // 필수 의존성 체크 가능
this.userRepository = userRepository;
}
}
✔ final 필드이므로 userRepository가 주입되지 않으면 컴파일 에러 발생
⚠ 반면, 필드 주입이나 Setter 주입 방식에서는 NullPointerException이 런타임에서 발생할 가능성이 있음
// Setter 주입 방식 (테스트 시 setUserRepository를 통해 변경 가능)
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
@Test
void testUserService() {
UserService userService = new UserService();
userService.setUserRepository(mockRepository1); // 한 번 주입
// 다른 테스트에서
userService.setUserRepository(mockRepository2); // 다른 객체로 변경 가능 (테스트 신뢰도↓)
}
⚠ 문제점:
✅ 생성자 주입 방식은 Setter 없이 한 번만 주입되므로, 테스트 시 예측 가능한 결과를 유지
| 주입 방식 | 장점 | 단점 |
|---|---|---|
| 생성자 주입 (Constructor Injection) ✅ | final 필드 사용 가능 → 불변성 유지, 컴파일 타임 체크 가능, Spring 없이 단위 테스트 가능 | 의존성이 많아지면 생성자가 길어질 수 있음 |
| 필드 주입 (Field Injection) | 코드가 짧고 간결함 | @Autowired 필요 → Spring Context 실행 필수, 테스트 어려움 |
| Setter 주입 (Setter Injection) | 선택적 의존성 주입 가능 | 테스트 시 의존성이 변경될 위험 존재 |
✅ 결론