[Spring Boot Core] Spring Boot Relaxed Binding using yaml

2022. 3. 30. 17:37Spring/Basic

300x250
반응형
  1. Externalized Configuration
    1. @ConfigurationProperties vs @Value
    2. 작성 방법
      1. @Value
      2. @ConfigurationProperties
  2. Relaxed Binding
    1. @ConfigurationProperties에 적용되는 Relaxed Binding
    2. @Value에 적용되는 Relaxed Binding
  3. 테스트 결과
  4. 참고

1. Externalized Configuration

Spring Boot는 설정의 외부화를 통해 다양한 환경에 따라 설정을 달리할 부분들을 별도로 관리할 수 있습니다.

설정의 외부화(Externalized Configuration)는 properties 또는 YAML 파일, 환경변수, Command-line 인자값 등을 통해 설정할 수 있습니다.

1.1. @ConfigurationProperties vs @Value

YAML 파일에 설정한 프로퍼티를 Spring Boot로 주입하는 방법은 크게 2가지가 있습니다.

  1. @ConfigurationProperties 로 동일한 계층에 위치한 프로퍼티들을 한번에 주입
  2. @Value 애너테이션으로 빈에 직접 값을 주입

위의 2가지 방법의 특징에 대해 간략히 요약하자면 아래와 같습니다.

@ConfigurationProperties @Value
Relaxed binding 지원 제한적 지원
Meta-data 지원 지원x
SpEL 지원x 지원

@ConfigurationProperties 는 계층단위로 한꺼번에 프로퍼티를 주입할 수 있으며, 컬렉션과 POJO 프로퍼티를 쉽게 읽어들일 수 있습니다.


1.2. 작성 방법

아래와 같은 YAML 파일이 정의되어 있을 때, 각 프로퍼티값을 읽어들이는 방법을 알아봅시다.

spring:
  datasource:
    hikari:
      auto-commit: false
      connection-test-query: SELECT 1
      minimum-idle: 10
      maximum-pool-size: 50
      pool-name: pool-jiniworld_demo

1.2.1. @Value

@Value 방식은 @Value("${property}") 방식으로 속성을 주입합니다.

@Data
@Component
public class ValueProperties {

	@Value("${spring.datasource.hikari.auto-commit}")
	private boolean autoCommit;
	@Value("${spring.datasource.hikari.connection-test-query}")
	private String connectionTestQuery;
	@Value("${spring.datasource.hikari.minimum-idle}")
	private int minimumIdle;
	@Value("${spring.datasource.hikari.maximum-pool-size}")
	private int maximumPoolSize;
	@Value("${spring.datasource.hikari.pool-name}")
	private String poolName;

}

만일 여러개의 속성을 주입해야하는 상황이라면 위와같이 동일한 prefix를 가지는 프로퍼티 정의에 반복적인 코드를 입력해야하는 번거로움이 있습니다.


1.2.2. @ConfigurationProperties

@ConfigurationProperties 를 이용하면 spring.datasource.hikari를 prefix로 가지는 프로퍼티값들을 한꺼번에 주입할 수 있습니다.

@Data
@Component
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public class DemoProperties {
	private boolean autoCommit;
	private String connectionTestQuery;
	private int minimumIdle;
	private int maximumPoolSize;
	private String poolName;
}

@ConfigurationProperties 를 사용할 때 주의 사항은 prefix는 반드시 kebab case로 정의해야한다는 것입니다.


2. Relaxed Binding

아래와 같은 프로퍼티를 기준으로 Relaxed Binding을 설명하겠습니다.

playground:
  relaxed-binding:
    userId: demo123
    PASSWORD: 1234
    api-key: vnhfM8vJjwzJEvGCqvNue9h8w77fhxPS
    secret_key: qEX3PSHHgm2mvaHur3RG2VD2eYbKc75j
    sites:
      - https://java.applebox.xyz
      - http://localhost:8080
    sites2: https://java.applebox.xyz, http://localhost:8080
    option:
      name: test
      flag: false

2.1. @ConfigurationProperties에 적용되는 Relaxed Binding

Spring Boot 에서는 @ConfigurationProperties를 이용한 프로퍼티 바인딩을 매우 유연하게 제공합니다.

prefix는 무조건 kebab-case 형식으로 작성해야하지만, 이하 프로퍼티에 대해서는 Relaxed Binding을 제공합니다.

kebab-case, snake_case, camelCase로 작성한 프로퍼티는 모두 camelCase 로 정의한 필드에 바인딩 되며, UPEERCASE로 작성된 프로퍼티는 lowercase로 변환되어 바인딩 됩니다.

Programming naming cases 참고


playground.relaxed-binding 아래에 있는 프로퍼티 키 값인 userId, PASSWORD, api-key, secret_key 는 모두 각기 다른 case로 설정이 되어있습니다.
그렇지만 @ConfigurationProperties 는 Relaxed Binding 을 지원하기 때문에 어떤 case로 쓰여있건 모두 camelCase 형식으로 읽어들일 수 있습니다.


아래 코드는 위의 프로퍼티를 읽어들이는 컴포넌트 클래스입니다.

@ToString
@Setter
@Component
@ConfigurationProperties(prefix = "playground.relaxed-binding")
public class RelaxedBinding {
    private String userId;
    private String password;
    private String apiKey;
    private String secretKey;
    @Getter private final List<String> sites = new LinkedList<>();
    @Getter private final List<String> sites2 = new LinkedList<>();
    @Getter private final Option option = new Option();

    @ToString
    @Setter
    public static class Option {
        private String name;
        private boolean flag;
    }
}

prefix를 playground.relaxed-binding 라고 설정한것 외에 별도의 설정을 하지 않아도 모두 camelCase형식의 필드값 설정으로 프로퍼티값을 주입할 수 있습니다.


2.2. @Value에 적용되는 Relaxed Binding

playground.relaxed-binding 아래에 있는 프로퍼티 키 값인 userId, PASSWORD, api-key, secret_key 는 모두 각기 다른 case로 설정 되어있지만 아래와 같이 Kebab case로 모든 값을 읽어들일 수 있습니다.

단, 최하단 프로퍼티값만 relaxed binding을 지원하고, 상위 계층은 반드시 Kebab case로 설정해야합니다.

@ToString
@Setter
@Component
public class ValueProperties {

    @Value("${playground.relaxed-binding.user-id}")
    private String userId;
    @Value("${playground.relaxed-binding.password}")
    private String password;
    @Value("${playground.relaxed-binding.api-key}")
    private String apiKey;
    @Value("${playground.relaxed-binding.secret-key}")
    private String secretKey;
    @Value("${playground.relaxed-binding.sites2}")
    @Getter private final List<String> sites2 = new ArrayList<>();

}

참고로, @Value 는 POJO 프로퍼티나 playground.relaxed-binding.sites 과같은 값은 읽어들일 수 없습니다.

,를 이용한 array는 읽어들일 수 있습니다.


3. 테스트 결과

3.1. ConfigurationProperties 테스트 결과

@SpringBootTest
class RelaxedBindingTest {

    @Autowired
    private RelaxedBinding relaxedBinding;

    @Test
    void test() {
        System.out.println(relaxedBinding);
    }

}
RelaxedBinding(userId=demo123, password=1234, apiKey=vnhfM8vJjwzJEvGCqvNue9h8w77fhxPS, secretKey=qEX3PSHHgm2mvaHur3RG2VD2eYbKc75j, sites=[https://java.applebox.xyz, http://localhost:8080], sites2=[https://java.applebox.xyz, http://localhost:8080], option=RelaxedBinding.Option(name=test, flag=false))

3.2. @Value 테스트 결과

@SpringBootTest
class ValuePropertiesTest {

    @Autowired
    private ValueProperties valueProperties;

    @Test
    void test() {
        System.out.println(valueProperties);
    }
}
ValueProperties(userId=demo123, password=1234, apiKey=vnhfM8vJjwzJEvGCqvNue9h8w77fhxPS, secretKey=qEX3PSHHgm2mvaHur3RG2VD2eYbKc75j, sites2=[https://java.applebox.xyz, http://localhost:8080])

4. 참고

프로퍼티에 설정된 값을 컴포넌트로 읽어들이는 것은 표준 Java Bean property descriptor에 의해 이뤄지며, 보통 NoArgsConstructor와 getter, setter를 필수적으로 가지는 편입니다.

간단하게, Lombok의 @Data 지정으로 한번에 읽어들일 수도 있겠으나, 먼저 아래의 특성이 있다는걸 알아두면 좋습니다.


4.1. 초기화 할거라면 getter 필수

Setter는 필수
Collection이나 POJO 프로퍼티에 final 설정을 하기위해서는 초기화가 필요하다.
이들을 초기화할 할 경우에는 반드시 getter 설정을 해야한다.

@ToString
@Setter
@Component
@ConfigurationProperties(prefix = "playground.relaxed-binding")
public class RelaxedBinding {
    private String userId;
    private String password;
    private String apiKey;
    private String secretKey;
    @Getter private final List<String> sites = new LinkedList<>();
    @Getter private final List<String> sites2 = new LinkedList<>();
    @Getter private final Option option = new Option();

    @ToString
    @Setter
    public static class Option {
        private String name;
        private boolean flag;
    }
}

4.2. 초기화 안한다면 getter는 필수아님

컬랙션이니 POJO 프로퍼티를 초기화하지 않은 경우, getter를 설정하지 않아도 값을 읽어들일 수 있습니다.

@ToString
@Setter
@Component
@ConfigurationProperties(prefix = "playground.relaxed-binding")
public class RelaxedBinding {
    private String userId;
    private String password;
    private String apiKey;
    private String secretKey;
    private List<String> sites;
    private List<String> sites2;
    private Option option;

    @ToString
    @Setter
    public static class Option {
        private String name;
        private boolean flag;
    }
}

GitHub 에서 java-playground(v1.0.1)를 다운받아 볼 수 있습니다.


+++

  • Externalized Configuration
  • Spring Boot Relaxed Binding
  • JavaBean properties binding
  • JavaBean에 프로퍼티 바인딩하기

References

300x250
반응형