[Spring] API 요청 성공, 예외처리에서 공통으로 사용하는 ResponseDto 구현

Doyeon·2023년 2월 23일
1
post-thumbnail
post-custom-banner

프론트와 협업할 때 어떤 request를 받을 것이며, 어떤 response를 보낼 것인지 협의하는 것이 중요하다. response 형식을 어떻게 할지 정하기 나름이겠지만, 이번에는 API 요청에 성공해서 응답을 보내거나, 예외처리를 보낼 때 response 형식을 통일하려고 한다. 공통으로 사용할 ResponseDto를 구현해보자!

API 요청이 성공한 경우

  • 현재 진행하고 있는 프로젝트에서는 API 요청이 성공한 경우, 크게 두 가지의 응답이 나갈 수 있다.

    1) 클라이언트가 요청한 데이터

    • ex) 게시글 responseDto, 댓글 responseDto 등 요청에 해당하는 responseDto

    2) 성공 상태코드와 메시지가 담긴 SuccessResponse

    @Getter
    public class SuccessResponse {
        private int status;
        private String message;
    
        @Builder
        private SuccessResponse(int status, String message) {
            this.status = status;
            this.message = message;
        }
    
        public static SuccessResponse of(HttpStatus status, String message) {
            return SuccessResponse.builder()
                    .status(status.value())
                    .message(message)
                    .build();
        }
    }

예외가 발생한 경우

  • 예외가 발생했을 때, 상태코드와 메시지가 담긴 ErrorResponse를 응답으로 보낸다.
    @Getter
    public class ErrorResponse {
        private int status;
        private String message;
    
        @Builder
        private ErrorResponse(int status, String message) {
            this.status = status;
            this.message = message;
        }
    
        public static ErrorResponse of(ErrorType errorType) {
            return ErrorResponse.builder()
                    .status(errorType.getCode())
                    .message(errorType.getMessage())
                    .build();
        }
    
        public static ErrorResponse of(HttpStatus status, String message) {
            return ErrorResponse.builder()
                    .status(status.value())
                    .message(message)
                    .build();
        }
    
        public static ErrorResponse of(BindingResult bindingResult) {
            String message = "";
    
            if (bindingResult.hasErrors()) {
                message = bindingResult.getAllErrors().get(0).getDefaultMessage();
            }
    
            return ErrorResponse.of(HttpStatus.BAD_REQUEST, message);
        }
    }

모든 요청에 공통으로 보낼 response 형식

  • response에 담을 내용은 성공 여부, 응답으로 보낼 데이터, 에러 메시지로 총 3개의 필드다.
  • 응답으로 보낼 데이터는 BoardResponseDto, CommentResponseDto, SuccessResponse 등 여러 DTO가 들어갈 수 있도록 Generics로 설정한다.
  • 에러 메시지 필드에는 ErrorResponse를 담는다.
    @Getter
    public class ApiResponseDto<T> {
    
        private boolean success;
        private T response;
        private ErrorResponse error;
    
        @Builder
        private ApiResponseDto(boolean success, T response, ErrorResponse error) {
            this.success = success;
            this.response = response;
            this.error = error;
        }
    
    }

요청 결과에 따른 static method 만들기

  • 요청 결과에 따라 알맞는 데이터를 넣어 ApiResponseDto를 생성할 static method를 만든다.
  • ResponseUtils class를 만들어 이 안에 static method를 만들 것이다.
  • 요청 성공인 경우, response에는 어떤 타입의 객체든 들어올 수 있다.
  • 예외가 발생한 경우, response에는 ErrorResponse 타입만 들어올 수 있다.
    public class ResponseUtils {
    
        // 요청 성공인 경우
        public static <T> ApiResponseDto<T> ok(T response) {
            return ApiResponseDto.<T>builder()
                    .success(true)
                    .response(response)
                    .build();
        }
    
        // 에러 발생한 경우
        public static <T> ApiResponseDto<T> error(ErrorResponse response) {
            return ApiResponseDto.<T>builder()
                    .success(false)
                    .error(response)
                    .build();
        }
    
    }

기능 구현 메서드와 예외처리 부분에서 ResponseUtils 사용하기

  • 게시글 작성 메서드
    • ApiResponseDto의 response 값으로 BoardResponeDto를 넣어 리턴한다.

      public ApiResponseDto<BoardResponseDto> createPost(BoardRequestsDto requestsDto, User user) {
      
              Board board = boardRepository.save(Board.of(requestsDto, user));
      
              return ResponseUtils.ok(BoardResponseDto.from(board));
      
          }
  • 예외처리
    • CustomExceptionHandler 에서 프로젝트 전역에서 발생하는 예외들을 처리해준다.

    • 예외처리시, 응답 결과 status에 상태 값을 넣어주어야 하므로 ResponseEntity를 리턴하도록 구현했다.

    • ResponseEntity의 body 부분은 ApiResponseDto가 나타나도록 한다.

      @Slf4j
      @ControllerAdvice
      public class CustomExceptionHandler {
      
          @ExceptionHandler(MethodArgumentNotValidException.class)
          public ResponseEntity<ApiResponseDto<ErrorResponse>> methodValidException(MethodArgumentNotValidException e) {
              ErrorResponse response = ErrorResponse.of(e.getBindingResult());
              log.error(response.getMessage());
              return ResponseEntity.badRequest().body(ResponseUtils.error(response));
          }
      
          @ExceptionHandler(RestApiException.class)
          public ResponseEntity<ApiResponseDto<ErrorResponse>> customException(RestApiException e) {
              ErrorResponse response = ErrorResponse.of(e.getErrorType());
              log.error(response.getMessage());
              return ResponseEntity.badRequest().body(ResponseUtils.error(response));
          }
      
      }

Response 결과

  • 성공
    • SuccessResponse
    • BoardResponseDto
  • 예외처리

성공 메시지, 에러 메시지, 요청에 대한 데이터 값 등 어떠한 응답을 보내더라도, response의 형식은 success, response, error를 담아 보내주고 있다. 어떠한 요청이든 공통된 response 형식으로 응답을 보내는 것이 반드시 해야할 정답은 아니겠지만, 프론트에서 response 데이터에서 필요한 값을 찾을 때 좀 더 편리하지 않을까 싶다. 어떤 요청이든 형식은 정해져있기 때문에 success가 false인 경우는 error 필드를 가져와 작업을 하고, success가 true인 경우는 response 필드를 가져와 작업을 하면 되니 말이다.

profile
🔥
post-custom-banner

0개의 댓글