ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SpringBoot] @ConfigurationProperties 로 프로퍼티들을 바인딩하기
    SpringBoot 2023. 9. 8. 17:17

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

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

Designed by Tistory.