'가로채다' 라는 의미가 있다.
위의 의미와 같이 Client에서 Server로 보낸Request
를Controller
에 도달하기 전 가로채도록 하는 역할을 한다.
Dispatcher Servlet이 Controller를 호출하기 전 / 후에 인터셉터가 끼어들어 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공한다.
웹 컨테이너에서 동작하는 필터와 달리 인터셉터는 스프링 컨텍스트에서 동작한다.
디스패처 서블릿이 핸들러 매핑을 통해 컨트롤러를 찾도록 요청하는데, 그 결과로 실행 체인(HandlerExecutionChain)을 돌려준다.
여기서 1개 이상의 인터셉터가 등록되어 있다면 순차적으로 인터셉터들을 거쳐 컨트롤러가 실행되도록 하고,
인터셉터가 없다면 바로 컨트롤러를 실행한다.
실제로 Interceptor가 직접 Controller로 요청을 위임하는 것은 아니다.
- 누락에 대한 위험 감수
- 코드 재사용성을 증가 시켜 메모리 낭비, 서버 부하 감소 가 있다.
인터셉터를 추가하기 위해서 org.springframework.web.servlet의 HandlerInterceptor 인터페이스를 구현(implements) 해야 하며, 다음과 같은 메소드를 가진다.
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// View의 모든 작업이 완료된 후 실행된다.
// 리소스를 반환해주기 적당한 메소드이다.
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
// Controller 가 실행 후 View 가 생성되기 이전에 호출한다.
// ModelAndView 를 통해 View 에 들어가는 데이터를 조작할 수 있다.
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
// Controller 가 호출되기 전에 실행된다. // Controller 실행 전 처리할 작업이나 정보를 가공, 추가하는 경우 사용된다.
// return 값이 true 일 경우 정상적으로 Controller 로 접근하고
// false 일 경우 Controller 에 접근하지 않는다.
}
preHandle()
- Controller가 호출되기 전에 실행된다.
- Controller 실행 전 처리할 작업이나 정보를 가공, 추가하는 경우 사용된다.
- return 값이 true일 경우 정상적으로 Controller로 접근하고
- false일 경우 Controller에 접근하지 않는다.
postHandle()
- Controller가 실행 후 View가 생성되기 이전에 호출한다.
- ModelAndView를 통해 View에 들어가는 데이터를 조작할 수 있다.
afterCompletion()
- View의 모든 작업이 완료된 후 실행된다.
- 리소스를 반환해주기 적당한 메소드이다.
사용 예시 LoginInterceptor
// 로그인된 사용자인지 검사할 인터셉터
public class LoginInterceptor implements HandlerInterceptor {
// Controller 메소드 수행직전에 로그인된 사용자 인지 검증을 해서
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 세션 객체의 참조값을 얻어와서
HttpSession session = request.getSession();
String id = (String) session.getAttribute("id");
// 만일 로그인을 하지 않았다면
if (id == null) {
// 로그인 페이지로 리다일렉트 이동시키고 false 를 리턴한다.
// 원래 가려던 url 정보 읽어오기
String url = request.getRequestURI();
// GET 방식 전송 파라미터를 query 문자열로 읽어오기 ( a=xxx&b=xxx&c=xxx )
String query = request.getQueryString();
// 특수 문자는 인코딩을 해야한다.
String encodedUrl;
if (query == null) {// 전송 파라미터가 없다면
encodedUrl = URLEncoder.encode(url);
} else {
// 원래 목적지가 /test/xxx.jsp 라고 가정하면 아래와 같은 형식의 문자열을 만든다.
// "/test/xxx.jsp?a=xxx&b=xxx ..."
encodedUrl = URLEncoder.encode(url + "?" + query);
}
// 3. 로그인을 하지 않았다면 /users/loginform 페이지로 리다일렉트 이동 시킨다. (HttpServletResponse)
String cPath = request.getContextPath();
response.sendRedirect(cPath + "/users/loginform?url=" + encodedUrl);
return false;
}
// 로그인을 했다면 흐름을 이어간다.
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 {
}
}
필터는 Request
와 Response
를 조작할 수 있지만, 인터셉터는 조작할 수 없다.
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 다른 request와 response를 넣어줄 수 있음
chain.doFilter(request, response);
}
}
필터가 다음 필터를 호출하기 위해서는 필터 체이닝(다음 필터 호출)을 해주어야 한다.
이때request
,response
객체를 넘겨주므로 우리가 원하는request
,response
객체를 넣어줄 수 있다.
하지만 인터셉터는 처리 과정이 필터와 다르다.
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// Request, Response를 교체할 수 없고 boolean 값만 반환 가능
return true;
}
}
디스패처 서블릿이 여러 인터셉터 목록을 가지고 있고, 순차적으로 실행시킨다.
true
를 반환하면 다음 인터셉터가 실행되거나 컨트롤러로 요청이 전달되며,false
가 반환되면 요청이 중단된다.
그러므로 다른request
,response
객체를 넘겨줄 수 없다.
Filter 의 사용 사례
필터는 기본적으로 스프링과 무관하게 전역적으로 처리해야 하는 작업들을 처리할 수 있다.
또한, 이미지나 데이터의 압축, 문자열 인코딩과 같이 웹 어플리케이션에 전반적으로 사용되는 기능을 구현하기에 적당하다.
Interceptor 의 사용 사례
인터셉터에서는 클라이언트의 요청과 관련되어 전역적으로 처리해야 하는 작업들을 처리할 수 있다.