[GGB] error 처리 공통화

Kim Hyen Su·2024년 7월 24일

GGB

목록 보기
11/13

# GGB # TIL # REVIEW


개요

프로젝트 개발 단계에 들어가기 전에 응답에 대한 공통 양식을 설정하기 위해 개발된 팀 코드를 리뷰하기 위해서 해당 포스팅을 작성하였습니다.

ApiResponse

정상/예외 응답 형식을 형식화 하기 위한 클래스입니다. 내부 코드는 다음과 같습니다.

@Getter
public class ApiResponse<T> {
    @Nonnull
    private final String code;
    @Nonnull
    private final String message;
    private final T data;

    private ApiResponse(@Nonnull String code, @Nonnull String message, T data){
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static <T> ApiResponse<T> ok() {
        return ok(null);
    }

    public static <T> ApiResponse<T> ok(T data) {
        return new ApiResponse<T>(HttpStatus.OK.name(), HttpStatus.OK.getReasonPhrase(), data);
    }

    public static <T> ApiResponse<T> error(ApiErrorType errorType) {
        return error(errorType.name(), errorType.getMessage());
    }

    public static <T> ApiResponse<T> error(String code, String message) {
        return new ApiResponse<T>(code, message, null);
  • code : 응답 type 이름
  • message : 응답 메시지
  • data : 응답 본문
  • static ok() : 정상 응답을 위한 정적 메서드
  • static error() : 예외 응답을 위한 정적 메서드

정상 또는 예외 응답 시 정형화된 양식으로 응답하고자 위와 같이 정의되어 있습니다.

ApiErrorType

예외 응답의 경우, 발생하는 예외에 따라서 메시지나 코드를 다르게 응답해줘야 합니다. 이를 위해서 '예외 타입' 이라는 enum으로 관리를 해줍니다.

@Getter
public enum ApiErrorType {

    BAD_REQUEST("잘못된 요청입니다."), // 400
    UNAUTHENTICATED("인증이 실패하였습니다."), // 401
    FORBIDDEN("권한이 없습니다."), // 403
    INTERNAL_SERVER_ERROR("서버에서 에러가 발생하였습니다."); // 500

    private final String message;

    ApiErrorType(String message) {
        this.message = message;
    }
}

기본적으로 400,401,403,500 이라는 오류 응답에 대한 타입들을 정의한 코드입니다. 하지만, 실제로 이를 사용하기 위해서는 각 구현부에 try~catch를 사용하여 실제 발생한 예외를 잡고 ApiResponse에 error() 메서드를 통해 일정한 양식으로 예외 응답을 수행하도록 해줘야 합니다.

하지만, 각 응답하는 곳마다 해당 방식으로 처리하기에는 중복된 코드의 량이 많아집니다. 이를 일괄적으로 공통된 양식으로 처리하기 위해서 ControllerAdvice + ExceptionHandler 를 활용해주면 됩니다.

GlobalExceptionHandler

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 잘못된 요청 경우 예외 처리
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<?> handler(BusinessException e) {
        return ApiResponse.error(e.getCode(), e.getMessage());
    }
    ...
}

RestControllerAdvice를 통해서 controller 전역에 처리가 가능하며, ExceptionHandler를 사용하여 예외를 핸들링 해줄 수 있습니다. 위 예시는 BusinessException 예외 발생 시, 해당 ExceptionHandler에서 catch하여 하위 메서드를 수행해줍니다.

메서드 내부에 ApiResponse 클래스의 error() 메서드를 활용하여 형식화된 응답처리가 가능합니다.

BusinessException

@Getter
public class BusinessException extends RuntimeException {

    private final String code;

    public BusinessException() {
        super(ApiErrorType.BAD_REQUEST.getMessage());
        this.code = ApiErrorType.BAD_REQUEST.name();
    }

    public BusinessException(final String message) {
        super(message);
        this.code = ApiErrorType.BAD_REQUEST.name();
    }

    public BusinessException(final String code, final String message) {
        super(message);
        this.code = code;
    }
}

커스텀 예외 정의 시 RuntimeException을 상속받습니다. 생성자 식 내부에 ErrorType 내 정의한 메시지는 RuntimeException 생성자로 전달하며, code는 Exception class 내부에 정의하여 응답 시 ApiResponse 클래스에 전달해줍니다.

마무리

이와 같이 응답처리를 형식화하는 과정에 대해서 기록했습니다. 해당 내용들은 프로젝트 팀원분의 코드를 기반으로 작성되었으며, 정갈한 코드라고 생각되어 리뷰 겸 기록하였습니다. 위 과정을 통해서 공통 예외 처리 및 성공 처리에 대한 틀을 생각해볼 수 있었습니다.

profile
백엔드 서버 엔지니어

0개의 댓글