Spring 의존성 주입 문제: @Autowired 한 class가 null로 나올 때

Sieun Sim·2020년 5월 31일
2

서버개발캠프

목록 보기
21/21

@Autowired 한 class가 null로 나오는 문제

등록된 Bean을 가져다가 사용하고 싶을 때 @Autowired로 편하게 사용하고는 한다. WebSocketConfig 에서 endpoint에 HandshakeInterceptor를 추가해 endpoint 접근하는 url의 query parameter에서 토큰을 캐치해 검증하려 했다. 이 검증을 위해 redis repository에 접근해 저장된 token이 있는지 확인해야 하는데 redis template Bean이 제대로 불러와지지 않는 문제가 있었다.


수정 전 WebSocketConfig

@RequiredArgsConstructor
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/pub");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/wscn").addInterceptors(new HandshakeInterceptor()).setAllowedOrigins("*").withSockJS();
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new FilterChannelInterceptor());
    }

}  

HandshakeInterceptor

@Component
public final class HandshakeInterceptor implements org.springframework.web.socket.server.HandshakeInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
                                   Map<String, Object> attributes) throws Exception {

        System.out.println("before handshake: " + stringRedisTemplate);
        return true;
    }
}

StringRedisTemplate은 Redis를 제대로 설정했다면 별도 설정 없이 자동으로 Bean으로 등록되어 있음에도 불구하고 프린트하면 stringRedisTemplate가 null로 나오는 것이었다.

문제는 WebSocketConfig에서 addInterceptors(new HandshakeInterceptor()) 와 같이 의존성이 주입된 HandshakeInterceptor객체를 내 마음대로 new로 새로 만들어버렸다는 것이다. 결국 HandShakeInterceptor@Component 로 Bean으로 잘 등록해놓고는 그 Bean을 쓰지 않고 새로운 객체를 만들어버리고는 그걸 사용한 것이다. 다른 예시에서 new로 가져와 인터셉터를 등록하는 걸 보고 작성한 코드였는데 db 연결을 하기 위해 수정하는 과정에서 이 객체를 불러온 과정은 잊고 객체 안에서만 수정하려고 하고 있었다.


수정 후 WebSocketConfig

@RequiredArgsConstructor
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

/*
    @Autowired
    HandshakeInterceptor handshakeInterceptor;
*/

    @Bean
    public HandshakeInterceptor handshakeInterceptor() {
        return new HandshakeInterceptor();
    }


    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/pub");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/wscn").addInterceptors(handshakeInterceptor).setAllowedOrigins("*").withSockJS();
    }
} 
  1. HandshakeInterceptor@Component를 이용해 Bean으로 등록한 뒤 config에서 @Autowired 해서 사용한다.
  2. 인터셉터 객체에 @Component 를 붙이지 않고 config파일에서 인터셉터 객체를 불러와 생성하면서 Bean으로 직접 등록해서 사용한다.

스프링에서 권장하는 방법은 2번으로, 1번은 흔히 말하는 필드 주입이고 2번은 생성자 주입이다. 맨날 봐도 뭔말인지 잘 이해가 안됐는데 직접 문제를 겪고 나니 이해가 된다. 관련 좋은 글 많으니 헷갈릴때마다 찾아보기

참고자료

DI(의존성 주입)가 필요한 이유와 Spring에서 Field Injection보다 Constructor Injection이 권장되는 이유 | Mimul Tech log

Cannot Autowire Service in HandlerInterceptorAdapter

0개의 댓글