보여줄 데이터는 많으나, 한번에 다 보여줄 수 없을 때 데이터들을 정렬해서 보여주는 기능으로 Paging이라고도 부른다.
오프셋 기반 페이지네이션(offset pagination)
(offset, limit)쿼리를 사용해 <<1 | 2 | 3 | 4 | 5>> 이런식으로 화면에 보여준다.
커서 기반 페이지네이션(cursor pagination)
무한 스크롤이나 더보기 기능 구현할 때 사용된다 인스타, 페북 등 sns를 생각하면 된다.
offset 방식은 전체 데이터에서 "offset부터 ~ limit까지"만 가져오는 방식이다.
즉, page는 어디서부터 가져올지 시작할 offset 값과, pageSize는 offset에서 몇 개의 데이터를 가져올지를 제한하는 수이다.
pagination은 Front-end & Back-end 모두 구현해야 한다. 프론트에서 현재의 위치(offset) 과 ~까지 보여줄 컨텐츠의 수(limit) 를 백엔드에 전달하면 백엔드에서는 해당하는 데이터를 어떻게 끊어서 보내줄지 방식을 구현해야 한다.
예를 들어 limit 7000,100이라 한다면 offset 방식은 7000개의 데이터를 모두 읽은 다음 처음 7000개를 버리도록 요청하고 제한했던 100개의 데이터를 가져오게 된다.
이 작업에서 응답 시간과 비용이 들고 결과를 다 가져와서 그 안에서 limit을 걸기 때문에 전체 게시글의 수를 알기는 편하지만, 그만큼 모든 데이터를 거치기 때문에 결과 값이 많아질수록 성능에 문제가 생길 수 밖에 없다
https://www.eversql.com/faster-pagination-in-mysql-why-order-by-with-limit-and-offset-is-slow/
(위 글은 MySQL의 limit, offset 방식이 느린 이유와 이를 개선할 수 있는 방법이 적혀 있다. )
최종 프젝에서 express 대신 Nest.js, TypeORM을 선택한 나는
take, skip을 써서 pagination을 구현했다.
take: 한 번에 보여줄 개수 - mysql에서의 limit을 생각하면 된다.
skip: 말 그대로 몇 개를 건너뛰고 보여줄건지 - offset을 생각하면 된다.
즉, offset 처럼 모든 데이터들을 거치지 않고 skip해 사용자가 원하는 특정 페이지 임의의 페이지에 접근해 바로 이동이 가능하다.
사용자가 가져간 마지막 row의 순서상 다음 row들을 n개 요청/응답하는 구현
offset 기반은 사용자가 원하는 데이터가 '몇 번째'에 있다는 데에 집중한다면
커서 기반은 사용자가 원하는 데이터가 '어떤 데이터 다음'에 있다는 데에 집중한다.
n개의 row를 skip 한 다음 10개 줘 가 아니라, 이 row 다음꺼부터 10개 줘를 요청하는 식인 것이다.
나는 offset은 모든 데이터를 다 거쳐 결과를 보여주고
skip, take를 쓰면 앞선 데이터들은 다 건너뛰어 필요한 데이터만 가져오기 때문에 offset은 보단 skip이 성능 면에서 더 뛰어나다 생각했다.
하지만 여러 글들을 보며 정말 많은 데이터를 다룰때 offset, limit이나 skip, take나 성능 차이는 나지 않고 그저 조금 다른 방식일 뿐이리걸 알았다.
skip도 데이터가 많아지면 건너뛰는 데에 시간이 들고 비용까지 든다는걸 알게 되었다.
skip과 take는 내가 사용했던 typeorm에서 지원하는 기능일 뿐이였다.
typeorm에서 offset 방식보다 skip,take 방식이 더 직관적이어서
가독성이 좋아 유지보수에 용이하다는걸 알았고
typeorm 공식페이지를 보면 limit에서는 join을 할 경우 결과값이 나오지 않을 수 있다고 나와있다.
그 이유는
https://stackoverflow.com/questions/68468192/difference-between-limit-and-take-in-typeorm
(여기에 있다)
limit은 select 당시에 limit을 먼저 실행한 후 join을 실행하기 때문에 원하는 값이 나오지 않는다는 것이다.
반면에 take은 먼저 join까지 적용하여 모든 select를 한 후에 mapping하여 페이징 처리를 한다는 것이 다른 점이였다.
그래서 join이 없으면 limit을 사용하는 것이 효율적이지만 join이 있는 경우에는 take를 사용하는게 좋다는 것 같다
클라이언트가 페이징 처리를 할 때 백엔드는 분명히 페이징 처리를 해야될 때가 온다.
그리고 orm을 typeorm 쓰고 있는 개발자라면 take를 쓸 것인지 limit을 쓸 것인지 고민해야될 때가 온다.
typeorm에서 처리하는 방법을 제시하고 있다