본문으로 건너뛰기
개발 머꼬
개발 노트API 설계
hohyeon.dev24

페이지 번호 API를 커서로 바꾸면서 정한 응답 계약

  • #API 설계
  • #Engineering Note

문제 발생

목록 API가 ?page=3&size=20이었습니다. 깊은 페이지가 느렸고, 새 글이 추가되면 같은 글이 두 페이지에 나오거나 아예 건너뛰는 문제가 있었습니다.

커서 방식으로 바꾸기로 했는데, 이번에는 응답 형태를 어떻게 줄지가 문제였습니다. 초기 구현은 클라이언트가 마지막 항목의 id를 직접 꺼내 다음 요청에 넣는 방식이었습니다 — 정렬 기준을 바꾸는 순간 클라이언트도 함께 고쳐야 했습니다.

원인 분석

커서를 클라이언트가 조립하면 그것이 곧 계약이 됩니다. id를 커서로 쓴다는 사실이 API 계약에 새어 들어가면, 나중에 (좋아요 수, id) 복합 커서로 바꿀 때 모든 클라이언트가 깨집니다.

이동 방법을 응답에 담는 표준적인 방법이 있습니다. RFC 8288의 Link 헤더는 하나 이상의 링크를 HTTP 헤더로 직렬화하는 수단이고, 형식은 <URI-Reference> 뒤에 ;로 구분된 파라미터입니다. rel 파라미터로 관계를 지정하며, next/prev는 IANA Link Relations 레지스트리에 등록된 관계 타입입니다.

즉 "다음 페이지가 어디인지"를 URL 통째로 주면, 커서의 내부 구조를 클라이언트가 알 필요가 없습니다.

해결 방안

  1. 커서를 불투명한 문자열로 만듭니다. 내부 구조(정렬키, id)를 인코딩해 감춥니다. 클라이언트는 받은 값을 그대로 되돌려주기만 합니다.
{ "items": [...], "nextCursor": "eyJpZCI6MTIzNH0" }
  1. 다음 페이지 유무를 서버가 판정합니다. limit + 1개를 조회해 판단하면 별도 COUNT 쿼리가 필요 없습니다. 클라이언트가 "결과 수가 size보다 적으면 마지막"이라고 추론하게 두지 않습니다 — 필터링 때문에 틀릴 수 있습니다.

  2. Link 헤더로도 제공할 수 있습니다. 본문 형식을 바꾸기 어려운 API에 특히 유용합니다.

Link: </api/posts?cursor=eyJpZCI6MTIzNH0>; rel="next"
  1. 정렬 기준이 바뀌면 커서 구조도 바뀝니다. "인기순"처럼 유일하지 않은 키로 정렬하면 (정렬키, id) 복합 커서가 필요합니다 — 불투명 커서로 감춰두면 이 변경이 서버 안에서 끝납니다.

  2. 임의 페이지 점프가 정말 필요한지 확인합니다. 커서 방식은 "다음/이전"에는 완벽하지만 "347페이지로"는 못 합니다. 대부분의 피드에서 그 기능은 실제로 쓰이지 않으며, 필요하면 검색·필터로 좁히는 UX가 더 낫습니다.

  3. 커서를 신뢰하지 않습니다. 클라이언트가 보낸 값이므로 서버에서 파싱 실패·범위 이탈을 안전하게 처리합니다. 조작된 커서가 500이 되지 않게 합니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.