[Spring] 서블릿 예외 처리와 스프링 부트 예외 처리

728x90

 

서블릿 예외 처리

스프링이 아닌 순수 서블릿 컨테이너는 다음 2가지 방식으로 예외 처리를 지원한다.

- Exception(예외 던지기)

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

 

Exception(예외)

자바 직접 실행 : 자바의 main( ) 메서드를 넘어 예외가 던져지면, 예외 정보를 남기고 해당 쓰레드는 종료

웹 애플리케이션

  : 요청별로 별도의 쓰레드가 할당되고, 실행

  : 예외가 발생했는데 예외를 잡지 못하면, WAS 까지 예외가 전달

  : 컨트롤러(예외 발생) > 인터셉터 > 서블릿 > 필터 > WAS(여기까지 전파)

  : 스프링 부트가 제공하는 기본 예외 페이지 (WhiteLable 화면)

  : server.error.whitelabel.enabled=false 으로 옵션 끄면 tomcat 이 제공하는 기본 예외 페이지

 

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

HttpServletResponse 가 제공하는 sendError 메서드는 response 내부에 오류가 발생했다는 상태를 저장한다.

그리고 서블릿 컨테이너는 고객에게 응답하기 전 response 에 sendError( ) 가 호출되었는지 확인하고,

설정한 오류 코드에 맞는 예외 페이지를 보여준다.

 

response.sendError(HTTP 상태 코드)

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

 

컨트롤러(response.sendError( )) > 인터셉터 > 서블릿 > 필터 > WAS(sendError 호출 기록 확인)

 

커스텀 에러 페이지

기본 에러 페이지 대신 커스텀 에러 페이지를 제공해보자.

 

@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {

  @Override
  public void customize(ConfigurableWebServerFactory factory) {
    ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");
    factory.addErrorPages(errorPage404);
  }
}

404 오류가 발생하면, 에러 페이지 정보를 찾아 /errorpage/404 컨트롤러가 호출된다.

이때, 해당 오류와 그 자식 타입의 오류를 함께 처리한다. 

 

이렇게 오류가 WAS 까지 전파되면, WAS 가 상태에 맞는 컨트롤러를 다시 호출한다.

@Controller
public class ErrorPageController {
  @RequestMapping("/error-page/404")
  public String errorPage404(HttpServletRequest request, HttpServletResponse response) {
    return "error-page/404";
  }
}

컨트롤러가 호출되면, error-page/404 뷰 템플릿을 화면에 렌더링하여 보여준다.

 

동작 흐름

컨트롤러(예외 발생) > 인터셉터 > 서블릿 > 필터 > WAS(/error-page/404 호출)

  > 필터 > 서블릿 > 인터셉터 > 컨트롤러(/error-page/404) > View

 

WAS 까지 오류가 전파되면 서버 내부에서 다시 오류 페이지를 호출한다.

이때, 클라이언트는 서버 내부에서 발생하는 일을 알지 못한다.

 

이렇게 WAS 가 오류 페이지를 요청할 때, request 에 오류 정보를 넘겨준다.

getAttribute(jakarta.servlet.error.exception) : 예외

getAttribute(jakarta.servlet.error.exception_type) : 예외 타입

getAttribute(jakarta.servlet.error.message) : 오류 메시지

getAttribute(jakarta.servlet.error.request_uri) : 클라이언트 요청 URI

getAttribute(jakarta.servlet.error.servlet_name) : 오류가 발생한 서블릿 이름

getAttribute(jakarta.servlet.error.status_code) : HTTP 상태 코드

getDispatcherType() : 요청구분

 

DispatcherType

서버 내부에서 에러 페이지를 호출할 때 필터나 인터셉터가 한번 더 호출되는 것은 비효율 적이다.

요청을 구분하여 특정 요청에 필터를 호출할 수 있다.

 

REQUEST : 클라이언트 요청

ERROR : 오류 요청

FORWARD : 다른 서블릿이나 JSP 호출할 때

INCLUDE : 다른 서블릿이나 JSP 의 결과를 포함할 때

ASYNC : 서블릿 비동기 호출

 

필터에서 사용법

filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);

이렇게 하면 클라이언트 요청과 에러 페이지 요청에서 필터가 호출된다.

 

DispatcherType.REQUEST 는 기본값으로, 생략 시 적용된다.

 

인터셉터에서 사용법

registry.excludePathPatterns("/error-page/**");

이렇게 하면 에러 페이지 경로일 때 인터셉터가 호출되지 않는다.

 

스프링 부트 에러 처리

스프링 부트는 에러 페이지를 만드는 과정 대부분을 자동으로 처리해 준다.

(WebServerCustomizer, ErrorPage, ErrorPageController 만들기 등)

 

ErrorPage 를 자동으로 등록하여 /error 라는 경로로 기본 에러 페이지를 설정하여,

예외가 발생하면 모든 오류를 /error 를 호출하도록 한다.

또한 BasicErrorController 라는 스프링 컨트롤러를 자동으로 등록하여,

ErrorPage 에서 등록한 /error 를 매핑하여 처리하는 BasicErrorController 컨트롤러를 자동으로 등록한다.

 

> 참고 <

에러 페이지를 자동으로 등록하는 역할은 ErrorMvcAutoConfiguration 클래스가 한다.

 

뷰 선택 우선순위

1. 뷰 템플릿 

  : resources/templates/error/500.html

  : resources/templates/error/5xx.html

2. 정적 리소스 (static, public)

  : resources/static/error/400.html

  : resources/static/error/4xx.html

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

  : resources/templates/error.html

 

해당 경로 위치에 HTTP 상태 코드 이름의 뷰 파일을 넣어두면 된다.

이때, 4xx.html 이면 400 대 오류를 처리한다.

 

BasicErrorController

model 에 다음 정보를 담아서 뷰에 전달한다.

 

timestamp : 오류 발생 시간

status : HTTP 상태 코드

error : 에러 유형

exception : 예외 클래스

trace : 예외의 상세 스택 트레이드

message : 예외 메시지

errors : Spring 의 BindingResult 객체에서 발생한 검증 오류 정보

path : 예외가 발생한 URL 경로

 

오류 관련 내부 정보를 노출하는 것은 보안상의 문제가 있다.

따라서 BasicErrorController 에서 다음 정보를 model 에 포함할지 여부를 선택할 수 있다.

 

개발 서버

server.error.include-exception=true

server.error.include-message=on_param

server.error.include-stacktrace=on_param

server.error.include-binding-errors=on_param

 

운영 서버

server.error.include-exception=false

server.error.include-message=never

server.error.include-stacktrace=never

server.error.include-binding-errors=never

 

never : 사용하지 않음

always : 항상 사용

on_param : 파라미터가 있을 때 사용

(ex. message=&errors=&trace=)

 

on_param 은 디버그 시 문제를 확인하기 위해 개발 서버에서 사용할 수 있다. (운영 서버에서는 권장 X)

 

오류 관련 옵션

server.error.whitelabel.enabled=true : 스프링 기본 오류 페이지 사용 여부

server.error.path=/error : 글로벌 오류 페이지 경로

 

확장

ErrorController 인터페이스를 상속받아 구현하거나, BasicErrorController 를 상속받아 기능을 추가하면

에러 공통 처리 컨트롤러의 기능을 변경할 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

출처 | 스프링 MVC 2(김영한) - 인프런

728x90