개발하는 자몽

[SpringBoot] @ConfigurationProperties 본문

Java/Spring

[SpringBoot] @ConfigurationProperties

jaamong 2023. 4. 22. 14:12
 

스프링 부트 - 핵심 원리와 활용 - 인프런 | 강의

실무에 필요한 스프링 부트는 이 강의 하나로 모두 정리해드립니다., - 강의 소개 | 인프런

www.inflearn.com

섹션 7. 외부설정과 프로필2

 

 

 

application.properties나 application.yml에 작성한 프로젝트 설정을 클래스에 적용시킬 때 @ConfigurationProperties를 사용하면 편리하다.

 

MyDataSource 클래스와 application.yml

import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.util.List;

@Slf4j
public class MyDataSource {

    private String url;
    private String username;
    private String password;
    private int maxConnection;
    private Duration timeout;
    private List<String> options;

    public MyDataSource(String url, String username, String password, int maxConnection, Duration timeout, List<String> options) {
        this.url = url;
        this.username = username;
        this.password = password;
        this.maxConnection = maxConnection;
        this.timeout = timeout;
        this.options = options;
    }

    //빈 초기화 후 값 확인용
    @PostConstruct
    public void init() {
        log.info("url={}", url);
        log.info("username={}", username);
        log.info("password={}", password);
        log.info("maxConnection={}", maxConnection);
        log.info("timeout={}", timeout);
        log.info("options={}", options);
    }
}
my:
  datasource:
    url: local.db.com
    username: local_user
    password: local_pw
    etc:
      max-connection: 1
      timeout: 60s
      options: LOCAL, CACHE
---
spring:
  config:
    activate:
      on-profile: dev
my:
  datasource:
    url: dev.db.com
    username: dev_user
    password: dev_pw
    etc:
      max-connection: 10
      timeout: 60s
      options: DEV, CACHE

application.yml의 my.datasource. ... 설정을 MyDataSource의 멤버 변수에 적용해 보자.

 

@ConfigurationProperties

@ConfigurationProperties을 사용하려면 getter/setter가 필요하다. 하지만 setter는 다른 개발자가 실수로 값을 수정할 수 있으므로 setter 대신 생성자를 사용하여 값을 주입받게 하자.

@ConfigurationProperties에 application.yml에 작성한 설정명을 적어주자. my.datasource 관련 설정을 주입받게 하고 싶으므로 이것을 적어주었다. 또한 my.datasource 아래에 url, username, password, etc.maxConnection 등이 있다. etc를 공통으로 시작하는 변수는 Etc 내부 클래스를 만들어서 담는다. 이때 Etc 클래스는 속한 클래스에 멤버 변수로 선언해줘야 한다.

import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;

import java.time.Duration;
import java.util.List;

@Getter
@ConfigurationProperties("my.datasource")
public class MyDataSourcePropertiesV2 {

    private String url;
    private String username;
    private String password;
    private Etc etc;

    //setter 대신 생성자 주입
    public MyDataSourcePropertiesV2(String url, String username, String password, @DefaultValue Etc etc) {
        this.url = url;
        this.username = username;
        this.password = password;
        this.etc = etc;
    }

    @Getter
    public static class Etc {
        private int maxConnection;
        private Duration timeout;
        private List<String> options;

        //setter 대신 생성자 주입
        public Etc(int maxConnection, Duration timeout, @DefaultValue("DEFAULT") List<String> options) {
            this.maxConnection = maxConnection;
            this.timeout = timeout;
            this.options = options;
        }
    }
}

이렇게 작성한 Properties는 빈으로 등록해야 한다.

 

@EnableConfigurationProperties

import hello.datasource.MyDataSource;
import hello.datasource.MyDataSourcePropertiesV2;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@EnableConfigurationProperties(MyDataSourcePropertiesV2.class)
public class MyDataSourceConfigV2 {

    private final MyDataSourcePropertiesV2 properties;

    public MyDataSourceConfigV2(MyDataSourcePropertiesV2 properties) {
        this.properties = properties;
    }

    @Bean
    public MyDataSource myDataSource() {
        return new MyDataSource(
                properties.getUrl(),
                properties.getUsername(),
                properties.getPassword(),
                properties.getEtc().getMaxConnection(),
                properties.getEtc().getTimeout(),
                properties.getEtc().getOptions()
        );
    }
}

설정 파일에서 @EnableConfigurationProperties를 사용하여 스프링에게 어떤 @ConfigurationProperties를 적용할 건지 알려줘야 한다. 이렇게 작성하면 빈으로 등록이 되고, 필요한 곳에서 주입받을 수 있다.

빈으로 등록한 properties의 속성은 생성자로 주입받아 사용하도록 했다.

 

@ConfigurationProperties와 Validation

@ConfigurationProperties를 사용하면 타입에 맞지 않는 입력은 알아서 에러처리가 된다. 하지만 범위라던지 반드시 입력해야 하는 값들은 추가적인 검증을 필요로 한다. 이때 자바 빈 검증기를 사용하면 된다.

 

자바 빈 검증기를 사용하기 위해서는 build.gradle에 아래 코드를 추가해야 한다.

implementation 'org.springframework.boot:spring-boot-starter-validation' //추가

 

 

아래는 검증기를 적용한 ConfigurationProperties 코드다. 검증기를 적용할 클래스에 @Validated를 추가하여 반드시 값을 입력하도록 하고, 최댓값이나 최솟값을 검증하도록 했다.

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import org.hibernate.validator.constraints.time.DurationMax;
import org.hibernate.validator.constraints.time.DurationMin;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.validation.annotation.Validated;

import java.time.Duration;
import java.util.List;

@Getter
@ConfigurationProperties("my.datasource")
@Validated
public class MyDataSourcePropertiesV3 {

    @NotEmpty
    private String url;

    @NotEmpty
    private String username;

    @NotEmpty
    private String password;

    private Etc etc;

    public MyDataSourcePropertiesV3(String url, String username, String password, Etc etc) {
        this.url = url;
        this.username = username;
        this.password = password;
        this.etc = etc;
    }

    @Getter
    public static class Etc {

        @Min(1)
        @Max(99)
        private int maxConnection;

        @DurationMin(seconds = 1)
        @DurationMax(seconds = 60)
        private Duration timeout;

        private List<String> options;

        public Etc(int maxConnection, Duration timeout, @DefaultValue("DEFAULT") List<String> options) {
            this.maxConnection = maxConnection;
            this.timeout = timeout;
            this.options = options;
        }
    }
}

참고! @DefaultValue는 application.yml(properties)에서 해당 값을 찾을 수 없는 경우 기본으로 적용되는 값을 설정할 수 있다.

 

 

Config 파일은 V2에서 V3로 이름이 바뀐 것 외에는 변경사항이 없다. 이에 맞춰 @EnableConfigurationProperties를 적용할 파일명도 수정하자.

import hello.datasource.MyDataSource;
import hello.datasource.MyDataSourcePropertiesV3;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@EnableConfigurationProperties(MyDataSourcePropertiesV3.class)
public class MyDataSourceConfigV3 {

    private final MyDataSourcePropertiesV3 properties;

    public MyDataSourceConfigV3(MyDataSourcePropertiesV3 properties) {
        this.properties = properties;
    }

    @Bean
    public MyDataSource myDataSource() {
        return new MyDataSource(
                properties.getUrl(),
                properties.getUsername(),
                properties.getPassword(),
                properties.getEtc().getMaxConnection(),
                properties.getEtc().getTimeout(),
                properties.getEtc().getOptions()
        );
    }
}

 

SUMMARY

@ConfigurationProperties를 사용하여 외부 설정을 간편하게 클래스에 적용할 수 있게 되었다. 이를 사용하려면 설정 파일(Config)에 @EnableConfigurationProperties를 사용하여 스프링에게 어떤 @ConfigurationProperties를 사용할지 알려줘서 빈으로 등록해야 한다.

맞지 않는 타입 외에 검증이 필요한 경우 @ConfigurationProperties를 적용한 클래스에 자바 빈 검증기를 사용한다.

 

  • build.gradle - dependencies
implementation 'org.springframework.boot:spring-boot-starter-validation'
  • @Validated
import org.springframework.validation.annotation.Validated;
Comments