[척척학사] API 응답 모듈 분리 과정

박상민·2025년 4월 29일
0

척척학사

목록 보기
4/15
post-thumbnail

기존 문제점

기존 구조

@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Schema(description = "API 응답 공통 포맷")
public class ApiResponse<T> {

    @Schema(description = "성공 여부", example = "true")
    private boolean success;

    @Schema(description = "응답 데이터")
    private T data;

    @Schema(description = "메시지", example = "요청 성공")
    private String message;

    @Schema(description = "에러 정보")
    private ErrorDetail error;

지금 구조는 ApiResponse 하나에 성공/실패를 모두 때려 넣은 구조

문제점

  • 기존 구조는 성공과 실패가 의미상 섞이는 문제가 발생
  • Swagger 문서에서 성공/실패 응답이 명확히 분리 안 됨

목표

  • 성공과 실패를 명확히 분리

핵심 아이디어

  • 성공 응답용: SuccessResponse<T>
  • 실패 응답용: ErrorResponse
  • 그리고 공통 인터페이스 하나로 묶기 → ApiResponse

리팩토링 구조

공통 상위 인터페이스

package com.chukchuk.haksa.global.common.response;

import io.swagger.v3.oas.annotations.media.Schema;

// 공통 상위 인터페이스
@Schema(description = "API 응답 공통 인터페이스")
public interface ApiResponse {
    boolean isSuccess();
}

API 성공 응답 포맷

@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Schema(description = "API 성공 응답 포맷")
public class SuccessResponse<T> implements ApiResponse {

    @Schema(description = "성공 여부", example = "true")
    private final boolean success = true;

    @Schema(description = "응답 데이터")
    private final T data;

    @Schema(description = "메시지", example = "요청 성공")
    private final String message;

    private SuccessResponse(T data, String message) {
        this.data = data;
        this.message = message;
    }

    public static <T> SuccessResponse<T> of(T data) {
        return new SuccessResponse<>(data, null);
    }

    public static <T> SuccessResponse<T> of(T data, String message) {
        return new SuccessResponse<>(data, message);
    }
}

API 실패 응답 포맷

package com.chukchuk.haksa.global.common.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Schema(description = "API 실패 응답 포맷")
public class ErrorResponse implements ApiResponse {

    @Schema(description = "성공 여부", example = "false")
    private final boolean success = false;

    @Schema(description = "에러 상세")
    private final ErrorDetail error;

    private ErrorResponse(ErrorDetail error) {
        this.error = error;
    }

    public static ErrorResponse of(String code, String message) {
        return new ErrorResponse(new ErrorDetail(code, message, null));
    }

    public static ErrorResponse of(String code, String message, Object details) {
        return new ErrorResponse(new ErrorDetail(code, message, details));
    }
}

리팩토링 구조의 장점

  • Swagger 문서에서도 성공 응답, 실패 응답 명확하게 분리됨
  • Controller 리턴 타입 통일 (ApiResponse 인터페이스)
  • 프론트엔드에서도 응답 타입 명확하게 구분할 수 있음
  • 유지보수성 대폭 상승

0개의 댓글