💠프로그래밍 언어/Java

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

2025. 4. 4. 20:41
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