먼저 스프링이 제공해주는걸 받아먹기 전에
HandlerExceptionResolver
를 사용해 봐따.
@Slf4j
public class UserHandlerExceptionResolver implements HandlerExceptionResolver {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
if (ex instanceof UserExceptione) {
log.info("UserException 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 {
// TEXT/HTML
return new ModelAndView("error/500");
}
}
} catch (IOException e) {
log.error("resolver ex", e);
}
return null;
}
}
아따... 길다 길어...
@GetMapping("/api/members/{id}")
public MemberDto getMember(@PathVariable("id") String id) {
if (id.equals("ex")) {
throw new RuntimeException("잘못된 사용자");
}
if (id.equals("bad")) {
throw new IllegalArgumentException("잘못된 입력 값");
}
if (id.equals("user-ex")) {
throw new UserExceptione("사용자 오류");
}
return new MemberDto(id, "hello " + id);
}
이건 컨트롤러!
여기 throw new UserException으로 들어온 거를 resolverException에서 400으로 바꿔주고 또 뭐 어쩌고 저쩌고 해가지고 처리....
이렇게 되면 컨트롤러에서 예외가 발생해도 서블릿 컨테이너까지 예외가 전달되지 않고 ExceptionResolver
에서 예외를 처리한다고 한다. 즉 스프링 MVC에서 예외 처리가 끝난다는 것. 결과적으로 WAS 입장에서는 정상 처리된 것이다.
하... 근데 이렇게 구현하니까 너무 복잡해!! 그러니까 스프링에서 제공하는걸 받아먹어보자!
스프링부트가 기본 제공하는 ExceptionResolver
는 아래와 같이 3가지가 있다고 한다.
- ExceptionHandlerExceptionResolver
- ResponseStatusExceptionResolver
- DefaultHandlerExceptionResolver (-> 우선 순위가 가장 낮다)
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
자 우선 둘째 녀석부터 알아보자. (강의 순서가 그러니까 나도)
이 녀석은 예외에 따라서 HTTP 상태 코드를 지정해주는 역할을 한다고 한다.
이 두가지 경우를 처리한다.
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "error.bad")
public class BadRequestException extends RuntimeException {
}
이렇게 애노테이션이 달려있을때 code 로 Http 상태 코드를 지정, reason으로 메세지를 지정한다.
! 여기서 잠깐 !
원래 메세지는 직접 입력했었지만... error.bad
처럼 코드로 남기면
messages.properties
에서 해당 코드를 찾아와 메시지를 남긴다고 한다! 여기서 중요한것은
메세지(message)가 아니라 메세지스~(messages) 라고 강조하심 ㅎㅎㅎㅎ
@GetMapping("/api/response-status-ex1")
public String responseStatusEx1() {
throw new BadRequestException();
}
그리고 이렇게 컨트롤러에서 BadRequestException()
을 불러주면..!
status가 400, message도 잘 담겨있는걸 확인할 수 있다.
그 다음으로 둘째의 두번째 방법인 ResponseStatusException 처리
@GetMapping("/api/response-status-ex2")
public String responseStatusEx2() {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "error.bad", new
IllegalArgumentException());
}
바로 그냥 컨트롤러에서 이렇게 처리해버리면 된다...!
여기서 http 상태 코드를 NOT_FOUND 로 보냈으니까 404로 들어오는 것을 확인 할 수 있다!!!
이번에 알아볼 녀석은 막내. 이 녀석은 스프링 내부에서 발생하는 스프링 예외를 해결 해준다.
강의에서 공부한 것은 TypeMismatchException이 발생한 경우인데
이 예외를 그냥 두게되면 서블릿 컨테이너까지 오류가 올라가서 결과적으로 500 에러가 발생한다고 한다.
근데 파라미터 바인딩은 대부분 클라이언트가 요청 정보를 잘못 호출해서 발생하는 문제...
그래서 DefaultHandlerExceptionResolver
는 이것을 500 -> 400으로 변경시켜준다!
@GetMapping("/api/default-handler-ex")
public String defaultException(@RequestParam Integer data) {
return "ok";
}
자 일단 컨트롤러를 만들고 data는 Integer로 설정.
숫자를 넣었을때는 ok가 나오는 것을 확인하고
숫자가 아닌 DrumJ를 넣었을 때는 400 에러가 나오는 걸 확인 할 수 있다.
exception도 보면 "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException"
어쩌고 저쩌고 타입 미스매치. 스프링 내부에서 터진 예외를 호로록 400으로 바꿔서 보여주는 것이다.
이거는 내부 예외를 바꿔주는 거라 스프링에서 DefaultHandlerExceptionResolver
를 따라 들어가면 더 많은 코드를 확인할 수 있다!!!
마지막 남은 대망의 ExceptionHandlerExceptionResolver
@ExceptionHandler 라는 혁신적인 예외 처리 기능을 제공한다고 하는데....
글이 길어지는건 싫으니 다음 게시글에서 (이미 충분히 길다)
총총....