Server/Spring

SpringBoot - 프로퍼티를 안전하게 가져오는 방법!

JaeHoney 2022. 6. 14. 22:34

개발을 하다보면 클래스에서 프로퍼티(Properties)를 가져오는 일이 종종 생긴다. 더 효율적이고 안전한 방법에 대해 알아보자.

 

예제를 위한 application.yml 상태는 다음과 같다.

directory:
  root-path: /app/
  temp-path: /temp/

@Value

가장 간단하게 값을 주입받는 방식이다. @Value 애노테이션에 프로퍼티 경로를 적으면 값이 주입된다.

@Getter
public class DirectoryProperties {

    @Value("${app.directory.root-path}")
    private String rootPath;
    
    @Value("${app.directory.temp-path}")
    private String tempPath;

}

이 방식은 간단하지만 몇 가지 문제가 있다.

  • 프로퍼티가 없으면 무조건 예외를 터트린다.
  • 타입이 불안정하다.
    • 프로퍼티가 true라면 Boolean, String 모두 캐스팅이 된다.
    • 이를 막으려면 spEL을 사용해야 한다. ex. @Value("{new Integer('${api.orders.pingFrequency}')}")
  • 프로퍼티가 많으면 코드가 지저분해진다.

@ConfigurationProperties

@Value가 가진 문제를 해소할 수 있는 방법이다.

@Getter
@Setter
@ConfigurationProperties("app.directory")
public class DirectoryProperties {

    private String rootPath;
    private String tempPath;

}

@ConfigurationProperties의 value에 있는 경로의 프로퍼티를 자동으로 필드에 바인딩해준다. (참고로 kebab-case 프로퍼티명을 camelCase 필드로 바인딩)

 

이 방법은 프로퍼티가 많더라도 깔끔하게 관리할 수 있다. 그리고 기본적으로 프로퍼티가 없어도 에러를 발생시키지 않는다. @ConfigurationProperties의 ignoreUnknownProperties 속성을 false로 설정하면 강제로 오류를 발생하도록 설정할 수 있다.

  • @ConfigurationProperties(value = "app.directory", ignoreUnknownFields = true)

 

문제는 프로퍼티가 Setter를 통해 주입된다는 점이다. private set 메서드로도 주입이 가능하지만, 의미 없는 Setter를 만들게 되고, 그래서 final 키워드를 사용할 수 없게 되고 도메인 의미가 불분명해진다.

@ConstructorBinding

Spring Boot 2.3 버전 이상부터는 생성자 주입방식으로 불변성을 가지고 프로퍼티를 필드에 주입할 수 있게 되었다. @ConstructorBinding 애노테이션을 사용하면 final 필드에 대해 프로퍼티를 주입해준다.

@Getter
@RequiredArgsConstructor
@CostructorBinding
@ConfigurationProperties("app.directory")
public class DirectoryProperties {

    private final String rootPath;
    private final String tempPath;

}

@ConstructorBinding을 사용하지 않음으로써 필드를 final 키워드로 구현하고 Setter를 사용하지 않게 되면서 불변성을 유지할 수 있게 되었다.

 

주의할 점은 @ConstructorBinding을 사용한 클래스는 스스로 프로퍼티를 주입할 수 없다. 따라서 Main 클래스 또는 Configuration 클래스에서 아래의 두 애노테이션 중 하나를 사용해서 프로퍼티를 주입한다.

  • @EnableConfigurationProperties
  • @ConfigurationPropertiesScan

@Validated

프로퍼티를 검증할 때는 @Validated 애노테이션을 클래스에 지정하면 된다. 프로퍼티를 주입받을 때 검증에서 걸리면 예외가 발생한다.

@Validated
@ConfigurationProperties("app.directory")
...
public class DirectoryProperties {
    @NotBlank
    private final String rootPath;
    @NotBlank
    private final String tempPath;
    
    // ...생략
}

마무리

이상으로 프로퍼티를 필드로 주입받아서 사용하는 방법에 대해서 알아봤다.

 

일반적인 케이스에서는 불변성을 유지할 수 있는 @ConfigurationProperties와 @ConstructBinding을 같이 사용하는 것이 좋아보인다. 반면, Late Binding을 위해 @Value를 사용해야 하는 경우도 발생한다. (ex. Spring Batch)

 

상황에 맞게 구현하는 것이 중요한 것 같다.

 


Reference