@RequiredArgsConstructor는 Lombok에서 제공하는 어노테이션으로, 클래스의 final 필드나 @NonNull로 선언된 필드에 대해 자동으로 생성자를 생성해주어 의존성 주입을 간편하게 만들어준다.
문제의 코드는 아래와 같다.
@Service
@RequiredArgsConstructor
public class MyService {
@Qualifier("restClientSSE")
private final RestClient restClient;
...
}
@Qualifier를 사용하여 특정 빈(restClientSSE)을 주입하여 "http://localhost:8080/"의 baseUrl을 포함한 API를 호출하려고 했는데, 다른 빈(restClient)이 주입되어 "www.googleapis.com/"의 baseUrl을 포함한 API가 호출되는 상황이 발생한다.
RestClientConfig는 다음과 같다.
@Configuration
@RequiredArgsConstructor
public class RestClientConfig {
@Bean
public RestClient restClient() {
return RestClient.builder()
.baseUrl("https://www.googleapis.com")
.build();
}
@Bean("restClientSSE")
public RestClient restClientSSE() {
return RestClient.builder()
.baseUrl("http://localhost:8081")
.build();
}
결론부터 말하면, 먼저 lombok이 제공하는 @RequiredArgsConstructor는 애노테이션 까지 함께 포함해서 생성자를 만들지는 않는다고 한다. 그래서, @Qualifier이 포함되지 않은 상태로 빈 이름을 찾아 등록이 되었던 것이다.
그런데 가능한 방법이 있다고 한다. [인프런] 김영한님 답변
- lombok.config 생성
lombok.config에 다음 내용을 넣는다.
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
IntelliJ를 사용하면, out이라는 폴더가 있는데 이 폴더를 꼭! 모두 지우고 다시 실행. gradle은 gradlew clean - @Qualifier를 지우고 필드 이름을 restClientSSE로 변경
@Service
@RequiredArgsConstructor
public class VideoApiService {
private final RestClient restClientSSE;
...
}
@RequiredArgsConstructor로 @Qualifier로 명시된 빈의 생성자 주입이 정상적으로 되지 않았을 때, @Autowired로 필드 주입을 잠시 했었다. 그때 위와 같이 Field injection is not recommended라는 경고문을 볼 수 있는데, Spring에서 필드 주입(Field Injection) 대신 생성자 주입(Constructor Injection)을 권장한다는 것이며 이유는 다음과 같다:
1. 불변성
생성자 주입을 사용하면 주입된 의존성을 final로 선언할 수 있다. 이는 객체가 생성된 이후에 의존성이 변경되지 않음을 보장한다. 필드 주입에서는 private 필드가 final로 선언될 수 없으므로 객체의 상태가 변경될 위험이 있다.
2. 테스트 용이성
필드 주입은 생성자나 Setter를 통한 주입 방식이 아니기 때문에, Spring DI 컨테이너에 강하게 결합된다. 이로 인해 Spring 외부에서는 해당 필드에 의존성을 주입할 수 있는 방법이 없으며, 이는 코드의 재사용성과 테스트 용이성을 저하시킨다.
3. 순환 의존성
생성자 주입은 스프링 컨테이너가 빈을 생성하는 시점에 사이클 관계가 생기기 때문에 순환 참조를 캐치하게 된다. 하지만 필드 주입은 객체가 이미 생성된 후에 필드를 주입하기 때문에, 순환 참조가 발생할 경우 두 객체가 모두 초기화되지 않은 상태에서 서로를 참조하게 돼 런타임 시 StackOverflowError와 같은 문제가 발생할 수 있다. (필드 주입은 해당 빈이 필요한 시점에 주입되기 때문에 생성 시점이 아닌 비즈니스 로직 상에서 순환참조가 일어나는 것이기 때문에 빈 생성이 정상적으로 되는 것처럼 보인다.)
'Spring' 카테고리의 다른 글
[Spring] Spring Security Filter 예외 핸들링 (0) | 2024.12.14 |
---|---|
[Spring] Logback 로그 관리 (logback-spring.xml 설정) (2) | 2024.12.07 |