이번주 정리 내용이 좀 길어져서 분리했다.
이전 글 링크
웹 서비스 어플리케이션에서는 외부에서 들어오는 요청에 담긴 데이터를 처리하는 경우가 많다. 그 과정에서 예외가 발생하면 예외를 복구하여 정상으로 처리하기보다는 요청을 보낸 클라이언트에 어떤 문제가 발생했는지를 전달하는 경우가 더 많다.
예외가 발생했을 때 클라이언트에 오류를 전달하려면 결국 컨트롤러로 전달해야 한다. 스프링 부트에서는 크게 두 가지의 방식으로 처리한다.
@RestControllerAdvice
와 @ExceptionHandler
을 이용해 모든 컨트롤러의 예외 처리@ExceptionHandler
을 이용해 특정 컨트롤러의 예외 처리@RestControllerAdvice
public class CustomExceptionHandler {
...
@ExceptionHandler(value = RuntimeException.class)
public ResponseEntity<?> handleException(RuntimeException e, HttpServletRequest request) {
...
}
...
}
위처럼 클래스에 @RestControllerAdvice
를 지정해주면 @Controller
나 @RestController
에서 발생하는 예외를 한 곳에서 관리하고 처리 가능하며, 패키지를 지정해서 예외를 관제하는 범위를 지정할 수도 있다. 그리고 @ExceptionHandler
에 처리하고 싶은 예외를 value
로 지정해주면 지정한 예외를 처리하는 메서드가 정의된다.
이렇게 구현하면 지정한 예외가 발생했을 때 응답으로 예외 처리 메소드를 실행하여 얻은 결과를 전달한다. 특정 컨트롤러에서만 예외를 처리하고 싶다면 @ExceptionHandler
가 달려 있는 메소드를 그 컨트롤러에 정의하면 된다.
동일하게 핸들러 메소드가 선언된 상태라면 조금 더 구체적인 예외 클래스가 선언된 쪽이 더 높은 우선순위를 갖게 된다. 다른 경우로는 글로벌 예외 처리와 컨트롤러 내의 예외 처리가 있을텐데, 이 경우에는 범위가 좁은 컨트롤러의 핸들러 메소드가 우선순위를 가진다.
예외를 상속받고 별도의 메세지를 지정해주는 방식으로 커스텀 예외를 정의하여 사용할 수 있다. 예를 들으면 다음과 같다.
public class CustomException extends RuntimeException {
private HttpStatus httpStatus;
private ErrorCode errorCode;
private String description;
public CustomException(ErrorCode errorCode) {
this.errorCode = errorCode;
this.httpStatus = errorCode.httpStatus;
this.description = errorCode.description;
}
}
public enum ErrorCode {
NOT_ENOUGH_MONEY(HttpStatus.BAD_REQUEST, "잔액이 부족합니다.");
// 이외에 다양한 에러들을 정의하여 사용
private HttpStatus httpStatus;
private String description;
}
다른 기본 구현된 예외를 던지는 것과 같은 방법으로 예외 처리하고 싶은 부분에 throw new CustomException()
을 작성해주면 된다.