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편