웹 애플리케이션의 경우 사용자 요청별로 별도의 스레드가 할당되고 서블릿 컨테이너 안에서 실행
애플리케이션에서 예외가 발생했을 때 , try - catch 로 예외를 잡아서 처리하면 아무 문제가 없지만, 애플리케이션에서 예외를 잡지 못하고 서블릿 밖으로 까지 예외가 전달될 경우 어떻게 동작하는가?
WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
WAS 까지 예외가 전달되는 문제가 발생한다.
@Slf4j
@Controller
public class ServletExController {
@GetMapping("/error-ex")
public void errorEx(){
throw new RuntimeException("예외 발생");
}
}
@Slf4j
@Controller
public class ServletExController {
@GetMapping("/error-ex")
public void errorEx(){
throw new RuntimeException("예외 발생");
}
@GetMapping("/error-404")
public void error404(HttpServletResponse response) throws IOException{
response.sendError(404,"404 오류");
}
@GetMapping("/error-500")
public void error500(HttpServletResponse response) throws IOException{
response.sendError(500,"500 오류");
}
}
WAS(SendError 호출 확인) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(response.SendError)
response.SendError() 호출시 response 내부에는 오류가 발생했다는 상태를 저장.
@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<ConfigurableWebServerFactory>
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");
@Slf4j
@Controller
public class ErrorPageController {
@RequestMapping("/error-page/404")
public String errorPage404(HttpServletRequest request, HttpServletResponse response){
log.info("errorPage 404");
return "error-page/404";
}
@RequestMapping("/error-page/500")
public String errorPage500(HttpServletRequest request, HttpServletResponse response){
log.info("errorPage 500");
return "error-page/500";
}
}
WAS 는 해당 예외 처리를 하는 오류 페이지 정보를 확인
new ErrorPage(RuntimeException.class,"/error-page/500");
500 에러가 발생시
@Slf4j
@Controller
public class ErrorPageController {
//RequestDispatcher 상수로 정의되어 있음
public static final String ERROR_EXCEPTION = "jakarta.servlet.error.exception";
public static final String ERROR_EXCEPTION_TYPE = "jakarta.servlet.error.exception_type";
public static final String ERROR_MESSAGE = "jakarta.servlet.error.message";
public static final String ERROR_REQUEST_URI = "jakarta.servlet.error.request_uri";
public static final String ERROR_SERVLET_NAME = "jakarta.servlet.error.servlet_name";
public static final String ERROR_STATUS_CODE = "jakarta.servlet.error.status_code";
@RequestMapping("/error-page/404")
public String errorPage404(HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 404");
printErrorInfo(request);
return "error-page/404";
}
@RequestMapping("/error-page/500")
public String errorPage500(HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 500");
printErrorInfo(request);
return "error-page/500";
}
private void printErrorInfo(HttpServletRequest request) {
log.info("ERROR_EXCEPTION: ex=", request.getAttribute(ERROR_EXCEPTION));
log.info("ERROR_EXCEPTION_TYPE: {}", request.getAttribute(ERROR_EXCEPTION_TYPE));
log.info("ERROR_MESSAGE: {}", request.getAttribute(ERROR_MESSAGE));
//ex의 경우 NestedServletException 스프링이 한번 감싸서 반환
log.info("ERROR_REQUEST_URI: {}", request.getAttribute(ERROR_REQUEST_URI));
log.info("ERROR_SERVLET_NAME: {}", request.getAttribute(ERROR_SERVLET_NAME));
log.info("ERROR_STATUS_CODE: {}", request.getAttribute(ERROR_STATUS_CODE));
log.info("dispatchType={}", request.getDispatcherType());
}
}