스프링 애플리케이션에서 Controller 내에서 글로벌하게 발생하는 예외를 ControllerAdvice를 사용해 핸들링 할 수 있다.
우선 ControllerAdvice란?
특정 패키지나 컨트롤러, 나아가서 컨트롤러 단에서 글로벌하게 발생하는 예외를 공통적으로 처리할 수 있는 어노테이션이다.
우선 실습을 위해 컨트롤러를 생성해보자.
@RestController
@RequestMapping("/api/exception")
class ExceptionApiController {
@GetMapping("/test")
fun controllerAdvice() {
val list = mutableListOf<String>()
val temp = list[0]
}
}
일부로 예외를 내도록 빈 리스트에서 0번째 인덱스를 가져오게 해보자. IndexOutOfBoundsException exception이 발생함을 예측할 수 있다.
로컬에서 서버를 구동시킨 다음에 Postman으로 방금 작성한 api를 호출해보자.

역시나 기본 500, Internal Server Error가 발생한다. 로그를 확인해보면 전형적으로 핸들링 처리가 되지 않은 인덱스 에러가 발생함을 확인할 수 있다.

이제 ControllerAdvice 어노테이션을 사용해 발생한 위 예외를 핸들링해보자.
advice 패키지를 생성해 GlobalControllerAdvice 클래스를 생성한다
@RestControllerAdvice
class GlobalControllerAdvice {
@ExceptionHandler(value = [IndexOutOfBoundsException::class])
fun indexOutOfBoundException(e: IndexOutOfBoundsException): String {
return "Index Error 발생"
}
}
@RestControllerAdvice 어노테이션을 달아주면 해당 클래스는 ControllerAdvice의 역할을 하게 된다. @ExceptionHandler 어노테이션을 사용한다 value 안에는 따로 핸들링하고 싶은 Exception을 Array로 받는다. 여기서는 IndexOutOfBoundsException만 필요하므로 위 같이 작성해준다. Exception을 처리하고 싶다면 @ExceptionHandler를 아래와 같이 작성할 수 있다.@ExceptionHandler(value = [IndexOutOfBoundsException::class, RuntimeException::class])서버를 다시 재가동 시킨 후 똑같이 api를 호출해보자.

IndexOutOfBoundsException가 따로 핸들링 되었다. 하지만 200 응답을 내주는건 조금 이상하니까 따로 500용 ResponseEntity를 반환해서 처리해주자.
@RestControllerAdvice
class GlobalControllerAdvice {
@ExceptionHandler(value = [IndexOutOfBoundsException::class])
fun indexOutOfBoundException(e: IndexOutOfBoundsException): ResponseEntity<String>{
return ResponseEntity.internalServerError().body("Index Error 발생")
}
}
그리고 다시 호출해보면 아래와 같이 500 response code와 함께 핸들링한 메세지로 응답이 오는 부분을 확인할 수 있다.

위 처럼 글로벌하게 정의해 애플리케이션의 컨트롤러에서 발생하는 모든 예외를 처리할수도 있지만 특정 패키지나, 컨트롤러에 한해서만 에러핸들링 처리가 가능하다.
@ResControllerAdvice 어노테이션에서 basePackageClasses를 활용해주면 된다.
예시) PutApiController 내부에서만 에러 핸들링을 제한해보기
@RestControllerAdvice(basePackageClasses = [PutApiController::class])
class PutApiControllerAdvice