스프링 부트가 기본으로 제공하는 ExceptionResolver 는 여러 가지가 있는데,
HandlerExceptionResolverComposite
에 다음 순서로 등록되어 있다.
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver (우선 순위가 가장 낮다)
ExceptionHandlerExceptionResolver
: @ExceptionHandler을 처리한다. API 예외처리는 이 방식을 가장 많이 사용한다.
ResponseStatusExceptionResolver
: HTTP 상태 코드를 지정해준다.
ex) @ResponseStatus(value = HttpStatus.NOT_FOUND)
DefaultHandlerExceptionResolver
: 스프링 내부 기본 예외를 처리한다.
예외 처리 메서드에 @ExceptionHandler
애노테이션을 선언하고, 해당 컨트롤러에서 처리하고 싶은 예외를 지정해주면 된다. 해당 컨트롤러에서 예외가 발생하면 해당 메서드가 호출된다.
RuntimeException을 던지면 결국 컨트롤러까지 올라오기 때문에 컨트롤러에서 예외를 잡으면 된다.
(참고로 지정한 예외 또는 그 예외의 자식 클래스는 모두 처리할 수 있으며, 다양한 예외를 한번에 처리할 수 있다)
위의 경우, ConstraintViolationException
예외가 컨트롤러에서 발생할 경우, 이 메서드가 호출된다.
@ExceptionHandler
애노테이션의 속성에 처리할 예외를 명시해도 되고, 생략할 경우에 파라미터의 예외가 지정된다.
@ControllerAdvice
또는 @RestControllerAdvice
를 사용하면, 위에서 작성한 예외처리 로직(@ExceptionHandler)들을 한 곳에 모아서 관리할 수 있다.또한, 대상 컨트롤러를 지정하여 여러 컨트롤러에 @ExceptionHandler, @InitBinder 기능을 부여해주는 역할을 한다.
(대상 컨트롤러 지정의 경우, 특정 패키지/클래스를 지정하거나 특정 애노테이션이 있는 컨트롤러를 직접 지정할 수도 있다)
대상을 지정하지 않을 경우 모든 컨트롤러에 적용된다 (글로벌 적용)
@RestControllerAdvice
는 @ControllerAdvice 와 같고, @ResponseBody가 추가되어 있다
여기서는 ErrorResponse
라는 에러 응답 클래스를 만들어 ResponseEntity로 응답을 내렸다.
컨트롤러를 호출한 결과 custom 예외 클래스인 BusinessLogicException 예외가 컨트롤러 밖으로 던져진다.
예외가 발생했으로 ExceptionResolver
가 작동한다. 가장 우선순위가 높은
ExceptionHandlerExceptionResolver 가 실행된다.
ExceptionHandlerExceptionResolver 는 해당 컨트롤러에 BusinessLogicException 을 처리할 수 있는 @ExceptionHandler 가 있는지 확인한다.
handleBusinessLogicException() 를 실행한다. @RestController 이므로 handleBusinessLogicException() 에도 @ResponseBody 가 적용된다.
따라서 HTTP 컨버터가 사용되고, 응답이 JSON으로 반환된다.