[TIL] API 응답 예외처리

정석·2024년 9월 7일

TIL

목록 보기
35/40
post-thumbnail

다른 팀에서 구현한 예외처리를 보고 학습했다.
여러 API 로 생기는 각각의 예외를 일관성 있게 관리할 수 있는 방식을 알아본다.

1. API Response 클래스 생성

Controller 레이어에서 성공 state 를 반환할 때, ResponseEntity로 바로 반환하지 않고 반환 값을 커스텀할 수 있게 만들어지는 클래스로 제네릭 변수를 받아 dto 의 값을 status code 와 함께 반환할 수 있도록 한다.

@Getter
@AllArgsConstructor
@JsonPropertyOrder({"isSuccess", "code", "message", "result"})
public class ApiResponse<T> {
    @JsonProperty("isSuccess")
    private final Boolean isSuccess;
    private final String code;
    private final String message;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private final T result;

    // 일반적인 응답 생성
    public static <T> ApiResponse<T> onSuccess(T result) {
        return new ApiResponse<>(true, SuccessStatus._OK.getCode(), SuccessStatus._OK.getMessage(), result);
    }

    public static <T> ApiResponse<T> of(BaseCode code, T result) {
        return new ApiResponse<>(true, code.getReasonHttpStatus().getCode(), code.getReasonHttpStatus().getMessage(), result);
    }
    
     // 실패한 경우 응답 생성
    public static <T> ApiResponse<T> onFailure(String code, String message, T data) {
        return new ApiResponse<>(false, code, message, data);
    }

    public static ApiResponse<String> onFailure(BaseErrorCode baseErrorCode) {
        return new ApiResponse<>(false, baseErrorCode.getReasonHttpStatus().getCode(), baseErrorCode.getReasonHttpStatus().getMessage(), "null");
    }

JsonPropertyOrder : Json 출력 시 필드의 순서를 지정할 수 있음.

JsonProperty : Json 출력 시 원하는 이름으로 표기


2. ApiException 클래스

Getter
@RequiredArgsConstructor
public class ApiException extends RuntimeException {

    private final BaseErrorCode errorCode;

}

BaseErrorCode 객체를 주입받아 생성될 수 있도록 만든다.

3. BaseErrorCode 인터페이스

public interface BaseErrorCode {
    public ErrorReasonDto getReasonHttpStatus();
}

안에서 오류 상태 코드를 불러오도록하며 ErrorReasonDto 객체로 전달하는 인터페이스 생성

4. BaseErrorCode 로 구현된 ErrorStatus 클래스


@Getter
@AllArgsConstructor
public enum ErrorStatus implements BaseErrorCode {
    _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "500", "Interval server error"),
    _BAD_REQUEST(HttpStatus.BAD_REQUEST,"400","잘못된 요청입니다."),
    _BAD_REQUEST_EMAIL(HttpStatus.BAD_REQUEST,"400","중복된 이메일입니다."),
    _BAD_REQUEST_USER(HttpStatus.BAD_REQUEST,"400","탈퇴한 회원입니다."),
    
    private HttpStatus httpStatus;
    private String code;
    private String message;

    @Override
    public ErrorReasonDto getReasonHttpStatus() {
        return ErrorReasonDto.builder()
                .code(code)
                .message(message)
                .isSuccess(false)
                .httpStatus(httpStatus)
                .build();
    }
}

enum 클래스를 활용해

  • httpStatus
  • 상태 code
  • 메시지

값을 설정

5. ErrorReasonDto 클래스

@Builder
@Getter
public class ErrorReasonDto {
    private String code;
    private String message;
    private HttpStatus httpStatus;
    private Boolean isSuccess;
}

에러 코드 및 에러 메세지와 성공유무 체크


6. GlobalExceptionHandler

ApiException 내부에 BaseErrorCode 즉 ErrorStatus가 존재하기에
미리 정해둔 에러 상태를 가져옴.

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResponseEntity<String> handleRuntimeException(RuntimeException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
    }

    @ExceptionHandler(ApiException.class)
    public ResponseEntity<ApiResponse<String>> handleCustomException(ApiException e) {
        BaseErrorCode errorCode = e.getErrorCode();
        return handleExceptionInternal(errorCode);
    }

    private ResponseEntity<ApiResponse<String>> handleExceptionInternal(BaseErrorCode errorCode) {

        return ResponseEntity.status(errorCode.getReasonHttpStatus().getHttpStatus())
                .body(ApiResponse.onFailure(errorCode));
    }
}

ApiException 으로 발생하는 에러를 처리하는데

각 예외 안에 BaseErrorCode 인터페이스로 구현된 ErrorStatus 클래스들이 존재하기에

해당 클래스 안에서

에러 코드를 ResponseEntitystatus 로 반환하고

에러 내용을 ApiResponse dto 객체를 활용하여 ResponseEntitybody 값으로 반환한다.

0개의 댓글