Offset vs Cursor

박종하·2025년 8월 28일
post-thumbnail

Pagination?

페이지네이션은 대량의 데이터를 일정 단위로 잘라 제공하는 방법이다. 단어 그대로 "페이지로 나눈다."라는 의미를 담고 있다. 개발과 관련없이 이 세상에 "페이지"라는 개념이 없다고 가정해보자. 책을 읽을 때 수많은 글자가 끊임없이 이어져 있다면 읽기도 매우 불편할 것이고, 특정 내용이 나오는 위치를 찾아가기도 힘들 것이다.

데이터도 마찬가지이다. 수만건의 데이터를 한 번에 보여주면 조회하는 사람도, 가져오는 리소스도 많이 들게 된다. 이러한 이유로 페이지네이션을 사용하게 된다.


구현 방식

페이지네이션은 크게 두 가지 구현 방법이 있다.

  • Offset
  • Cursor

Offset

Offset 방식은 offset을 준 지점부터 limit만큼 레코드를 가져오는 방식이다

SELECT * 
FROM ~ 
ORDER BY id
LIMIT 10 OFFSET 30;
  • 31~40 조회

EXPLAIN을 통해 어떻게 쿼리가 실행되는지 알아보자. 테스트를 위해 9.1.0 버전의 MySQL을 docker로 실행한 상태에서 만 건의 데이터를 넣었다. 처음의 100건을 조회하는 경우와 5001번째부터 100건을 조회하는 경우를 알아보았다.

결과를 보게 되면 첫 번째 경우에는 100건, 두 번째 경우에는 5100건을 읽게 된다. 두 번째 경우에서도 100건만 읽어올 것이라는 예상과는 다를 것이다.

offset은 처음부터 5100건을 읽고, 앞의 5000건은 버리는 방식으로 동작한다. 데이터가 적거나, 앞의 데이터만 필요한 경우에는 문제가 안되지만, 데이터가 양이 많아질수록 뒤쪽 데이터에 대한 조회가 느려질 수 밖에 없다.

Offset이 앞의 데이터를 읽는 이유

인덱스가 있는데 왜 앞의 데이터를 굳이 읽어오는 것에 대한 의문이 있을 수 있다. 이는 인덱스를 잘 생각해보면 간단하다. 위의 쿼리에서 정렬에 PK인 id에 인덱스가 사용되기는 하나, 인덱스는 레코드의 저장 위치를 나타내는 것이지, 몇 번째에 있는지를 보장하지는 않는다.

idname
1Park
2Kim
3Go
4Kim

예를 들어 위와 같은 테이블에서 ORDER BY name으로 페이지네이션을 한다고 가정해보자. name을 기준으로 정렬하면 Kim이 중복되어 나타나는데, 단순히 인덱스만으로는 해당 Kim이 정렬된 결과에서 몇 번째인지 보장할 수 없다. 따라서 OFFSET은 원하는 위치를 정확히 보장하기 위해 앞에서부터 데이터를 순차적으로 읽어와야 한다. 반대로 생각하면, 유니크한 값이 아니어도 몇 번째인지 알 수 있다는 장점이 있다.

Cursor

Cursor 방식은 where구문으로 이용해 특정 지점 이후의 limit만큼 조회하는 방식이다.

SELECT *
FROM users
WHERE id > 2
ORDER BY id
LIMIT 10;
  • id가 2를 처음 넘는 곳에서부터 10건 조회

Offset 방식과 동일하게 쿼리를 수행해 보았다. actual을 보게 되면 두 쿼리 동일하게 100건을 조회하는 것을 알 수 있다. 인덱스를 통해 특정 지점에 바로 접근하기 때문에 뒤쪽의 데이터라도 동일한 성능으로 조회할 수 있다는 장점이 있다. 하지만 Cursor 방식은 정렬 기준이 유니크한 값을 기준으로 하지 않을 때 문제가 생긴다. 값이 중복되면 일관성이 깨질 수 있기 때문이다.


정리

Offset

  • offset번째부터 limit만큼 조회.
  • 장점: 간단한 구현, 임의의 페이지로 바로 이동이 가능, 몇 번째인지 반드시 보장 가능.
  • 단점: 첫 레코드부터 읽어오는 이슈로 인한 성능적 저하.

Cursor

  • where맞는 조건에서부터 limit만큼 조회.
  • 장점: 무한스크롤 구현 시 유용, 뒤쪽 데이터도 동일 성능 보장.
  • 단점: 유일한 정렬 기준 필요, 특정 페이지로 바로 이동의 어려움.

*Cursor 방식에서 특정 페이지 접근의 경우, 페이지 크기를 고정시켜 놓고 id가 연속적으로 단조 증가하면 가능하다(DELETE 불가).

두 방식 모두 장단점이 명확하니 도메인과 상황에 맞춰 유연하게 사용하면 좋다.

profile
Ex Pilot, Now Developer

0개의 댓글