이전 글에서 서블릿을 통한 예외 처리에 대해 알아보았고 이번 글에서는 아래의 과정에서 필터가 중복 호출되는 것을 막기 위한 방법에 대해 알아보겠습니다.
1. WAS - 필터 - 서블릿 - 인터셉터 - 컨트롤러 // 컨트롤러에서 예외가 발생하면? 2. 컨트롤러 - 인터셉터 - 서블릿 - 필터 - WAS // 컨트롤러는 예외를 WAS까지 전달한다. 3. WAS - 필터 - 서블릿 - 인터셉터 - 컨트롤러 // WAS는 예외를 확인하고 에러 페이지를 호출하기 위해 다시 컨트롤러를 호출 |
(여기서 중복 호출은 1번 3번 과정으로 필터의 기능을 하도록 하는 메서드를 호출하는 것을 말합니다.)
우선 코드와 실행 결과를 먼저 보여드리고 상세한 설명을 덧붙이겠습니다.
1. 예외 컨트롤러
public class ServletExController {
@GetMapping("/error-ex")
public void errorEx() {
throw new RuntimeException("예외 발생!");
}
@GetMapping("/error-404")
public void error404 (HttpServletResponse response)throws IOException {
response.sendError(404, "404 오류");
}
@GetMapping("/error-500")
public void error500 (HttpServletResponse response) throws IOException {
response.sendError(500);
}
}
2. 로그 필터
@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);
}
}
3. 에러 페이지
@RequestMapping("/error-page/404")
public String errorPage404 (HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 404");
printErrorInfo(request);
return "error-page/404";
}
@RequestMapping("/error-page/500")
public String errorPage500 (HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 500");
printErrorInfo(request);
return "error-page/500";
}
실행 결과
INFO 13228 --- [exception] [nio-8080-exec-5] hello.exception.filter.LogFilter : REQUEST [aa68afcf-1be5-434d-b635-fd2994e7841f][REQUEST][/error-500]
INFO 13228 --- [exception] [nio-8080-exec-5] hello.exception.filter.LogFilter : RESPONSE [aa68afcf-1be5-434d-b635-fd2994e7841f][REQUEST][/error-500]
INFO 13228 --- [exception] [nio-8080-exec-5] hello.exception.filter.LogFilter : REQUEST [11a9513b-6f6d-4827-8d52-6e01169f596c][ERROR][/error-page/500]
INFO 13228 --- [exception] [nio-8080-exec-5] h.exception.servlet.ErrorPageController : errorPage 500
INFO 13228 --- [exception] [nio-8080-exec-5] h.exception.servlet.ErrorPageController : dispatchType=ERROR
INFO 13228 --- [exception] [nio-8080-exec-5] hello.exception.filter.LogFilter : RESPONSE [11a9513b-6f6d-4827-8d52-6e01169f596c][ERROR][/error-page/500]
필터, 인터셉터의 흐름에 대해 간략히 말하자면
HTTP 요청 - WAS - 필터 - 서블릿 - 인터셉터 - 컨트롤러 |
이렇게 흘러갑니다.
1. HTTP 요청이 오면 WAS를 거쳐서 필터에 도착한다. 필터는 REQUEST 로그를 찍고 chain.doFilter로 인하여 다음 단계로 넘어간다.
2.요청은 서블릿, 인터셉터를 거쳐서 ServletExController 에 도착을 하고 컨트롤러는 URL이 error-500이기 때문에 response에 500 에러를 담아서 다시 WAS까지 거꾸로 보낸다.
3. RESPONSE를 전달 받은 WAS는 에러 페이지를 호출하기 위해 다시 Controller로 REQUEST를 보낸다. (이때 첫번째 REQUEST와는 달리 request.getDispatcherType이 [REQUEST]에서 [ERROR]로 바뀐 것을 볼 수 있다.)
4. 에러 페이지 컨트롤러는 500 에러 페이지를 호출하게 된다.
위 과정에서 필터는 2번 호출된 것을 볼 수 있다.
여기서 필터를 1번 호출하게 하려면 Config 클래스에 있는
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST);
return filterRegistrationBean;
}
setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR) 부분을
setDispatcherTypes(DispatcherType.REQUEST)로 바꾸면 된다.
INFO 30288 --- [exception] [nio-8080-exec-2] hello.exception.filter.LogFilter : REQUEST [dc9906c6-39d4-4265-ba1e-1e7030ca38dc][REQUEST][/error-500]
INFO 30288 --- [exception] [nio-8080-exec-2] hello.exception.filter.LogFilter : RESPONSE [dc9906c6-39d4-4265-ba1e-1e7030ca38dc][REQUEST][/error-500]
INFO 30288 --- [exception] [nio-8080-exec-2] h.exception.servlet.ErrorPageController : errorPage 500
INFO 30288 --- [exception] [nio-8080-exec-2] h.exception.servlet.ErrorPageController : dispatchType=ERROR
그러면 위의 실행 결과와 비교했을 때 REQUEST가 ERROR로 요청되는 부분이 없어진 것을 볼 수 있다.
즉, 맨 처음 HTTP 요청에서는 정상 요청이 실행되지만 컨트롤러에서 에러가 터지고 다시 WAS로 내려간 뒤 오류 페이지로 향하는 과정에서는 필터가 동작하지 않는 것이다.
출처 : 인프런 - 스프링 MVC 2편 (김영한)
'Spring > Spring' 카테고리의 다른 글
[Spring] @ExceptionHandler를 이용한 API 예외 처리 (0) | 2024.12.07 |
---|---|
[Spring] 서블릿 예외 처리 - 인터셉터 (0) | 2024.12.05 |
[Spring] 서블릿 예외 처리 -1 (0) | 2024.12.04 |
[Spring] 스프링 인터셉터 (0) | 2024.12.03 |
[Spring] 서블릿 필터 (2) | 2024.12.02 |