What?
이전에는 @Value
를 사용해 *.yml
, *.properties
의 프로퍼티 값들을 가져왔습니다.
개인적으로는 기능에 문제가 없었기 때문에 개선의 필요성을 인지하지 못하고 있었는데, 지인분의 추천으로 @ConfigurationProperties
과 @ConstructorBinding
어노테이션을 알게 되어 공유합니다.
Why?
기존의 문제점부터 짚고 넘어가겠습니다.
아래와 같은 yml이 있다고 가정합니다.
server:
port: 8080
domain: http://localhost:8080
동적 타입 문제
@Value("${server.port}")
private String stringPort;
@Value("${server.port}")
private Integer integerPort;
@Value("${server.port}")
private Float floatPort;
로그를 찍어본 결과 같은 숫자에 대해서 각기 다른 타입으로 변환되고 있다는걸 알 수 있습니다. 문제는 서버의 포트 번호를 문자열이나 실수로 다룰 일이 없는데도 이런 가능성을 열어두고 있다는 것입니다.
변수로 친다면 타입이 없는 파이썬의 변수와도 같다고 할 수 있습니다.
휴먼 에러 가능성
@Value("${...}")
어노테이션의 인자값에 대한 반복적인 개발 비용을 줄이고 휴먼 에러를 방지할 수 있습니다.
객체 지향적으로
가장 중요한 것은, @ConfigurationProperties
어노테이션과 객체를 사용하면 프로퍼티들을 객체지향적으로 다룰 수 있습니다. 말 그대로 객체에서 다루게 되기 때문입니다.
프로퍼티를 지금보다 훨씬 많은 곳에서 산발적으로 사용한다고 상상해본 결과, @ConfigurationProperties
방법이 필요하다고 생각했습니다.
How?
위의 yml 파일을 쓴다는 가정 하에, 프로퍼티 객체는 다음과 같이 구성될 수 있습니다.
@Getter
@Setter
@ConfigurationProperties(prefix = "server")
public class ServerProperties {
private String domain;
private int port;
}
방법은 간단합니다. 대상 객체에 @ConfigurationProperties
어노테이션을 , Application 객체에는 @EnableConfigurationProperties
어노테이션을 달고 대상 객체를 인자로 넣는 것입니다.
// ...
@SpringBootApplication
@EnableConfigurationProperties(ServerProperties.class)
public class SuwikiApplication {
public static void main(String[] args) {
SpringApplication.run(SuwikiApplication.class, args);
}
}
Problem! 불변성 문제
위 방법은 Setter을 사용합니다. 프로퍼티 값들에 대한 불변성이 보장되지 않는 것은 애플리케이션 안정성에 있어 큰 리스크입니다.
설마 누군가가 저 값을 바꿀 일은 없더라도, 명시성을 높이는 것이 좋은 프로그래밍이라고 생각합니다.
Then How?
스프링 2.2.0부터는 @ConstructorBinding
을 통해 위 문제를 해결합니다.
해당 어노테이션에 친절하게 목적과 사용법이 적혀있습니다.
ConstructorBinding : 설정 프로퍼티(@ConfigurationProperties 적용된 클래스) 인스턴스 변수가 setter보다는 생성자 인자로써 바인딩된다는 것을 명시하는 어노테이션.
Note: @EnableConfigurationProperties 혹은 configuration property scanning 과 함께 쓰여야 한다.
최종 클래스는 아래와 같습니다.
인스턴스 변수들은 모두 final 이어야 합니다. 그렇지 않으면 setter로 값을 주입받습니다.
@Getter
@RequiredArgsConstructor
@ConstructorBinding
@ConfigurationProperties(prefix = "server")
public class ServerProperties {
private final String domain;
private final int port;
}
Hmm…
어떤 레퍼런스에서는 Properties를 한 클래스에 모아서 관리하는걸 봤습니다.
하나의 클래스로만 관리할 수 있어서 편리하다고 생각하지만, prefix 별로 객체를 분리해 사용하는 것도 나쁘진 않다고 생각합니다.
참고
'Back-end > Spring Boot' 카테고리의 다른 글
[Spring Boot] logback으로 CloudWatch에 서버 요청 로깅하기 (1) | 2023.10.06 |
---|---|
[Spring JPA] 페이지네이션 일대다 FETCH JOIN 문제와 default_batch_size (0) | 2023.09.25 |
[Spring JPA] 엔티티 컬렉션 필드를 초기화해야 하는 이유 (0) | 2023.09.18 |
[Spring Boot] 커서 페이징(no offset)에서 Page 대신 Slice 사용하기 (1) | 2023.09.11 |
[Spring JPA] Hibernate에서 지원하지 않는 MySQL 랜덤 함수 직접 만들기 + Expressions 파헤쳐보기 (0) | 2023.09.06 |