[스프링부트] Response를 객체에 담아 반환하기 (커스텀 Response 생성하기)

minji·2021년 10월 15일
8

우리가 일반적으로 api 요청을 한 후, 그에 대한 응답(response)로 받는 데이터는 크게 세가지로 나눌 수 있다.

  • 단일 데이터
  • 리스트 데이터
  • 에러

이 때, 이를 아무 가공도 없이 엔티티 그대로 반환하게 되면 엔티티가 외부에 그대로 노출되어버린다는 문제점과 상황에 따른 에러들을 적절하게 처리할 수 없게 되어버린다.

문제점을 예시를 통해 직접 확인해보자.

😿 문제상황 : 엔티티를 그대로 반환하는 경우

현재 데이터 베이스에는 Book 테이블과, User 테이블이 존재하며 N:1 관계를 맺는다.
이 때, 다음과 같이 데이터 단건조회, 데이터 리스트조회 api를 각각 작성할 수 있다.

[ BookController 클래스 ]
[ BookService 클래스 ]책 단건조회 api를 호출할 경우 단일 엔티티인 Book을,
책 목록 조회 api를 호출할 경우 엔티티 리스트인 List<Book> 을 반환하도록 작성했다.

조회 결과를 확인해보자.

  • 단건조회 - Book 반환
  • 다중조회 - List<Book> 반환

위와 같이 두 경우 모두 Status : 200 OK 의 정상 응답 결과를 반환받을 수 있다.

그렇다면 데이터베이스에 존재하지 않는 책을 조회하려고 하면 어떻게 될까?

존재하지 않는 id인 bookId = 3 으로 단건조회 api를 호출한 결과이다.

이 경우 위와 같이 Status:500, ServerError , 서버상의 에러가 발생하는데,
이를 그대로 반환하는 것은 당연히 좋지 않은 방법이다.

대신, 해당 아이디를 가진 책은 존재하지 않는다는 응답을 돌려줘야 할 것이다.

따라서, 이는 서버단에서 직접 코드로 문제상황에 따라 에러를 내려주는 과정이 필요하다.

😺 커스텀 Response 클래스 생성하기

그렇다면, 우리가 정의할 응답에 포함되어야할 속성에는 무엇이 있을까?

  • 데이터 (엔티티 또는 엔티티 리스트)
  • 성공 여부
  • status code
  • 에러에 해당하는 경우 메세지(에러 발생 요인)

다음과 같은 정보들이 응답에 포함되도록 코드를 수정해보자.

일단 response 라는 이름을 가진 패키지를 생성하자.
이제 아래 4개의 클래스들을 생성할 것이다.

CommonResponse - 공통 속성


SingleResponse<T> - 공통속성 + 엔티티 T의 단일 데이터


ListResponse<T> - 공통속성 + 엔티티 T의 리스트 데이터

단일 데이터와 리스트 데이터는 그 결과의 자료형에만 차이가 있다. (T 또는 List<T>)

따라서 CommonResponse클래스에 공통 속성( success, code, message ) 을 부여하고,
각각 CommonResponse를 상속해 데이터만 추가해주면 된다.


😼 ResponseService 클래스 생성하기

기존의 데이터는 T 또는 List<T> 자료형을 갖는다.
이제 얘네를 각각 우리가 만든 SingleResponse 또는 ListResponse 로 바꿔서 내보내주면 된다.
반환받은 데이터를 감싸서 Controller단에 넘겨줄 서비스 클래스를 생성해준다.
이 때 bean으로 등록되어야하므로, @Service 어노테이션을 추가한다.

  • getSingleResponse
    T를 받아 SingleResponse<T> 로 반환

  • getListResponse
    List<T> 를 받아 ListResponse<T> 로 반환

이제, 기존의 BookController의 반환값을 수정하자.
ResponseService 객체를 생성자 주입한 후, 이를 이용해 기존 데이터를 감싸 반환하면 된다.

즉, 데이터베이스에서 받아온 데이터(BookService.XXX)들은 모두 Book 또는 List <Book> 자료형이다.
이를 Response객체로 감싸서 (ResponseService.XXX), 우리가 정의한 자체 클래스를 최종적으로 반환할 수 있다.

이제, 책 단건조회 api와 책 목록 조회 api를 다시 호출해보자.
예상과 달리 406,Not Acceptable 에러가 발생한다.. 당황

406 에러의 경우, 그 대상에 대한 표현이 불가한 경우 발생하는 에러로(rfc 참조), 주로 Json 오브젝트로 변환하는데에 문제가 생겨 발생한다.

BookController에서는 SingleResponse를 Json 객체로 변환하면서 GetXXX를 이용해 데이터를 가져와야하는데
해당 속성들이 private 필드로 선언되어 있어 불가능한 것이다.

따라서, CommonResponse, Single/ListResponse 클래스에 @Getter 어노테이션을 추가해준다.

😻 최종 결과

다시 api를 호출해보자.

  • 단건조회
  • 리스트 조회

다음과 같이, 성공여부, 코드, 메세지와 함께, data 내부에 실제 원하던 값들이 함께 반환되는 것을 확인할 수 있다.

이렇게 수정할 경우, 이 api를 호출한 뷰 단에서도 success 여부를 검사하고, 그에 따라 data 를 꺼내 사용하기에 용이해진다.

이제 다음 포스트에서, 앞서 말했던 에러를 그대로 노출하지 않고, 상황에 따라 반환할 수 있도록 코드를 수정해보자.

profile
SW Engineer

2개의 댓글

comment-user-thumbnail
2021년 12월 20일

큰 도움 됐습니다!

답글 달기
comment-user-thumbnail
2024년 7월 30일

감사합니다!

답글 달기