Free Lines Arrow
본문 바로가기
Spring/spring mvc 2 스터디

[Spring] API 예외 처리(HandlerExceptionResolver)

by skahn1215 2022. 6. 26.
728x90
반응형

API 예외 처리

  • 백엔드 개발자면 API 예외 처리를 가장 많이 다룰 것이다.
  • 보통 JSON 형태로 200, 400, 500 대 상태코드로 응답을 주게 된다.
  • 그럼 어떻게 JSON 으로 응답을 주는지 차근 차근 공부해 보면서 알아본다.

 

 

API 예외 처리 응답방식

  • 스프링 부트는 BasicErrorController 가 제공하는 기본 정보들을 활용하여 API 를 생성해서 준다.
{
"timestamp": "2021-04-28T00:00:00.000+00:00", "status": 500,
"error": "Internal Server Error",
"exception": "java.lang.RuntimeException",
"trace": exception 추적 내용,
"message": "잘못된 사용자",
      "path": "/api/members/ex"

 

 

 

HandlerExceptionResolver 예외 처리

  • 예외가발생 해서 WAS 까지 가는 순간 상태 코드는 무조건 500 이다.
    하지만 예외종류는 많고 400, 404등 다른 상태코드도 전달하고 싶다.

  • HanlerExceptionResolver 을 통해서 동작 방식을 변경 할 수 있다.

 

예외 발생 예제

  • HandlerExceptionResolver 예외 처리를 적용하지 않으면 어떻게 되는지 보자
  • 상태 코드는 500 이다 WAS 까지 도달 하면 500 이기 때문이다.
@GetMapping("/api/exception")
 public MemberDto getMember(@PathVariable("id") String id) {
      if (id.equals("ex")) {
        throw new RuntimeException("잘못된 사용자"); 
      }
}

상태코드

{
      "status": 500,
      "error": "Internal Server Error",
      "exception": "java.lang.IllegalArgumentException",
      "path": "/api/members/bad"
}

 

ExceptionResolver 동작 방식

1. 요청이 들어오면 preHandler 호출
2. Handler Adapter 호출 
3. Controller 호출 
 - 여기서 예외 발생
4. 예외 전달
5. ExceptionResolver가 Exception 을 가로채어 해결시도를 한다.
6. 해결하면 알맞는 View 를 호출하고 
7. afterCompletion 을 호출한다.
8. ExceptionResolver 가 예외를 해결 했기 때문에 200 응답으로 나간다.

 

HandlerExceptionResolver 구현해보기

  • 파라미터를 보면 Object handler, Exception 이 있다.
    - handler: 컨트롤러 정보
    - Exception: 컨트롤러에서 발생한 예외
  • 그리고 반환 타입이 중요하다.
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    public ModelAndView resolveException(HttpServletRequest request,
    HttpServletResponse response, Object handler, Exception 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;
}

 

반환타입에 따른 처리

  • ModelAndView 반환:
     - 빈 모델인 경우: 뷰를 렌더링 하지 않고 정상흐름으로 반환
     - 안에 정보가 있는경우: 뷰를 렌더링 한다.
  • NULL: 다음 ExceptionResolver 를 찾아 실행한다. 
    - 처리가 안되면 기존에 발생한 예외를 서블릿 밖으로 보낸다

 

ExceptionResolver 등록

  • 단순히 구현한다고 동작하는게 아니라 아래와 같이 등록을 해줘야 한다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
    // 익셉션 리솔버 등록
    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        resolvers.add(new MyHandlerException());
    }
}

 

 

ExceptionResolver 예제1

  • accept 가 json 인경우는 json 형식으로 보내주고 그렇지 않으면
    HTML 화면으로 이동 시킨다
  • 여기서 중요한건 response.setStatus 이다 
    http status 를 지정해 주지 않으면 200 으로 나가기 때문에 여기서 변경을 해줘야 한다.
public class TestHandlerException implements HandlerExceptionResolver {
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (ex instanceof TestException) {
            String acceptHeader = request.getHeader("accept");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            
            if ("application/json".equals(acceptHeader)) {
                Map<String,Object> errorResult = new HashMap<>();
                errorResult.put("예외 종류", ex.getClass());
                errorResult.put("예외메세지", ex.getMessage());
                String result = null;
            
                try {
                    result = objectMapper.writeValueAsString(errorResult);
                    response.setContentType("application/json");
                    response.setCharacterEncoding("utf-8");
                    response.getWriter().write(result);
                    return new ModelAndView();
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
            } else {
                new ModelAndView("/500.html");
            }
        }

        return null;
    }
}

 

 

 

참고:

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard

 

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의

웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있

www.inflearn.com

 

728x90
반응형
LIST

댓글