스프링에서 property 사용할 때 @Value 사용을 추천하지 않는 이유

Ssol·2024년 3월 25일
0

@Value 사용을 추천하지 않는 이유

@Value 어노테이션을 사용하면 쉽게 프로퍼티 값을 가져올 수 있다.

@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class EmailService {

	@Value("${email-address.business-manager}")
    private String businessManagerEmail;
    
    // ......
}

하지만 이렇게 사용하는 것을 추천하지 않는데 이 접근법의 단점으로는

  1. 외부파일 분리가 어렵다. @Value는 애플리케이션 코드 내에서 직접 설정 값을 정의하고 사용하기에 외부 파일을 통해 설정 값을 변경하려면 코드를 수정해야 한다.
  2. 테스트에서 주입받기 힘들어진다.
  3. 유연성이 떨어지고 오류가 발생하기 쉬워진다.

복잡한 표현식이나 다중 프로퍼티를 다루기 어렵습니다. 이로 인해 특정한 프로퍼티 값을 계산하는 등의 작업을 처리하기에는 한계가 있다.

그리고 테스트 가능성을 망치게되는데, 프로퍼티들은 스프링에 의해 자동 주입될 것이므로 먼저 실행되는 테스트에 스프링 컨텍스트가 필요해진다. (이것은 단위테스트를 느리게 만든다.)
그리고 다른 프로퍼티를 사용해 테스트를 사용하려면? 일반적으로 테스트를 위해 별도의 application 프로퍼티를 가질 수 있지만 지저분해질 수 있다.
이를 해결하기 위해 @Autowired 어노테이션을 사용해서 하나 씩 생성자 주입하는 방법이 있지만 생성자가 길어지고 지저분해지게 된다.

마지막으로 @Value는 접근 방식의 유연성이 떨어지고 데이터 검증을 위한 지원 기능이 내장되어 있지 않다는 점이다.

대신 @ConfigurationProperties를 사용하자.

그럼 뭘 사용하면 될까?
@ConfigurationProperties는 프로퍼티를 분리하고 외부화 하는 기능을 제공한다.

email-address:
  education-manager: apple@gmail.com
  business-manager: banana@gmail.com
  error-recipient: watermelon@gmail.com
@Getter
@Setter
@Validated
@ConfigurationProperties(prefix = "email-address")
@Configuration
public class EmailPropertiesConfig {

    @NotBlank
    private String educationManager;

    @NotBlank
    private String businessManager;

    @NotBlank
    private String errorRecipient;
}
@Slf4j
@Service
public class EmailService {

    private final GroupRepository groupRepository;
    private final JavaMailSender mailSender;
    private final EmailPropertiesConfig emailProperties;

    public EmailService(GroupRepository groupRepository, JavaMailSender mailSender, EmailPropertiesConfig emailProperties) {
        this.groupRepository = groupRepository;
        this.mailSender = mailSender;
        this.emailProperties = emailProperties;
    }
    
    // ...
    
}

이렇게 configuration으로 분리해서 프로퍼티를 한 곳에서 POJO 클래스로 그룹화하여 관리할 수 있으며, 타입 안정성과 IDE 지원 등의 이점을 누릴 수 있다.

유효성 검사 가능

@Validated 어노테이션을 사용해서 유효성 검사를 적용할 수 있다. 프로퍼티가 로드되기 전에 필수 형식을 준수하는지 확인할 수 있다.유효성 검사를 진행할 수 있다. 이 유효성 검사를 충족하지 못할 경우 해당 빈이 생성되고 주입될 때 유효성 검사를 실패하게 되어 애플리케이션이 실행 중지 된다.

만약 @Value로 프로퍼티를 주입받고 있는데 해당 프로퍼티의 값이 null이거나 empty이면 애플리케이션에서 해당 프로퍼티를 받아서 돌아가는 메서드가 실행될 때 NPE가 발생하게 된다. 이를 방어하기 위해 @Value로 주입받은 필드를 사용하기 전에 null 체크를 수행해야 하는데 @Value가 많아질 수록 코드도 길어지게 된다…

프로퍼티를 가져와서 설정하는 시점은?

@ConfigurationProperties 인스턴스는 스프링 컨텍스트에 로드될 때 생성되며 이는 일반적으로 애플리케이션이 시작 시점에 해당된다. 프로퍼티 값은 이 인스턴스에 주입될 때 읽힌다. 스프링은 해당 클래스의 필드와 프로퍼티 파일에서 매핑된 프로퍼티 값을 가져와 주입한다. 따라서 프로퍼티를 가져오는 시점은 스프링 애플리케이션이 시작되고 해당 빈이 생성되고 주입될 때 이다.

따라서 유효성 검사도 이 시점에 진행되는데 만약 필드가 유효성 검사를 통과하지 못하면, 스프링은 애플리케이션 시작 시점에서 예외를 발생시킨다. (애플리케이션을 시작하는 과정에서 발생하므로 런타임 예외)

@ConfigurationProperties에서 발생한 예외는 애플리케이션 실행을 중지시키는 런타임 예외로, 이 예외는 주로 애플리케이션의 초기화 과정에서 필수적인 설정값을 로드하고 검증하는데 사용된다.


제목에 @Value 사용을 추천하지 않는다고는 했지만 무조건 적으로 @Value를 사용하지 말라는 것은 아니다. 단순하게 하나의 값을 가져와 사용할 때에는 간단하고 직관적이게 사용할 수 있으므로 동적으로 설정을 변경할 필요가 없는 경우에는 @Value 값에 대한 null 체크 같은 검증만 하고 사용하는 것이 적합할 수 있다. 하지만 대부분의 경우에는 @ConfigurationProperties의 유효성 검증이 아주 유용하므로 해당 방법으로 사용하는 것을 추천한다.
profile
Junior Back-end Developer 🫠

0개의 댓글