기본적으로 등록되는게 3가지가 있는데,
우선순위 순으로
ExceptionHandlerExceptionResolver : @ExceptionHandler 라는 애노테이션이 붙은 걸 처리한다. API는 대부분 이걸로 해결이 된다.
ResponseStatusExceptionResolver : @ResponseStatus(value = HttpStatus.NOT_FOUND) 등 응답 상태 코드를 지정해 준다.
DefaultHandlerExceptionResolver : 스프링 내부에서 처리해주는 기본 예외
저렇게 다 거친 후에도 처리가 안되면 그제서야 WAS로 간다.
ResponseStatusExceptionResolver
이건 예외에 따라 상태코드를 지정해 주는 역할을 한다.
@ResponseStatus가 달려있는 예외거나
ResponseStatusException이라는 예외가 정의되어 있다.
위 두가지 경우에 예외를 처리해 준다.
@ResponseStatus( code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류")
public class BadRequestException extends RuntimeException {
}
대충 예외 상속받아서 만들어 주고
@GetMapping("/api/response-status-ex1")
public String responseStatusEx1(){
throw new BadRequestException();
}
컨트롤러에서 예외 뿜게 했다.
@ResponseStatus( code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류")
public class BadRequestException extends RuntimeException {
}
여기보면 이제 ResponsStatusExceptionResolver 이용하려고 만든 예외에 @ResponseStatus 붙여 놨는데,
안에 code는 상태코드이고, reason은 직역은 이유인데, 에러 메시지 넣을 수 있는거다.
정의한 바와 같이 400으로 나온다.
protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
throws IOException {
if (!StringUtils.hasLength(reason)) {
response.sendError(statusCode);
}
else {
String resolvedReason = (this.messageSource != null ?
this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
reason);
response.sendError(statusCode, resolvedReason);
}
return new ModelAndView();
}
이게 실제 스프링에서 제공해주는 ResponsStatusExceptionResolver 클래스 안에 있는 코드인데,
보면 그냥 response.sendError(상태코드, 메시지) 해주고
비어있는 ModelAndView를 반환하여 WAS가 정상으로 처리하게끔 한다. 물론 sendError()기 때문에 WAS가 /error을 호출하긴 한다.
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
int statusCode = responseStatus.code().value();
String reason = responseStatus.reason();
return applyStatusAndReason(statusCode, reason, response);
}
안에 또 있는 코드인데, ResponseStatus 저걸로 애노테이션 확인하는 거다.
참고로,
@ResponseStatus( code = HttpStatus.BAD_REQUEST, reason = "error.bad")
public class BadRequestException extends RuntimeException {
}
여기서 reason을 문자열 말고 아예 messages.properties에 메시지에 지정하고 그걸 호출해서 쓸 수도 있다.
이렇게 할 수 있는 이유는, 위에 ResponseStatusExceptionResolver 정의한 코드 들 중에 sendError() 하는 코드 보면 그 전에 먼저 저 reason의 문자열에 대해 정의한 것이 있는지 messageSource에서 뒤져본다.
@Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
이렇게 되어 있는거보니까 밖에서 주입 받는 듯?
근데 @ResponseStatus는 봐서 알겠지만 우리가 정의한 에러에 직접 붙여주는거기 때문에, 내가 수정할 수 없는 정의된 예외에 대해서는 적용할 수 없다,
추가로 애노테이션으로 사용하는 거기 때문에 조건에 따라 동적으로 변경하는 것도 어렵다.
이 때는 ResponseStatusException을 사용하면 된다.
@GetMapping("/api/response-status-ex2")
public String responseStatusEx2(){
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "error.bad", new IllegalArgumentException());
}
굉장히 쉽다. 그냥 조건부에 따라 예외 날려주면 된다.
throw new ResponseStatusException(상태코드, 메시지, 원래에러)를 넣어준다.
'스프링 > 4. 스프링 MVC-2' 카테고리의 다른 글
73. @ExceptionHandler API 예외 (0) | 2023.09.16 |
---|---|
72. 스프링 내부에서 발생하는 예외를 처리해주는 Resolver (0) | 2023.09.15 |
70. HandlerExceptionResolver로 API 예외 처리 (0) | 2023.09.13 |
69. API를 위한 예외처리 기능 (0) | 2023.09.13 |
68. 스프링 부트의 API 오류 처리 (0) | 2023.09.12 |