[Spring] Interceptor란 (HandlerInterceptor, WebMvcConfigurer)

 

✍️ Interceptor, 인터셉터 개념

사전적인 의미로 Intercept란 가로막다, 가로채다라는 의미를 가지고 있다.

이와 유사하게 Spring에서 제공하는 Interceptor도 Cilent의 Request와 Server의 Response를 가로채는 역할을 한다.

일반적으로 Login 검증, Token 검증에 Interceptor가 사용된다. 

 

 

Spring이 Request를 처리하는 흐름에서 인터셉터를 살펴보면

1. Client로부터 Request가 들어오면 프론트 컨트롤러인 DispatcherServlet이 이를 가장 먼저 받는다.

2. DispatcherServlet은 HandlerMapping을 거쳐 Request를 처리할 Controller를 찾는다

3. Controller를 찾고 나서, HandlerInterceptor(인터셉터) 구현체의 preHandle 메서드를 실행한다.

3. Request를 Controller로 위임할 HandlerAdapter를 찾는다.

4. HandlerAdapter가 Request를 Controller로 위임한다.

5. 비즈니스 로직을 처리하고 결괏값이 Controller-HandlerAdapter-DispatcherServlet 순으로 전달된다.

6. ViewResolver를 호출하기 전, HandlerInterceptor(인터셉터) 구현체의 postHandle 메서드를 실행한다.

7. DispatcherServlet은 ViewResolver를 통해 View를 찾고 반환한다.

8. 마지막으로 View가 Rendering 된 후, HandlerInterceptor(인터셉터) 구현체의 afterComplete 메서드가 실행된다.

 

https://mangchhe.github.io/springboot/2021/02/20/SpringMvcLifeCycle/

 

🍊 HandlerInterceptor

인터셉터 사용하기 위해선 HandlerInterceptor의 구현체를 정의해아한다.

먼저, HandlerInterceptor의 메서드들을 살펴보면

 

1. preHandler

Controller를 실행할 HandlerAdaptor가 결정되기 전에 호출되는 메서드이다. 단순하게 생각하면 Controller가 호출되기 전에 호출되는 메서드라고 할 수 있다.

반환값이 true인 경우 Controller를 호출하고 반환값이 false인 경우 Controller에 진입하지 않는다.

default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   return true;
}

2. postHandler

Controller가 완료되고 ViewResolver를 호출하기 전에 호출하는 메서드로, 쉽게 말해서 View가 Rendering 되기 전에 실행되는 메서드이다. 

default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
      @Nullable ModelAndView modelAndView) throws Exception {
}

3. afterComplete

View가 정상적으로 Rendering된 이후에 수행되는 메서드이다.

default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
      @Nullable Exception ex) throws Exception {
}

 

보면 아시겠지만 모두 default 메서드로 상황에 따라 필요한 메서드만 선택적으로 재정의해서 활용할 수 있다.

 

🌱  Interceptor 예제 코드

메서드 이름을 출력하는 SimpleInterceptor 구현체를 만든다.

public class SimpleInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("Interceptor : preHandle!");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, 
                           Object handler, 
                           ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor : postHandle!");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, 
                                HttpServletResponse response, 
                                Object handler, Exception ex) throws Exception {
        System.out.println("Interceptor : afterCompletion!");
    }
}

 

Interceptor는 WebMvcConfigureraddInterceptors 메서드를 통해 등록해야 한다. 등록과 함께 인터셉터를 적용할, 제외할 URL 경로를 명시할 수 있다.

@Configuration
public class SimpleWebConfig implements WebMvcConfigurer {

    @Bean
    public HandlerInterceptor simpleInterceptor(){
        return new SimpleInterceptor();
    }

    /**
     * /simple/bye로 들어오는 request만 인터셉터를 적용하지 않는다.
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(simpleInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/simple/bye");
    }
}

 

인터셉터 테스트를 위한 Controller

@RestController
@RequestMapping("simple")
public class SimpleController {

    @GetMapping
    @RequestMapping("/hello")
    public void hello() {
        System.out.println("Controller : hello world!");
    }

    @GetMapping
    @RequestMapping("/bye")
    public void bye() {
        System.out.println("good bye!");
    }
}

 

/simple/hello는 패턴에 포함되어 인터셉터를 타지만, /simple/bye는 예외 패턴에 포함되어 인터셉터를 타지 않는다.

localhost:8080/simple/hello

Interceptor : preHandle!
Controller : hello world!
Interceptor : postHandle!
Interceptor : afterCompletion!
localhost:8080/simple/bye

Controller : good bye!