에러 처리 개선

DanC·2025년 7월 17일
0

Project: CheckBoo

목록 보기
3/3

에러 응답이 통일되지 않는 문제 확인

에러가 발생하면 다음과 같이 응답을 받을 수 있도록 구현해놓았다.

{
    "code": "ALREADY_SIGNED_UP_EMAIL",
    "message": "이미 가입된 이메일입니다."
}

그런데 Spring Validation을 사용하면서 @Valid에서 오류가 발생하는 경우 응답이 다음과 같았다.

{
    "type": "about:blank",
    "title": "Bad Request",
    "status": 400,
    "detail": "Invalid request content.",
    "instance": "/"
}

이러한 응답은 원하는 바가 아닐뿐더러, 프론트엔드 개발을 할때 통일되지 않은 응답은 혼란을 야기할 것이라고 생각했다.

ExceptionHandlerExceptionResolver에서 처리되었기 때문에, ResponseEntityExceptionHandler에 있는 handleMethodArgumentNotValid를 오버라이드 하지 않아서 기본 처리방식이 적용된 것으로 보인다.

에러 처리 개선

현재로서는 이메일만 형식을 검증하고 있지만 나중에 여러가지를 검증할 수도 있다고 생각하기 때문에, 여러 값에 대한 검증을 errors라는 값으로 내보내고자 한다. 구현 방법에 대해서는 이 블로그를 참고했다.

@Builder
public record ErrorResponse(
        String code,
        String message,
        @JsonInclude(JsonInclude.Include.NON_EMPTY) List<ValidationError> errors
) {

    @Builder
    public record ValidationError(String field, String message) {

        public static ValidationError of(final FieldError fieldError) {
            return ValidationError.builder()
                    .field(fieldError.getField())
                    .message(fieldError.getDefaultMessage())
                    .build();
        }
    }
}

우선 오류 응답에 어떤 필드에 대해서 어떠한 오류가 발생했는지를 알기 위해 fieldmessage를 갖는 ValidationError 리스트를 추가했다. 이때, 응답에 필드 오류가 없을수도 있기 때문에 @JsonIncludeNON_EMPTY를 추가했다.

    @Nonnull
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            @Nonnull MethodArgumentNotValidException ex,
            @Nonnull HttpHeaders headers,
            @Nonnull HttpStatusCode status,
            @Nonnull WebRequest request) {
        log.warn("handleIllegalArgument", ex);
        ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER;
        return handleException(ex, errorCode);
    }

    private ResponseEntity<Object> handleException(MethodArgumentNotValidException ex, ErrorCode errorCode) {
        return ResponseEntity.status(errorCode.getHttpStatus())
                .body(makeErrorResponse(ex, errorCode));
    }

    private ErrorResponse makeErrorResponse(MethodArgumentNotValidException ex, ErrorCode errorCode) {
        List<ErrorResponse.ValidationError> validationErrorList = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(ErrorResponse.ValidationError::of)
                .collect(Collectors.toList());

        return ErrorResponse.builder()
                .code(errorCode.name())
                .message(errorCode.getMessage())
                .errors(validationErrorList)
                .build();
    }

그 다음으로는 handleMethodArgumentNotValid를 오버라이드해서 처리하도록 구현했다.

결과 확인

{
    "code": "INVALID_PARAMETER",
    "message": "입력값 오류.",
    "errors": [
        {
            "field": "email",
            "message": "이메일 형식이 올바르지 않습니다."
        }
    ]
}

무사히 내가 원하는 형태로 응답을 받을 수 있게 되었다.

0개의 댓글