SpringBoot 프로젝트에서는 application.yml
파일을 사용하여 외부 프로퍼티 값을 관리할 수 있다.
이 프로퍼티 값을 Java 코드내에서 바인딩하여 사용할 수 있게 도와주는 Spring 어노테이션인 @Value
@ConfigurationProperties
어노테이션에 대해 알아보자
테스트를 위해 다음과 같은 프로퍼티를 작성한다.
app:
test:
name: Hello
list: a,b,c
프로퍼티의 key를 통해 값을 Java 변수에 바인딩할 수 있다.
@Getter
@Setter
public class PropertiesTest {
@Value("${app.test.name}")
private String testName;
@Value("${app.test.list}")
private List<String> testList;
}
위 방식은 간단하게 프로퍼티의 키를 바인딩할 수 있지만 몇 가지 문제가 있다.
프로퍼티 키의 특정 prefix를 지정하여 Class에 선언된 변수명과 매핑하여 필드 값이 할당된 객체를 생성할 수 있다.
@Value
가 가진 문제를 해소할 수 있다.@Getter
@Setter
@ConfigurationProperties("app.test")
public class PropertiesTest {
private String name;
private List<String> list;
}
@ConfigurationProperties의 value에 있는 경로의 프로퍼티를 자동으로 필드에 바인딩해준다. (참고로 kebab-case 프로퍼티명을 camelCase 필드로 바인딩)
이 방법은 프로퍼티가 많더라도 깔끔하게 관리할 수 있다. 그리고 기본적으로 프로퍼티가 없어도 에러를 발생시키지 않는다.
하지만 문제는 프로퍼티가 Setter를 통해 주입된다는 점이다. private set 메서드로도 주입이 가능하지만, 의미 없는 Setter를 만들게 되고, 그래서 final 키워드를 사용할 수 없게 되고 도메인 의미가 불분명해진다.
Spring Boot 2.3 버전 이상부터는 생성자 주입방식으로 불변성을 가지고 프로퍼티를 필드에 주입할 수 있게 되었다. @ConstructorBinding
애노테이션을 사용하면 final 필드에 대해 프로퍼티를 주입해준다.
@Getter
@RequiredArgsConstructor
@CostructorBinding
@ConfigurationProperties("app.test")
public class PropertiesTest {
private String name;
private List<String> list;
}
@ConstructorBinding을 사용하지 않음으로써 필드를 final 키워드로 구현하고 Setter를 사용하지 않게 되면서 불변성을 유지할 수 있게 되었다.
주의할 점은 @ConstructorBinding을 사용한 클래스는 스스로 프로퍼티를 주입할 수 없다. 따라서 Main 클래스 또는 Configuration 클래스에서 아래의 두 애노테이션 중 하나를 사용해서 프로퍼티를 주입한다.
프로퍼티를 검증할 때는 @Validated 애노테이션을 클래스에 지정하면 된다. 프로퍼티를 주입받을 때 검증에서 걸리면 예외가 발생한다.
...
@ConfigurationProperties("app.test")
public class PropertiesTest {
@NotBlank
private String name;
@NotBlank
private List<String> list;
}
유연한 바인딩
프로퍼티 값을 객체에 바인딩할 때 Camel표기법으로 선언하고 프로퍼티 키는 카멜, 케밥 등 다양한 표기법으로 선언해서 바인딩할 수 있다.
메타데이터 지원
프로퍼티 키에 대한 정보를 메타데이터 파일로 제공한다. 이름, 타입, 디폴트값 등 힌트가 되는 정보를 얻을 수 있다.
간단하게 변수 한 두개 정도에 프로퍼티를 바인딩하여 쓸 경우엔 @Value
어노테이션을 사용할 수 있겠으나
일반적인 케이스에서는 불변성을 유지하고 코드를 간결하게 작성할 수 있는@ConfigurationProperties와 @ConstructBinding을 같이 사용하는 것이 좋아보인다.
상황에 맞게 구현하는 것이 중요한 것 같다.