React 페이지네이션 전략에 대한 고민

김민서·2025년 4월 3일

DOKBARO

목록 보기
6/6
post-thumbnail

들어가기 앞서

이 포스팅은 DOKBARO를 개발하면서 경험한 것을 기반으로 제작하였습니다.

DOKBARO란 ?

자기개발과 성장을 위해 독서와 스터디를 활용하는 개발자들을 위한 퀴즈 학습 플랫폼, DOKBARO입니다.

개발 서적을 즐겨 읽지만, 매번 내용을 제대로 이해했는지 확인하기 어렵지 않으셨나요? 혹은 이해 부족으로 인해 독서 스터디가 소수만 적극적으로 참여하는 형태로 변질되는 경험을 하셨을지도 모릅니다.

그래서, DOKBARO는

📚 퀴즈 출제 및 풀이 기능으로 도서 내용을 재미있고 효과적으로 이해하도록 도와드려요.

💡 스터디 리포트 기능으로 스터디원들이 책에 대해 자유롭게 의견을 나누고, 서로의 학습 현황을 확인할 수 있어요.

DOKBARO와 함께라면 도서 이해도를 높이고, 스터디 활동을 보다 풍성하고 활발하게 만들어 이상적인 독서 환경을 경험하실 수 있습니다. ✌️

현재 베타 오픈중이니 아래 링크를 통해 이용해보실 수 있어요!
https://dokbaro.com


개요

이번 독바로 프로젝트를 진행하면서, 효과적인 페이지네이션 구현 방법에 대해 고민하게 되었다. 처음에는 메인 페이지의 책 목록에 대해서만 고려했지만, 다른 영역에서도 페이지네이션이 필요하게 되면서 자연스레 각 상황에 적합한 최적의 방식이 무엇일지 고민하게 되었다.

1. 쿼리 스트링 기반 페이지네이션

쿼리 스트링 기반 페이지네이션은 현재 페이지 번호를 URL의 쿼리 스트링(Query String)으로 저장하는 방식이다.

예를 들어, 책 목록 페이지에서 페이지네이션을 적용할 경우

/books?page=100

이렇게 URL에 현재 페이지 번호가 반영된다.

장점

  • 새로고침해도 상태 유지 가능: URL이 상태를 포함하기 때문에, 새로고침하더라도 현재 페이지가 유지된다.
  • 링크 공유 및 북마크 가능: 특정 페이지를 URL로 공유할 수 있어 사용자 경험에 좋다.
  • 뒤로 가기/앞으로 가기 지원: 브라우저의 내장된 네비게이션 기능을 활용할 수 있다.

단점

  • 여러 개의 페이지네이션 관리 어려움: 한 페이지에 여러 개의 페이지네이션이 있는 경우, 각 페이지 번호를 URL에서 관리하려면 복잡한 로직이 필요하다.

  • 불필요한 전체 리렌더링 발생 가능: URL이 변경되면 페이지 전체가 다시 렌더링될 수 있어 성능에 영향을 줄 수 있다.

2. 상태 기반 페이지네이션

상태 기반 페이지네이션은 컴포넌트 내부에서 상태를 관리하는 방식으로, URL과 관계없이 상태를 저장한다.

장점

  • 독립적인 상태 관리 가능: 여러 개의 페이지네이션이 있을 때, 각각의 상태를 독립적으로 유지할 수 있다.

  • URL이 깔끔해짐: 불필요한 쿼리 스트링을 줄여 URL이 길어지지 않는다.

  • 리렌더링 최적화 가능: 특정 컴포넌트만 렌더링할 수 있어 불필요한 전체 리렌더링을 방지할 수 있다.

단점

  • 새로고침 시 상태 유지 어려움: 상태가 메모리에 저장되므로, 새로고침하면 페이지 번호가 초기화된다.

  • 링크 공유 및 북마크 불가능: 현재 페이지 상태가 URL에 반영되지 않아, 특정 페이지를 바로 공유할 수 없다.

프로젝트에서의 적용

책 목록 페이지

책 목록 페이지에서는 페이지네이션을 쿼리 스트링 기반으로 관리했다.
책 목록 페이지에서의 쿼리 스트링 기반 페이지네이션

우선, 책 목록 페이지의 데이터 개수가 많았다. 한 페이지당 20개의 책 데이터를 불러왔을 때, 전체 페이지 수가 1,000페이지를 넘어간다.

이때, 사용자가 페이지를 새로고침을 했는데 다시 1페이지로 돌아가버리는 상황이나, 책 목록의 435페이지를 친구에게 공유할 수 없는 상황이 발생하지 않게 하기 위해 쿼리 스트링 기반 페이지네이션을 적용하는 것이 나을 것이라 판단했다.

마이페이지

마이페이지에서도 처음에 쿼리 스트링 기반 페이지네이션을 적용하고자 했다.
하지만 난관에 봉착했다.

마이페이지 > 내 스터디 페이지에서는 "풀어야 할 퀴즈"와 "제출한 퀴즈" 목록을 위한 두 개의 페이지네이션이 필요했다.
마이페이지 내에서의 상태 기반 페이지네이션

따라서 여기에 쿼리 스트링 기반 페이지네이션을 적용한다면,

/my/study-groups/${studyGroupId}/unsolvedPage=1&solvedPage=2

과연 이렇게 되어야 하는 것일까..에 대해 고민이 되었다.

이렇게 되었을 때 고민했던 부분은 다음과 같았다.

마이페이지 페이지네이션 방식 적용 시 고민되는 부분

  1. 마이페이지에서 특정 페이지 공유 기능이 필요한가?
  • 마이페이지는 개인적인 공간이므로 특정 페이지를 공유할 가능성이 낮다.
  • 물론 개인적인 메모 용도로 공유할 수도 있지만, 일반적인 사용 패턴을 고려했을 때 그 필요성이 크다고 보긴 어렵다고 생각했다.
  1. 새로고침했을 때 페이지 유지가 중요한가?
  • 마이페이지에서는 특정 페이지(예: 5페이지, 7페이지)로 URL을 통해 직접 이동할 필요성이 적고, 보통 최신 데이터를 확인하는 경우가 많다.
  • 이미 최신순 정렬 기능이 제공되므로, 특정 페이지를 유지할 필요성이 상대적으로 낮다.
  • 따라서, 새로고침 시 1페이지로 돌아가더라도 사용자가 원하는 정보(최신 퀴즈)를 빠르게 확인할 수 있어 불편함이 크지 않을 것이다.
  1. URL이 복잡해지는 문제
  • 두 개의 페이지네이션(unsolvedPage와 solvedPage)을 URL에서 함께 관리하면 구조가 복잡해진다.

따라서, 굳이 페이지 번호를 URL과 동기화하는 대신, Jotai를 통한 상태 관리로만 구현하는 방법을 생각하게 되었다.

그 이유는, 이미 Jotai를 통해 전역으로 페이지네이션 상태를 관리하고 있었기 때문이다.

원래 동적인 페이지네이션(페이지 번호에 따라 동적으로 페이지 목록을 계산하여 직관적인 UX 제공)을 구현해야 했기 때문에 페이지네이션 상태를 이미 관리하고 있었다.
따라서, 추가적인 로직 없이 상태 기반 페이지네이션을 적용할 수 있었으며, 이 방식이 가장 적합하다고 판단했다.

// 마이페이지 > 내 스터디 퀴즈 > 풀어야 할 퀴즈
export const myStudyUnsolvedQuizPaginationAtom = atom<PaginationType>({
  currentPage: 1,
  pagePosition: "START",
  totalPagesLength: undefined,
  middlePages: [],
  middlePagesLength: 6,
  isMiddlePagesUpdated: false,
});

// 마이페이지 > 내 스터디 퀴즈 > 제출한 퀴즈
export const myStudySolvedQuizPaginationAtom = atom<PaginationType>({
  currentPage: 1,
  pagePosition: "START",
  totalPagesLength: undefined,
  middlePages: [],
  middlePagesLength: 6,
  isMiddlePagesUpdated: false,
});

따라서 이렇게 마이페이지 > 내 스터디 페이지에서 "풀어야 할 퀴즈"와 "제출한 퀴즈" 목록의 각 페이지네이션이 독립적인 상태를 가지도록 상태 기반 방식을 적용했다.

마무리

페이지네이션 방식에 대해 깊이 고민해볼 수 있었다.
처음에는 단순히 "페이지네이션을 어떻게 구현해야 할까?" 라는 기술적인 부분에만 집중했지만, 실제로 적용해보니 사용자 경험(UX), 유지보수성, URL 구조의 복잡성 등 더 넓은 관점에서 고민해야 한다는 점을 깨닫게 되었다.

결국, 한 가지 방식이 절대적으로 더 좋은 것은 아니며, 페이지의 특성과 사용자 경험을 고려해 적절한 방법을 선택하는 것이 중요하다는 것을 느꼈다.

0개의 댓글