기본 제공 Resolver
Spring Boot가 기본으로 제공하는 ExceptionResolver는 아래와 같다.
1. ExceptionHandlerExceptionResolver
2. ResponseStatusExceptionResolver
3. DefaultHandlerExceptionResolver
- ExceptionHandlerExceptionResolver는 Api 예외처리할 때 @ExceptionHandler로 대부분 처리한다.
- ResponseStatusExceptionResolver는 HTTP 상태 코드를 지정해 준다. ex) @ResponseStatus(value = Httpstatus.NOT_FOUND)
- DefaultHandlerExceptionResolver는 스프링 내부 기본 예외를 처리한다.
가장 간단한 방법인 ResponseStatusExceptionResolver 먼저 살펴보자.
ResponseStatusExceptionResolver
예외에 따라 HTTP 상태 코드를 지정해 주는 역할을 한다.
이 Resolver는 @ResponseStatus 가 달려있는 예외와 ResponseStatusException 예외를 처리한다.
둘 다 선언하여 사용해 보자.
@ResponseStatus를 구현해 보자.
RuntimeException을 상속받는 BadRequestException을 생성한다.
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류")
public class BadRequestException extends RuntimeException{
}
code는 이 전 포스팅에서 response.setStatus(HttpServletResponse.SC_BAD_REQUEST)를 선언해서 사용한 것과 같은 원리이다. 애노테이션을 타고 부모 클래스로 타고 올라가다 보면 우리가 구현한 것과 똑같이 되어있다. reason은 message와 같다.
전 포스팅에서 UserException을 생성하여 RuntimeException을 상속받아서 메서드를 override 했던 것을 기억하는가?
거기서의 message와 같다. 이것도 마찬가지로 부모 클래스로 타고 올라가다 보면 우리가 구현한 것과 똑같이 되어있다.
@GetMapping("/api/response-status-ex1")
public String responseStatusEx1() {
throw new BadRequestException();
}
@ResponseStatus를 붙인 우리가 직접 정의한 BadRequestException 예외를 유도해 보자.
@ResponseStatus 실행 결과
전 포스팅에서 직접 구현했던 것들을 애노테이션 하나로 해결했다.
하지만 이상하다. error: 에 우리가 정의한 메시지가 왜 전달이 되지 않았을까?
이는 Spring Boot 버전 차이에 있다. Spring Boot 2.3 버전에서 reason에 담는 메시지를 그대로 전달하는 로직이 제거되었다. 이를 직접 오류 메시지를 전달해 주려면, 예외 처리의 가장 마지막 학습 예정인 @ControllerAdvice를 통해 선언해야 한다.
예전에는 reason으로 전달이 됐지만 지금은 안 되는 것이 당연한 것이다.
다시 본론으로 돌아와서, @ResponseStatus는 정말 편리하다! 하지만 기억해야 할 점이 있다.
@ResponseStatus 애노테이션은 개발자가 직접 변경할 수 없는 예외에는 적용할 수 없다. 이때 ResponseStatusException를 사용한다.
예를 들어, IllegalArgumentException이 일어났을 때, 우린 404 오류를 내리고 싶다고 가정해 보자.
하지만 IllegalArgumentException는 개발자가 직접 변경할 수 없는 예외이므로 ResponseStatusException를 사용하여 어떠한 오류로 바꿔줄지 첫 번째 인자로 넣어서 사용할 수 있다.
중간에 있는 메시지 또한 예전에는 전달이 가능했지만 2.3 버전 이후로 불가능해졌다.
ResponseStatusException를 구현해 보자.
@GetMapping("/api/response-status-ex2")
public String responseStatusEx2() {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, // 404
"잘못된 요청 오류입니다. ResponseStatusException 사용",
new IllegalArgumentException());
}
ResponseStatusException 결과
오류를 간단하게 처리할 수 있는 Resolver 중에 ResponseStatusExceptionResolver를 알아보았다.
DefaultHandlerExceptionResolver
DefaultHandlerExceptionResolver는 스프링 내부에서 발생하는 스프링 예외를 해결한다.
대표적으로 파라미터 바인딩 시점에 맞지 않을 경우 내부에서 TypeMismatchException이 발생한다.
이 경우 예외가 발생했기 때문에 그대로 두면 이 전에 봤던 것처럼 서블릿 컨테이너까지 오류가 올라가고 결과적으로 500 오류가 나타나게 된다.
하지만 이는 클라이언트와 서버 간 차이가 있어 파라미터 값을 잘못 보냈거나 대부분 클라이언트의 잘못이기 때문에 4xx오류가 발생하는 것이 맞다.
DefaultHandlerExceptionResolver는 이것을 5xx 오류를 4xx 오류로 바꾼다.
TypeMismatchException을 유도해보자
@GetMapping("/api/default/handler-ex")
public String defaultException(@RequestParam Integer data) {
return "ok";
}
DefaultHandlerExceptionResolver의 실행 결과
DefaultHandlerExceptionResolver가 없다면 500 오류가 정상인데 Bad Request가 나오는 것을 확인할 수 있다.
DefaultHandlerExceptionResolver 내부에는 다음과 같이 기본적인 오류에 대해 500 오류가 아닌 4xx오류로 변경하는 많은 메서드들이 있고, 이 메서드가 결국 우리가 이 전에 구현했던 return new ModelAndView(); 와 같은 것을 볼 수 있다.
@Override
@Nullable
protected ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
try {
if (ex instanceof HttpRequestMethodNotSupportedException) {
return handleHttpRequestMethodNotSupported(
(HttpRequestMethodNotSupportedException) ex, request, response, handler);
}
else if (ex instanceof TypeMismatchException) {
return handleTypeMismatch(
(TypeMismatchException) ex, request, response, handler);
}
// ... 오류를 처리하는 많은 메서드
}
catch (Exception handlerEx) {
if (logger.isWarnEnabled()) {
logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
} }
return null;
}
다음 포스팅에서는 가장 중요한 @ExceptionHandler, ExceptionHandlerExceptionResolver를 알아본다.
코드링크
https://github.com/toychip/StudySpring/commit/b7e70231909045965decd35b15e9c3b66dc3a681
Feat: Spring-mvc2-exception Spring 제공 ExceptionResolver-ResponseStatu… · toychip/StudySpring@b7e7023
…sException
github.com
https://github.com/toychip/StudySpring/commit/f07df1c48a82f7e301ae2380e3529df842339bc2
Feat: Spring-mvc2-exception Spring 제공 ExceptionResolver-DefaultHandle… · toychip/StudySpring@f07df1c
…rExceptionResolver
github.com
김영한님 mvc2 - Exception 강의로 학습하였습니다.
공부한 내용을 상기시키고 해당 내용을 몰랐을 때의 입장에서 쉽게 배울 수 있지 않을까 해서 작성한 글입니다. 잘못된 내용이 있을 경우 말씀해 주세요. 피드백 환영합니다.
'Spring > Spring Exception' 카테고리의 다른 글
Spring Exception 10. @ControllerAdvice (0) | 2023.08.26 |
---|---|
Spring Exception 9. Spring이 제공하는 @ExceptionHandler (0) | 2023.08.21 |
Spring Exception 7. Api에서 ExceptionResolver 활용 (0) | 2023.08.18 |
Spring Exception 6. ExceptionResolver (HandlerExceptionResolver) (0) | 2023.08.17 |
Spring Exception 5. 스프링 부트 Api 오류처리 (BasicErrorController) (0) | 2023.08.16 |