페이지네이션(Pagination)

Pagination의 개념

Pagination이란, 많은 데이터를 부분적으로 불러오는 기술을 의미한다.

 

우리가 일반적으로 데이터를 불러올 때, 만약에 모든 데이터를 불러오게 되면 매우 비효율적일 것이다. 데이터가 1억 개가 있다고 한다면, 사용자의 입장에서는 이 데이터를 모두 보지도 않는데 1억 개의 데이터가 모두 불러와질 때까지 기다려야 한다. 또한, 모든 데이터를 불러오게 되면 데이터를 보내는 데 드는 시간과 비용이 더 올라가게 되며, 이는 둘째치고 클라이언트 단에서 모든 데이터를 저장하기에 메모리가 부족할 것이다.

 

때문에 UX적 측면이나, 비용 효율화의 측면이나, 기술적인 한계의 측면에서 모든 데이터를 불러오기보다 일부의 데이터만을 불러오는 게 훨씬 효율적이다.

 

이것이 바로 Pagination의 개념이다.

 

본 글은 클라이언트 입장에서 바라본 페이지네이션의 개념이다.

Pagination의 종류

페이지네이션을 구현하는 방법은 크게 두 가지로 나뉜다. 하나는 페이지 기반 페이지네이션(Page Based Pagination)이고, 다른 하나는 커서 기반 페이지네이션(Cursor Based Pagination)이다.

Page Based Pagination

조금 말장난같기는 한데, 페이지네이션이라는 단어 자체가 페이지를 나눈다는 의미로 가져왔을 것이다. 아이러니하게도 현대의 UI에서 페이지 기반 페이지네이션은 더이상 많이 사용하지 않는다.

 

말 그대로 페이지를 기반으로 데이터를 잘라서 요청하는 방식이다. 요청을 보낼 때 몇 개의 데이터를 가져올지, 몇 번째 페이지를 가져올 지를 명시한다. 그래서 페이지 숫자를 누르면, 해당 페이지로 넘어가는 형태의 UI에서 사용하게 된다.

우리 학교 컴퓨터공학과의 공지사항들인데, 위와 같이 페이지 번호가 있고, 이에 맞추어서 쿼리를 보내는 게 바로 페이지 기반 페이지네이션이다.

 

우선 이 방법은 구현이 쉽다는 장점이 있다. 그리고 어디를 페이지네이션하는 지 명확한 네비게이션이 존재한다. 그러나 이 방법의 단점으로는 페이지네이션의 과정 중 데이터가 추가되거나 삭제될 경우, 데이터가 누락되거나 중복될 가능성이 존재한다.

동작 원리

위 그림과 같은 과정을 통해서 페이지네이션이 진행된다. 예를 들어 한 페이지 당 3개의 아이템이 들어가고, 페이지 넘버가 3이라면, 요청된 오프셋은 총 6이 될 것이다(3 items per page * (3-1)).

Page based Pagination의 문제점 - 데이터 신규 삽입 시 데이터 중복

이 상태에서, 만약에 서버에서 새로운 데이터가 추가되었다고 가정해보자. 물론 새로운 데이터의 삽입은 서버에서 이루어지기에, 클라이언트 단에서는 이 사실을 알 수 없다.

새로운 데이터의 삽입

이러한 상태에서, 클라이언트에서 다음 페이지로 페이지네이션을 해보자.

중복된 데이터의 발생

위와 같이, 중복된 데이터(id=3)가 발생하고 있다. 이처럼 페이지 기반 페이지네이션의 경우 신규 데이터가 삽입될 시 중복된 데이터가 발생할 수 있다.

Page based Pagination의 문제점 - 데이터 삭제 시 데이터 누락

이번에는 위 상태에서, 데이터가 삭제가 된다고 가정해보자. id=3인 아이템이 삭제된다.

데이터의 누락 발생

이때 3번 데이터가 삭제되었기에 서버에는 1, 2, 4번까지가 3개의 오프셋으로 남고, page 2에 대해서 페이지네이션 쿼리를 요청하면 id=5번부터 아이템이 불러와진다. 따라서, id=4인 아이템이 누락된다.

 

기본적으로 페이지 기반 페이지네이션의 경우 이러한 문제점이 존재한다. 그렇기 때문에 그에 대한 대안으로 나오게 된 것이 후술할 커서 기반 페이지네이션이다.

페이지 기반 페이지네이션의 예시

에브리타임의 페이지네이션

이것은 에브리타임의 페이지네이션인데, 여기는 페이지 번호가 없는대신 이전, 다음 버튼만 존재하는 식으로 UI를 구성하였다.

 

커뮤니티의 경우는 사실 정확한 페이지네이션 넘버가 필요하지 않은 경우가 있다. 커뮤니티의 글은 단발성으로 사라지는 글이기 때문에, 굳이 오래된 정보를 찾기 위해 내려갈 필요가 많지 않다. 그렇기 때문에 오직 이전, 다음 버튼만 표시해서, 최신의 글들을 위주로 화면에 표시하는 방식을 채택하는 경우가 종종 있다.

네이버 웹툰의 페이지네이션

네이버 웹툰의 페이지 기반 페이지네이션 방식이다. 여기서는 페이지 번호를 직접 표시하는 방식을 사용하고 있음을 알 수 있다. 사용자는 웹툰의 목록을 찾는 과정에서, 자신이 정확히 어느 위치에 있는지를 궁금해할 수 있다. 예를 들어 페이지 2에서 봤던 한 웹툰을 다시 보고 싶다든가 하는 식 말이다. 이때 빠르게 원하는 페이지로 돌아가기 위한 UI로는 이러한 페이지 기반 페이지네이션을 채택할 수 있다.

Cursor Based Pagination

  • 가장 최근에 가져온 데이터를 기준으로 다음 데이터를 가져오는 방식의 Pagination이다.
  • 요청을 보낼 때, 가장 마지막 데이터의 id와 함께 몇 개의 데이터를 가져올 지 개수를 명시한다.
  • 스크롤 형태의 리스트에서 자주 사용하며, 주로 어플리케이션에서 많이 사용하게 되는 형태이다. 무한 스크롤 방식이 바로 커서 페이지네이션 방식.
  • Keyset pagination, 혹은 Token Based Pagination이라고도 한다.

페이지 기반 페이지네이션에 비해서 비교적 구현이 복잡하다. 그러나 최근 데이터의 id 값(id 값이 아니더라도, 마지막 데이터를 식별할 수 있는 식별자)을 기준으로 쿼리가 작성되기 때문에 데이터가 중복되거나 누락될 확률이 낮아진다.

 

이때 이 식별자를 커서(Cursor)라고 한다.

 

대표적으로 SNS(인스타그램 등)가 이러한 방식의 커서 기반 페이지네이션을 채택한다. 유튜브도 이러한 방식의 커서 기반 페이지네이션을 채택하고 있다.

 

모바일에서는 거의 대부분의 앱이 페이지 기반 페이지네이션보다는 커서 기반 페이지네이션을 사용하고 있다. 커서 기반 페이지네이션은 사용성 측면에서 데이터가 연속적으로 불러와지다 보니, UX 측면에서 끊킴없는 연속된 경험을 제공할 수 있다. 특히 사용자가 앱에 오래 남아있어야 하는 SNS, 유튜브 등은 사용자의 경험이 끊어진다는 느낌이 들지 않도록, 연속된 스크롤 뷰 형태의 UI를 제공하는 경우가 대부분이다.

 

따라서 사용자의 체류 시간이 중요한 UI, 특히 모바일에서는 페이지 기반 페이지네이션보다는 거의 대부분 커서 기반 페이지네이션으로 동작한다.

동작 원리

커서 기반 페이지네이션에서 가장 처음에 3개의 데이터를 요청한다. 이때 클라이언트에서는, 들어온 데이터 중 가장 마지막 아이템의 id를 기억한다.

다음 요청을 날릴 때, 클라이언트에서는 마지막 아이템의 id인 3과 함께 추가로 3개의 데이터를 더 가져와달라고 서버에 요청한다. 서버는 id=3 초과의 데이터 3개를 반환한다.

이 과정을 반복한다.

 

주의사항: 만약에 클라이언트가 id를 보내는 게 아니라, 서버가 클라이언트에게 전송한 데이터의 마지막 id를 기억하고, 새롭게 추가 데이터 요청이 들어올 시 그 id를 기준으로 추가 데이터를 보내는 형식으로 서버를 구현한다면 어떨까? 이는 클라이언트 입장에서는 편할 수 있다. 그러나 이 경우 서버 측 구현이 매우 복잡해진다. 서버의 입장에서 각 클라이언트의 상태를 기억해야 하고, 이는 곧 수평적인 확장이 어려워짐을 의미한다. 서버는 최대한 stateless를 지향해야 하고, 최대한 passive하게 설계되어야 한다.

데이터가 새로 삽입된다면?

커서 기반 페이지네이션에서 데이터가 새로 삽입된다면 어떨까?

새로운 데이터 삽입
커서 기반에서는 데이터 중복 미발생

커서 기반 페이지네이션은 마지막 아이템의 id를 기반으로 하기에, 신규 데이터가 추가된다 하더라도 데이터의 중복 문제 없이 페이지네이션이 진행된다.

 

이때, 신규 데이터가 누락되는 것 자체는 어쩔 수 없다. 논리적 설계 측면에서도, 사용자가 관심있는 데이터는 id>3인 데이터지 id=2.5인 데이터는 현재의 관심사가 아니다. 이는 새로고침 등을 통해서 신규 데이터를 확보하는 게 자연스럽다.

데이터가 삭제된다면?

데이터가 삭제될 때도 살펴보자.

아이템 삭제 시
커서 기반에서는 데이터 누락 미발생

이때도 마찬가지로 id를 기반으로 하는 커서 기반 페이지네이션에서는 데이터 누락 문제 없이 페이지네이션이 동작한다.

커서 기반 페이지네이션의 예시

커서 기반 페이지네이션은 매 시간 새롭게 데이터가 fetch되는 실시간 환경에 매우 적합하다. 그 대표적인 예시가 바로 SNS.

사진 출처: https://www.gq.com/story/instagram-feed-chronology

인스타그램 피드가 대표적인 커서 기반 페이지네이션의 예시이다. 내리다보면 계속해서 포스트가 나오는 것을 확인할 수 있다. 페이지의 구분이 없다.

사진 출처: https://blog.twitter.com/ko_kr/topics/product/2022/how-recommendations-help-discover-more-twitter
사진 출처: https://kr.freepik.com/premium-psd/youtube-on-mobile-phone-mockup_13907859.htm

유튜브, X(구 twitter), 페이스북 등 정말 다양한 모바일 어플리케이션에서 커서 기반 페이지네이션 UI를 채택하고 있다.

 

선술했듯이 이러한 어플리케이션들은 모두 공통점이 있다. 사용자가 오래 머물수록 앱이 얻는 이익이 커진다. 따라서 앱은 최대한 사용자를 오래 붙잡고 있어야 하고, 그러기 위해선 연속된 UX 통해 몰입감을 극대화시켜야 한다.

 

페이지 기반 페이지네이션은 사용자가 페이지를 넘기는 순간, 흐름이 끊킨다. 그러나 커서 기반 페이지네이션은 끊킴 없는 UX를 제공한다는 점에서 장점이 존재한다.

나의 tistory 블로그인데, tistory에서 주는 기본 html & css를 사용하고 있다.

 

커서 기반 페이지네이션이나 동시에 ‘목록 더보기’라는 버튼을 눌러서 수동으로 데이터 추가 fetch를 요청해야 하는 UI를 띄고 있다. 이런 형태의 커서 기반 페이지네이션도 가능하다.

 

더 고급스러운 기법은, 위의 어플리케이션이 그렇듯이, 사용자의 스크롤 위치에 맞추어서, 스크롤 위치가 어느 시점으로 내려가면 자동으로 그 다음 데이터를 가져오는 방식이다. 이렇게 되면 사용자가 모르게 새로운 데이터 요청을 계속해서 할 수 있고, 사용자는 데이터가 끊어지지 않고 계속해서 들어오고 있다고 생각하게 할 수 있다.

비교 요약

  Cursor-based Pagination Page-based Pagination
방식 커서(식별자) 기반, 데이터의 한 지점을 기준으로 요청 페이지 넘버와 고정된 페이지 내 아이템 개수 기반으로 요청
구현 난이도 비교적 복잡함 비교적 간단함
변화 민감성 데이터가 변경되어도, 문제 없이 동작 데이터 변경 시 일부 데이터의 누락, 중복 발생 가능
사용 예시 무한 스크롤 UI에 사용 페이지 넘버링 UI에 사용
네비게이션 UI 데이터의 위치를 가늠하기 어려움 데이터의 위치가 직관적으로 드러남

References