[Toy Project] @ControllerAdvice 사용한 전역 예외 처리 및 API 응답 형식 통일

최지나·2023년 10월 24일
3

지난 포스팅에서 개선이 필요한 사항에서, API 요청 성공|실패 시 메세지를 간결하고, 통일성 있게 바꾸기로 계획했었다 👾 이를 적용해보고자 한다!

AS- ISAPI 요청 성공 시 HTTP status만 return 됨, 실패 시 trace 레벨 로그까지 응답으로 옴
TO -BEAPI 요청 성공 또는 실패 시 response의 형식을 통일

코드

1. 공용 ResponseCommon 객체 생성

  • 기본적인 HTTP 응답 코드, 응답 메시지와 함께, 요청을 받은 시간을 return 하는 객체를 생성하였다
@Getter
@Setter
public class ResponseCommon {
    private String timestamp;
    private int status;
    private String message;
    public ResponseCommon(int status, String message) {
        this.timestamp = LocalDateTime.now().toString();
        this.status = status;
        this.message = message;
    }
}

2. @ControllerAdvice를 사용한 전역 예외처리

  • requestBody를 JSON 형식으로 받는 POST, PATCH method를 사용하는 API의 경우, 공통적으로 input json의 형식이 잘못했을 때 에러가 발생하였다. 이런 에러들을 전역적으로 잡아 ResponseCommon의 형식에 맞게 error를 return 하기 위해 @ControllerAdvice@ExceptionHandler 어노테이션을 사용하였다

@ControllerAdvice란?
스프링 프레임워크에서 예외 처리와 관련된 클래스에 적용되는 어노테이션으로, 전역 예외 처리와 관련된 기능을 제공

  • 여러 컨트롤러 클래스에서 발생하는 예외를 중앙 집중식으로 처리하고 일관된 방식으로 응답을 생성
  • @ExceptionHandler 어노테이션을 사용하여 특정 예외 유형에 대한 처리를 제공
@ControllerAdvice
public class ControllerExceptionHandler {

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<ResponseCommon> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
        String errorMessage = "INVALID JSON FORMAT";
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(new ResponseCommon(HttpStatus.BAD_REQUEST.value(), errorMessage));
    }
}

3. Controller

ex) 제품 생성 API

  • 응답을 모두 ResponseCommon 객체 형태로 받도록 변경하였다
@PostMapping
    public ResponseEntity<Object> create(
            @RequestBody @Valid ProductInput productInput,
            Errors errors) {

        if (errors.hasErrors()) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body(new ResponseCommon(HttpStatus.BAD_REQUEST.value(),
                            errors.getFieldError().getDefaultMessage()));
        }

        try {
            productService.createProduct(productInput);

            return ResponseEntity.status(HttpStatus.CREATED)
                    .body(new ResponseCommon(HttpStatus.CREATED.value(), "PRODUCT IS CREATED"));
        } catch (ResponseStatusException ex) {
            return ResponseEntity.status(ex.getStatusCode())
                    .body(new ResponseCommon(ex.getStatusCode().value(), ex.getReason()));
        }
    }

결과

이미 존재하는 제품 등록 요청 (Service에서 처리하는 에러 발생 시)

상품 등록 성공 시

잘못된 Input (@ControllerAdvice에서 전역적으로 catch)

-> API 요청에 따른 각 응답을 공통적인 ResponseCommon에 담도록 변경하였다 😸


이제 어느 정도 기본 틀을 정했으니, 필요한 API를 하나씩 만들어 나갈 계획이다 front를 하고 있는 친구도 학교 (대학원,,) 다니면서도 열심히 하고 있는 모습을 보며 자극받고 있다 🔥🔥

profile
의견 나누는 것을 좋아합니다 ლ(・ヮ・ლ)

2개의 댓글

comment-user-thumbnail
2023년 10월 26일

몰랐던거 배워가요!

1개의 답글