API는 응답을 통일하자.

이원찬·2023년 12월 25일
0

Spring

목록 보기
3/13
post-custom-banner
{
	"isSuccess ": true,
	"code" : "2000",
	"message" : "OK",
	"result" : 
		{
			"testString" : "This is test!"
		}
}

위 처럼 응답을 통일해야 프론트측에서 편하게 사용가능하다

  • isSuccess : 응답의 성공, 실패를 좌우!
  • code : http 응답 코드를 반환! HTTP 상태코드
  • message : Ok또는 실패 메세지를 반환!
  • result : 우리의 진짜 데이터를 직렬화 해서 반환!!

APIResponse 클래스를 만든다. (제네릭 타입으로)

먼저 위 json의 형식을 맞추어줄 클래스를 만든다.

@Getter
@AllArgsConstructor
@JsonPropertyOrder({"isSuccess", "code", "message", "result"})
public class
ApiResponse<T> {
    @JsonProperty("isSuccess")
    private final Boolean isSuccess;
    private final String code;
    private final String message;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private final T result;

    // 일반적인 응답 생성
    public static <T> ApiResponse<T> onSuccess(T result){
        return new ApiResponse<>(true, SuccessStatus._OK.getCode() , SuccessStatus._OK.getMessage(), result);
    }

    public static <T> ApiResponse<T> of(BaseCode code, T result){
            return new ApiResponse<>(true, code.getReasonHttpStatus().getCode() , code.getReasonHttpStatus().getMessage(), result);
    }

    // 실패한 경우 응답 생성
    public static <T> ApiResponse<T> onFailure(String code, String message, T data) {
        return new ApiResponse<>(false, code, message, data);
    }
}

위에서 메서드를 제외하면 응답되는것(필드) 간단하다. (당장 빨간불이 있다면 주석!)

위에서 명시한 메서드중 onSuccess를 가장 많이 사용하지만 onFailure 같은 메서드는 에러 핸들링을 위해 사용된다.

private final Boolean isSuccess;
private final String code;
private final String message;
private final T result;

{
	"isSuccess ": true,
	"code" : "2000",
	"message" : "OK",
	"result" : 
		{
			"testString" : "This is test!"
		}
}

이렇게 4개이다.

위에서 Message와 Code 필드는 일관성이 있어야 하기에 다른 클래스로 관리한다.

Status와 Message관련 클래스를 만들자.

Status들은 열거형이다

BaseCode (interface)

public interface BaseCode {
    public ReasonDTO getReason();
    public ReasonDTO getReasonHttpStatus();
}

BaseErrorCode (interface)

public interface BaseErrorCode {
    public ErrorReasonDTO getReason();
    public ErrorReasonDTO getReasonHttpStatus();
}

위 두가지 인터페이스 모두 getReason() 과 getReasonHttpStatus() 메서드를 가지고 있으며

새로운 응답 코드를 만들때 위 인터페이스를 상속시켜 구현을 강제 한다!!

ReasonDTO, ErrorReasonDTO

public class ReasonDTO {
    private String code;
    private String message;
    private Boolean isSuccess;
    private HttpStatus httpStatus;
}

---

public class ErrorReasonDTO {
    private String code;
    private String message;
    private HttpStatus httpStatus;
    private Boolean isSuccess;
}

그러면 이제 가장 많이 쓰일 SuccessStatus를 만들어보자

SuccessStatus (implements BaseCode)

성공코드이기 때문에 BaseCode를 구현한다. (실패코드면 BaseErrorCode)

@Getter
@AllArgsConstructor
public enum SuccessStatus implements BaseCode {

		// enum중 하나 _OK 이라는 이넘값
    _OK(HttpStatus.OK, "2000", "Ok"),;

		// 이넘 값도 필드를 가질수 있으며 롬복에 의해 생성자로 주입된다.
		//HttpStatus는 Spring에 내장되어있는 클래스로 우리가 그냥 에러 뱉을때 사용하는 클래스이다.
    private HttpStatus httpStatus;

		//code는 우리가 일관성 있게 만들어 줘야 한다.
    private String code;

		//message를 이용해 상태를 알려줄수 있다.
    private String message;

    @Override
    public ReasonDTO getReason() {
        return ReasonDTO.builder()
                .code(message)
                .message(code)
                .isSuccess(true)
                .build();
    }

    @Override
    public ReasonDTO getReasonHttpStatus() {
        return ReasonDTO.builder()
                .code(message)
                .message(code)
                .isSuccess(true)
                .httpStatus(httpStatus)
                .build();
    }
}
  • 이넘 타입도 필드를 가질수 있으며 롬복을 이용해 생성자로 주입하는 모습이다.

ErrorStatus (implements BaseErrorCode)

@Getter
@AllArgsConstructor
public enum ErrorStatus implements BaseErrorCode {
    // 가장 일반적인 응답
    _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버 에러, 관리자에게 문의 바랍니다."),
    _BAD_REQUEST(HttpStatus.BAD_REQUEST,"COMMON400","잘못된 요청입니다."),
    _UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"COMMON401","인증이 필요합니다."),
    _FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."),

		...
    ;

    private HttpStatus httpStatus;
    private String code;
    private String message;

    @Override
    public ErrorReasonDTO getReason() {
        return ErrorReasonDTO.builder()
                .code(message)
                .message(code)
                .isSuccess(false)
                .build();
    }

    @Override
    public ErrorReasonDTO getReasonHttpStatus() {
        return ErrorReasonDTO.builder()
                .message(message)
                .code(code)
                .isSuccess(false)
                .httpStatus(httpStatus)
                .build()
                ;
    }
}

우리는 이제 API응답을 통일이 가능하다.

실 사용코드

TempRestController

@RestController
@RequiredArgsConstructor
@RequestMapping("/temp")
public class TempRestController {
    private final TempQueryService tempQueryService;
    @GetMapping("/test")
    public ApiResponse<TempResponse.TempTestDTO> testAPI() {
        return ApiResponse.onSuccess(TempConverter.toTempTestDTO());
    }
}
profile
소통과 기록이 무기(Weapon)인 개발자
post-custom-banner

0개의 댓글