BaseResponse로 API Response Type 통일하기.

짱센호랑이·2023년 11월 28일

BaseResponse

SSAFY 프로젝트를 진행하며 혹은 다른 Back-end 팀원이 작성한 코드가 에러를 뿜었을 때 어떤 에러인지 파악하기 힘들고 또한 Front-end 팀원들과 협업을 할 때 문제가 발생하거나, Response가 제대로 되었는지 등등.... 문제가 종종 발생하곤 했다.

우리의 멋진 팀원 SJP가 BaseResponse라는 체제를 들여오기 전까진 말이다.

발생할 수 있는 에러를 예측하고 미리 코드, 메시지로 작성해 놓아 Response에 담아 보내면 위 사항들은 쉽게 해결될 수 있을 것이다.


Directory 구조

domain엔 기능과 관련된 폴더들이 존재하고

전체적으로 사용하는 부분은 global에 담았다.


Controller

BaseResponse를 사용하기 전에는 직접 ResponseDto를 리턴타입으로 보냈다.
이에 따라 Response Type이 일정하지 않았다.

BaseResponse를 적용한 Controller를 보겠다.


@PostMapping("/regist")
    public BaseResponse<Object> registClothes(@RequestHeader(value = "accessToken", required = false) String token,
                                              @RequestPart("clothesRegistRequestDto") ClothesRegistRequestDto clothesRegistRequestDto,
                                              @RequestPart("file") MultipartFile file) {
        try {
            UUID memberId = getMemberIdFromToken(token); // 사용자 체크
            // 옷 정보 등록
            ClothesRegistResponseDto clothesRegistResponseDto = clothesService.registClothes(clothesRegistRequestDto, memberId, token, file);
            return baseResponseService.getSuccessResponse(clothesRegistResponseDto);
        } catch (BaseException e) {
            return baseResponseService.getFailureResponse(e.status);
        }
    }
    

Postman을 통해 이 Controller에 요청을 보내보겠다.

다음과 같은 Response를 반환하였다.

message를 보면 "등록되지 않은 사용자가 요청을 했구나!"

하고 한번에 알 수 있다.

먼저 회원가입을 진행 해주고

다시 요청을 보냈더니,

다음과 같이 성공 코드인 1000번과 메세지
그리고 ResponseDto에 담긴 정보들을 확인할 수 있다.

확실히 좋다!!!

아래는 사용했던 코드이다.


Code

BaseResponse

package com.ssafy.moeutto.global.response;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class BaseResponse<T> {

    private boolean isSuccess; // 성공, 실패 여부

    private String message; // 메시지

    private int code; // 코드

    private T data; // 전달 데이터

    @Builder
    public BaseResponse(boolean isSuccess, String message, int code, T data) {
        this.isSuccess = isSuccess;
        this.message = message;
        this.code = code;
        this.data = data;
    }
}

BaseResponseService

package com.ssafy.moeutto.global.response;

public interface BaseResponseService {

    /**
     * 성공 응답 메서드 - 전달 데이터 O
     *
     * @param data - 결과 데이터
     * @param <T>  - 반환 타입 => Generic
     * @return BaseResponse - 응답 객체
     */
    <T> BaseResponse<Object> getSuccessResponse(T data);

    /**
     * 성공 응답 메서드 - 전달 데이터 X
     *
     * @param <T> - 반환 타입 => Generic
     * @return BaseResponse - 응답 객체
     */
    <T> BaseResponse<Object> getSuccessResponse();

    /**
     * 실패 응답 메서드
     *
     * @param status - BaseResponseStatus에서 생성한 status
     * @param <T>    - 반환 타입 => Generic
     * @return BaseResponse - 응답 객체
     */
    <T> BaseResponse<Object> getFailureResponse(BaseResponseStatus status);
}

BaseResponseServiceImpl

package com.ssafy.moeutto.global.response;

import org.springframework.stereotype.Component;

@Component
public class BaseResponseServiceImpl implements BaseResponseService {

    /**
     * 성공 응답 메서드 - 전달 데이터 O
     *
     * @param data - 결과 데이터
     * @param <T>  - 반환 타입 => Generic
     * @return BaseResponse - 응답 객체
     */
    public <T> BaseResponse<Object> getSuccessResponse(T data) {
        return BaseResponse.builder()
                .isSuccess(true)
                .code(BaseResponseStatus.SUCCESS.getCode())
                .message(BaseResponseStatus.SUCCESS.getMessage())
                .data(data)
                .build();
    }

    /**
     * 성공 응답 메서드 - 전달 데이터 X
     *
     * @param <T> - 반환 타입 => Generic
     * @return BaseResponse - 응답 객체
     */
    public <T> BaseResponse<Object> getSuccessResponse() {
        return BaseResponse.builder()
                .isSuccess(true)
                .code(BaseResponseStatus.SUCCESS.getCode())
                .message(BaseResponseStatus.SUCCESS.getMessage())
                .build();
    }

    /**
     * 실패 응답 메서드
     *
     * @param status - BaseResponseStatus에서 생성한 status
     * @param <T>    - 반환 타입 => Generic
     * @return BaseResponse - 응답 객체
     */
    public <T> BaseResponse<Object> getFailureResponse(BaseResponseStatus status) {
        return BaseResponse.builder()
                .isSuccess(status.isSuccess())
                .code(status.getCode())
                .message(status.getMessage())
                .build();
    }
}

BaseResponseStatus (Enum)


package com.ssafy.moeutto.global.response;

import lombok.Getter;

@Getter
public enum BaseResponseStatus {

    // -------- 성공 코드 시작 -------- //
    SUCCESS(true, 1000, "요청에 성공했습니다."),
    // -------- 성공 코드 종료 -------- //

    // -------- 실패 코드 시작 -------- //
    // -------- 필요한 에러 코드 추가 => Code 만들 때 안겹치게 몇번대 사용할 건지 얘기할 것  -------- //
    /**
     * Member
     * Code : 2000번대
     */
    NOT_FOUND_MEMBER(false, 2001, "일치하는 사용자가 없습니다."),
   
   ....

    /**
     * Clothes
     * Code : 3000번대
     */
    NOT_FOUND_CLOTHES(false, 3001, "옷 정보가 존재하지 않습니다),
    
    ...
    /**
     * Calendar
     * Code : 4000번대
     */

    NOT_FOUND_CALENDAR_INFO(false, 4001, "캘린더가 존재하지 않습니다."),
   
   ... 기타 실패 코드 
   
   
    // -------- 실패 코드 종료 -------- //

    private boolean isSuccess; // 성공 여부
    private String message; // 메시지
    private int code; // 코드

    /**
     * BaseResponseStatus 에서 해당하는 코드를 매핑
     *
     * @param isSuccess
     * @param code
     * @param message
     */
    BaseResponseStatus(boolean isSuccess, int code, String message) {
        this.isSuccess = isSuccess;
        this.code = code;
        this.message = message;
    }
}

BaseException


package com.ssafy.moeutto.global.response;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseException extends Exception {

    public BaseResponseStatus status;
}

작성자 : 박성준 ( https://velog.io/@park98sj )

profile
뭐라도 해야지

0개의 댓글