Pink Spider/@Value 의 값이 바인딩 되지 않으신가요

Created Wed, 27 Nov 2024 17:14:05 +0900 Modified Mon, 08 Dec 2025 08:41:47 +0900
2600 Words 12 min

@Value 의 값이 바인딩 되지 않으신가요?

  • Spring Boot 애플리케이션을 실행시킬 떄 @Value로 (org.springframework.beans.factory.annotation.Value) 값을 주입하려고 할떄 생각과는 다르게 @Value로 주입한 값이 제대로 바인딩되지 않는 경우가 있습니다.

  • 이러한 현상은 왜 생기며, 어떻게 해결할 수 있는지 알아보겠습니다.

  • 우선 Spring Boot를 실행시킬떄 loading 순서는 어떻게 되는지 먼저 살펴보겠습니다.

  • Spring Boot 애플리케이션이 실행될 때의 로딩 순서는 크게 다음과 같은 단계로 이루어집니다. 이 순서는 Spring Framework의 동작 원리에 기반하며, Spring Boot 특유의 기능들이 추가적으로 포함됩니다.


  1. JVM 초기화 및 Main 클래스 실행 애플리케이션이 실행되면 JVM이 메인 클래스의 main() 메서드를 호출합니다. Spring Boot 애플리케이션의 진입점은 일반적으로 @SpringBootApplication이 선언된 클래스입니다.

  1. SpringApplication 초기화 SpringApplication.run() 메서드가 호출됩니다. SpringApplication 객체가 생성되며, 실행 환경(ApplicationEnvironment)과 초기화 설정이 수행됩니다. 이떄 프로파일 Spring Boot 실행시 -Dspring.profiles.acitve=dev 등으로 설정한 프로파일에 따라서 속성(application.properties 또는 application.yml)이 로드됩니다.

그다음 main(String[] args) 에서 넘긴 arg = 커맨드 라인 인자가 처리됩니다.


  1. Application Context 생성 및 준비 Spring Boot는 애플리케이션 컨텍스트를 생성합니다. 기본적으로 AnnotationConfigApplicationContext 또는 ServletWebServerApplicationContext를 사용합니다. ApplicationContextInitializer가 실행되어 초기 설정을 수행합니다.

  1. EnvironmentPostProcessor 실행 application.properties 또는 application.yml에서 정의된 설정과 커맨드 라인 인자 등을 기반으로 Environment 객체를 구성합니다. 이를 통해 애플리케이션의 프로파일, 포트, 데이터베이스 연결 정보 등이 설정됩니다.

  1. Bean Definition 등록 및 로드 Spring은 컴포넌트 스캔(@Component, @Service, @Repository 등) 또는 명시적으로 정의된 설정 클래스(@Configuration)로부터 빈 정의를 로드합니다. 각 빈의 의존성이 분석되고 생성이 준비됩니다.

  1. Auto-Configuration 처리 Spring Boot의 핵심 기능인 Auto-Configuration이 실행됩니다. @EnableAutoConfiguration에 의해 정의된 클래스들이 활성화됩니다. 자동 설정은 spring-boot-autoconfigure 모듈의 빈들을 조건(@ConditionalOn…)에 따라 동적으로 등록합니다.

  1. Servlet/WebServer 초기화 (웹 애플리케이션일 경우) 내장 웹 서버(Tomcat, Jetty, Undertow 등)가 시작됩니다. 서블릿 컨테이너와 DispatcherServlet이 초기화됩니다.

  1. Application Context Refresh ApplicationContext가 리프레시(refresh) 됩니다. 빈 생성, 의존성 주입, 초기화 메서드 호출(@PostConstruct 등)이 수행됩니다. 이벤트 리스너(ApplicationListener)가 초기화됩니다.

  1. CommandLineRunner 및 ApplicationRunner 실행 애플리케이션이 실행 준비를 완료한 후, CommandLineRunner 또는 ApplicationRunner 인터페이스를 구현한 빈들이 실행됩니다. 초기화 로직을 추가하고 싶을 때 이 단계에서 구현할 수 있습니다.

  1. 애플리케이션 준비 완료 애플리케이션이 요청을 받을 준비를 마칩니다. 이 시점 이후부터 애플리케이션은 클라이언트 요청을 처리하거나, 비동기 작업을 수행합니다.

추가적인 주요 컴포넌트

Event Mechanism: Spring Boot는 로딩 과정에서 여러 이벤트를 발생시킵니다. (예: ApplicationStartedEvent, ApplicationReadyEvent) Actuator Monitoring: Spring Boot Actuator가 활성화된 경우, 애플리케이션의 상태 정보가 제공됩니다.

그럼 왜 바인딩이 안돼?

  • 위 설명대로 설정파일(application.properties 혹은 yml)을 먼저 로드한다면 @Value를 통해 주입한 값이 바인딩되지 않는 경우는 없어야 하지만, 실제로 발생하죠. 왜 그럴까요?

  1. 프로퍼티 소스가 아직 ApplicationContext에 반영되지 않은 경우 @Value는 Spring이 빈을 생성할 때 사용하는 Environment로부터 값을 가져옵니다. 하지만 프로퍼티 로딩과 @Value 바인딩이 일어나는 타이밍에 차이가 있다면, 바인딩이 실패할 수 있습니다. 예를 들어, @Value가 바인딩되기 전에 설정이 완전히 로드되지 않았거나 Environment에 반영되지 않았다면 값이 주입되지 않습니다.

해결 방법: @ConfigurationProperties를 사용하는 것이 좋습니다. 이는 Spring Boot에서 프로퍼티 값을 객체로 매핑하는 권장 방식으로, 보다 안정적이고 타입 안전합니다.


  1. 잘못된 프로파일 활성화 활성화된 프로파일이 올바르게 설정되지 않았거나, 원하는 프로파일의 설정이 application.yml에 없을 수 있습니다. Spring Boot는 기본적으로 application.properties 또는 application.yml 파일을 로드하며, 활성화된 프로파일에 따라 다른 설정을 적용합니다.

확인 방법: 올바른 프로파일이 활성화되었는지 확인 (spring.profiles.active 설정). @Value로 주입하려는 키가 해당 프로파일에 존재하는지 확인.


  1. @Value에서 사용하는 키가 존재하지 않음 @Value에 명시된 키가 yml 또는 properties 파일에 정의되지 않았을 수 있습니다. 키가 없으면 @Value는 기본적으로 바인딩을 실패합니다.

확인 방법: application.yml 또는 application.properties에 해당 키가 정확히 정의되어 있는지 확인하세요. 예: @Value("${app.name}")는 app.name 키가 정의되어 있어야 합니다.


  1. 스프링 컨텍스트 초기화 순서의 문제 특정 빈의 초기화 시점이 너무 빨라 Environment로부터 값을 가져오기 전에 @Value가 처리될 수 있습니다. 이는 @Component가 등록될 때 발생할 수 있습니다.

해결 방법: @PostConstruct를 사용하여 초기화 시점 이후 값을 확인하거나, ApplicationContextAware를 구현하여 ApplicationContext에서 직접 값을 가져오도록 설계할 수 있습니다.


  1. 타입 불일치 @Value는 문자열 기반으로 값을 주입합니다. yml이나 properties 파일에서 제공된 값이 바인딩 대상 필드와 타입이 맞지 않을 경우, 주입이 실패합니다.

해결 방법: 주입하려는 필드의 타입이 설정된 값과 호환되는지 확인하세요. 필요하다면 값에 명시적인 변환을 적용합니다.


  1. SpEL(Sprig Expression Language) 구문 문제 @Value가 SpEL 표현식(#{})을 사용하고 있다면, 구문 오류가 있을 가능성이 있습니다. SpEL은 복잡한 표현식을 처리할 때 정확한 구문을 요구합니다.

해결 방법: SpEL 표현식을 다시 확인하고, 테스트합니다.

내가 겪은 상황과 해결 방법

@Component로 만든 class에서 사용하는 static member에서 @Value 바인딩이 안되서 null로 처리되는 경우

  • load 안되는 case
@Component
@RefreshScope
public class SomeComponentClass {

    @Value("${some-static-member}")
    public static String SOME_STATIC_MEMBER; // null로 처리됨
}
  • -> 이렇게 바꾸면 해결
@Component
@RefreshScope
public class SomeComponentClass {

    public static String SOME_STATIC_MEMBER;

    @Value("${some-static-member}")
    public void setSomeStaticMember(String someStaticMember) {
        SOME_STATIC_MEMBER = someStaticMember;
    }
}