인터셉터 (Interceptor)

클라이언트에서 특정 URL 요청시 DispatcherServlet 이 Controller 를 호출하기 이전에 해당 요청을 가로채는 역할을 수행한다. 


인터셉터가 수행되는 흐름 세가지

  1. DispatcherServlet --> Controller : Controller 가 수행되기 이전에 호출된다.
  2. Controller --> DispatcherServlet : HandleAdapter 가 Handle() 을 호출하고, View 를 Render() 하기 이전에 호출된다.
  3. View --> DispatcherServlet : 핸들러가 실행되고, View 가 Rendering 되고 이후에 호출되는 HandlerInterceptor 의 콜백메소드이다.

그림으로 보자.


위의 그림처럼 총 인터셉터는 세가지의 기능을 수행한다.

  • boolean preHandle()
  • void postHandle()
  • void afterCompletion()
boolean preHandle()
DispatcherServlet 은 모든 웹 요청은 진입점이다. 요청이 클라이은터로 들어오는 경우 컨트롤러를 요청에 보내주기 전에 중간에 요청을 가로챈다. 리턴타입이 불린인데, 그 이유는 해당 요청에 맞는 요청인지 틀린 요청인지 확인하여 이후에 postHandle() 과 afterCompletion() 메소드 수행여부를 결정한다. true 타입을 반환하면 그대로 컨트롤러는 해당 요청을 실행하고 구현된 나머지 인터셉터 메소드를 수행하는 반면에 false 를 반환하면 수행하지 않는다.  false 를 반환하면 이후에 HttpServletResponse 로 redirect 여부를 결정한다. 왜냐하면 틀린 요청이 들어왔으니 그에 따른 로직을 처리해주어야 하기 때문이다.

void postHandle()
HandlerAdapter 가 핸들러를 호출하였지만 DispatcherServlet 이 아직 View 를 Rendering 하지 않을 때 호출된다. 해당 메소드에서는 뷰 페이지에서 사용될 수 있는 ModelAndView 객체에 속성을 추가하여 이용할 수 있다.

void afterCompletion()
핸들러가 실행되고, 뷰가 렌더링되면 호출되는 메소드이다.


구현

인터셉터를 구현하기 위해서는 두가지 방법이 존재한다.

  1. abstract class HandlerInterceptorAdapter
  2. interface HandlerInterceptor
하나는 추상메소드로 extends 해줘도 되고, 반대로 implements 로 인터페이스를 구현해도 된다.

인터페이스 HandlerInterceptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.springframework.web.servlet;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.method.HandlerMethod;
 
public interface HandlerInterceptor {
 
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception;
 
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
        throws Exception;
 
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception;
}
 
cs


추상클래스 HandlerInterceptorAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package org.springframework.web.servlet.handler;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
 
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
 
        return true;
    }
 
    @Override
    public void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
    }
 
    @Override
    public void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
 
    @Override
    public void afterConcurrentHandlingStarted(
            HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
    }
}
 
cs


위의 내용에 있다시피 하나는 인터페이스로 하나는 추상클래스로 존재한다. 그리고 실제 인터셉터를 쓰려고 한다면 아래와 같이 이용할 수 있다.


(1) 추상클래스를 통한 인터셉터 클래스 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
public class RequestProcessing extends HandlerInterceptorAdapter {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        return true;
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
}
cs


(2) 인터페이스를 통한 인터셉터 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
public class RequestProcessing implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        return true;
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
}
cs


두 구현의 내용모두 오버라이딩되는 모습은 동일하다. 그럼, 이제 스프링의 servlet-context.xml 에 아래의 내용을 추가하면 된다. 우선 빈으로 xmlns:mvc 내용을 추가한다. 그리고 인터셉터 config 설정을 아래와 같이 하는데, beans:bean 을 인터셉터 클래스에 대해서 패키지 포함 전체 경로를 기입하고 mapping 은 클라이언트로부터 들어오는 요청 URL 에 대해서 인터셉터가 작동하는 경로를 기입한다. 그리고 exclude-mapping 은 제외할 URL 이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
<beans:beans 
    xmlns:mvc="http://www.springframework.org/schema/mvc">
 
<!---------------------------------------------------------------------------->
 
<!-- 인터셉터 config 설정 -->
<!-- Configuring interceptors based on URI -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/board/**" />
        <mvc:exclude-mapping path="" />
        <beans:bean class="edu.doubler.util.RequestProcessingURL"></beans:bean>
    </mvc:interceptor>
</mvc:interceptors>
cs


Posted by doubler
,