프로젝트 중 @RestControllerAdvice 를 이용한 전역적인 예외 처리를 구현해봤다.
그를 토대로 작성했다.
ErrorCode 클래스
@Getter
@RequiredArgsConstructor
public enum ErrorCode {
// 토큰 전달 하지 않은 경우, 정상 토큰이 아닌 경우
INVALID_TOKEN(HttpStatus.BAD_REQUEST, "토큰이 유효하지 않습니다."),
// 토큰이 있으며 유효한 토큰이나, 해당 사용자의 게시글/댓글이 아닌 경우 (즉, 해당 사용자의 토큰이 아닌 경우)
AUTHORIZATION(HttpStatus.BAD_REQUEST, "작성자만 삭제/수정할 수 있습니다."),
// DB 에 이미 존재하는 username 으로 회원가입 요청한 경우
DUPLICATED_USERNAME(HttpStatus.BAD_REQUEST, "중복된 username 입니다"),
// 로그인 시, username 과 password 중 일치하지 않는 정보가 있을 경우
NOT_MATCH_INFORMATION(HttpStatus.BAD_REQUEST, "회원을 찾을 수 없습니다."),
// DB 에 해당 게시글이 존재하지 않는 경우
NOT_FOUND_BOARD(HttpStatus.BAD_REQUEST, "게시글을 찾을 수 없습니다."),
// DB 에 해당 댓글이 존재하지 않는 경우
NOT_FOUND_COMMENT(HttpStatus.BAD_REQUEST, "댓글을 찾을 수 없습니다."),
// admin 계정으로 회원가입 시, ADMIN_TOKEN 과 일치하지 않을 경우
NOT_MATCH_ADMIN_TOKEN(HttpStatus.BAD_REQUEST, "관리자 암호가 일치하지 않습니다.");
private final HttpStatus httpStatus;
private final String message;
}
Client 에게 보내줄 에러 코드를 한 곳에서 관리할 수 있다.
원하는 에러 및 메시지를 직접 추가해주면 된다.
Enum Class 임에 주의!
에러 이름 : INVALID_TOKEN, AUTHORIZATION ...
HTTP 상태 : HttpStatus.BAD_REQUEST
HTTP 메시지 : "토큰이 유효하지 않습니다." ...
ErrorResponse 클래스
@Getter
@RequiredArgsConstructor
public class ErrorResponse {
private final int statusCode;
private final String message;
public ErrorResponse(ErrorCode errorCode) {
this.statusCode = errorCode.getHttpStatus().value();
this.message = errorCode.getMessage();
}
}
에러 응답 클래스
get 메서드를 이용하여, ErrorCode 클래스에서 정의해둔 각 예외들을 가져온다
.value() 쓰는 이유
CustomException 클래스
@Getter
@RequiredArgsConstructor
public class CustomException extends RuntimeException {
private final ErrorCode errorCode;
}
발생한 예외를 처리해줄 예외 클래스
언체크 예외(RuntimeException 런타임 예외)를 상속받는다
언체크 예외를 상속받은 이유?
이유 1.
일반적인 비지니스 로직들은 따로 catch해서 처리할 것이 없기 때문이다.
만약 체크 예외로 할 경우, 불필요하게 throws가 전파될 것이다.이유 2.
Spring 은 내부적으로 발생한 예외를 확인해서, '언체크 예외' or '에러' 일 경우, 자동으로 롤백시키도록 처리한다.
반면, '체크 예외' 의 경우에만 롤백을 하지 않는다. (체크 예외는 처리가 강제되므로, 개발자가 무언가를 처리할 것이라는 기대 때문이다)
GlobalExceptionHandler 클래스
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
// 직접 정의한 CustomException 에러 클래스에 대한 예외 처리
@ExceptionHandler({CustomException.class})
public ResponseEntity<ErrorResponse> handleAllException(CustomException 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));
}
}
ResponseEntityExceptionHandler
spring 예외를 미리 처리해둔 추상 클래스
만약 이 추상 클래스를 상속받지 않는다면, Spring 예외들은 DefaultHandlerExceptionResolver가 처리하게 된다.
@RestControllerAdvice
@ExceptionHandler
참고: Spring 전역 예외 처리: @RestControllerAdivce 적용
참고: [Spring] @RestControllerAdvice를 이용한 Spring 예외 처리 방법 - (2/2)