API 예외 - HandlerExceptionResolver 활용

바그다드·2023년 5월 30일
0

예외

목록 보기
6/9
  • 예외가 발생했는데, 예외를 처리하지 못한다면 예외가 WAS까지 전달이 되고, WAS에서는 다시 에러 페이지 정보를 찾아서 요청을 호출하는 복잡한 과정이 일어나게 된다.
    그런데 이전의 포스팅에서 알아보았던 ExceptionResolver를 활용하면 이런 과정 없이 Handler밖으로 던져진 예외를 처리할 수 있다. 이번에는 HandlerExceptionResolver의 활용 방법에 대해 알아보자.

1. 사용자 정의 예외 추가

  • 활용법을 알아보기 위해 RuntimeException을 상속받는 사용자 정의 예외를 추가해주자.
public class UserException extends RuntimeException{
    public UserException() {
        super();
    }

    public UserException(String message) {
        super(message);
    }

    public UserException(String message, Throwable cause) {
        super(message, cause);
    }

    public UserException(Throwable cause) {
        super(cause);
    }

    protected UserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

2. 컨트롤러 추가

@Slf4j
@RestController
public class ApiExceptionController {

    @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 UserException("사용자 오류");
        }

        return new MemberDto(id, "hello" + id);
    }

    @Data
    @AllArgsConstructor
    static class MemberDto {
        private String memberId;
        private String name;
    }
}
  • 파라미터로 user-ex가 넘어오면 UserException이 발생하게 된다.

HandlerExceptionResolver 생성

  • 이제 UserException을 처리할 UserHandlerExceptionResolver를 생성해주자.
@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 UserException) {
                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;
    }
}
  • HandlerExceptionResolver를 상속받자
  • 파라미터로 받는 값은 다음과 같다
    request : 요청 정보
    response : 응답 정보
    handler : 예외가 발생한 핸들러 정보
    Exception : 예외에 대한 정보
  • 만약 발생한 예외 정보가 UserException의 인스턴스이고, request의 accept가 application/json이라면 Json데이터를 반환하고, 그 외의 경우에는 500.html을 보여준다.

그런데 문제는 ExceptionResolver가 ModelAndView를 반환하기 때문에 응답을 일일이 처리를 해줘야한다는 것이다.

UserHandlerExceptionResolver추가

  • WebConfig에 UserHandlerExceptionResolver를 추가해주자.
    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        resolvers.add(new MyHandlerExceptionResolver());
        // 추가
        resolvers.add(new UserHandlerExceptionResolver());
    }
  • application/json의 경우

  • 그 외

  • 이처럼 ExceptionResolver를 활용하면 컨트롤러에서 예외가 발생해도 WAS까지 전달하지 않고, ExceptionResolver에서 처리를 하게 된다. 덕분에 WAS에서 다시 요청을 보내는 복잡한 과정을 거치지 않아도 된다.
    하지만 앞서 확인했듯이 ExceptionResolver를 직접 구현하는 것은 너무 번거롭다. 다음 포스팅에서는 스프링에서 제공하는 ExceptionResolver에 대해 알아보자.

출처 : 김영한 스프링MVC2편

profile
꾸준히 하자!

0개의 댓글