이번에 팀 프로젝트를 진행하면서 예외를 어떻게 처리해야 할지를 고민했다. 고민한 과정과 어떻게 예외 처리를 했는지 기록한 글이다.
가장 먼저 생각했던 방법은 표준 예외를 활용하는 방법이다.
public void activateLike(){
if(this.status == LikePostStatus.ACTIVE){
throw new IllegalStateException("이미 좋아요 한 글은 좋아요가 불가능합니다.");
}
}
커스텀 예외, 표준 예외 사용의 차이점은 아래 링크 참조를 추천합니다.
https://tecoble.techcourse.co.kr/post/2020-08-17-custom-exception/
자세한 내용은 아래 링크 참조
https://bcp0109.tistory.com/303
@Getter
@AllArgsConstructor
public class CustomException extends RuntimeException{
private final ErrorCode errorCode;
@Override
public String getMessage() {
return errorCode.getDetail();
}
}
@Getter
@AllArgsConstructor
public enum ErrorCode {
/* 400 BAD_REQUEST : 잘못된 요청 */
USER_ID_NOT_THE_SAME(BAD_REQUEST, "로그인 정보[ID]가 올바르지 않습니다."),
SOCIAL_LOGIN_ID_AND_AUTH_PROVIDER_NOT_THE_SAME(BAD_REQUEST, "로그인 정보[SOCIAL_LOGIN_ID, AUTH_PROVIDER]가 올바르지 않습니다."),
REFRESH_TOKEN_NOT_FOUND(BAD_REQUEST, "쿠키에 리프레시 토큰이 존재하지 않습니다."),
...
private final HttpStatus httpStatus;
private final String detail;
}
@Slf4j
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler(value = {ConstraintViolationException.class, MethodArgumentNotValidException.class, MethodArgumentTypeMismatchException.class})
public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(Exception e){
log.error("handleMethodArgumentNotValidException throw CustomException : {}", e.getMessage());
return ErrorResponse.toResponseEntity(ErrorCode.valueOf("INVALID_INPUT_VALUE"));
}
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e){
log.error("handleHttpRequestMethodNotSupportedException throw CustomException : {}", e.getMessage());
return ErrorResponse.toResponseEntity(ErrorCode.valueOf("METHOD_NOT_ALLOWED"));
}
@ExceptionHandler(value = {CustomException.class})
public ResponseEntity<ErrorResponse> handleCustomException(CustomException e){
log.error("handleCustomException throw CustomException : {}", e.getErrorCode());
return ErrorResponse.toResponseEntity(e.getErrorCode());
}
}
예외 발생시 응답을 보낼때 사용할 dto이다.
@Getter
public class ErrorResponse {
private final LocalDateTime timestamp = LocalDateTime.now();
private final String error;
private final String message;
@Builder
private ErrorResponse(String error, String message){
this.error = error;
this.message = message;
}
public static ResponseEntity<ErrorResponse> toResponseEntity(ErrorCode errorCode){
return ResponseEntity
.status(errorCode.getHttpStatus())
.body(ErrorResponse.builder()
.error(errorCode.getHttpStatus().name())
.message(errorCode.getDetail())
.build()
);
}
}
이 방법은 커스텀 예외 클래스를 하나만 두고 메시지로 각 예외들을 구분하는 방법이다.
예외 처리 시, CustomException을던지 되 enum 클래스에 예외 코드만 입력해 주면 된다. 여럿이서 개발을 진행할 때도 enum 클래스에 있는 예외 코드를 활용하거나 필요한 예외가 없다면 enum 클래스에 추가해서 커스텀 예외를 던지면 된다.
또 ExceptionHandler로 전역에서 발생하는 예외를 잡아서 처리하기 때문에 코드에 try-catch 문을 반복해서 넣을 필요도 없다.
https://tecoble.techcourse.co.kr/post/2020-08-17-custom-exception/
https://tecoble.techcourse.co.kr/post/2020-07-28-global-exception-handler/
https://velog.io/@aidenshin/Spring-Boot-Exception-Controller
https://cheese10yun.github.io/spring-guide-exception/
https://bcp0109.tistory.com/303