이전에 페이징에 대해서 다룬 적이있다
이전에 다룬 페이징은 쪽지기능에 적용하기 위한 기능이었다
하지만 그 때 구현한 페이징 기법은 오프셋 기반 페이지네이션으로 쪽지 기능에서 사용하기엔 몇가지 문제점이 있었다
그래서 이를 보완하기 위해서 페이징 방식을 커서 기반 방식으로 변경하였다
오프셋 기반 페이지네이션 적용 : https://velog.io/@shameless8/SpringKotlin-Paging
오프셋 기반 페이지네이션은 간단하게 조회를 시작할 기준점을 정하여 그 기준에 따라 페이지 단위로 구분하여 데이터를 보여주는 방식이다
오프셋 기반 방식에는 두 가지 문제점이 있었다

예를 들어 게시판의 1페이지에서 20개의 게시물 목록을 보여주는데 그 사이에 5개의 게시물이 추가되었다
그리고 2페이지를 클릭했을 때는 어떻게 될까?
게시물은 1페이지부터 최신순으로 보이므로 1페이지에서는 방금 추가된 게시물 5개를 포함한 가장 최신 게시물들이 20개가 보여지고 2페이지에서는 1페이지에 있었던 마지막 5개의 게시물이 중복되어 보여진다
말이 어렵지 실제로는 간단한 문제이다
오프셋 기반 페이지네이션은 row값이 아주 클 때 offset값이 올라갈 수록 쿼리의 효율성은 떨어진다
예를 들어서 limit가 10 offset이 1000이면 총 1010개의 데이터를 읽은 후에 1000개의 데이터는 버려야 하기 때문에 row값이 커질수록 성능이 떨어질 가능성이 커진다
커서기반 페이지네이션의 방식을 간단하게 풀면 이렇다
클라이언트에게 보낸 마지막 row의 순서상 다름 row들을 n개 요청/응답하도록 구현하는 것이다
n개의 row를 스킵한 다음 10개를 주는 것이 아니라 마지막으로 보낸 row에 책갈피를 해놓은 후에 이 row부터 10개의 데이터를 주는 식이다
이전에 블로그에서 다룬 오프셋 기반 페이지네이션은 쿼리의 offset과 limit를 사용하여 구현하였다 그렇다면 커서기반 페이지네이션은 어떤 것이 다를까?
본인이 사용한 예시를 보며 간단히 설명하겠다
<select id="findDmListWithCursorBasedPaging" resultType="ResultDmDto">
select id,
IF(is_from_sender = #{isSender}, 1, 0) as is_me,
content,
time,
is_read,
room_id,
visible
from dm
where room_id = #{room_id} AND
#{pagingCursor} > id
order by id desc
LIMIT #{limit}
</select>
이 코드에서 중요한 것은 바로 where절에있는 "#{pagingCursor} > id" 부분이다
비유를 사용하여 설명을 하자면, 파라미터로 받은 cursor의 값이 id보다 큰 곳에 책갈피를 두고 그 이후부터 limit페이지만큼을 넘겨 보여주는것이다
이 쿼리문에서는 cursor가 id값으로 사용이되었지만 이는 꼭 id값이 아니어도 된다
쪽지 기능을 만들면서 사용됐던 offset기반 페이지네이션에 문제점이 다수 있다는 것을 알게되었고 이에 대한 해답으로 cursor기반 페이지네이션이 사용되고있다는것을 알게되었다
많은 문제를 해결할 수 있는 기법이기 때문에 매우 복잡할것 같았지만 오히려 offset기반 페이지네이션보다 훨씬 간단했다
느낀점이라고 하긴 뭐하지만 문제해결은 끝이 없는것같다