개발을 하다보면 클래스에서 프로퍼티(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
'Server > Spring' 카테고리의 다른 글
Spring - REST에서 예외를 처리하는 다양한 방법! (0) | 2022.07.03 |
---|---|
Spring - JpaRepository와 Querydsl 연결하기! (사용자 정의 리포지토리) (2) | 2022.06.18 |
SpringBoot - @SpringBootTest vs @WebMvcTest (0) | 2022.05.30 |
QueryDSL - Repository 구조잡기 (extends / implements 사용 X) (0) | 2022.05.29 |
Spring - Multi DataSource 환경에서 Transaction, QueryDSL 사용하는 방법 (0) | 2022.05.21 |