1. 서블릿 예외처리

  • 서블릿은 2가지로 예외를 처리합니다.
  • Exception
  • response.sendError(Http 상태 코드, 오류 메시지)

 

"정상적인 흐름"

 

  • WAS -> 필터 - > 서블릿 -> 인터셉터 -> 컨트롤러

 

Exception

 

"컨트롤러에서 예외가 발생한 경우"

 

  • WAS <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외 발생)

 

"오류 처리 페이지 흐름"

 

  • WAS(예외 처리해주는 경로) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(예외 처리해주는 경로)
  • 컨트롤러에서 발생한 예외가 WAS까지 전파가 됩니다.
  • 서버 내부에서 처리할 수 없는 오류가 발생한 것으로 판단하여 HTTP 상태 코드를 500을 반환합니다.
  • WAS에서 오류를 처리하는 페이지 경로로 다시 수행이 됩니다.
  • WAS에서 오류를 처리하는 페이지 경로를 컨트롤러에서 호출을 하여 오류 페이지를 보여줍니다.

Exception 오류 페이지 예

@Controller
public class ExceptionController {
    
    @GetMapping("/exception") {
    public void error() {
        throw new RuntimeException("오류 발생");
    }
 }
 
 @Component 
 public ExceptionEx implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
 
     // ErrorPage ex = new ErrorPage(예외 or 상태코드, 오류처리 할 컨트롤러 주소)
     
     @Override
     public void customize(ConfigurableWebServerFactory factory) {
         
         ErrorPage exceptionPage = new ErrorPage(RuntimeException.class, "/error-page/500");
         
         factory.addErrorPages(exceptionPage);
     
     }
}



@Controller
public class Controller {
    
    @GetMapping("/error-page/500")
    public String error500() {
        return "error-page/500";
    }
}

 

  • /exception 경로를 호출하면 예외가 발생합니다.
  • WebServerFactoryCustomizer 인터페이스를 구현해서 customize를 재정의해서 에러 페이지를 등록합니다.
  • new ErrorPage(상태 코드 or 예외, 오류 처리할 컨트롤러 주소)를 넣어주고 factory에 등록을 합니다.
  • 오류 처리할 컨트롤러 주소에서 뷰 템플릿을 return 합니다.

 

response.sendError(Http 상태 코드, 오류 메시지)

"컨트롤러에서 예외가 발생한 경우"

 

  • WAS(resposne.sendError 호출 기록 확인) <- 필터 <-  서블릿 <- 인터셉터 <- 컨트롤러(response.sendError(Http상태 코드, 오류 메시지))

 

"오류 처리 페이지 흐름"

 

  • WAS(예외 처리해주는 경로) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(예외 처리해주는 경로)
  • 컨트롤러에서 예외가 발생하면 response.sendError로 예외를 저장합니다. 저장된 에러를 WAS에서 확인을 하고 설정된 오류 코드에 맞추어 예외처리를 해줍니다.
  • sendError(Http 상태 코드, 오류 메시지)에서 Http 상태 코드로 예외를 설정합니다.

 

resposne.sendError() 오류 페이지 예

 

@Controller
public class ResponseErrorController {

	/*
    response.sendError(Http상태코드,오류 메시지);
    response.sendError(Http상태코드);
    */
    
    @GetMapping("/response-error1") {
    public void error(HttpServletResponse response) {
        response.sendError(404,"클라이언트 오류");
    }
    
     @GetMapping("/response-error2") {
    public void error(HttpServletResponse response) {
        response.sendError(500);
    }
 }
 
 @Component
 public ResponseErrorEx implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
 
     // ErrorPage ex = new ErrorPage(예외 or 상태코드, 오류처리 할 컨트롤러 주소)
     
     @Override
     public void customize(ConfigurableWebServerFactory factory) {
         
         ErrorPage responseErrorPage404 = new ErrorPage(
                                     HttpStatus.NOT_FOUND,"/error-page/404");
                                   
         ErrorPage responseErrorPage500 = new ErrorPage(
                                     HttpStatus.INTERNAL_SERVER_ERROR,"/error-page/500");
         
         factory.addErrorPages(responseErrorPage404, responseErrorPage500);
     
     }
}



@Controller
public class Controller {
    
    @GetMapping("/error-page/500")
    public String error500() {
        return "error-page/500";
    }
    
    @GetMapping("/error-page/404")
    public String error404() {
        return "error-page/404";
    }
}

 

  • response.sendError()로 상태 코드와 에러 메시지를 저장을 합니다.
  • WAS에서 저장된 response.sendError을 확인하고 factory에 저장된 컨트롤러 주소를 호출을 합니다.
  • 호출된 컨트롤러 주소로 해당 에러 뷰 템플릿을 호출해서 오류 페이지를 보여줍니다.

 

2. 스프링 부트 오류 페이지

서블릿 예외처리에서는 WebServerFactoryCustomizer를 구현하고 factory에 에러 페이지를 저장을 하는데 스프링 부트에서 컨트롤러를 자동으로 등록해주는 BasicErrorController과 오류 페이지를 자동으로 등록해주는  ErrorMvcAutoConfiguration을 제공해준다.

 

  • WebServerFactoryCustomizer를 생략하고 에러가 발생하는 컨트롤러와 에러를 처리하는 컨트롤러 주소는 그대로 두고 뷰 템플릿에 오류를 처리해주는 페이지를 만듭니다.
  • BasicErrorController의 기본 경로는 /error 이기 때문에 뷰 템플릿에서 /error 경로 하위에 오류를 처리해줄 페이지를 만들면 오류를 보여줍니다.
@Controller
public class BasicErrorControllerEx {
    
    @GetMapping("/error-500")
    public void basicError(HttpServletResponse response) {
        response.sendError(500);
    }
}

 

  • /error-500으로 호출을 하면 resource/templates/error 하위에 있는 500.html 또는 5xx.html 등 500 에러와 관련된 페이지를 반환합니다.

BasicErrorController 뷰 선택 우선순위

 

1. 뷰 템플릿

2. 정적 리소스

3. 적용대상이 없을때 뷰 이름

 

3. 문제점 및 해결방법

"문제점"

 

정상적인 흐름에서나 예외가 났을 경우를 보면 필터와 인터셉터를 계속 호출합니다.

 

"해결방법"

 

서블릿 Filter와 스프링 Interceptro의 개념과 사용방법에서 필터와 인터셉터 등록하는 부분을 함께 확인해주세요.

 

 

필터

필터에 setDispatcherTypes를 추가합니다. DispatcherType에 REQUEST, ERROR, FORWARD, INCLUDE, ASYNC 등이 있습니다.

  • REQUEST : 클라이언트 요청
  • ERROR : 오류 요청
  • FORWARD : 다른 서블릿이나 JSP를 호출할 때
  • INCLUDE : 서블릿에서 다른 서블릿이나 JSP의 결과를 포함할 때
  • ASYNC : 서블릿 비동기 호출할 때
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);

 

  • 클라이언트 요청일 때와 오류 오청일 때 필터를 호출합니다.

 

filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST);

 

  •  클라이언트 요청일 때만 필터를 호출합니다.
  • setDispatcherTypes의 default 값은 DispatcherType.REQUEST입니다.

 

인터셉터

인터셉터일 경우에는 excludePathPatterns에 에러 페이지의 뷰 템플릿 경로를 넣어주면 됩니다.