이 글에서는 기본적인 페이지네이션 종류와 차이점, 그리고 실무 경험으로 바탕으로 각 페이지네이션 적용 방법, CursorPagination에서 오해 할 수 있는 깊이있게 다루고자 한다.
장고 rest-framework에서 기본적으로 제공하는 페이지네이션은 아래와 같다.
(https://www.django-rest-framework.org/api-guide/pagination/#pagination)
요청 시, 원하는 페이지(page) 번호를 정해서 가져오는 방법이다.
Request:
GET https://api.example.org/accounts/?page=4
Response:
HTTP 200 OK
{
"count": 1023
"next": "https://api.example.org/accounts/?page=5",
"previous": "https://api.example.org/accounts/?page=3",
"results": [
…
]
}
요청시, 원하는 지점(offset)과 그 지점으로 부터 데이터 갯수(limit)를 정해서 가져오는 방법이다.
Request:
GET https://api.example.org/accounts/?limit=100&offset=400
Response:
HTTP 200 OK
{
"count": 1023
"next": "https://api.example.org/accounts/?limit=100&offset=500",
"previous": "https://api.example.org/accounts/?limit=100&offset=300",
"results": [
…
]
}
요청 했던 데이터 기준으로 앞, 뒤로만 이동할 때 사용한다.
Request:
GET https://api.example.org/accounts/?cursor=bz0xJnI9MQ
Response:
HTTP 200 OK
{
"next": "bz0xJnI9MQ",
"previous": null,
"results": [
…
]
}
CursorPagination 이외에 위에 언급된 다른 페이지네이션과 응답값을 확인해보면 CursorPagination에 없는 키 값이 존재 하는데 "count" 값이다.
테이블에 데이터가 누적이 될 수록 전체 목록 조회 시, "count" 의 계산 시간이 커지기 시작한다. 내부적으로 전체 목록 갯수를 구하는 페이지네이션은 점차 느려지게 되기에 그런 경우에 한 에서는 CursorPagination 좋은 대안이 될 수 있고, 바꾸는 작업도 했었다.
첫번재 호출
SELECT ...
FROM `...`
WHERE (...
AND `employments_employment`.`id` < 1257)
ORDER BY `employments_employment`.`id` DESC
LIMIT 2
다음 페이지 호출
SELECT ...
FROM `...`
WHERE (...
AND `employments_employment`.`id` < 1255)
ORDER BY `employments_employment`.`id` DESC
LIMIT 2
CursorPagination는 정렬 key값을 기준으로 해서 비교연산(employments_employment
.id
< 1257)를 활용하여 쿼리를 생성하고 있다. 비교 연산으로 scope를 줄이고 limit로 데이터 갯수를 제한하고 있다.
비교 연산 시, 문제가 될 수 있는 부분은 위의 예제는 정렬 키 값이 'id'라 유니크 하지만 유니크하지 않는 키 값으로 비교 시, 중복 데이터가 다음 요청에서 누락이 될 수 있다고 생각 할 수 있다. 그러나 키 값이 중복이라도 그 부분까지 내부적으로 처리해서 쿼리를 만들고 있는게 확인이 되었다.
WHERE (...
AND `employments_employment`.`name` < 'bbb')
ORDER BY `employments_employment`.`name` ASC
LIMIT 2
OFFSET 1
필드가 name인 경우 중복으로 저장 할 수 있다. 이 때 limit과 offset으로 키 값이 중복이 있더라도 문제가 되지 않는다.
중복 값으로 비교연산을 하는 경우 개발팀 내부적으로 문제가 있을 수가 있다고 제기를 해서 생성되는 쿼리를 관찰 하였다.