두둥 프로젝트에서는 처리중에 에러가 발생할경우
RuntimeException 을 상속받은 DuDoongException 에서
다시 상속받아서 코드별 에러클래스를 만들고 있다.
@Getter
@AllArgsConstructor
public class DuDoongCodeException extends RuntimeException {
private BaseErrorCode errorCode;
public ErrorReason getErrorReason() {
return this.errorCode.getErrorReason();
}
}
문제는 에러코드를 Enum으로 관리하고 있는데 프로젝트의 크기가 커지면서
한곳에 모아두기엔 너무 복잡았다.
그래서 에러코드를 도메인 별로 분리하는 방법이 있어 공유하고자한다.
목차
1. 문제점
2. Enum 에 인터페이스를?
3. 적용하기
1. 문제점
@AllArgsConstructor
public enum ErrorCode {
TOO_MANY_REQUEST(429, "GLOBAL_429_1", "과도한 요청을 보내셨습니다. 잠시 기다려 주세요.");
private Integer status;
private String code;
private String reason;
public ErrorReason getErrorReason() {
return ErrorReason.builder().reason(reason).code(code).status(status).build();
}
}
두둥에서 사용하고있던 기존의 Errorcode 형식이다. 코드로 만들고, ErrorReason 객체로 리턴해줘서
클라이언트에 응답해줄때 응답값을 쉽게 만들어 놓을수 있게 설정해놨다.
작업하면서 일단 Errorcode를 한군데 모으면
물론 보기는 한눈에 보이니깐 편하다.
문제는 컨플릭트가 자주 났다.
여러 도메인에서 나눠서 작업하다보니 ErrorCode 부분에서 컨플릭트 나는 경우가 많이 생겼었다.
또한 두둥은 스웨거에서 위와 같은 방식으로 에러 코드들을 스웨거에 모여서 보여주고 있었고,
클라이언트에서는 reason을 커스텀해서 보여주고 싶다면 code 로 구분해서 에러 이유를 커스텀해서 보여주고 있었다.
즉 한군데 모아주게되면 모든 도메인들에 관련한 에러가 한군데에서 보이다보니
클라이언트 입장에선 더 불편하게 되는 그런 상황이였다.
도메인별로 ErrorCode 이넘을 새로 만들면 되는문제이지만,
GlobalException 핸들러에서 에러 코드들을 일일히 기술해야 하는 문제가 있었다.
하지만 이넘은 상속이 불가능 하므로 인터페이스로 받는 방식을 생각해냈다.
2. Enum에 인터페이스를?
이펙티브 자바 item 38 번
확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라 라는 내용이있다.
public interface Operation {
double apply(double x, dobule y);
}
public enum BasicOperation implements Operation {
PLUS("+"){
public double apply(double x, dobule y) {return x + y; }
}
}
public enum ExtendedOperation implements Operation {
EXP("^"){
public double apply(double x, dobule y) {return Math.pow(x ,y ); }
}
}
출처 : 이펙티브 자바 p.233
위 내용처럼 인터페이스를 선언한뒤에 다형성을 사용해서
사용하는 부분에서는 Operation 인터페이스만 바라보도록 할 수 있다.
두둥에서도 위 방식을 사용해서 에러코드들을 도메인 별로 분리 했다.
3. 적용하기
public interface BaseErrorCode {
public ErrorReason getErrorReason();
}
//ErrorReason
@Getter
@Builder
public class ErrorReason {
private final Integer status;
private final String code;
private final String reason;
}
BaseErrorCode 라는 인터페이스를 만들었다.
@Getter
@AllArgsConstructor
public enum GlobalErrorCode implements BaseErrorCode {
TOO_MANY_REQUEST(429, "GLOBAL_429_1", "과도한 요청을 보내셨습니다. 잠시 기다려 주세요.");
private Integer status;
private String code;
private String reason;
@Override
public ErrorReason getErrorReason() {
return ErrorReason.builder().reason(reason).code(code).status(status).build();
}
// Event 도메인 관련
@Getter
@AllArgsConstructor
public enum EventErrorCode implements BaseErrorCode {
오버라이딩 해서 각 도메인별 에러코드를 구현 해준다.
// 에러 발생 예시
throw TicketItemQuantityLackException.EXCEPTION; // DuDoongCodeException 상속
// ExceptionHandler
@ExceptionHandler(DuDoongCodeException.class)
public ResponseEntity<ErrorResponse> DuDoongCodeExceptionHandler(
DuDoongCodeException e, HttpServletRequest request) {
BaseErrorCode code = e.getErrorCode();
ErrorReason errorReason = code.getErrorReason();
ErrorResponse errorResponse =
new ErrorResponse(errorReason, request.getRequestURL().toString());
return ResponseEntity.status(HttpStatus.valueOf(errorReason.getStatus()))
.body(errorResponse);
}
ExceptionHandler 부분에서 CodeException 을 잡아준고,
클라이언트 한테 응답을 돌려준다.
위 방식을 이용해서, 이넘에 인터페이스를 사용해서 다형성을 적용해 보았다.
도메인(어그리게이트) 별로 에러코드가 분리가 되어서, 컨플릭트 날일도 없이 편하게 작업할 수 있었다.
해당 소스들은 두둥 프로젝트에서 볼 수있다.
'스프링' 카테고리의 다른 글
[스프링] spring swagger 같은 코드 여러 에러 응답 예시 만들기 (3) | 2023.03.06 |
---|---|
[스프링] 공통 응답 형식 만들기 ResponseBodyAdvice (0) | 2023.03.05 |
[스프링] spring swagger api 하나만 인증 풀기 (2) | 2023.03.05 |
[스프링] spring open api swagger basic auth 세팅 (1) | 2023.03.04 |
[스프링] Spring disable Aop in test (1) | 2023.01.23 |