Spring

[SpringBoot]Api Response

codi-3 2024. 9. 6. 13:32

Spring Boot에서 ApiResponse는 REST API 개발 시 클라이언트에게 응답을 보낼 때 사용되는 구조를 정의하는 클래스이다. API 호출이 성공했는지 실패했는지, 그리고 그에 따른 데이터를 포함하여 다양한 정보를 응답으로 보낼 수 있다.

ApiResponse 기본 구조

ApiResponse 객체는 주로 다음과 같은 필드를 가진다:

  1. status (HttpStatus): 응답의 상태를 나타내며, 성공 또는 실패 여부를 표시한다. HttpStatus는 HTTP 응답 상태 코드를 담고 있다. (예: 200 OK, 404 Not Found)
  2. message (String): 응답에 대한 설명 메시지를 포함한다. 성공 또는 오류에 대한 메시지를 클라이언트에게 전달한다.
  3. data (Object): 응답 데이터이다. 요청의 결과에 따라 반환할 실제 데이터가 여기에 포함된다. 데이터는 객체, 배열, 또는 다른 구조일 수 있다.
  4. timestamp (LocalDateTime): 응답이 발생한 시간을 기록한다.

✏️ 코드

이번 팀 프로젝트 중 튜터님에게 받은 피드백중 Api Response를 사용해 보라는 말씀을 하셨는데 구현을 해 사용해 보니 훨씬 단일화되고 간결한 Response를 볼 수 있었다 코드는 아래와 같았다.

 

package com.sparta.springnewsfeed.global.dto;

import com.fasterxml.jackson.annotation.JsonInclude; // null 값을 포함하지 않도록 JSON 직렬화 시 설정
import lombok.Getter; // Lombok을 사용하여 getter 메서드를 자동 생성
import org.springframework.validation.BindingResult; // 스프링에서 검증 결과를 처리하는 클래스
import org.springframework.validation.FieldError; // 필드에 대한 오류를 나타내는 클래스
import org.springframework.validation.ObjectError; // 객체 레벨의 오류를 나타내는 클래스

import java.util.HashMap; // Map을 사용하기 위해 HashMap을 import
import java.util.List; // 리스트를 처리하기 위한 import
import java.util.Map; // 키-값 쌍을 관리하기 위한 Map 인터페이스 사용

@JsonInclude(JsonInclude.Include.NON_NULL) // JSON 응답에서 null 값은 포함하지 않도록 설정
@Getter // Lombok을 사용하여 모든 필드의 getter 메서드를 자동으로 생성
public class ApiResponse<T> {

    // 응답 상태를 나타내는 enum
    public enum Status {
        SUCCESS, FAIL, ERROR
    }

    private final Status status; // 응답의 상태 (성공, 실패, 오류)
    private final T data; // 응답 데이터 (제네릭 타입)
    private final String message; // 응답 메시지 (성공, 실패 등 관련 설명)

    // 생성자: status, data, message를 초기화하는 private 생성자
    private ApiResponse(Status status, T data, String message) {
        this.status = status;
        this.data = data;
        this.message = message;
    }

    // 성공적인 응답을 생성하는 static 메서드 (데이터 포함)
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(Status.SUCCESS, data, "요청이 성공적으로 처리되었습니다");
    }

    // 성공적인 응답을 생성하지만 내용이 없을 때 사용
    public static ApiResponse<?> successWithNoContent() {
        return new ApiResponse<>(Status.SUCCESS, null, "요청이 성공적으로 처리되었지만 내용이 없습니다");
    }

    // 검증 실패 시 BindingResult에서 오류를 추출하여 응답을 생성하는 메서드
    public static ApiResponse<?> fail(BindingResult bindingResult) {
        Map<String, String> errors = new HashMap<>(); // 오류를 저장할 Map 객체 생성

        List<ObjectError> allErrors = bindingResult.getAllErrors(); // 모든 오류를 가져옴
        for (ObjectError error : allErrors) { // 오류 리스트를 순회
            if (error instanceof FieldError) { // 필드 오류인 경우
                errors.put(((FieldError) error).getField(), error.getDefaultMessage()); // 필드 이름과 메시지를 Map에 저장
            } else { // 객체 오류인 경우
                errors.put(error.getObjectName(), error.getDefaultMessage()); // 객체 이름과 메시지를 Map에 저장
            }
        }
        return new ApiResponse<>(Status.FAIL, errors, "검증에 실패했습니다"); // 오류와 함께 실패 응답 반환
    }

    // 이미 만들어진 오류 목록을 받아서 실패 응답을 생성하는 메서드
    public static ApiResponse<?> fail(Map<String, String> errors) {
        return new ApiResponse<>(Status.FAIL, errors, "검증에 실패했습니다");
    }

    // 오류 발생 시 오류 메시지와 함께 에러 응답을 생성하는 메서드
    public static ApiResponse<?> error(String message) {
        return new ApiResponse<>(Status.ERROR, null, message);
    }
}

 

  • @JsonInclude(JsonInclude.Include.NON_NULL): 이 애노테이션은 응답에서 null 값을 제거하여 반환한다.
  • ApiResponse 클래스: 제네릭 타입의 API 응답 객체를 생성한다. 성공, 실패, 오류에 대한 다양한 응답을 처리할 수 있다.
  • Status enum: 응답의 상태를 SUCCESS, FAIL, ERROR 세 가지로 구분하여 사용한다.
  • success 메서드: 요청이 성공했을 때 응답을 생성하는 메서드로, 데이터가 포함된다.
  • fail 메서드: 유효성 검사에서 오류가 발생했을 때 BindingResult를 통해 오류를 수집하고 이를 응답으로 반환한다.
  • error 메서드: 서버 오류 또는 기타 처리 중 오류가 발생했을 때, 해당 오류 메시지와 함께 응답을 반환한다.

🔡 사용예시

    @PostMapping("/signup")
        public ResponseEntity<ApiResponse<?>> signup(@RequestBody UserRequestDto userRequest, HttpServletResponse res) {
        try {
            return ResponseEntity.ok(ApiResponse.success(userService.signup(userRequest,res)));
        }catch (Exception e){
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(ApiResponse.error("회원 가입 중 오류가 발생햇습니다."));
        }
    }

 

 

위와 같이 ResponseEntity에서 ApiResponse로 한번 감싸준뒤 try-catch 문으로 상태에 따라 정해진 dto를 보내주면 된다.