@ConfigurationProperties 의 용도
properties 혹은 yml 에 존재하는 프로퍼티들을 객체에 바인딩할 수 있도록 해준다.
하나의 클래스에서만 사용되는 경우에는 @Value 어노테이션을 이용해서 사용해왔다.
@Value("${jwt.public-key}")
private String publicKey;
@Value("${jwt.private-key}")
private String privateKey;
그런데 만약, jwt 관련 설정 정보가 여러 군데에서 필요하다면?!
다음 상황을 가정해보자. 현재는 하나의 클래스에서 토큰을 생성하고 유효성을 검사하고 있지만, 이를 다른 클래스로 분리하게 된다면 두 클래스에서 모두 해당 설정 정보가 필요할 것이다.
이런 경우에 매번 @Value 로 바인딩하는 것은 코드의 반복과도 같다고 생각했다.
이런 경우에 해당 어노테이션을 쓰면 장점이 존재할 것이라고 생각했는데, 이 이외에도 많은 장점들을 발견할 수 있었다.
@ConfigurationProperties 의 장점 by chatGPT
1. 속성 그룹화
- 하나의 클래스로 관리 가능
- 관련성을 유지할 수 있다
2. 타입 안정성
- 설정 값을 자바 객체로 매핑하기 때문에 안정성을 보장한다.
3. IDE 지원
- IDE 에서 설정 값을 자동 완성하고 추론하는 기능을 제공해준다.
4. 프로퍼티 변경 감지
- 설정 값 변경 시에 이를 캐치하고 대응하는 로직을 따로 설정해줄 수 있다.
5. 효율적인 속성의 로딩
- 한 번 로드하고 필요할 때 주입하는 방식이기 때문에 중복된 로딩을 방지하여 부하를 줄일 수 있다.
@ConfigurationProperties 적용하기 (스프링 2.2 이상 버전)
1. 애플리케이션의 메인 클래스에 @ConfigurationPropertiesScan 추가하기
@SpringBootApplication
@ConfigurationPropertiesScan
public class XXXApplication {
public static void main(String[] args) {
SpringApplication.run(XXXApplication.class, args);
}
}
@ConfigurationPropertiesScan 어노테이션
@ConfigurationProperties 이 붙은 클래스들을 자동으로 스캔하고 빈으로 등록할 수 있도록 해준다.
해당 어노테이션이 존재하지 않던 스프링부트 이전 버전의 경우에는 다음과 같이 일일이 @ConfigurationProperties 붙은 클래스들을 명시해줘야 했다. ConfigurationPropertiesScan 어노테이션을 통해서 비교적 코드가 간결해질 수 있게 된 것이다.
@EnableConfigurationProperties({
XXXConfigurationClass.class
})
2. 설정 클래스 작성하기
@Getter
@ConstructorBinding
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "temp.config")
public class XXXConfigurationClass {
private final String secretKey;
}
ConfigurationProperties 를 사용할 클래스를 작성해준다.
위와 같이 작성된 경우에 yaml 파일은 다음과 같다.
temp:
secret-key: ${secret}
3. 설정 클래스를 주입하기
@Component
@RequiredArgsConstructor
public class XXXClass {
private final ConfigurationClass configurationClass;
}
해당 설정 값을 사용할 곳에 주입한다.
이렇게 설정하면 끝! 이다.
이를 통해서 @Value 가 붙어 있던 필드들을 간단하게 객체로 바인딩해서 사용할 수 있게 되었다.
⚡️ 트러블 슈팅
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in ... required a bean of type 'java.lang.String' that could not be found.
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.
위와 같은 오류가 지속해서 발생했었다.
당시의 코드는 다음과 같았다.
@Getter
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "temp.config")
public class XXXConfigurationClass {
private final String secretKey;
}
혹시 yaml 파일이 문제인가? 했었는데 그렇다면 @Value 를 이용해서 바인딩했었을 때 오류가 발생해야 했었을 것이다.
그러나 이 경우에 에러가 발생하지 않았었기 때문에 .. 도저히 감을 잡을 수 없었다.
에러 메시지를 잘 보면 생성자에서 문제가 발생한 것을 알 수 있다.
우선 결론만 이야기하자면, @ConstructorBinding 어노테이션을 추가하지 않아서 그렇다.
그렇다면 @ConstructorBinding 는 무엇일까?
@ConstructorBinding
현재 에러 원인은 생성자에서 프로퍼티 값이 제대로 주입되지 않은 것이다.
나의 경우에는 설정 클래스인 XXXConfigurationClass 의 필드들을 final 로 선언했기 때문에 생성자를 통해서만 초기화 될 수 있다.
이 어노테이션을 사용하면 생성자에서 프로퍼티를 바인딩할 수 있도록 해준다.
에러가 발생했던 이유는, final 로 선언되어 있고 생성자를 통해서만 초기화될 수 있는데 생성자를 통해 프로퍼티를 바인딩할 수 있게 해주는 ConstructorBinding 어노테이션이 누락됐기 때문에 이 오류가 발생한 것이다.
해당 어노테이션을 사용하면 불변 객체로 만들 수 있기 때문에 안정성 측면에서 이점을 얻을 수 있을 것이다!
'SpringBoot' 카테고리의 다른 글
[SpringBoot] 이벤트 기반 아키텍처를 알아보고 스프링부트의 이벤트를 구현해보자 (0) | 2024.03.01 |
---|---|
[SpringBoot] Spring REST Docs 로 API 명세를 문서화 하자 (1) | 2024.01.23 |
[SpringBoot] 동시성 문제를 해결하자 (Synchronized, MySQL, Redis) (2) | 2024.01.10 |
[SpringBoot] N+1 을 고려하여 페이징 쿼리 작성하기 (1) | 2024.01.01 |
[SpringBoot] 확장성을 고려하여 OAuth2.0 로 Kakao 소셜 로그인 구현하기 (0) | 2023.09.27 |