Spring MVC Request Life Cycle - Interceptor

목차

Interceptor

스프링 InterceptorHandler(Controller) 호출 전, 후에 사용자 요청 및 응답을 가로채 특정 로직을 수행하는 역할을 한다.

  • DispatcherServlet 이후에 동작한다.
  • 스프링 Filter 처럼 Request, Response 객체를 다른 객체로 변경하는 것이 불가능 하다.
    • Interceptor 는 Request, Response 객체를 넘겨주는 방식으로 진행하지 않고, boolean 값을 반환하는 형태로 로직 수행

Interceptor 인터페이스

  • preHandle
    • Handler 호출 전에 실행된다.
    • HttpServletRequest 객체를 이용해
    • preHandle 이 true 를 반환하면 다음으로 진행하고, false 면 진행하지 않는다.
  • postHandle
    • Handler 호출 후에 실행된다.
  • afterCompletion
    • View 가 렌더링 된 이후에 호출된다.
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}

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

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

Interceptor 예외

Handler(Controller) 실행 중간에 예외가 발생하게 되면 postHandler 는 호출되지 않고, 던져진 예외는 afterCompletion 에 전달된다.

  • preHandler 의 경우 Handler 수행 전에 실행되므로 Handler 예외와는 상관이 없다.
  • postHandler 의 경우 Handler 예외 발생시 로직이 수행되지 않으므로 예외 발생 상황과 관계없이 Handler 실행 후 작업을 진행하고 싶으면 afterCompletion 메소드를 이용하도록 한다.
  • afterCompletion 에 전달된 예외는 로직을 수행한 후 Filter 에도 전달된다.

요청 로그 남기기

@Slf4j
public class LogInterceptor implements HandlerInterceptor {

public static final String LOGIN_ID = "loginId";

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
String uuid = UUID.randomUUID().toString();

request.setAttribute(LOGIN_ID, uuid);

if(handler instanceof HandlerMethod){
HandlerMethod hm = (HandlerMethod) handler;// 호출할 컨트롤러의 메소드의 모든 정보가 포함돼 있다.
}

log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler);

return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle [{}]", modelAndView);
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String requestURI = request.getRequestURI();
String loginId = (String) request.getAttribute(LOGIN_ID);

log.info("RESPONSE [{}][{}][{}]", loginId, requestURI, handler);

if(ex != null){
log.error("afterCompletion error!!", ex);
}
}
}

Interceptor 등록하기

스프링 MVC 설정을 위한 WebMvcConfigurer 인터페이스는 Interceptor 를 등록하기 위한 addInterceptors 메소드를 제공한다. InterceptorRegistry 객체에 새로 만든 Interceptor 와 설정 정보를 넣어주면 적용된다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/**", "/*.ico", "/error");
}
}

로그인 Check Interceptor 작성하기

@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

String requestURI = request.getRequestURI();

log.info("인증 체크 인터셉터 실행 {}", requestURI);

HttpSession httpSession = request.getSession();
if(httpSession == null || httpSession.getAttribute(SessionConst.LOGIN_MEMBER) == null){
log.info("미 인증 사용자 요청");
// 로그인으로 redirect
response.sendRedirect("/login?redirectURL="+requestURI);

return false;
}

return true;
}
}

Interceptor 등록하기

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/**", "/*.ico", "/error");

registry.addInterceptor(new LoginCheckInterceptor())
.order(2)
.addPathPatterns("/**")
.excludePathPatterns("/", "/members/add", "/login", "/logout", "/css/**", "/*.ico", "/error");
}
}
Share