@Controller
또는 @RestController
가 적용된 Bean들의 예외를 catch해서 하나의 메서드에서 예외를 처리해주는 기능을 제공한다. (@Service
또는 @Repository
의 Bean의 예외는 처리X)
@ExceptionHandler
어노테이션은@ExceptionHandler(value = Exception.class)
와 같이 사용하여, value에 해당하는 예외를 잡아 처리할 수 있다.
@RestController
public class TestController {
@GetMapping("/test1")
public void test1() throws RuntimeException {
throw new RuntimeException();
}
@GetMapping("/test2")
public void test2() throws NoSuchElementException {
throw new NoSuchElementException();
}
@ExceptionHandler(value = NoSuchElementException.class)
public void test1Catch() {
System.out.println("NoSuchElementException Catched");
}
@ExceptionHandler(value = RuntimeException.class)
public void test2Catch() {
System.out.println("RuntimeException Catched");
}
}
만약 여러개의 예외를 하나의 메서드에서 처리하고 싶다면 @ExceptionHandler(value = {NoSuchElementException.class, RuntimeException.class})
와 같이 사용하는 것도 가능하다.
@ExceptionHandler
를 사용해 예외 처리를 해줌으로써, try-catch를 사용할 필요가 없어져 전체적인 코드의 가독성이 좋아지고, 중복을 줄일 수 있게 된다.
이보다 더 중복을 줄일 수는 없을까?
지금 우리가 구현한 @ExceptionHandler
는 TestController
라는 특정 컨트롤러 내에 위치한 메서드에 구현되어 있다. 이는 TestController
내에서 발생한 예외에 대한 처리만 가능함을 의미한다. 만약 다른 컨트롤러에서 같은 예외가 발생했을 때 이를 전역적으로 처리할 수 있는 방법은 없을까?
이러한 고민을 해결하기 위해 스프링은 @ControllerAdvice
라는 어노테이션을 제공하고 있다.
@ControllerAdvice
를 클래스 레벨에 선언해준 뒤, 해당 클래스 내에 @ExceptionHandler
어노테이션이 붙은 메서드를 정의해주면 @Controller
어노테이션이 있는 모든 컨트롤러에서 발생한 예외를 처리할 수 있도록 도와준다.
별 다른 속성을 설정하지 않으면 모든 패키지에 있는 컨트롤러의 예외를 담당하게 되며, 속성 설정을 통해 원하는 컨트롤러 또는 패키지만 관리하도록 설정할 수 있다.
추가적으로 @RestController
를 클래스 레벨에 선언해준 뒤, 해당 클래스 내에 @ExceptionHandler
어노테이션이 붙은 메서드를 정의해주면 RestController
어노테이션이 있는 모든 컨트롤러에서 발생한 예외를 처리할 수 있도록 도와준다.