춤추는 개발자

[Spring] Static 변수와 스프링 빈 본문

Developer's_til/스프링 프레임워크

[Spring] Static 변수와 스프링 빈

Heon_9u 2023. 12. 20. 22:14
728x90
반응형

 

✅ Static / 스프링 빈

[ 🔑 간단요약 ]

특정 클래스에서 멤버 변수를 선언 및 초기화를 동시에 할 때,
static 클래스의 메서드를 호출해서 초기화 하는 상황에서 NPE가 발생
스프링이 빌드되면서 객체들이 메모리에 저장되는 시점과 스프링 컨테이너가 로드되는 시점 등에 따른 차이 이해하기

 

✅ 빈 주입이 안되는...

 WAS 프로젝트에는 공통적으로 보안 시스템에 로그를 적재하는 로직이 존재한다. 해당 로직들을 리팩토링하는 과정에서 빌드 자체가 실패하는 문제가 있었다.

public class YamlUtil {

    @Autowired
    private Environment env;

    public String getProperty(String key) {
        return env.getProperty(key);
    }
}


public class LogUtil {

    private static YamlUtil yamlUtil;

    @Autowired
    public void setYamlUtil(YamlUtil yaml) {
        this.yamlUtil = yaml;
    }

    private final static String VAL = yamlUtil.getProperty("serverState");

    public String getVal() {
        return VAL;
    }
}

 

 위와 유사한 로직에서 yamlUtil에 대한 NullPointerException(NPE)가 발생하고 있었다. 분명 static 변수에도 setter와 @Autowired로 빈을 주입했는데 왜 yamlUtil이 null이라고 하는지 처음에 이해할 수 없었다..

 

 문제 상황을 해석하자면, static 변수인 VAL를 선언 및 초기화할 때. 즉, yamlUtil에 빈을 주입하기 전에 getProperty라는 메서드가 수행됐다는 의미가 된다. 해석을 바탕으로 static 변수와 스프링 빈 주입 순서에 대해 알아본 결과, 스프링이 빌드되는 시점으로 인해 발생한 문제라는 것을 알 수 있었다.

 

 간단하게 설명하자면, Static 객체들이 메모리에 저장되는 시점이 스프링 빈이 주입되는 시점보다 빨라서 static 변수인 VAL를 초기화할 때, yamlUtil에 의존성 주입이 되지 않아 NPE가 발생한 것이다.

 

✅ Static과 스프링 빈에 대해

 다음과 같이 Static 키워드와 스프링 빈의 특징을 알고 있고 있었다면, 위와 같은 상황을 마주했을 때, 바로 문제 원인을 이해할 수 있을 것이다.

 

[ Static ]
JVM이 호출되면서 런타임 시점에 Class Area(static 영역)에 저장.
메모리에 한번 저장되어 공유된다는 특징이 크다.

외부에 종속적이지 않다.

프로그램이 종료되기 전까지 계속 메모리에 존재.

잦은 사용은 성능 부하 이슈

[ 스프링 빈 ]
스프링 컨테이너(ApplicationContext)가 로드된 이후, 저장된다.
외부에 종속적이다

[ @Value, @Autowired ]

해당 Annotation은 변수에 의존성을 주입할 때 사용된다.

변수에 적용할 때, 주입하려는 객체가 ApplicationContext에 스프링 빈으로 저장되어 있어야 의존성 주입이 가능하다.

원래 Static 변수에는 의존성 주입이 불가하지만, 위에 예시 코드처럼 Setter에 @Autowired로 의존성 주입이 가능하다.

하지만, 이 역시 스프링 제어를 벗어나기 때문에 잠재적인 에러가 있어서 권장하지 않는 방법이다.

 

✅ 스프링빈과 정적 메서드

 앞서 다뤘던 문제는 스프링이 빌드되는 프로세스와 시점을 이해하여 해결할 수 있었다.

 

 추가적으로 리팩토링하면서 공통적으로 사용되는 메서드들을 모듈화하기 위해 유틸성 클래스를 만드는데 이걸 스프링 빈으로 관리할지, Static 기반으로 관리할지 의문이 들었다. 여러 블로그를 참조하고 Static의 특징들을 알아본 결과, 1가지 기준을 세울 수 있었다.

 

[ 외부와의 종속성 ]

 외부와의 종속성 유무에 따라 유틸성 클래스를 어떻게 관리할지 생각해보자. 보통 Java 라이브러리 내에서도 Math 클래스는 특정 연산자를 호출했을 때, 사용자가 예상하는 결과가 나온다. 즉, 항상 같은 결과가 나온다는 것이다. 이런 클래스의 경우, 메모리에 한번 저장되어 공유되고, 프로그램이 종료되기 전까지 사라지지 않게하는 것이 효과적이다. 즉, 외부와의 종속성이 없는 유틸성 클래스는 Static 기반으로 관리하는 것이다.

 

 반면에, DB와 연동하여 쿼리문을 호출하는 Mapper 클래스는 DB에 저장된 값에 따라 다른 결과를 가져온다. 이렇게 외부와의 종속성이 있는 경우, Static 키워드와 적합하지 않기 때문에 스프링 빈 기반으로 관리하는 것이다. (첨언하자면, 스프링 빈은 하나의 스프링 컨테이너에서 객체의 생성과 삭제에 대한 DI, IoC 등을 관리하고 있다. 컨테이너가 사라질 때, 스프링 빈도 제거되며, 스프링 빈은 컨테이너끼리 참조할 수 없다.)

 

✅ Reference

Spring Bean과 유틸성 메서드

Static과 스프링 빈의 차이

Spring Bean 총 정리

 

 

728x90
반응형