Exception
- 자바 직접 실행
- 실행 도중 예외를 잡지 못하고 처음 실행한 main() 메서드를 넘어서 예외가 던져지면, 예외 정보를 남기고 해당 쓰레드는 종료
- 웹 애플리케이션
- try - catch로 예외를 잡지 못하면 톰캣 같은 WAS까지 예외 전달
- WAS(전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외 발생)
response.sendError
response.sendError(Http 상태 코드)
response.sendError(Http 상태 코드, 오류 메시지)
- HttpServlerResponse가 제공하는 메서드
- 호출 시 당장 예외가 발생하는 것이 아닌, 서블릿 컨테이너에게 오류가 발생했다는 것을 전달
- HTTP 상태 코드와 오류 메시지도 추가 가능
- WAS(sendError 호출 기록 확인) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(response.sendError())
사용 예시
@GetMapping("/error-404-test")
public void error404(HttpServletResponse response) throws IOException {
response.sendError(404, "404 오류 발생");
}
오류 페이지 작동 원리
- WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
- WAS '/error-page/500' 다시 요청 -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(/error-page/500) -> view
- 예를 들어 RuntimeException 예외가 발생하여 WAS까지 전달되면, WAS는 오류 페이지 정보를 확인한다. 확인해보니 해당 오류의 오류 페이지로 /error-page/500이 지정되어 있다면, 해당 오류 페이지를 출력하기 위해 /error-page/500을 다시 요청
- WAS는 오류 페이지를 단순히 다시 요청하는 것이 아닌 오류 정보를 request의 attribute에 추가해서 넘겨줌
- request.getAttribute("javax.servlet.error.exception");
- 중요한 점은 클라이언트는 서버 내부에서 이런 일이 일어난지 전혀 모름. 서버 내부에서 오류 페이지를 찾기 위해 추가적인 호출
필터 예외 처리
- 오류 발생 시 오류 페이지를 출력하기 위해 WAS 내부에서 다시 한번 호출 발생
- 이때, 필터, 서블릿, 인터셉터도 모두 다시 호출
- 그런데 로그인 인증 체크 같은 경우 이미 완료하였기에 서버 내부에서 오류 페이지를 호출한다고 해서 해당 필터나 인터셉터가 한번 더 호출되는 문제 발생
- 결국 클라이언트로부터 발생한 정상 요청인지, 오류 페이지를 출력하기 위한 내부 요청인지 구분 필요
- 이러한 문제를 해결하기 위해 DispatcherType이라는 추가 정보를 제공
DispatcherTpye
public enum DispatcherType {
FORWARD, // 서블릿에서 다른 서블릿, JSP 호출
INCLUDE, // 서블릿에서 다른 서블릿이나 JSP의 결과를 포함할 때
REQUEST, // 클라이언트 요청
ASYNC, // 서블릿 비동기 호출
ERROR // 에러 요청
}
사용 예시
@Configuration
public class WebConfig implements WebMvcConfigurer {
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 이므로 클라이언트의 요청이 있는 경우에만 필터가 적용
- ServletRequest에서 getDispatcherType() 메서드를 통해 타입 확인 가능
인터셉터 예외 처리
- 필터의 경우 DispatcherType을 통해 필터 적용 유무 선택 가능
- 그러나 인터셉터의 경우 서블릿이 제공하는 기능이 아닌 스프링이 제공하는 기능으로 DispatcherType과 무관하게 항상 호출
- excludePathPatterns를 사용하여 중복 호출 제거
사용 예시
@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/**");
}
}
- excludePathPatterns에/error, /error-page/** 를 추가하여 중복 호출 제거
전체 흐름
- 정상 요청
- WAS(/hello, dispatcherType=REQUEST) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러 -> View
- 오류 요청
- WAS(/error-ex, dispatcherType=REQUEST) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러
- WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외 발생)
- WAS 오류 페이지 확인
- WAS(/error-page/500, dispatcherType=ERROR) -> 필터(호출x) -> 서블릿 -> 인터셉터(호출x) -> 컨트롤러(/error-page/500) -> View
/error-ex 오류 요청 시 필터는 DispatcherTpye으로 중복 호출 제거, 인터셉터는 경로 정보로 중복 호출 제거
WebServerFactoryCustomizer 인터페이스
- 해당 인터페이스를 구현하여 사용자가 직접 표출할 에러 페이지 지정 가능
- new ErrorPage(상태 코드 또는 예외, 오류 처리할 컨트롤러)를 지정하여 factory에 등록
- 오류를 처리 할 컨트롤러에서 뷰 템플릿을 return
사용 예시
@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory factory) {
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");
ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error-page/500");
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500");
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
스프링 부트 기본 오류 페이지
- 예외 처리 페이지를 만들기 위해서 아래와 같이 진행
- WebServerFactoryCustomizer 인터페이스를 구현하여 WebServerCustomizer를 만들고 ErrorPage 추가
- ErrorPageController를 만듬
- 이러한 과정을 스프링 부트는 기본으로 제공
- ErrorPage를 자동으로 등록해줌. 이때 /error라는 경로로 기본 오류 페이지를 설정
- 상태코드와 예외를 설정하지 않으면 기본 오류 페이지로 사용
- 서블릿 밖으로 예외가 발생하거나, response.sendError(...)가 호출되면 모든 오류는 /error를 호출
- BasicErrorController라는 스프링 컨트롤러를 자동으로 등록(ErrorPage에서 등록한 /error를 매핑해서 처리)
- BasicErrorController는 오류 페이지 등 HTML 페이지를 제공하는 경우 주로 사용
- BasicErrorController에서 View 선택 우선순위 (구체적인 것이 우선순위가 높음)
- View 템플릿
- resources/templates/error/500.html
- resources/templates/error/5xx.html
- 정적 리소스
- resources/static/error/400.html
- resources/static/error/404.html
- resources/static/error/4xx.html
- 적용 대상이 없을 때 View 이름(error)
- resources/templates/error.html
- BasicErrorController가 model에 담아서 View에 전달하는 기본 정보
timestamp //Fri Feb 05 00 :00:00 KST 2021
status // 400
error // Bad Request
exception // org.springframework.validation.BindException
trace // 예외 trace
message // Validation failed for object='data'.Error count : 1
errors // Errors(BindingResult)
path // '/hello'
// 모든 정보를 노출하는 것은 보안 문제 등이 존재하기에 application.properties에서 model에 포함 여부 선택 가능
server.error.include-exception=true // exception 포함 여부
server.error.include-message=on_param // message 포함 여부
server.error.include-stacktrace=on_param // trace 포함 여부
server.error.include-binding-errors=on_param // errors 포함 여부
// 옵션
never // 사용하지 않음
always // 항상 사용
on_param // 파라미터가 있을 때 사용, ex : URL?message=&error=trace=
'Spring > MVC' 카테고리의 다른 글
API 예외 처리 (0) | 2024.07.21 |
---|---|
HttpEntity (0) | 2024.07.21 |
스프링 인터셉터 (0) | 2024.07.11 |
서블릿 필터 (0) | 2024.07.11 |
Bean Validation (0) | 2024.06.29 |