Exception Handling(예외 처리)
스프링의 기본적인 예외 처리 방법
스프링에서 컨트롤러에서 예외가 발생하게 되면, 아래의 사진에서처럼 역순으로 전파되어 WAS(톰캣)까지 전달된다.
WAS에서는 등록한 에러에 맞게 요청을 전달하게 되는데, 에러 컨트롤러(BasicErrorController
)를 한 번 더 호출하여 에러를 처리하게 된다.
여기서 요청할 때 인터셉터나 필터가 작동하지 않기 위해 dispatcherType을 ERROR
로 하여 요청을 보낸다.(일반적인 요청은 REQUEST
)
이 방법은 WAS에서 두 번의 요청이 발생하기 때문에 비효율적이며, 예외 처리를 세밀하게 제어하기 어렵다는 단점이 있다.
떄문에 스프링에서는 HandlerExceptionResolver
라는 인터페이스를 제공하여 예외 처리를 세밀하게 제어하게 제공해주며, 우선 순위는 종류는 아래와 같다.
ExceptionHandlerExceptionResolver
: 에러 응답을 위한 Controller나 ControllerAdvice에 있는 ExceptionHandler 처리ResponseStatusExceptionResolver
: 상태 코드를 지정하는 @ResponseStatus / ReponseStatusException 처리DefaultHandlerExceptionResolver
: 스프링 내부 기본적인 예외 처리
ResponseStatusExceptionResolver
@ResponseStatus
혹은 ResponseStatusException
을 통해 예외 처리 하는 방법으로 아래와 같이 사용할 수 있다.
@ResponseStatus
: 컨트롤러에서 발생하는 예외에 대해 상태 코드와 메시지를 지정
BadRequestException을 발생시키면, ResponseStatusExceptionResolver
가 위에 설정된 애노테이션을 확인해 상태 코드와 메시지를 담아 응답을 보낸다.
같은 예외는 같은 상태와 에러 메시지를 반환하게 되어 에러 응답 커스터마이징을 하기 어려운 단점이 있다.
ResponseStatusException
: 예외를 발생시키면서 상태 코드와 메시지를 지정
불필요하게 많은 예외 클래스를 만들지 않고도 예외를 발생시키면서 상태 코드와 메시지를 지정할 수 있다. 직접 예외 처리를 하게 되어 일관된 예외 처리가 어렵고 중복 코드가 발생할 수 있다는 단점이 있다.
위의 두 방법은 결국 WAS에서 다시 오류 페이지를 내부 요청하는 과정을 거치게 된다.
ExceptionHandlerExceptionResolver
@ExceptionHandler
를 통해 예외를 처리하는 방법으로 위의 방식보다 유연하게 에러를 처리할 수 있게 해준다.
컨트롤러의 메서드에 @ExceptionHandler
를 적용하면 해당 컨트롤러에서 발생하는 예외를 처리할 수 있다.
여기서 선언된 ExceptionHandler는 해당 컨트롤러에서만 작동하기 때문에 다른 컨트롤러에서 발생하는 예외는 처리하지 못하여 중복 코드를 발생시킨다.
@ControllerAdvice(@RestControllerAdvice)
위의 한계를 극복하기 위해 전역적으로 @ExceptionHandler를 적용할 수 있는 @ControllerAdvice를 제공한다. (@RestControllerAdvice는 @ResponseBody가 추가된 @ControllerAdvice)
위와 같이 별도의 클래스를 만들어 @ControllerAdvice를 적용하면 전역적으로 ExceptionHandler를 적용해줄 수 있으며, 특정 컨트롤러에만 적용하고 싶다면 아래와 같이 사용할 수 있다.
때문에 현재 Spring에서는 ControllerAdvice를 사용하여 예외 처리를 하는 것을 주로 사용한다.
전체 흐름
ExceptionHandlerExceptionResolver
예외가 발생한 컨트롤러 안에 @ExceptionHanlder 애노테이션이 있는지 확인
있으면 해당 메서드를 실행, 없는 경우 ControllerAdvice에서 적용되는 @ExceptionHandler 애노테이션이 있는지 확인
ResponseStatusExceptionResolver
@ResponseStatus 애노테이션이나 ResponseStatusException 예외가 있는지 확인
있는 경우 실행하여 ServletResponse의 sendError()로 예외를 서블릿에 전달 후, BasicErrorController로 요청 전달
DefaultHandlerExceptionResolver
스프링 내부에서 발생한 예외인지 검사 후 예외 처리
위의 과정에서 에러가 처리되지 않은 경우 SpringBoot의 설정에 맞게 BasicErrorController로 요청을 다시 전달
참고자료
Last updated