-
API 예외처리-3스프링 2023. 1. 27. 23:55
예외가 발생하면 WAS까지 예외가 던져지고, WAS에서 오류 페이지 정보를 찾아서 다시 /error 를 호출하는 과정은 생각해보면 너무 복잡하다.
WAS까지가지 않고 예외를 처리해보자.
@Slf4j public class UserHandlerExceptionResolver implements HandlerExceptionResolver { private ObjectMapper objectMapper = new ObjectMapper(); @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { try { if (ex instanceof UserException) { log.info("User Exception Resolver to 400"); String acceptHeader = request.getHeader("accept"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); if ("application/json".equals(acceptHeader)) { Map<String, Object> errorResult = new HashMap<>(); errorResult.put("ex", ex.getClass()); errorResult.put("message", ex.getMessage()); String result = objectMapper.writeValueAsString(errorResult); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().write(result); return new ModelAndView(); } else { return new ModelAndView("error/500"); } } } catch (IOException e) { log.info("resolver ex", e); } return null; } }
전과 똑같이 HandlerExceptionResolver를 implements하여 구현한다.
이때 예외가 우리가 설정한 UserException이라면 예외 처리를 시작한다.
accept가 Json일경우와 아닐 경우로 나뉘는데, Json일 경우 ObejctMapper를 이용하여
HttpBody에 직접 그 내용들을 하나하나 넣어준다.
해당 데이터들을 담고, 끝내는 것이다.
서블릿 컨테이너까지 간다음, 다시 /error로 가는 복잡한 과정을 거치치 않는다.
Json이 아닐경우 ModelAndView에 보여줄 View를 넣어준다.
이때, 해당 과정의 작성이 매우 복잡해진다.
이를 스프링이 제공하는 ExceptionResolver로 편리하게 사용이 가능하다.
스프링 부트가 기본으로 제공하는 ExceptionResolver 는 다음과 같다.
HandlerExceptionResolverComposite 에 다음 순서로 등록
ExceptionHandlerExceptionResolver
@ExceptionHandler를 처리한다.
API 예외 처리는 대부분 이 기능으로 처리.
ResponseStatusExceptionResolver
HTTP 상태 코드를 지정해 준다.
DefaultHandlerExceptionResolver
스프링 내부 기본 예외를 처리한다.
우선 ExceptionHandlerExceptionResolver -> ResponseStatusExceptionResolver -> DefaultHandlerExceptionResolver
이 순서대로 처리가 진행되며, 첫번째 처리가 안되면 두번째로 처리... 이 흐름으로 ExceptionResolver로 진행된다.
ResponseStatusExceptionResolver
예외에 따라서 HTTP 상태 코드를 지정해 주는 역할을 한다.
@ResponseStatus가 달려있는 예외와 ResponseStatusException예외 이 두가지를 처리한다.
예외를 만들어 보자.
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류") public class BadRequestException extends RuntimeException{ }
해당 예외가 발생하면 BadRequest인 400에러가 발생한다.
BadRequestException이 터지면, ExceptionResolver가 ResponseStatusException을 처리하면서 @ResponseStatus가 있는지 확인하고 상태코드대로 에러처리를 해준다. 따라서 에러가 400으로 처리가 된다.
@ResponseStatus 는 개발자가 직접 변경할 수 없는 예외에는 적용할 수 없다.
애노테이션을 사용하기 때문에 조건에 따라 동적으로 변경하는 것도 어렵다.
이때는 ResponseStatusException 예외를 사용하면 된다.
@GetMapping("/api/response-status-ex2") public String responseStatusEx2() { throw new ResponseStatusException(HttpStatus.NOT_FOUND, "bad", new IllegalArgumentException()); }
이 또한 ResponseStatusExceptionResolver가 처리한다.
DefaultHandlerExceptionResolver
DefaultHandlerExceptionResolver 는 스프링 내부에서 발생하는 스프링 예외를 해결한다.
파라미터 바인딩 시점에 타입이 일치하지 않으면 TypeMismatchException예외가 발생하는데 그냥 두면 서블릿 컨테이너까지 올라가서 500에러가 발생한다.
하지만 이는 대부분 클라이언트의 HTTP문제이다. 따라서 400오류를 발생시켜야 한다.
DefaultHandlerExceptionResolver 는 이것을 500 오류가 아니라 HTTP 상태 코드 400 오류로 변경한다.@GetMapping("/api/default-handler-ex") public String defaultException(@RequestParam Integer data) { return "ok"; }
오류가 400으로 발생한다.
2023-01-27 23:52:44.593 WARN 9572 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: "qqq"]
로그를 보면 DefaultHandlerExceptionResolver가 해당 오류를 처리해준다.
728x90'스프링' 카테고리의 다른 글
API 예외처리-5 (RestControllerAdvice) (0) 2023.01.28 API 예외처리-4 (ExceptionHandler) (0) 2023.01.28 API 예외처리-2 (0) 2023.01.27 API 예외처리-1 (0) 2023.01.26 OAuth2.0과 JWT적용 - 3 (0) 2023.01.25