Interceptor에서 왜 Service Layer를 호출하지 못하지?

오늘 신규 프로젝트 개발 중에 해결이 안되는 상황을 겪었다. Custom Interceptor 하나를 등록해뒀는데 이 Interceptor 안에서 @Resource로 의존성 주입을 한 Service 객체가 작동을 안하는 것이다.

코드는 대략 아래와 같았다.

@Slf4j
public class CustomInterceptor extends HandlerInterceptorAdapter {

    @Resource
    private CustomService customSvc;
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    	log.debug("Custom Interceptor > postHandle ===> " + customSvc.selectData());
    }
}

Controller에 진입하고나서 ModelAndView를 return 하기 직전에 특정 행위를 모든 URL마다 포함해야했다. 그래서 생각해보다가 Interceptor를 이용하기로 했고 그 중에 postHandle을 Override하여 기능을 구현하면 될 거 같았다.

실제 코드는 위와 같이 단순 log용은 아니고 좀 더 복잡하지만 저걸로도 충분히 설명이 가능하여 간략하게 줄였다. 그냥 CustomService를 @Resource로 주입받아서 DB에서 특정 데이터를 조회해오는 부분이다.

그런데 막상 실행하니깐 저 log가 안 찍히는 것이다. CustomService을 구현한 CustomServiceImpl에서 selectData()라는 곳에 log를 찍어봐도 안 찍혔다. 그냥 애초에 CustomService 자체가 실행이 안되는 것이었다.

원인은 WebMvcConfigurer를 구현한 WebMvcConfig에 있었다.

요새는 xml 설정보다는 Spring에서 Java Config로 기본적인 설정들을 많이 하는 추세이다. 기존의 xml 설정들도 다 걷어내고 Java Config로 바꾸곤 했는데 이 프로젝트 역시 Java Config 기반이었다. WebMvcConfig는 대략 아래와 같다.

@Slf4j
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    
    (...생략...)
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/**");
    }
    
    (...생략...)
}

위와 같이 처음에 만들었던 Interceptor를 addInterceptors를 Override하여 등록해주는 과정이다. 그런데 이 과정에 문제가 있었다. 위와 같이 new()를 통해 Interceptor 객체를 만들어서 등록하면 Spring Container에서 이 Interceptor를 관리하지 못한다고 한다.

참고 : https://stackoverflow.com/questions/18218386/cannot-autowire-service-in-handlerinterceptoradapter

내가 구현한 코드처럼 Interceptor를 등록하면 Spring이 이것을 관리하지 못하게 되고, Interceptor에서 @Resource@Autowired를 통해 의존성을 주입하려고 해도 Spring에서 관리하지 않는 것이다보니 안되는 것이었다. 그럼 어떻게 바꾸면 잘 작동할까?

@Slf4j
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    
    (...생략...)
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(customInterceptor()).addPathPatterns("/**");
    }
    
    @Bean
    public CustomInterceptor customInterceptor() {
        return new CustomInterceptor();
    }
    
    (...생략...)
}

위와 같이 @Bean을 생성하면 Spring에서 이 Bean을 실행하면서 만들고 관리하게 된다. 이것을 통해 Interceptor를 등록하게 되면 정상적으로 Service Layer를 @Resource를 통해 주입받고 작동하게 된다. @Bean이라는 어노테이션을 정말 많이 사용하는 것 같은데 몰랐던 부분을 이렇게 알아가게 되니 좋았던 것 같다. 뭐든 잘 알아보고 써야한다는 점을 오늘 또 깨달은 하루였다.