▶️상황
현재 Art:Care 라는 프로젝트를 진행중인데, 앱에 무한스크롤 기능을 구현해야 했다. 스크롤을 내릴 때 이전 데이터가 중복되서 갱신되지 않고, 마지막 데이터 기준으로 데이터를 계속 불러오게 해야한다.
페이지네이션이란?
대용량의 데이터에서 필요한 데이터만 전달하는 방법이다.
크게 오프셋 기반 페이지네이션(Offset Based Pagination) 과 커서 기반 페이지네이션(Cursor Based Pagination) 이 있다.
페이지네이션을 사용하면 한 번에 로드해야 하는 데이터 양이 줄어들어 로딩 시간을 줄일 수 있다.
▶Offset Based Pagination
오프셋 기반 페이지네이션에 대해 간단하게 알아보겠다.
일정 Offset부터 일정 개수의 아이템을 가져오는 방식이다. 예를 들어 게시판에 100개의 게시글이 있고, 1페이지에 10개의 게시글이 들어간다고 가정해보자.
1.사용자 A가 1페이지에서 10개의 게시글을 조회했다.
2.사용자 A가 아닌 다른 사용자들이 10개의 개시글을 생성했다.
3.사용자 A가 2페이지를 조회했다.
4.사용자 A는 1단계에서 봤던 10개의 게시글을 똑같이 보게된다.
이처럼 오프셋 기반 페이지네이션은 데이터 중복 문제를 야기한다. 만약 해당 게시판에 생성 또는 삭제가 빈번하게 일어나는 경우, 데이터들이 뒤죽박죽 될 것이고 사용자들에게 혼란을 야기시킬 수 있다.
성능 문제
오프셋 기반 페이지네이션은 페이지 번호가 증가할수록 데이터베이스나 서버에서 불필요한 작업을 수행해햐한다. 페이지 100에 있는 게시글을 보려면 처음부터 100번째 페이지의 모든 데이터를 읽어야 하기 때문이다. 즉 Offset 값이 커지면 커질수록 성능 저하 문제가 발생한다.
▶Cursor Based Pagination
Cursor 라는 것이 있는데, 이 Cursor는 게시글에서는 게시글을 불러오는 '기준'인 postId 가 되는 것이고, 댓글에서는 댓글을 불러오는 기준인 commentId가 되는 것이다. 즉, 사용자에게 응답해준 마지막 데이터의 식별자 값을 의미한다.
마지막 데이터를 기준으로 다음 n개의 데이터를 응답해주는 방식이다.
▶구현 QueryDSL
where에 있는 postIdLessThan(cursorId) 만 보면된다.
처음 페이지인 경우
처음 페이지인 경우 null 또는 0인 값을 받게 하였다. 즉, 클라이언트에서는 cursorId에 대한 값을 보내지 않는다. null 인경우 where 절은 실행되지 않는다.
일반 페이지인 경우
postId 보다 작은 값을 가져오는 where 절이 실행된다.
▶마무리
현재는 id 값을 cursorId로 사용하고 있어서 쉬웠지만, 만약 다른 조건으로 정렬해야한다면 여러 문제가 생길 수 있을 것같다. 예를 들어 정렬해야하는 조건이 중복이 되는 경우에 데이터들이 생략되는 경우가 있을 것이다. 예를 들어 cursorId 를 day로 했다고 가정해 보자. 2월 16일 게시글이 8개 2월 17일 게시글이 12개 일 때 10개씩 불러온다고 해보자. 맨 마지막은 17일 일것이고 cursorId는 17일이 될 것이다. 그렇다면 2월 17일의 데이터 10개는 불러오지 않게된다. 이러한 조건이 있다면 다른 unique 값과 연결 지어 해결하면 좋을 것같다...