오봉이와 함께하는 개발 블로그

스프링 MVC 2 - 서블릿 예외 처리 : 인터셉터 본문

BE/Spring

스프링 MVC 2 - 서블릿 예외 처리 : 인터셉터

오봉봉이 2022. 8. 30. 21:51
728x90

서블릿 예외 처리 - 인터셉터

LogInterceptor - DispatcherType 로그 추가

@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("postHandle [{}]", 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);
        }
    }
}

오류를 발생시키면 postHandle은 실행되지 않는다.

WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**", "*.ico",
                        "/error", "/error-page/**"); // "/error", "/error-page/**"는 오류 페이지 경로
    }
    // ........
}

앞서 필터의 경우에는 필터를 등록할 때 어떤 DispatcherType인 경우에 필터를 적용할 지 선택할 수 있었다.
하지만 인터셉터는 서블릿이 제공하는 기능이 아니라 스프링이 제공하는 기능이기 때문에 DispatcherType과 무관하게 항상 호출된다.

대신에 인터셉터는 다음과 같이 요청 경로에 따라서 추가하거나 제외하기 쉽게 되어 있기 때문에, 이러한 설정을 사용해서 오류 페이지 경로를 excludePathPatterns를 사용해서 빼주면 된다.

여기에서 /error-page/**를 제거하면 error-page/500같은 내부 호출의 경우에도 인터셉터가 호출된다.

"/error-page/**"를 빼고 error-404를 접속했을 때는 에러 코드를 보내기 때문에 에러가 발생하여 postHandle이 호출되지 않는다.
하지만, 에러 처리에서 작성한 ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");가 실행될 때는 @GetMapping("/error-page/404")을 통해 컨트롤러가 정상 호출되는 것이기 때문에 postHandle이 호출된다.

전체 흐름 정리

/hello정상 요청

WAS(/hello, dispatchType=REQUEST) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러 -> View

/error-ex오류 요청

  • 필터는 DispatchType 으로 중복 호출 제거(dispatchType=REQUEST)
  • 인터셉터는 경로 정보로 중복 호출 제거(excludePathPatterns("/error-page/**"))
1. WAS(/error-ex, dispatchType=REQUEST) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러
2. WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
3. WAS 오류 페이지 확인
4. WAS(/error-page/500, dispatchType=ERROR) -> 필터(x) -> 서블릿 -> 인터셉터(x) -> 컨트롤러(/error-page/500) -> View
출처 : 인프런 김영한 지식공유자님 강의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
728x90
Comments