API는 각 시스템마다
따라서, 세밀한 예외처리가 필요하다
BasicErrorController는 웹 브라우저에서 html 화면을 통해 예외 정보를 전달할 땐 편리하지만, api 응답으론 어렵다.
이런 문제를 어떻게 해결하지?!?!?
스프링이 이 귀찮은 작업을 대신 구현해두었다
스프링 부트가 기본으로 제공하는 ExceptionResolver 중
가장 우선순위가 높은 ExceptionHandlerExceptionResolver
가 이 귀찮음을 대신 해준다
ExceptionHandlerExceptionResolver
은 @ExceptionHandler 을 처리해주는 리졸버이다.
@ExceptionHandler같은 경우는 @Controller
, @RestController
가 적용된 Bean내에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능을 한다.
@ResponseStatus(HttpStatus.BAD_REQUEST) // 200 ok로 내려가지 않도록 추가
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandler(IllegalArgumentException e) { // 이 예외의 자식까지 처리해줌
//ExceptionHandlerExceptionResolver가 처리해줌
log.error("[exceptionHandler] e", e);
return new ErrorResult("BAD", e.getMessage()); // json만들어서 반환해줌
// @ResponseStatus 설정 없으면, 정상흐름(200ok)으로 바꿔서 응답 내려감.
// 서블릿 컨테이너로 에러가 올라가지 않고, 여기서 흐름 끝남
}
이 예시를 살펴보면,
IllegalArgumentException
이 발생할 경우,
참고할 점은
만약 @ExceptionHandler만 있다면, 컨트롤러 내에서 정상흐름과 예외처리 흐름을 동시에 관리해야 한다.
이것을 분리해서 관리할 수 없을까?!?!
이런 니즈를 해결하려면, @ControllerAdvice
, @RestControllerAdvice
(=@ControllerAdvice+@ResponseBody) 를 사용해야 한다.
위 어노테이션은
대상으로 지정한 여러 컨트롤러에 @ExceptionHandler, @InitBinder 기능을 부여한다
대상으로 지정하는 방법은 3가지가 있다
@ControllerAdvice에 대상을 지정하지 않으면, 전체 컨트롤러에 적용된다.(글로벌함)
@RestControllerAdvice(basePackages = "burrito.exception.api") // 대상을 지정하지 않으면, 모든 컨트롤러에 적용됨
//@ControllerAdvice(annotations = RestController.class) // 1. RestController 만 적용
//@ControllerAdvice("burrito.example.controllers") // 2. 특정 패키지와 그 하위에 적용
//@ControllerAdvice(assignableTypes = {MenuController.class,OrderController.class}) // 3. 특정 컨트롤러 클래스 지정
public class BurritoExceptionControllerAdvice {
@ResponseStatus(HttpStatus.BAD_REQUEST) // 200 ok로 내려가지 않도록 추가
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandler(IllegalArgumentException e) { // 이 예외의 자식까지 처리해줌
//ExceptionHandlerExceptionResolver가 처리해줌
log.error("[exceptionHandler] e", e);
return new ErrorResult("BAD", e.getMessage()); // json만들어서 반환해줌
// @ResponseStatus 설정 없으면, 정상흐름(200ok)으로 바꿔서 응답 내려감.
// 서블릿 컨테이너로 에러가 올라가지 않고, 여기서 흐름 끝남
}
@ExceptionHandler
public ResponseEntity<ErrorResult> userExHandler(UserException e) { // 이 예외의 자식까지 처리해줌
//ExceptionHandlerExceptionResolver가 처리해줌
log.error("[exceptionHandler] e", e);
ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST);
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 200 ok로 내려가지 않도록 추가
@ExceptionHandler // 예외 잡는 범위는 이 컨트롤러 한정임
public ErrorResult exHandler(Exception e) { // 구체적으로 처리되지않은 예외들이 여기서 처리됨
//ExceptionHandlerExceptionResolver가 처리해줌
log.error("[exceptionHandler] e", e);
return new ErrorResult("EX", "내부 오류"); //반환타입이 거의 컨트롤러 급으로 여러 가지임
}
}
IllegalArgumentException
이 발생하면 어떻게 될까?