공통 Response 객체 설계하기

박병주·2025년 3월 1일
0

Spring

목록 보기
7/9

백엔드에서 공통 Response 객체를 사용하는 이유는 주로 일관성, 유지보수성, 그리고 클라이언트와의 효율적인 통신을 보장하기 위함

처음 설계한 ApiResponse.java

@Builder
@Data
public class ApiResponse<T> {
    @Builder.Default
    private String status = ResponseCode.SUCCESS;
    @Builder.Default// "SU" 또는 "VF" 등...
    private String message = ResponseMessage.SUCCESS;       // 응답에 대한 설명
    private T data;                                         // 실제 반환 데이터
}

문제점

  1. data라는 변수명 때문에 클라이언트에서 데이터에 접근하기 위해 axios를 사용해서 값을 가져오면 response.data.data 처럼 값을 가져와야 함.
  2. 에러 발생 시 예외처리에서도 해당 객체에 응답 상태와 메세지를 담아 보내주어도 http status는 항상 200으로 응답 해줌.

해결방법

  1. 이름은 object로 변경 함(프로젝트에 맞게 변경해도 될 것 같음)
  2. ResponseEntity를 상속해서 ApiResponse 재구성

결과물

public class ApiResponse<T> extends ResponseEntity<T> {
    // 생성자
    public ApiResponse(T object, HttpStatus status) {
        super(object, status);
    }

    // 내부 빌더 클래스
    public static class builder<T> {
        private String message = ResponseType.SUCCESS.getMessage();
        private String code = ResponseType.SUCCESS.getCode();
        private HttpStatus status = ResponseType.SUCCESS.getStatus();
        private T object;

        // 메세지 설정
        public builder<T> message(String message) {
            this.message = message;
            return this;
        }

        // 코드 설정 (HTTP status code 사용, 존재하지 않는 경우 OK로 기본 처리)
        public builder<T> code(String code) {
            this.code = code;
            return this;
        }

        public builder<T> object(T object) {
            this.object = object;
            return this;
        }

        public builder<T> errorStatus(ResponseType responseType) {
            this.message = responseType.getMessage();
            this.code = responseType.getCode();
            this.status = responseType.getStatus();
            return this;
        }

        // 빌드 메소드: 메시지와 코드를 담은 ResponseEntity 생성
        public ApiResponse<Map<String, Object>> build() {
            Map<String, Object> body = new HashMap<>();
            body.put("message", message);
            body.put("code", code);
            body.put("object", object);

            if (status == null) {
                status = HttpStatus.OK;
            }

            return new ApiResponse<>(body, status);
        }
    }

}

ResponseType.java

public enum ResponseType {
    // HTTP Status 200
    SUCCESS("SU", HttpStatus.OK, "요청이 성공적으로 처리되었습니다."),

    NOT_FOUND_PAGE("NFP", HttpStatus.BAD_REQUEST, "페이지를 찾을 수 없습니다."),

    // HTTP Status 401
    SIGN_IN_FAIL("SF", HttpStatus.UNAUTHORIZED, "로그인 실패하였습니다."),
    AUTHORIZATION_FAILED("AF", HttpStatus.UNAUTHORIZED, "인증에 실패하였습니다."),

    // HTTP Status 403
    NO_PERMISSION("NP", HttpStatus.FORBIDDEN, "권한이 없습니다."),

    // HTTP Status 500
    DATABASE_ERROR("DBE", HttpStatus.INTERNAL_SERVER_ERROR, "데이터베이스 오류입니다."),
    SERVER_ERROR("SER", HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다.");

    private final String code;
    private final HttpStatus status;
    private final String message;

    ResponseType(String code, HttpStatus status, String message) {
        this.code = code;
        this.status = status;
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public HttpStatus getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }
}
  • 에러코드, 메세지, 상태 Enum으로 한번에 관리 및 사용
    @GetMapping("/success")
    public ApiResponse<?> test() {
        return new ApiResponse.builder<Object>()
                .object("데이터")
                .build();
    }
  • 다음과 같이 리턴에 사용.
@RestControllerAdvice
public class ControllerExceptionHandler {


    @ExceptionHandler(NotFoundPageException.class)
    public ApiResponse<?> notFoundPageHandler(NotFoundPageException e) {
        return new ApiResponse.builder<Object>()
                .errorStatus(ResponseType.NOT_FOUND_PAGE)
                .build();
    }
}
  • @RestControllerAdvice를 사용해 에러를 핸들링 및 에러 상태 지정 후 리턴
profile
응애

0개의 댓글