Spring에서의 API 예외 처리

hoyong.eom·2023년 7월 27일
0

스프링

목록 보기
23/59
post-thumbnail

Spring

API 예외 처리

서블릿 방식

customize 오버라이드하는것!!

html이 아닌 json을 반환해야함.
produces = 설정으로 지정함!
produce는 클라이언트가 보낸 accept에 따라서 별도로 호출됨. 즉 우선순위를 갖음.
Accept는 클라이언트가 받을 수 있는 데이터 타입을 지정하는것임.

ResponseEntity를 이용해서 바디에 데이터를 쉽게 넣을 수 있음. Map<String, Object> result가 바로 제이슨으로 변환됨.

즉, ResponseEntity를 사용해서 produce를 지정해놓음 json으로ㅓ!!!

스프링 부트 기본 오류 처리

스프링 부트에서는 BasicErrorController를 기본적으로 제공하는데 이방법을 그대로 사용할 수 있다.

만약 컨트롤러에서 예외가 발생하면 WAS까지 올라오고 @RequestMapping("{server.error.path:{error.path:/error}}") 처럼 /error로 들어와 BasicErrorController 로직을 탄다. 이미 개발되어있음. API인 경우에도!
물론 Accept를 application/json이어야함. text/html로 오면 /error 하위에 html을 보여줌.
그외는 responseentity를 씀.
(더 나은 방법이 있으니 참고로만!)->@ExceptionHandler

API 예외 처리 - HandlerExceptionResolver

오류 메시지, 형식등을 API마다 다르게 처리하기 위해서는??
일반적으로는 WAS서버 까지 예외가 오면 500에러가 난다. 근데 나는 400에러로 하고 싶다면?

스프링 MVC는 컨트롤러밖으로 예외가 던져진 경우 예외를 해결하고 동작을 새로 정의할 수 있는 방법을 제공함.
컨트롤러 밖으로 던져진 예외를 해결하고 동작 방식을 변경하고자 한다며면 HandlerExceptionResolver를 사용 줄여서
exceptionResolver라함.

ExceptionResolver 가 있으면예외를 WAS까지 올리지 않고 예외를 잡아서

예외를 해결해도 인터셉터의 posthandle은 호출안됨, 그래도 예외를 정상적으로 처리하도록 해줌.

@Slf4j
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

        log.info("call resolver", ex);

        try {
            if (ex instanceof IllegalArgumentException) {
                log.info("IllegalArgumentException resolver to 400");
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
                return new ModelAndView();
            }

        } catch (IOException e) {
            log.error("resolver ex", e);
        }

        return null;
    }
}
// exceptionResolver 스프링빈으로 등록하기

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        resolvers.add(new MyHandlerExceptionResolver());
        resolvers.add(new UserHandlerExceptionResolver());
    }

위처럼 ExceptionResolver 를 구현해놓으면 resolveException을 오버라이드함. ex가 오면 modelandView를 리턴해서 정상처리되도록함. 위에서 illegalargumentException이 발새앻도 400에러로 바꿔줌. 즉, resolveExceoption에서 예외를 먹어버림. null을 리턴하면 그대로 다시 was로 올라감.

반환값에 따라 동작방식이 다름.

ExceptionHandler의 활용

  • 예외 상태 코드 변환(500 -> 400)
  • 뷰 템플릿 처리
  • API 응답 처리

주의) configureHandleExceptionHandler를 사용하면 스프링이 기본으로 등록하는 ExceptionHandler가 없어짐.extendHandlerExceptionResolvers를 사용해야함.

API 예외 처리 - handlerExceptionResolver 활용

예외가 발생하면 WAS까지 안가고 내부 호출과정없이 handlerExceptionResolver에서 끝낼수있따.

아래의 코드처럼 ExceptionHandler를 만들어놓으면 WAS까지가지않고 바로 처리된다.

@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;
    }
}

그러나, 위처럼 직접 구현하지 않고도 스프링에서는 ExceptionHandler를 제공해준다.

스프링이 제공하는 ExceptionResolver

스프링부트가 기본적으로 ExceptionResolver를 제공해준다.
1) ExceptionHandlerExceptionResolver
@ExceptionHandler를 처리한다. 대부분 이걸로 처리

2) ResponseStatusExceptionResolver
@ResponseStatus(value = HttpStatus.NOT_FOUND)

3) DefaultHandlerExceptionResolver

1이 우선순위가 가장높다.

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "error.bad")
public class BadRequestException extends RuntimeException {
}

즉, ResponseStatus 애노테이션이 있으면 미리 등록된 ExceptionHandler에서 애노테이션있으면 찾아서 알아서 처리해준다. 위에 우리가 직접 만들었던것처럼!

DefaultHandlerExceptionResolver는 서버 내부 오류의 500오류를 Http 상태코드 400오류로 변경한다.
즉, 스프링 내부 오류를 Http 400코드로 변경한다.

참고

아래의 강의를 참고하여 정리한 내용입니다.
김영한님의 스프링MVC2

0개의 댓글