부트 개념과 활용-10.외부 설정 2부


외부 설정 2부

타입-세이프 프로퍼티 @ConfigurationProperties

  • 여러 프로퍼티를 묶어서 읽어올 수 있음

  • 빈으로 등록해서 다른 빈에 주입할 수 있음
    • @EnableConfigurationProperties
    • @Component
    • @Bean
  • 융통성 있는 바인딩
    • context-path (케밥)
    • context_path (언드스코어)
    • contextPath (캐멀)
    • CONTEXTPATH
  • 프로퍼티 타입 컨버전
    • @DurationUnit
  • 프로퍼티 값 검증
    • @Validated
    • JSR-303 (@NotNull, …)
  • 메타 정보 생성

  • @Value
    • SpEL 을 사용할 수 있지만…
    • 위에 있는 기능들은 전부 사용 못합니다.

여러 프로퍼티가 많은 경우에 하나로 묶어서 가능

@Component
@ConfigurationProperties("people")
public class PeopleProperties {
    String name;
    int age;
    String fullName;
... getter setter 구현 (자바 표준 스펙을 사용해 구현됨)

원래는 // @EnableConfigurationProperties(PeopleProperties.class)걸 해 줘야 하지만
전에 배운 자동설정 이해 파트에 정리된(@EnableAutoConfiguration)에 이미 들어 있는 상태이고
PeopleProperties 클래스에 컴포넌트만 선언해주면 주입 가능하다.

@ConfigurationProperties 사용하면 추가 의존성이 필요한데,

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

프로퍼티를 설정할 때 메타정보 파일을 확인하는데 (자동완성 구현 등..)이 때 필요한 의존성이다.

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("=============== real application ===============");
        System.out.println("people.name : " + peopleProperties.getName());
        System.out.println("people.age : " + peopleProperties.getAge());
        System.out.println("people.fullName : " + peopleProperties.fullName);
        System.out.println("=============== real application ===============");
    }

이전처럼 @value로 받지 않고 프로퍼티에서 꺼내서 사용 가능하다.

참고
프로퍼티 파일 랜덤 설정시
people.age = ${random.int(10,30)} -> 공백 없이 사용

공백있으면 에러남
people.age = ${random.int(10, 30)} -> 10, 30 사이에 공백 있어서 에러 발생

융통성 있는 바인딩

people.name = real-jaeuk 15
people.age = ${random.int(10,30)}
#people.fullName = ${people.name} choi
people.full_Name = ${people.name} choi

people.full_Name 이런 식으로 쓰더라도 바인딩을 해 주고,
100 이라쓰면 사실 문자열인데 int 로 받음. 기본적인 타입 컨버전은 제공한다.

추가로 PeopleProperties에 @DurationUnit(ChronoUnit.SECONDS) 로 선언을 해 주고

    @DurationUnit(ChronoUnit.SECONDS)
    private Duration sessionTimeout = Duration.ofSeconds(30);
people.sessionTimeout = 25            

로 선언해주면 자동으로 people.fullName : PT25S
이렇게 받는다 또는 저런 @DurationUnit을 쓰지 않고도
people.sessionTimeout = 25s
이런 서브픽스를 제공한다.
people.sessionTimeout : PT25S
동일하게 출력됨

또한 프로퍼티에 들어오는 값을 검증하는 방법도 있다..
(앞서 핵심기술에서 공부한 내용-이전포스트 참조, min, max 등..)

@Component
@ConfigurationProperties("people")
@Validated // 벨리데이테트 추가
public class PeopleProperties {
    
    @NotEmpty 
    String name;

프로퍼티에 값을 지우면


Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-12-15 14:20:44.237 ERROR 30679 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'people' to me.jaeuk.properties.PeopleProperties failed:

    Property: people.name
    Value: 
    Origin: class path resource [application.properties]:3:0
    Reason: 반드시 값이 존재하고 길이 혹은 크기가 0보다 커야 합니다.


Action:

Update your application's configuration

이런 에러메세지가 발생한다.

참고
FailureAnalyzer
에러메세지를 이쁘게 이해히 쉽게 메세지를 보여준다. (부트 자동설정)

@value 보다는 이렇게 쓰는게 더 좋다.
얘는 키워드 그대로 정확하게만 받을, 메타정보도 제공 안하고 (자동완성)
매핑도 유연하고. 키멜케이스, 다 대문자로 써도 되고…

#people.fullName = ${people.name} choi
#people.full_Name = ${people.name} choi
#people.FULLNAME = ${people.name} choi
people.FULL_NAME = ${people.name} choi

다 잘받는다..

딱 하나 장점이 @vaule에서는 SPEL이 사용가능하다.

이전포스트 참조 스프링 핵심 기술-12.SpEL(스프링 Expression Language)




© 2019. by jaeuk