🍿 강의수강/스프링MVC2편 🔒

[스프링MVC2편] 섹션 8. 예외 처리와 오류 페이지 - DispatcherType과 필터

케로⸝⸝◜࿀◝ ⸝⸝ 2024. 6. 12. 10:22

1. DispatcherType

1. WAS(여기까지 전파!!) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생!!)
2. WAS(/error-page/500 다시 요청) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(/error-page/500) -> View
  1. 예외가 발생해서 WAS까지 전파된다.
  2. WAS는 오류 페이지 경로를 찾아서 내부에서 오류 페이지를 호출한다. 이때 오류 페이지 경로로 필터, 서블릿, 인터셉터, 컨트롤러가 모두 다시 호출된다.
  • 그런데, 로그인 인증 체크 같은 경우를 생각해 보면, 이미 필터나 인터셉터에서 로그인 체크를 모두 완료했다.
  • 따라서 서버 내부에서 오류 페이지를 호출한다고 해서 해당 필터나 인터셉터가 한번 더 호출되는 것은 매우 비효율적이다.
  • 결국 클라이언트로부터 발생한 정상 요청인지, 아니면 오류 페이지를 출력하기 위한 내부 요청인지 구분할 수 있어야 한다.

package jakarta.servlet;

public enum DispatcherType {
    FORWARD,
    INCLUDE,
    REQUEST,
    ASYNC,
    ERROR;

    private DispatcherType() {
    }
}
  • REQUEST: 클라이언트 요청
  • ERROR: 오류 요청
  • FORWARD: 서블릿에서 다른 서블릿이나 JSP를 호출할 때 RequestDispatcher.forward(request, response);
  • INCLUDE: 서브릿에서 다른 서블릿이나 JSP의 결과를 포함할 때 RequestDispatcher.include(request, response);
  • ASYNC: 서블릿 비동기 호출

 

2. DispatcherType과 필터

실습

LogFilter

package me.progfrog.exception.filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.UUID;

@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("FILTER REQUEST [uuid: {}][dispathcerType: {}][URI: {}]", uuid, request.getDispatcherType(), requestURI);
            chain.doFilter(request, response);
        } catch (Exception e) {
            log.info("FILTER EXCEPTION!!! {}", e.getMessage());
            throw e;
        } finally {
            log.info("FILTER RESPONSE [uuid: {}][dispathcerType: {}][URI: {}]", uuid, request.getDispatcherType(), requestURI);
        }
    }

    @Override
    public void destroy() {
        log.info("log filter destroy");
    }
}

 

webConfig

package me.progfrog.exception;

import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import me.progfrog.exception.filter.LogFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebConfig {

    @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;
    }
}
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);
  • 이렇게 두가지를 모두 넣으면 클라이언트 요청은 물론이고, 오류 페이지 요청에도 필터가 호출된다.
  • 아무것도 넣지 않으면 기본 값이 DispatcherType.REQUEST이다. 즉, 클라이언트의 요청이 있는 경우에만 필터가 적용된다.
  • 특별히 오류 페이지 경로도 필터를 적용할 것이 아니라면, 기본값을 그대로 사용하면 되고 오류 페이지 전용 필터를 적용하고 싶다면 DispatcherType.ERROR만 지정하면 된다.

 

확인

  1. LogFilter가 호출되어, log.info("FILTER REQUEST [uuid: {}][dispathcerType: {}][URI: {}]", uuid, request.getDispatcherType(), requestURI); 로그 남음
  2. chain.doFilter(request, response);로 다음 필터나 서블릿 지나서 ServletExController의 "/error-ex"에 매핑된 errorEx() 호출
  3. 응답이 돌아와서 try ~ catch 문에서 예외를 잡아 log.info("FILTER EXCEPTION!!! {}", e.getMessage()); 로그 남음
  4. 그 다음에 finally가 무조건 호출되므로 log.info("FILTER RESPONSE [uuid: {}][dispathcerType: {}][URI: {}]", uuid, request.getDispatcherType(), requestURI); 로그 남음

  1. 예외를 throw e;라고 던졌기 때문에, 예외가 WAS까지 올라왔다가 오류 페이지를 출력하기 위해 WAS 내부에서 다시 호출이 발생
  2. log.info("FILTER REQUEST [uuid: {}][dispathcerType: {}][URI: {}]", uuid, request.getDispatcherType(), requestURI); 로그가 남는데, 이때 DispatcherType은 ERROR 인 것을 확인할 수 있음
  3. 오류 페이지 출력을 다하고 나서, 마지막에 finally 로그 남는 것까지 확인

 

반응형