[에러처리101] Enum으로 에러코드 관리하기

Waldo·2020년 11월 19일
5
post-thumbnail

1. 첫번째 니즈는 에러를 공통된 포맷으로 return해주고 싶다

2. 두번째 니즈는 에러 코드를 기록해두고 각각의 usage를 관리하고 싶다

일단 첫번째는 어떤 방식으로든 return할 공통 error class를 선언하면 해결될 일이었습니당
그러나, 두번째 니즈를 위해서 class에 Enum을 활용하게 되었습니다!

(application에서 공통으로 사용될 각종 코드들에도 동일하게 적용이 가능한 부분이라서 추가포스팅!)

DB 대신 Enum으로 공통코드 처리하기 😄

ErrorResponse class 생성

  • 동일한 포맷으로 Error를 return하기 위한 class를 생성

    요기서 포인트는 ErrorCode라는 Enum을 받아서 해당 class를 생성하도록 구현!
    해당 Enum을 통해서 제각각 생성될 수 있는 Error를 한정된 Enum들로 한눈에 보도록 함

@Getter
@Setter
@NoArgsConstructor
public class ErrorResponse {

  private String message;
  private String code;
  private int status;
  private String detail;

  public ErrorResponse(ErrorCode code) {
    this.message = code.getMessage();
    this.status = code.getStatus();
    this.code = code.getCode();
    this.detail = code.getDetail();
  }

  public static ErrorResponse of(ErrorCode code) {
    return new ErrorResponse(code);
  }
}

공통 Enum의 관리를 위한 Interface 생성

  • 에러코드뿐 아니라 공통적인 interface를 가지고 Enum을 관리하기 위해서 생성
public interface EnumModel {

  String getKey();
  String getValue();

}

EnumModel Interface를 구현한 ErrorCode class 생성

  • 요러캐 EnumType으로 공통 정의할 수 있는 아이들을 구조화 했습니당
    각각의 case들을 나눠서 Enum 자체를 미리 정의해두었습니당!
    다시 보니까 굳이 http status를 또 넣을 필요가 있나 싶기는 하네요...
    무튼, 아래와 같은 값들이 주로 필요할 듯 하여 구성해 두었습니다
    status(HttpStatus),
    code(자체 정의한 ErrorCode),
    messsage(Error메시지),
    detail(저는 추가정보 또는 e.getMessage 값)
@RequiredArgsConstructor
@Getter
@JsonFormat(shape = Shape.OBJECT)
public enum ErrorCode implements EnumModel {

  // COMMON
  INVALID_CODE(400, "C001", "Invalid Code"),
  RESOURCE_NOT_FOUND(204, "C002", "Resource not found"),
  EXPIRED_CODE(400, "C003", "Expired Code"),
  
  // AWS
  AWS_ERROR(400, "A001", "aws client error");

  private int status;
  private String code;
  private String message;
  private String detail;

  ErrorCode(int status, String code, String message) {
    this.status = status;
    this.message = message;
    this.code = code;
  }

  @Override
  public String getKey() {
    return this.code;
  }

  @Override
  public String getValue() {
    return this.message;
  }
}

그럼 어떻게 사용하냐!?

  1. 아래 글에서 말한 것처럼 스프링 부트에서 ControllerAdvice를 사용해서 일괄적으로 Exception을 handling하는 부분에는 요런 코드로 바로 return에 사용할 수 있어용😎
  @ExceptionHandler(value = Exception.class)
  @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  protected ResponseEntity<ErrorResponse> handleException(Exception e) {
    ErrorResponse response = ErrorResponse.of(ErrorCode.TEMPORARY_SERVER_ERROR);
    response.setDetail(e.getMessage());
    return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
  }
  1. Business로직 상으로 Error를 일으키는 경우들에는 RuntimeException을 상속한 CustomException을 만들어서 처리!

    Tip. 왜 RuntimeException일까요?
    RuntimeException은 말그대로 실행단계에 발생하는 UncheckedException으로, 비지니스로직 상 발생 시키고 싶은 경우(=Rollback이 필요) 해당 Exception을 상속받아서 구현하는 것이 맞음!
    트랜젝션 중에 Exception이 발생 시, Rollback을 수행하기 때문이죵

public class CustomException extends RuntimeException {

  private ErrorCode errorCode;

  public CustomException(String message, ErrorCode errorCode) {
    super(message);
    this.errorCode = errorCode;
  }

  public CustomException(ErrorCode errorCode) {
    super(errorCode.getMessage());
    this.errorCode = errorCode;
  }

  public ErrorCode getErrorCode() {
    return this.errorCode;
  }
}
  • 위 처럼 구현한 CustomException은 Controller/Service단에서 비지니스로직을 구현하면서 Exception이 필요할 때 편하게 던져줍니당! (짜피 ExceptionHandler에서 잡아주니께 🤗)
  @Transactional
  public UserDto.EmailResponse sendVerification(String userId) {
    Optional<User> user = this.userRepository.findByUserId(userId);
    if (user.isPresent()) {
      throw new CustomException("Duplicated Email", ErrorCode.USER_EXISTS);
    }else{
    	//blah blah....
    }
  }

이제 기본적으로 Controller/Service단에서 일어날 수 있는 Exception들과
로직으로 정의 할 수 있는 Error들은 CustomException class를 만들어서 구조화하고
Enum을 사용해서 ErrorCode를 한눈에 관리 할 수 있는 부분까지 DONE!

다음 번에는 Filter에서 Error가 나는 경우에 대해서 다뤄보려고 합니다!
(Filter의 경우에는 RestControllerAdvice에서 처리하지 못하기 때문이죠 😟)

이전글

스프링부트에서 공통 에러 처리하기

다음글

스프링부트 Filter에서 Exception나는 경우 공통 처리하기 😄
DB 대신 Enum으로 공통코드 처리하기 😄

profile
늘어가는 연차, 애매해진 경력을 딛고 하고 싶은 거 다 하고 싶은 (아직은) 백엔드 개발자

2개의 댓글

comment-user-thumbnail
2021년 1월 17일

글잘읽었습니다. 그럼 성공관련 코드는 SuccessCode / SuccessResponse로 만들면되겠죵?

답글 달기
comment-user-thumbnail
2022년 12월 15일

좋은 글 감사합니다~ㅎㅎ

답글 달기