[spring] Response Entity 구조화하기

sujin·2023년 11월 23일
0

spring

목록 보기
12/13

REST Api 응답 반환, 어떻게 해야할까?

rest api의 반환 결과, 어떻게 하는게 좋을까?

spring 프로젝트를 하며 기능적으로 완성됐다고 생각하는 api들에 대해서 리팩토링을 진행하다 문득 생각이 들었다.

우선 나는 아래와 같이 구조화했다.

  • GET, POST에 대한 정상 응답 Response -> DTO마다 다르다.

  • 예외처리를 위한 custom Error Response

frontend 팀원에게 Api를 설명하다보니, error인지 아닌지 구분하기 위해서 "errorType"이라는 key값이 있는지 파악하는 코드를 사용해야한다고 설명했고 말하다보니,, 뭔가 이상하다. 타입안의 값이 아닌 errorType의 key값 자체가 있는지 없는지로 에러를 판단하는게 과연 구조화가 잘 된 Response일까? 아니다라는 생각이 들었다.
차라리 모든 응답에 "status"와 같은 key를 가지게 하고 key 안의 값으로 판단하는게 더 구조화되고 가독성 있는 코드를 만들지 않을까?생각이 들었다. 그러면서 response 타입들을 알아보기 시작했다!

Api Response 형태 벤치마킹을 진행하자??

찾아보니, 정답은 정해져있지 않은 것 같다. 그래서, 이럴때는 대기업의 api developers 문서를 슬~~쩍 참고하며 익숙해지면 좋을 것 같다고 생각했다.

우선 카카오의 response 형태는 Header와 Body의 조합이다.

다음으로 네이버의 response의 형태는? 역시 마찬가지였다. HTTP 상태 코드로 리턴하고 Body로 정보를 전달한다고 나와있다.

  • 해당 방법이 나의 프로젝트에서 과연 알맞을까?
    현재 프로젝트에서는 규모가 그렇게 크지 않기에 모든 error에 대해서 handling하는 page가 동일하다. 따라서 status code를 특별히 구분해줄 필요까지는 없다고 생각했다.
    그리고 다 나열하고 있기에는 오히려 정상 reponse일때의 code와 error일때의 response를 구분하는 로직을 넣는게 간단한 로직인데 더욱 복잡하게 만들 수 있다고 생각했다.
  • 따라서, 벤치마킹은 하지 않도록 했다.

그래서 그냥 error일때와 정상 response일때의 반환 구조를 맞춰주는 방법을 생각했다.

구조만들기

  • error response
package whereQR.project.exception;

import lombok.Getter;

@Getter
public class ErrorResponse {

    private ErrorType errorType;
    private String message;
    private String path;

    public ErrorResponse(ErrorType errorType, String message, String path){
        this.errorType = errorType;
        this.message = message;
        this.path = path;
    }

    public ErrorResponse(ErrorType errorType, String message){
        this.errorType = errorType;
        this.message = message;
    }

}

error response를 위한 class는 위와같이 작성했다. 그리고 그때의 결과 형식은 아래와 같다.

{
"errorType": "BAD_REQUEST",
"message": "kakao api 요청이 유효하지 않습니다.",
"path": "class whereQR.project.service.KakaoAuthService"
}

  • DTO ex
  @GetMapping("/detail")
    public ResponseEntity<MemberDetailDto> detail() {
        Member currentMember = MemberUtil.getMember();
        return ResponseEntity.ok(memberService.getMemberById(currentMember.getId()).toMemberDetailDto());
    }
@Data
public class MemberDetailDto {

    private String username;
    private String phoneNumber;
    private List<Qrcode> qrcodes;

    public MemberDetailDto(String username, String phoneNumber, List<Qrcode> qrcodes){
        this.username = username;
        this.phoneNumber = phoneNumber;
        this.qrcodes = qrcodes;
    }
}

DTO가 위와 같을 때, Response는 아래와 같다.

{
"username": "이름",
"phoneNumber": "01012345678",
"qrcodes": []
}

우선 error인지 success type인지를 구분하기 위한 status를 key로 추가해주자! 그리고 data에 나머지를 넣어주자!

그리고 DTO를 반환하는 controller에서 보이는 것처럼 ResponseEntity로 감싸서 반환하도록 할 것이다.
상태코드, header값은 넣지 않을 것이다!

반환하면서 패턴도 코드를 읽을 때 가독성도 올라가도록 Builder pattern을 사용해서 구조화를 진행해줬다!

  • ResponseEntity Class 만들기
@Data
@Builder
public class ResponseEntity {

    @Enumerated(EnumType.STRING)
    public Status status;

    public Object data;

}
  • status를 enum으로 생성하기
public enum Status {
    SUCCESS("SUCCESS"),
    FAILED("FAILED");

    private final String type;

    Status(String type) {
        this.type = type;
    }

}

변경 후

  • id값만 반환하는 api의 경우, 가장 보기가 좋아졌다..ㅎㅎ

status와 data가 없었다면?? "3~~~700"만 존재했을 것이다,,,ㅜㅜ

마무리

처음부터 response 타입까지 미리 정해두고 작성을 했었다면 더 빠르게 작업할 수 있었을텐데, 그러지 못했다. 다음에는, 처음부터 api 설계를 할 때 형태까지 제대로 잡고 들어가자! 그리고 덕분에 builder 패턴까지 알아보게 되었다! 제대로 각잡고 다 builder 패턴으로 바꿔버릴까? 생각중이다!!
우선은 제 기준에서 controller에서 코드가 중구난방인게 싫어서 response를 우선으로 builder 패턴을 적용해봤습니다~

  • ) 더욱 좋은 방법이나 구조가 있다면 추천해주시면 감사하겠습니다~~ :)

0개의 댓글