오늘은 mysql 기준 pagination 개념과 사용방식에 대해서 정리하려고 한다.
데이터베이스의 pagination 개념은 또 처음 보는 개념이라 스터디 하는 김에 포스팅한다!
검색 결과를 가져올 때 데이터를 쪼개 번호를 매겨 일부만 가져오는 기법
이때, 특정한 정렬 기준에 따라 + 지정된 갯수의 데이터를 가져오는 것
위 사진처럼 한 페이지 당 몇 개의 콘텐츠를 보여줄 것인지 쿼리를 작성하는 것을 뜻한다.
사용자가 상품 목록 등을 요청 시 결과 값이 총 100만개(매우 많을)일 경우 조회가 느려지는 상황을 방지하기 위해서 사용한다.
먼저 offset-based pagination을 살펴보려고 한다.
앞서 MySQL에서는 limit은 두 가지 방식으로 사용할 수 있는데, 의미는 학생 목록에서 뒤에서부터 ID 기준으로 정렬한 후, 21번째부터 60번째까지의 40개 데이터를 가져오는 것으로 동일하다.
LIMIT 20, 40 : 20개 건너뛰고(offset), 그다음 40개 가져오기
SELECT id FROM `students` ORDER BY id DESC LIMIT 20, 40
SELECT id FROM `students` ORDER BY id DESC LIMIT 40 OFFSET 20;
``
OFFSET: 시작 위치 (0부터 시작)
LIMIT: 가져올 개수
만약 1~20페이지의 row를 불러와서 유저에게 1페이지를 띄워준 후, 그 사이에 사이트 담당자가 5개의 상품을 새로 올렸을 경우 2페이지로 넘겼을 때, 1페이지에서 봤던 5개의 상품이 2페이지에 보여짐. => 중복 데이터를 보일 수 있다
물론 모든 것이 cursor페이지네이션으로 이루어져야 하는 것은 아니다.
왜냐? 필요없는 경우도 있기 때문!
그런 경우에 대해 한 번 나열해보도록 하겠다.
위에 설명한 내용처럼 커서 기반 페이징은 마지막으로 본 항목 이후부터 가져오는 방식이다.
📍 id = 3보다 더 옛날에 생성된 책 중에서 15개를 가져오는 쿼리
SELECT *
FROM book
WHERE created_at < (SELECT created_at FROM book WHERE id = 3)
ORDER BY created_at DESC
LIMIT 15;
📍 정렬 기준 1개
where절을 살펴보면 id가 996 이상인 것들을 5개(limit) 가져올 수 있는데
여기서, cursor가 product 테이블의 id 996이다.
SELECT id, title
FROM `products`
WHERE id < 996
ORDER BY id DESC
LIMIT 5
📍 정렬 기준 2개
커서 기반 페이지네이션을 위해서는 반드시 정렬 기준이 되는 필드 중 (적어도 하나는) 고유값이어야 하므로, 예시로 정렬기준이 price일 경우 무조건 고유값인 id를 두 번째 정렬 기준으로 추가해야 한다.
SELECT id, title, price
FROM `products`
WHERE
(price > 14100
OR
(price = 14100 AND id > 446))
ORDER BY price ASC, id ASC
LIMIT 5
=> 이 경우 클라이언트가 ORDER BY에 걸려있는 모든 필드를 알아야 하므로 문제가 생김
그래서 항상 같은 방향으로 특정 값을 부여하고, 이를 cursor로 사용할 수 있게 만들어야 한다.
📍 커스텀 cursor 생성
정렬 기준들을 문자열로 합쳐서 "고유하고 일관된 커서 값"을 만들어주려는 것
SELECT id, title, price,
CONCAT(LPAD(price, 10, '0'), LPAD(id, 10, '0')) as `cursor`
FROM `products`
ORDER BY price DESC, id DESC
LIMIT 5;
여기서 핵심은 CONCAT() + LPAD() 조합이다.
🧩 LPAD 함수란?
숫자나 문자열 앞쪽을 특정 문자로 채워서 고정된 길이로 만드는 함수이다.LPAD(값, 총길이, 채울문자) // LPAD(123, 5, '0') → "00123"
🧩 CONCAT 함수란?
문자열들을 붙여서 하나로 만드는 함수CONCAT(문자열1, 문자열2, ...)
함수 | 역할 |
---|---|
LPAD(price, 10, '0') | 가격을 앞에 0을 채워서 10자리 문자열로 변환 |
LPAD(id, 10, '0') | ID도 마찬가지로 10자리로 만듦 |
CONCAT(...) | 이 둘을 붙여서 하나의 "정렬 기준 커서값"으로 사용 |
위 쿼리는 아까 order by에 사용되었던 정렬 기준인 가격과 id를 lpad 문법으로 10자리 문자열로 변환한 뒤, 합쳐서 하나의 정렬 기준 커서값인 "cursor"로 만들어 절대 순서를 만든 것이다.