API-예외처리

Shaun·2022년 10월 31일
1

SpringBoot

목록 보기
12/21
post-thumbnail

오류 페이지는 단순히 고객에게 오류 화면을 보여주고 끝이지만, API는 각 오류 상황에 맞는 오류 응답 스펙을 정하고, JSON으로 데이터를 내려주어야 한다.

서블릿을 활용한 API - 예외처리

  • 서블릿을 사용할꺼라 -> 저번에 만든 new ErrorPage 사용

  • 간단 컨트룰러 작성

  • Http header Accept가 applicaiton/json 인지 확인
  • 정상호출시 JSON으로 잘온다.
  • 하지만 오류 호출시 HTML로 반환되는 문제 발생

=> 우리가 원하는건 정상이든,오류든 JSON형식으로 받아오는것!

문제를 해결 하려면 오류 페이지 컨트롤러도 JSON 응답을 할 수 있도록 수정해야 한다

오류페이지 컨트룰러 수정

  • produes = MediaType.APLICATION_JSON_VALUE 를 설정해 이 컨트룰러가 JSON형식으로 오류를 리턴하도록 한다.(Http header ACCEPT 가 JOSN) 이니까

  • Accept = json이면 이 컨트룰러가 우선권을 같는다 같은 url이 있어도

  • map 에는 각 오류내용을 담아 ResponseEntity로 반환

BasicErrorController

  • 사실 지금까지 해왔던 과정은 이미 BasicErrorController에 정의 되어있다. 자세히 보면 코드가 비슷하다.

  • properties를 통해 더 자세한 오류내용을 출력할 수 있지만 보안상 위험하다.

  • 단순히 HTML 오류 페이지만 출력할거면 BasicErrorController이 좋지만 API 예외는 각각 상황에 따라 다르므로 매우 세밀한 작업이 필요하다.

  • BasicErrorController-
  1. error-page(HTML) 자동생성
  2. JSON(ResponseEntity)으로 반환하도록 자동설정

HandlerExceptionResolver

  • WAS까지 전달되는 500에러 예외를 4xx 에러로 처리하고 싶을때, 다른 메세지, 다른 형식으로 처리하고 싶을때사용(=예외처리 관리)
  • 각각 상황에 다른 API 예외에 적합

Flow

  • ExceptionResolver 적용전은 preHandle -> 어댑터-> 예외발생-> 디스패쳐서블릿 -> postHandler 실행 x -> WAS 예외 반환

  • ExceptionResolver 을 적용하면 디스패쳐 서블릿으로 예외를 받은뒤 ExceptionResolver을 호출해 예외를 처리할 수 있는지 확인을 한다. 예외를 처리하면 (postHandler 실행 x)

  • 서버 500에러를 400으로 바꿔보자

HandlerExceptionResolver 실습


  • 사용자 정의 예외, 컨트룰러 생성

HandlerExceptionResolver 생성

  • 이 예외를 처리하는 UserHandlerExceptionResolver 생성

  • response.sedError 로 500에러 -> 400에러 변환 , 정상흐름 반환

  • 이후 WAS는 서블릿 오류페이지를 찾아서 내부 호출,예를 들어서 스프링부트 가 기본 으로 설정한 /error 가 호출됨

  • 빈 ModelAndView 반환: Exception 을 처리해서 정상 흐름 처럼 변경하는 것이 목적(뷰를 렌더링하지 않고 정상흐름 반환)
  • null 반환 : 다음 ExceptionResolver 실행, 없으면 그냥 예외발생

HandlerExcpetionResolver 등록

  • 500 에러가 400으로 반환되는 것을 볼 수 있다.

  • 하지만 예외가 발생해 WAS까지 올라가고 다시 오류페이지를 찾아 /error 호출 하는 과정은 복잡하면서도 너무나 반복적이다. ExceptionResolver 을 통해 깔끔하게 해결 가능

HanlderExceptionResolver 활용

  • 사용자 정의 예외가 터지도록 만들어준다.

  • 그것을 해결 하는 ExceptionResolver 생성

  • HttpHeader Accpet 가 JSON 이면 JSON으로 오류를 내려주고 그외의 경우에는 error/500에 있는 HTML을 보여준다

  • 똑같이 ExceptionHanlder 등록

-> 서블릿 컨테이너(WAS) 까지 예외가 올라가지 않고 여기서 다 처리 됀다.

스프링 제공 ExceptionResolver

1.ExceptionHandlerExceptionResolver
2.ResponseStatusExceptionResolver
3.DefaultHandlerExceptionResolver

  • 1,2,3 순으로 우선 순위를 갖는다.

ResponseStatusExceptionResolver

= 예외에 따라서 HTTP 상태 코드를 지정해주는 역할

처리예외

1. @ResponseStatus()

  • RuntimeExceptin(500) - > 400오류로 바꿔줌 + 메세지 설정 가능

  • 이 애노테이션을 내부적으로 보면 직접 구현했던HanlderExceptionResolver와 거의 흡사하다

  • 결국에는 내부에서 response.sendError() 형식 -> WAS /error 오류페이지 내부 호출

  • reason -> MessageSource 에서 호출기능

2.ResponseStatusException 예외

  • 개발자가 직접 변경할 수 없는 예외에 사용

DefaultHandlerExceptionResolver

  • 스프링 내부에서 발생하는 예외 처리

  • TypeMismatchException 같은 클라 잘못을 오는 예외를 자동으로 500
    ->400에러로 바꿔줌

  • 이것도 역시 내부적으로 response.sendError()방법을 사용 -> WAS 에서 /error 요청

  • 굳이 서블릿 컨테이너까지 예외가 올라가지 않고 한곳에서 처리해 불필요한 프로세스를 줄일 수 있지만 만드는 과정이 너무 복잡하다. 이를 해결하도록 스프링에서는 미리 @ExceptionHandler 를 제공 한다

@ExceptionHandler

사용이유

  • 위에 배운 예제들 HandlerExceptionResolver의 단점은 API 오류 처리시 사용하지 않는 ModelAndView를 반환 한다는점
  • HttpServletResponse 에 직접 응답 데이터를 넣어주는 불편함
  • 특정 컨트롤러에서만 발생하는 예외를 별도로 처리하기 어렵다.

API 응답 객체

@ExceptionHandler 활용

  • @ExceptionHandler는 해당 컨트룰러에만 영향을 준다.

  • ExceptionHandlerResolver 중에서 우선순위가 1임으로 예외가 ExceptionResolver로 들어올때 항상 이것부터 먼저 체크함

  • 해당 컨트룰러에서 해당예외를 처리할떄 사용함. 지정한 예외 그 자식까지 잡을 수 있음.

- @RestController 이므로 illegalExHandle() 에도 @ResponseBody 가 적용된다. 따라서 HTTP 컨버터가 사용되고, 응답이 다음과 같은 JSON으로 반환 + @ResponseStatus 가 상태코드 400으로 변경

  • 마지막 Exception (예외 최상위) 로 잡아 주면서 위에 특정한 에러를 잡는 두가지 컨트룰러를 뺀 나머지들을 모두 잡게 했다.

  • @ExceptionHandler에 예외를 생략할시에 파라미터 예외가 지정됀다.

  • ResponseEntity 를 사용해서 HTTP 메시지 바디에 직접 응답한다. 물론 HTTP 컨버터가 사용된다.ResponseEntity 를 사용하면 HTTP 응답 코드를 프로그래밍해서 동적으로 변경할 수 있다. 앞서 살펴본 @ResponseStatus 는 애노테이션이므로 HTTP 응답 코드를 동적으로 변경할 수 없다.

  • 물론 HTML 뷰로도 랜더링 가능

@ControllerAdvice

  • @ExceptionHanlder러만 사용 했을 경우 해당 컨트룰러에서 발생하는 예외만 잡는다. 전체 컨트룰러에서 공통으로 잡을 수 있게 만들어 보자

  • @ControllerAdvice 는 대상으로 지정한 여러 컨트롤러에 @ExceptionHandler , @InitBinder 기능을 부여해주는 역할을 한다.

  • @ControllerAdvice 에 대상을 지정하지 않으면 모든 컨트롤러에 적용된다. (글로벌 적용)

  • @RestControllerAdvice 는 @ControllerAdvice 와 같고, @ResponseBody 가 추가되어 있다. @Controller , @RestController 의 차이와 같다.

profile
호주쉐프에서 개발자까지..

0개의 댓글