모르지 않다는 것은 아는것과 다르다.

Spring

서블릿 예외처리

채마스 2022. 2. 26. 02:38

서브릿이 예외를 처리하는 방식

  • Exception(예외)
    • 웹 애플리케이션은 사용자 요청별로 별도의 쓰레드가 할당되고, 서블릿 컨테이너 안에서 실행된다.
    • 애플리케이션에서 예외가 발생했는데, 어디선가 try ~ catch로 예외를 잡아서 처리하면 아무런 문제가 없다.
    • 애플리케이션에서 예외가 발생했는데, 어디선가 try ~ catch로 예외를 잡아서 처리하면 아무런 문제가 없다.
    • 동작과정은 아래와 같다.
      WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
    • 결국 톰캣 같은 WAS 까지 예외가 전달된다.
  • response.sendError(HTTP 상태 코드, 오류 메시지)
    • 오류가 발생했을 때 HttpServletResponse 가 제공하는 sendError 라는 메서드를 사용해도 된다.
    • 이것을 호출한다고 당장 예외가 발생하는 것은 아니지만, 서블릿 컨테이너에게 오류가 발생했다는 점을 전달할 수 있다.
    • 동작과정은 아래와 같다.
      WAS(sendError 호출 기록 확인) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(response.sendError())
    • 컨트롤러에서 에러가 발생하면 -> response.sendError() 를 호출해서 response 내부에 오류가 발생했다는 상태를 저장해둔다.
    • 서블릿 컨테이너는 고객에게 응답 전에 response 에 sendError() 가 호출되었는지 확인한다.
    • 호출되었다면 설정한 오류 코드에 맞추어 기본 오류 페이지를 보여준다.





서블릿 오류페이지 동작 과정

    1. WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
    2. WAS `/error-page/500` 다시 요청 -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(/error- page/500) -> View
  • 위와 같이 예외가 발생해서 WAS까지 전파된다.
  • WAS는 오류 페이지 경로를 찾아서 내부에서 오류 페이지를 호출한다. -> 이때 오류 페이지 경로로 필터,
    서블릿, 인터셉터, 컨트롤러가 모두 다시 호출된다.





서블릿 예외 처리 - 필터

  • 예외 발생과 오류 페이지 요청 흐름
    1. WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
    2. WAS `/error-page/500` 다시 요청 -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(/error- page/500) -> View
  • 오류가 발생하면 오류 페이지를 출력하기 위해 WAS 내부에서 다시 한번 호출이 발생한다.
  • 이때 필터, 서블릿, 인터셉터도 모두 다시 호출된다. -> 비효율적이다.
    • 서블릿은 이런 문제를 해결하기 위해 DispatcherType 이라는 추가 정보를 제공한다.
    • DispatcherType
      REQUEST : 클라이언트 요청 
      ERROR : 오류 요청
    
    • WebConfig 에 위에 해당하는 설정을 할 수 있다.
      @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;
              }
      }
    • 이렇게 두 가지를 모두 넣으면 클라이언트 요청은 물론이고, 오류 페이지 요청에서도 필터가 호출된다.
    • 아무것도 넣지 않으면 기본 값이 DispatcherType.REQUEST 이다.





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

  • 인터셉터의 경우 Spring에서 지원해 주는 것이기 때문에 서블릿에서 지원해주는 필터와 달리 DispatcherType 가 없다.
  • 하지만 excludePathPatterns 을 통해서 더 간편하게 구현할 수 있다.
  • WebConfig 에서 위의 설정을 해줄 수 있다.
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
          registry.addInterceptor(new LogInterceptor())
                  .order(1)
                   .addPathPatterns("/**")
                .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






스프링 부트가 제공하는 오류페이지

  • 스프링 부트는 아래와 같은 과정으로 오류페이지를 제공한다.
    • ErrorPage 를 자동으로 등록한다. 이때 /error 라는 경로로 기본 오류 페이지를 설정한다.
    • new ErrorPage("/error") , 상태코드와 예외를 설정하지 않으면 기본 오류 페이지로 사용된다.
    • 서블릿 밖으로 예외가 발생하거나, response.sendError(...) 가 호출되면 모든 오류는 /error 를 호출하게 된다.
    • 위와 같이 에러를 요청하면, 그 에러를 처리하는 컨트롤러가 필요한데, BasicErrorController 라는 스프링 컨트롤러를 자동으로 등록된다.
    • ErrorPage 에서 등록한 /error 를 매핑해서 처리하는 컨트롤러다.
  • 위와 같이 설정되므로 개발자는 다른 구현없이 오류 페이지만 만들어 놓으면 된다.
    • 따로 컨트롤러나 WebServerCustomizer 를 구현할 필요가 없어진다.
    • BasicErrorController 는 기본적인 로직이 모두 개발되어 있다.
    • 개발자는 오류 페이지 화면만 BasicErrorController 가 제공하는 룰과 우선순위에 따라서 등록하면 된다.
      • 우선순위는 아래와 같다.
          1순위 : 뷰템플릿 -> ex> resources/templates/error/5xx.html
          2순위 : 정적리소스(static,public) -> ex> resources/static/error/400.html
          3순위 : 적용 대상이 없을 때 뷰 이름(error) -> ex> resources/templates/error.html




REFERENCES

  • 김영한님의 스프링 MVC 2편

'Spring' 카테고리의 다른 글

스프링 타입 컨버터  (0) 2022.02.26
서블릿 구조  (0) 2022.02.26
빈 스코프  (0) 2022.02.26
빈 생명주기와 콜백  (0) 2022.02.26
Transactional Propagation  (0) 2022.02.26