@ControllerAdvice
와 @RestControllerAdvice
어노테이션을 제공하고 있다.@ControllerAdivce
는 여러 컨트롤러에 대해 전역적으로 ExceptionHandler를 적용해준다. 다음과 같이 에러를 핸들링하는 클래스를 만들어 어노테이션을 붙여주면 에러 처리를 위임할 수 있다.@RestControllerAdvice
의 basePackages 등을 설정함으로써 제한할 수 있다@RestControllerAdvice
는 @ControllerAdvice
와 달리 @ResponseBody
가 붙어 있어 응답을 Json으로 내려준다.@ControllerAdvice
클래스가 이를 상속받게 하면 된다.@RestControllerAdvice // 전역 예외 처리
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { // 추상 클래스 상속
// NotFoundAccountException 발생시 에러 처리
@ExceptionHandler(NotFoundAccountException.class)
public ResponseEntity<?> handleNotFoundEntity(NotFoundException e) {
return handleExceptionInternal(e.getExceptionCode());
}
}
ResponseEntityExceptionHandler의 handleExceptionInternal() 메소드를 오버라이딩하여 응답을 커스터마이징할 수 있다.
ExceptionHandlerExceptionResolver: 에러 응답을 위한 Controller나 ControllerAdvice에 있는 ExceptionHandler를 처리함
@ExceptionHandler
가 있는지 검사한다.@ExceptionHandler
에서 처리가 가능하다면 처리하고, 그렇지 않으면 ControllerAdivce로 넘어간다.@ExceptionHandler
가 있는지 검사하고, 없으면 다음 처리기로 넘어간다.ResponseStatusExceptionResolver: Http 상태 코드를 지정하는 @ResponseStatus
또는 ResponseStatusException를 처리함
@ResponseStatus
가 있는지 또는 ResponseStatusException인지 검사한다.DefaultHandlerExceptionResolver: 스프링 내부의 기본 예외들을 처리한다.
적합한 ExceptionResolver가 없으므로 예외가 서블릿까지 전달되고, 서블릿은 SpringBoot가 진행한 자동 설정에 맞게 BasicErrorController로 요청을 다시 전달한다.
@RestControllerAdvice
를 사용하겠다.@Getter
@RequiredArgsConstructor
public enum ErrorCode {
/*
* 400 BAD_REQUEST: 잘못된 요청
*/
BAD_REQUEST(HttpStatus.BAD_REQUEST, "Invalid request."),
/*
* 401 UNAUTHORIZED: 인증되지 않은 사용자의 요청
*/
UNAUTHORIZED_REQUEST(HttpStatus.UNAUTHORIZED, "Unauthorized."),
/*
* 403 FORBIDDEN: 권한이 없는 사용자의 요청
*/
FORBIDDEN_ACCESS(HttpStatus.FORBIDDEN, "Forbidden."),
/*
* 404 NOT_FOUND: 리소스를 찾을 수 없음
*/
NOT_FOUND(HttpStatus.NOT_FOUND, "Not found."),
/*
* 405 METHOD_NOT_ALLOWED: 허용되지 않은 Request Method 호출
*/
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "Not allowed method."),
/*
* 500 INTERNAL_SERVER_ERROR: 내부 서버 오류
*/
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Server error.");
private final HttpStatus httpStatus;
private final String message;
}
@Getter
public class ErrorResponse {
private final LocalDateTime timestamp = LocalDateTime.now();
private final int statusCode;
private final String error;
private final String message;
public ErrorResponse(ErrorCode errorCode) {
this.statusCode = errorCode.getHttpStatus().value();
this.error = errorCode.getHttpStatus().name();
this.message = errorCode.getMessage();
}
}
@Getter
@RequiredArgsConstructor
public class RestApiException extends RuntimeException {
private final ErrorCode errorCode;
}
@ExceptionHandler
: 발생한 특정 예외를 잡아 하나의 메소드에서 공통 처리할 수 있게 한다.@RestControllerAdvice
: 프로젝트 전역에서 발생하는 모든 예외를 잡아준다.@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
/*
* Developer Custom Exception: 직접 정의한 RestApiException 에러 클래스에 대한 예외 처리
*/
@ExceptionHandler(RestApiException.class)
protected ResponseEntity<ErrorResponse> handleCustomException(RestApiException ex) {
ErrorCode errorCode = ex.getErrorCode();
return handleExceptionInternal(errorCode);
};
// handleExceptionInternal() 메소드를 오버라이딩해 응답 커스터마이징
private ResponseEntity<ErrorResponse> handleExceptionInternal(ErrorCode errorCode) {
return ResponseEntity
.status(errorCode.getHttpStatus().value())
.body(new ErrorResponse(errorCode));
}
}
Controller에서 무조건 BAD_REQUSEST 응답을 내리는 코드를 작성해보았다.
@RestController
@RequestMapping("/api")
public class NaverLoginApiController {
@GetMapping("/test")
public ResponseEntity<?> test() {
throw new RestApiException(ErrorCode.BAD_REQUEST);
}
}
Postman에서 GET "http://localhost:8080/api/test" 실행,
https://mangkyu.tistory.com/204
https://bcp0109.tistory.com/303
https://velog.io/@evelyn82ny/Exception-handling-using-RestControllerAdvice