앞서 예외와 그 흐름에 대해서 알아보았다. 그 흐름은 아래와 같다.
1. WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
2. WAS `/error-page/500` 다시 요청 -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(/errorpage/
500) -> View
package org.springframework.boot.web.servlet;
/**
* Enumeration of filter dispatcher types, identical to
* {@link javax.servlet.DispatcherType} and used in configuration as the servlet API may
* not be present.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public enum DispatcherType {
/**
* Apply the filter on "RequestDispatcher.forward()" calls.
*/
FORWARD,
/**
* Apply the filter on "RequestDispatcher.include()" calls.
*/
INCLUDE,
/**
* Apply the filter on ordinary client calls.
*/
REQUEST,
/**
* Apply the filter under calls dispatched from an AsyncContext.
*/
ASYNC,
/**
* Apply the filter when an error is handled.
*/
ERROR
}
@Slf4j
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("log filter init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
String uuid = UUID.randomUUID().toString();
try {
log.info("REQUEST [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
chain.doFilter(request, response);
} catch (Exception e) {
log.info("EXCEPTION {}", e.getMessage());
throw e;
}finally{
log.info("RESPONSE [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
}
}
@Override
public void destroy() {
log.info("log filter destroy");
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);
return filterRegistrationBean;
}
}
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
public static final String LOG_ID = "logId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
String uuid = UUID.randomUUID().toString();
request.setAttribute(LOG_ID, uuid);
log.info("REQUEST [{}][{}][{}][{}]", uuid, request.getDispatcherType(), requestURI, handler);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandler [{}]", modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String requestURI = request.getRequestURI();
String logId = (String) request.getAttribute(LOG_ID);
log.info("RESPONSE [{}][{}][{}]", logId, request.getDispatcherType(), requestURI);
if (ex != null) {
log.error("afterCompletion error!!", ex);
}
}
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/**","*.ico","/error", "/error-page/**"); // 오류 페이지 경로
}
// 기존 필터는 등록해제
// @Bean
public FilterRegistrationBean logFilter() {
이것으로 필터와 인터셉터를 적용한 예외 처리를 해보았는데, 예외 종류에 따라 각 에러 페이지를 등록하고, 컨트롤러를 만드는 등의 복잡한 과정이 필요했다. 다음 포스팅에서는 스프링 부트를 활용해서 이런 과정이 어떻게 생략되는지 확인해보자.
출처 : 김영한 스프링MVC2편