승이의 기술블로그

@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 어노테이션이 누락됐기 때문에 이 오류가 발생한 것이다.

해당 어노테이션을 사용하면 불변 객체로 만들 수 있기 때문에 안정성 측면에서 이점을 얻을 수 있을 것이다!

검색 태그