코드를 작성하다보면, Exception을 throw 해야 하는 경우가 발생한다! 이 때, 예외를 제대로 처리하지 않으면 WAS까지 예외가 전달이 되고, 아래와 같은 에러가 반환된다
{
"timestamp": "2024-06-05T10:36:30.084+00:00",
"status": 400,
"error": "Bad Request",
"exception": "java.lang.IllegalArgumentException",
"path": "/api/members/bad"
}
이런 에러를 반환하지 않고, API 단에서 바로 처리해서 정상 응답
이 될 수 있도록 하는 방법이 있다
스프링이 API 예외 처리 문제를 해결하기 위해 제공하는 기능으로, @ExceptionHandler 어노테이션을 통해 사용할 수 있다.
@ExceptionHandler
어노테이션을 선언하고, 해당 컨트롤러에서 처리하고 싶은 예외를 지정해주면 된다!
해당 컨트롤러 내에서 예외가 발생하면 이 메소드가 호출된다
예시 코드
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExceptionHandler(IllegalArgumentException e) {
return new ErrorResult("BAD", e.getMessage());
}
예외 발생 시, 응답되는 값
{
"code": "BAD",
"message": "잘못된 입력 값"
}
@ExceptionHandler({AException.class, BException.class})
public ErrorResult illegalExceptionHandler(Exception e) {
return new ErrorResult("BAD", e.getMessage());
}
다양한 예외를 한 번에 처리할 수 있다
@ExceptionHandler
public ErrorResult illegalExceptionHandler(IllegalArgumentException e) {
return new ErrorResult("BAD", e.getMessage());
}
메소드의 파라미터
로 예외를 지정할 수 있기 때문에, @ExceptionHandler
어노테이션에서 예외를 생략할 수 있다
@ExceptionHandler
public ResponseEntity<ErrorResult> userHandler(UserException e) {
ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
return new ResponseEntity(errorResult, HttpStatus.BAD_REQUEST);
}
컨트롤러의 파라미터 응답처럼 다양한 파라미터와 응답을 지정할 수 있다!
자세한 내용은 아래의 공식 문서에서 확인할 수 있다
ExceptionHandler 공식 문서 🔍
스프링의 우선순위는 항상 자세한 것
이 우선순위를 가진다.
@ExceptionHandler(부모예외.class)
public String 부모예외처리()(부모예외 e) {}
@ExceptionHandler(자식예외.class)
public String 자식예외처리()(자식예외 e) {}
예를 들어, 위와 같이 부모 클래스
와 자식 클래스
의 예외 처리를 지정해두었고
자식 클래스 예외가 발생한다면 부모 예외처리
와 자식 예외처리
둘 다 호출 대상이 되지만, 둘 중 더 자세한 것이 우선권을 가지기 때문에 자식 예외처리
가 호출된다!
물론, 예외처리가 작성되지 않은 자식 클래스는 부모 예외처리
가 호출된다
@ExceptionHandler
를 사용하면, 예외를 깔끔하게 처리할 수는 있지만 컨트롤러 클래스 내에 정상 코드와 예외 코드가 섞여 있다.@ControllerAdvise
와@RestControllerAdvise
를 사용한다면 정상 코드와 예외 코드를 분리할 수 있다
Controller
와 RestController
의 차이와 같다.
@RestControllerAdvise
에는 내부에 ResponseBody
어노테이션이 붙어 있다
ExceptionHandler
, InitBinder
기능을 부여해준다//컨트롤러 어노테이션이 붙은 클래스를 대상으로 함
@ControllerAdvise(annotations = Controller.class)
public class ExampleAdvise {}
//해당 패키지에 있는 클래스를 대상으로 함
@ControllerAdvise("com.heesue.controllers")
public class ExampldAdvise {}
//직접 컨트롤러를 지정함
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice {}
아래의 공식 문서를 통해 더 자세히 알아볼 수 있다
ControllerAdvise 공식 문서 🔍
@ExceptionHandler
와 @ControllerAdvise
를 조합하여, 예외를 깔끔하게 해결할 수 있다!