등록된 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();
}
}
HandshakeInterceptor
에 @Component
를 이용해 Bean으로 등록한 뒤 config에서 @Autowired
해서 사용한다.@Component
를 붙이지 않고 config파일에서 인터셉터 객체를 불러와 생성하면서 Bean으로 직접 등록해서 사용한다.스프링에서 권장하는 방법은 2번으로, 1번은 흔히 말하는 필드 주입이고 2번은 생성자 주입이다. 맨날 봐도 뭔말인지 잘 이해가 안됐는데 직접 문제를 겪고 나니 이해가 된다. 관련 좋은 글 많으니 헷갈릴때마다 찾아보기
DI(의존성 주입)가 필요한 이유와 Spring에서 Field Injection보다 Constructor Injection이 권장되는 이유 | Mimul Tech log