Spring security 에서 세션을 관리하기 위해 listener 를 커스텀 하면서 listener 안의 @Autowired 한 객체 상태가 null인 현상을 만났다.
public class SecurityConfig {
@Bean
public ServletListenerRegistrationBean<CustomHttpSessionListener> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<>(new CustomHttpSessionListener());
}
}
@Component
public class CustomHttpSessionListener implements HttpSessionListener, HttpSessionIdListener {
@Autowired
private LogService logService;
@Autowired
private NotifyService notifyService;
CustomHttpSessionListener의 notifyService가 null이었던 이유는 Spring의 의존성 주입(Dependency Injection)과 빈(Bean) 생명주기와 관련이 있다.
처음 코드에서는 new CustomHttpSessionListener()로 CustomHttpSessionListener를 직접 인스턴스화하고 있다. 이렇게 하면 이 객체는 Spring의 IoC(Inversion of Control) 컨테이너에서 관리되지 않게 된다.
따라서, CustomHttpSessionListener 내부에 @Autowired로 선언된 의존성(notifyService, logService 등)이 Spring에 의해 주입되지 않기 때문에 notifyService와 logService는 null 상태가 되는 것이다.
수정된 코드
@Autowired
private CustomHttpSessionListener customHttpSessionListener;
@Bean
public ServletListenerRegistrationBean<CustomHttpSessionListener> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<>(customHttpSessionListener);
}
이 코드에서는 @Autowired를 사용하여 CustomHttpSessionListener를 주입받고 있다. 이렇게 하면 CustomHttpSessionListener 인스턴스가 Spring에 의해 관리되며, Spring은 이 빈(Bean)을 생성할 때 logService와 notifyService와 같은 필요한 의존성을 자동으로 주입한다.
근데 @Component를 사용했는데도 왜 오류가 난걸까?
@Component를 사용하면 CustomHttpSessionListener가 Spring의 관리하에 있는 빈(Bean)으로 등록되지만, 문제는 @Component로 등록된 빈을 사용하는 방식에 있다.
첫 번째 코드에서는 @Component로 CustomHttpSessionListener를 정의했지만, SecurityConfig 클래스 내에서 직접 new CustomHttpSessionListener()로 객체를 생성하고 있다.
이렇게 직접 new 키워드를 사용하여 객체를 생성하면, 해당 객체는 Spring 컨테이너가 관리하지 않는 "일반 자바 객체"가 되는 것이다. 이 때문에 Spring이 제공하는 의존성 주입 기능(예: @Autowired)이 작동하지 않으며, 이 객체 내부의 notifyService와 logService는 null로 남게 된다.
수정된 코드에서는 @Autowired를 사용하여 CustomHttpSessionListener 빈을 SecurityConfig 클래스에 주입받고 있다. 이렇게 하면 Spring이 관리하는 빈이 그대로 주입되며, 이 빈의 의존성들도 Spring이 주입한 상태로 유지된다.
@Autowired
private CustomHttpSessionListener customHttpSessionListener;
이렇게 주입된 customHttpSessionListener는 Spring이 관리하는 빈이므로, 내부의 @Autowired 필드들도 정상적으로 주입된 상태로 사용된다.
@Component를 사용하면 CustomHttpSessionListener가 Spring의 관리하에 있는 빈(Bean)으로 등록되지만, 문제는 @Component로 등록된 빈을 사용하는 방식에 있다.
문제의 핵심
첫 번째 코드에서는 @Component로 CustomHttpSessionListener를 정의했지만, SecurityConfig 클래스 내에서 직접 new CustomHttpSessionListener()로 객체를 생성하고 있다. 이렇게 직접 new 키워드를 사용하여 객체를 생성하면, 해당 객체는 Spring 컨테이너가 관리하지 않는 "일반 자바 객체"가 된는 것이다. 이 때문에 Spring이 제공하는 의존성 주입 기능(예: @Autowired)이 작동하지 않으며, 이 객체 내부의 notifyService와 logService는 null로 남게 된다.
@Autowired를 사용한 수정된 코드의 의미
수정된 코드에서는 @Autowired를 사용하여 CustomHttpSessionListener 빈을 SecurityConfig 클래스에 주입받고 있다. 이렇게 하면 Spring이 관리하는 빈이 그대로 주입되며, 이 빈의 의존성들도 Spring이 주입한 상태로 유지된다.
@Autowired
private CustomHttpSessionListener customHttpSessionListener;
이렇게 주입된 customHttpSessionListener는 Spring이 관리하는 빈이므로, 내부의 @Autowired 필드들도 정상적으로 주입된 상태로 사용된다.
요약