페이지네이션 (Offset, Cursor)

Woody·2024년 9월 16일

Pagination

목록 보기
1/1

페이지네이션이란?

데이터 셋이 수천만 건일 때, 해당 데이터를 모두 전송하는 것은 서버의 부하가 생기고, 클라이언트 또한 랜더링 시간이 증가되고, 과도한 스크롤로 사용자 경험이 안좋아진다.

이를 해결하기 위해서 데이터를 일정 개수로 잘라서 제공하면서 서버와 클라이언트 모두 윈-윈할 수 있다.

페이지네이션 방식에는 무엇이 있을까?

크게 offset 방식과 cursor 방식이 있다.

Offset 방식

offset 방식은 offsetlimit를 사용하는 방식이다.

여기서 offset n(자연수)은 n만큼의 데이터를 건너뛰라는 의미이고, limit n(자연수)은 n만큼의 데이터를 가져오라는 의미이다.

따라서 select * from table limit 10 offset 10 쿼리가 있을 때, 20개의 데이터를 조회해서 앞의 10개의 데이터는 건너뛰고, 뒤의 10개의 데이터만 가져온다.

offset 방식의 장점은 다음과 같다.

  • 구현하기가 매우 쉽다. 몇 개의 데이터를 가져올 것인지 정해서 limit의 값을 설정하고, 현재 몇 페이지인지 입력받은 값을 offset의 값으로 설정하면 페이지네이션 구현이 끝난다.

offset 방식의 단점은 아래와 같다.

  • 데이터 양이 많은 경우에 성능 저하가 발생한다.

    • offset 방식은 모든 데이터를 불러온 뒤, 해당 페이지 값이 아닌 데이터들은 모두 건너뛰어서 데이터를 전송한다.

    • 따라서 limit가 100이고, 현재 페이지가 200페이지라면 총 20,000개의 데이터를 조회한 후에 19,900의 데이터를 건너뛰어서 100개의 데이터를 전송한다.

  • 데이터의 삽입과 삭제가 자주 일어나는 경우 데이터가 누락되거나 중복될 수 있다.

총 8개의 데이터가 있고, 데이터를 3개씩 페이징해서 보고 있다고 가정하자.

사용자A가 1페이지를 보고 있다면 A,B,C 총 3개의 데이터를 보고 있을 것이다.

사용자A가 1페이지를 보고 있는 중에 사용자B가 데이터 C를 제거한 후에 사용자 A가 2페이지로 넘어간다고 해보자.

그 결과, 6개의 데이터를 가져와서 앞의 3개의 데이터를 건너뛰기 때문에, 사용자 A는 D,E,F가 아닌 E,F,G를 보게 되어 이전 페이지로 넘어가지 않는다면 데이터 D를 볼 수 없다.

마찬가지로 A,B,C 사이에 데이터 B-2가 추가가 된다면, 2페이지에서는 C,D,E를 보게 되어 데이터 C를 중복해서 보게 된다.

Cursor 방식

특정 포인터(커서)를 사용해서 필요로 하는 데이터가 있는 위치부터 시작해서 필요한 만큼 데이터를 전송하는 방식이다.

커서로는 순차적으로 증가한 id 또는 타임 스탬프, 인코딩된 커서, 복합 커서 등이 있다.

created_at 날짜를 기준으로 10개씩 커서 기반 페이지네이션을 한다고 가정해보자.

그렇다면 첫 데이터를 다음과 같이 가져오고, 클라이언트에게 반환할 것이다.

GET /table?limit=10
select * from table order by created_at desc limt 10
{
	"tables": [...]
	"cursor": "last_created_at"
}

이후 다음 페이지부터 다음과 같이 가져올 것이다.

GET /table?limit=10&cursor="last_create_at"
select * from table where created_at < 'last_created_at' order by created_at desc limt 10
{
	"tables": [...]
	"cursor": "last_created_at2"
}

cursor 방식의 장점으로는 다음과 같다.

  • 불필요한 데이터를 조회하지 않고, 필요한 데이터만 반환할 수 있다.

    • offset 방식과 달리 모든 데이터를 조회하지 않기 때문에 대규모 데이터 환경에 매우 효율적이다.
  • 일관된 결과를 제공한다.

    • 데이터가 변경되더라도 커서가 특정 위치를 가리키기 때문에 일관된 결과를 제공할 수 있다.

이러한 장점 덕분에 SNS와 같이 데이터가 자주 변동되는 경우에 cursor 방식이 유용하다.

cursor 방식의 단점은 다음과 같다.

  • 구현이 복잡하다.

    • 커서 방식의 성능은 인덱스에 따라서 결정된다. 만약 인덱스가 없는 컬럼을 커서로 사용한다면 오히려 좋은 성능이 안 나올 수 있다.

    • 장점으로 일관된 결과를 제공한다고 했다. 이는 커서가 순차적인 id 또는 타임스탬프일 경우 쉽게 구현할 수 있지만, 커서가 복합 커서 또는 필터와 같이 사용하는 경우 일관된 결과를 제공하기 위해 개발자가 고려해야 할 점이 많아진다.

offset, cursor 언제 사용할까

offset 방식의 경우 다음과 같을 때 사용하면 좋다.

  1. 데이터의 삽입과 삭제가 자주 일어나지 않거나 정확한 순서 보장이 필요하지 않을 때: offset 방식은 데이터 중복과 누락이 발생할 수 있다. 하지만 이점이 그렇게 중요하지 않다면 적용할 만하다.

  2. 데이터 양이 많지 않을 때: 데이터 양이 많을 때 성능 저하가 발생하기 때문에 데이터 양이 적다면 적용할 만하다.

  3. 페이지 건너뛰기가 필요할 때: 사용자가 페이지 번호를 입력해서 특정 페이지로 바로 이동하는 경우 offset을 사용하는 것이 유리하다.

cursor 방식의 경우 다음과 같을 때 사용하면 좋다.

  1. 대규모 데이터일 때: offset 방식의 경우 대규모 데이터를 조회할 때 성능 저하가 발생하므로 대규모 데이터에선 커서 방식이 적합하다.

  2. 정확한 순서 보장이 필요할 때: 커서 방식은 페이지 간 중복이나 누락 없이 데이터를 처리할 수 있기 때문에 데이터 순서를 유지해야 할 때 cursor 방식이 더 적합하다.

마치며

offset과 cursor 방식의 장 단점을 학습하면서 프로젝트 진행 상황, 데이터 양를 기준으로 페이징 방식을 선택할 것 같다.

참고

적절한 Pagination 방법은 뭘까?

Offset vs Cursor-Based Pagination: Which is the Right Choice for Your Project?

Cursor-based Pagination

0개의 댓글