[프로젝트 회고] 7STARBALL 커서 기반 페이지네이션과 응답 구조 정리
들어가며
7STARBALL의 Meeting 목록 조회는 커서 기반 페이지네이션을 사용했다.
처음에는 Spring Data의 Slice를 그대로 활용하면 충분할 것 같았다. 하지만 프론트엔드와 맞춰보니 그대로 내려주기보다는 프로젝트에서 사용하는 응답 형식에 맞춰 감싸는 과정이 필요했다.
그래서 CustomSlicePageResponse 형태로 응답을 정리했다.
왜 커서 기반 페이지네이션을 사용했나
Meeting 목록은 사용자가 계속 스크롤하며 볼 수 있는 데이터였다.
이런 경우 offset 기반 페이지네이션은 페이지 번호가 커질수록 DB가 건너뛰어야 하는 데이터가 많아질 수 있다. 또한 중간에 데이터가 추가되거나 삭제되면 페이지 결과가 흔들릴 수 있다.
그래서 마지막으로 조회한 데이터의 ID를 기준으로 다음 데이터를 가져오는 커서 방식을 선택했다.
1
GET /meetings?cursorId=100&size=10
이 방식은 API를 사용하는 쪽에서도 이해하기 쉽다.
응답의 마지막 ID를 다음 요청에 넘기면 된다.
Slice 그대로 내려주기의 아쉬움
Spring Data의 Slice는 편리하다.
하지만 Slice 객체를 API 응답으로 그대로 노출하면 다음 문제가 생길 수 있다.
- 응답 구조가 프레임워크에 의존한다.
- 프론트엔드가 필요한 필드와 불필요한 필드가 섞인다.
- 프로젝트 전체 응답 포맷과 맞지 않을 수 있다.
- 나중에 내부 구현을 바꾸기 어렵다.
그래서 내부에서는 Slice를 사용하더라도, 외부 응답은 프로젝트가 정의한 DTO로 변환하는 편이 낫다고 판단했다.
CustomSlicePageResponse
CustomSlicePageResponse는 커서 기반 응답에 필요한 정보만 담도록 정리했다.
핵심은 다음과 같다.
- 현재 데이터 목록
- 다음 페이지 존재 여부
- 다음 요청에 사용할 cursor
- 요청 size
프론트엔드는 이 응답을 보고 다음 요청을 보낼지 판단할 수 있다.
1
2
hasNext가 true이면 nextCursor로 다음 목록 요청
hasNext가 false이면 더 이상 요청하지 않음
이렇게 응답 구조를 명확히 해두면 프론트엔드와 백엔드가 같은 기준으로 기능을 이해할 수 있다.
DTO 변환의 의미
처음에는 Slice를 DTO로 감싸는 일이 단순한 포장 작업처럼 느껴졌다.
하지만 지금 생각해보면 이 작업은 API 경계를 정리하는 일이었다.
내부에서 어떤 조회 방식을 쓰는지는 백엔드 구현의 문제다.
외부에는 프론트엔드가 안정적으로 사용할 수 있는 응답 계약을 제공해야 한다.
즉, Slice는 내부 구현이고 CustomSlicePageResponse는 외부 계약이다.
이 둘을 분리하면 나중에 내부 조회 방식을 바꾸더라도 API 응답은 유지할 수 있다.
배운 점
이 작업을 통해 API 응답 구조의 중요성을 배웠다.
백엔드 개발을 처음 할 때는 데이터를 잘 조회하는 것에 집중했다.
하지만 실제 협업에서는 데이터를 어떤 모양으로 내려주는지도 매우 중요했다.
특히 페이지네이션 응답은 프론트엔드 UI 동작과 직접 연결된다.
- 다음 페이지가 있는지
- 다음 요청에 어떤 값을 넘겨야 하는지
- 현재 응답이 몇 개의 데이터를 포함하는지
이 정보가 명확해야 화면 구현이 쉬워진다.
마무리
커서 기반 페이지네이션과 CustomSlicePageResponse 정리는 작은 작업처럼 보일 수 있다.
하지만 이 작업을 하면서 API는 단순히 데이터를 내려주는 것이 아니라, 클라이언트와 서버 사이의 약속이라는 점을 다시 느꼈다.
앞으로도 내부 구현을 그대로 노출하기보다, 사용하는 쪽이 이해하기 쉬운 응답 구조를 먼저 생각해야겠다.
Leave a comment