@Value Annotation 개요
Spring Framework에서 제공하는 @Value 어노테이션은 외부 설정 파일(예: application.properties 또는 application.yml)에서 값을 주입하는 데 사용됩니다. 이 외에도 주로 환경 변수, 시스템 속성 등을 주입할 수 있습니다.
일반적으로 SpEL(Spring Expression Language) 표현식이나 스타일 속성 자리표시자를 사용합니다.
사용예시
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyComponent {
@Value("${app.name}")
private String appName;
@Value("${app.timeout:5000}") // 기본값 5000
private int timeout;
@Value("#{2 * T(java.lang.Math).PI}") // SpEL 사용
private double circleCircumference;
// getters and setters
}
동작 원리
@Value 어노테이션은 Spring Framework의 의존성 주입(Dependency Injection) 메커니즘을 기반으로 합니다. 다음 그 동작원리를 절차적으로 보도록 하겠습니다.
1. ApplicationContext 초기화
Spring 애플리케이션이 시작될 때, ApplicationContext가 초기화됩니다. 이 과정에서 Spring은 .properties 또는 .yml 형식의 설정 파일을 로드하여 프로퍼티 값을 메모리에 저장합니다.
2. Bean 생성
@Component, @Service, @Controller 등으로 정의된 클래스가 Spring 컨테이너에 의해 인스턴스화됩니다. 이 때 클래스의 모든 필드를 검사하며 @Value 어노테이션이 붙은 필드를 찾습니다.
3. 프로퍼티 해석 및 값 주입
이어서 @Value의 파라미터에 작성된 ${...} 또는 #{..} 형식의 문자열에서 키를 추출합니다. 예를 들어서 ${app.name}이라면, app.name 이 키가 됩니다.
추출한 키를 가지고 ApplicationContext에 로드된 프로퍼티 소스에서 값을 검색합니다. 이 과정에서 여러 프로퍼티 소스(ex: 환경 변수, 시스템 속성, 프로퍼티 파일 등)를 참조합니다.
검색이 되었다면, 값을 해당 필드에 주입합니다. 값이 존재하지 않을 경우엔 NullPointException이 발생합니다.
4. 타입 변환
1) 기본적인 타입 변환
Spring은 주입되는 값의 타입을 자동으로 변환합니다. 예를 들어, 프로퍼티 값이 문자열로 주어지더라도, 해당 필드의 타입이 int라면 이를 정수로 변환하여 주입합니다. 만약 타입 변환이 실패할 경우 ConversionFailedException으로 컴파일 시점에 문제가 발생합니다.
@Value("${app.timeout}") // application.properties에 "app.timeout=5000"
private int timeout; // 문자열 "5000"이 int로 변환됨
@Value("${app.active}") // application.properties에 "app.active=true"
private boolean active; // 문자열 "true"가 boolean으로 변환됨
2) 사용자 정의 타입 변환
Spring에서 프로퍼티의 타입을 변환시키기 위해 Converter 인터페이스의 구현체를 통해서 변환합니다. 따라서 해당 인터페이스를 활용하면 사용자가 직접 구현체를 정의할 수 있습니다. 이때 Bean 생성을 위해서 @Configuration으로 지정된 클래스에 구현해야 합니다.
public class CustomType {
private String value;
public CustomType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return "CustomType{" +
"value='" + value + '\'' +
'}';
}
}
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@Component
public class StringToCustomTypeConverter implements Converter<String, CustomType> {
@Override
public CustomType convert(String source) {
// 변환 로직 구현
return new CustomType(source);
}
}
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToCustomTypeConverter());
}
}
3) SpEL을 통한 타입 변환
SpEL(Spring Expression Language)을 지원하는 덕분에 복잡한 표현식을 사용하여 변환할 수 있습니다.
@Value("#{2 * T(java.lang.Math).PI}") // 원주율을 계산하여 double로 변환
private double circleCircumference;
@Value("#{environment['jwt.secrett'] == null " +
"? '기본 시크릿 정보'" +
": environment['jwt.secrett']}") // null 방지를 위한 유연한 사용
private String secret;
@Value Annotation 주의사항
@Value은 설정 파일에 설정한 값을 주입할 수 있는 어노테이션입니다. 첫 번째로 주의해야 할 부분은 주입 시점입니다. @Value 어노테이션은 대상 컴포넌트가 스프링 빈으로 등록되고 의존 관계를 주입할 때 동작합니다. 따라서 환경 변수를 주입받는 대상 클래스에 @Component 어노테이션을 붙여주지 않는다면 해당 클래스는 컴포넌트 스캔이 대상이 되지 않아 스프링 빈으로 등록되지 않고, @Value 어노테이션 또한 동작하지 않습니다.
또한 상황에 따라서 적절한 주입 방식을 선택해야 하는데요. 빈을 주입받을 때와 마찬가지로 @Value 어노테이션을 사용할 때도 필드 주입, 생성자 주입, setter 주입 등의 방식을 사용할 수 있습니다. 따라서 이들의 장단점을 비교하고, 상황에 따라 적절한 주입 방식을 선택해야 합니다.
마지막으로 프로퍼티 파일의 경로와 스코프를 확인해야 합니다. application.yaml 이 클래스 패스에 존재해야 하고, 프로퍼티 파일이 여러 개일 경우 우선순위를 고려해야 합니다.
@ConfigurationProperties 어노테이션과의 차이점
스프링의 프로퍼티 파일의 값은 Environment에 등록되는데요. 두 어노테이션 모두 이 값을 불러올 수 있다는 공통점이 있습니다. 단, @Value의 경우에는 단일 값을 주입받기 위해서 사용되며, RelaxedBinding이 적용되지 않습니다. RelaxedBingding이란 프로퍼티 이름이 조금 달라도 유연하게 바인딩을 시켜주는 규칙을 의미합니다. 반면, @ConfigurationProperties 어노테이션은 프로퍼티에 있는 값을 클래스로 바인딩하기 위해 사용됩니다. 그리고, 한 번에 여러 값을 바인딩받을 수 있으며 RelaxedBinding을 적용합니다.
@ConfigurationProperties 사용 예시
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private int timeout;
// getters and setters
}
application.properties에서 app.name, app.timeout의 값을 AppProperties 클래스의 필드에 자동으로 주입할 수 있습니다.
'개발 > Spring&JPA' 카테고리의 다른 글
[JPA] N+1 문제와 Query 성능 개선 (Fetch Join) (10) | 2023.05.11 |
---|---|
[JPA] 도메인 클래스 컨버터 사용하기 (1) | 2023.04.20 |
[JPA] @Transactional과 JPA의 플러시와 변경 감지(Dirty Checking) (6) | 2023.04.08 |
[스프링] 'JavaMailSender'를 통한 이메일 발송 기능 구현 (Gmail SMPT) (10) | 2023.03.23 |
[스프링] @Controller와 @RestController의 차이를 알고 있나요? (4) | 2023.03.20 |