1. 페이지네이션(페이징) 이란 ?

- 서버와 클라이언트의 상황에서 보통 모든 데이터를 한 번에 가져오지 않습니다.

 

- 보통 필요한 갯수를 지정하고 상황에 맞춰 정렬기준이 조건에 추가됩니다( 정렬기준 + 갯수)

 

- 이러한 조건을 맞춰서 데이터를 가져오는 것을 Pagenation(페이지네이션), 페이징 이라고 합니다.

 

- 페이지네이션을 처리하는 방법으로는 대표적으로 1. 오프셋 기반 페이지네이션, 2.커서 기반 페이지네이션

  으로 처리 가능합니다.

 

- 비교적 구현이 매우 간단한 오프셋 기반 페이지네이션 부터 살펴보겠습니다.

 

 

 

2. Offset-based Pagination (오프셋 기반)

 

- 제가 사용하는 MySQL로 말씀드리자면 LIMIT을 사용하여 개수를 지정하면 됩니다.

SELECT UserId FROM membership ORDER BY CreateAt desc LIMIT 20,40

 

- 좀 더 일반적인 변수를 사용한 쿼리문은 이렇게 되겠네요

( pagenum -> 페이지(1.2.....) )

( takenum -> 한번에 불러올 데이터(row) 수)

'SELECT UserId FROM membership
ORDER BY CreateAt DESC
LIMIT' + ( $takenum*($pagenum-1))+','+$takenum;

- 이렇듯 매우 직관적이고 이해하기가 쉽습니다. 사용하기도 편하구요.

 

- 보통 밑에 이동할 수 있는 페이지가 있고 페이지를 뛰어 넘어갈 수 있다면 offset 기반입니다.

 

- 하지만 제가 생각하는 가장 와닿는 문제는 예를 들겠습니다.

 

- 사용자가 매우 많은 사이트에서 글을 쓰는 사람이 매우 많다고 하겠습니다.

 

- 초당 평균 20개의 새로운 데이터가 들어온다고 가정하고, 한 페이지당 20개의 데이터를

 

- 보여준다면, 이를 오프셋 기반으로 구현을 한다면 1페이지의 20개의 글을 본 사람이

 

- 2페이지의 글을 보려고 눌렀을때, 방금 본 20개의 데이터를 그대로 다시 보게 됩니다.

 

- 즉, 중복데이터를 출력하게 됩니다. 그리고 이런 최악은 아니더라도 생각보다 이런 상황은

 

- 빈번하게 나타나고, 아마  경험하신 분들도 많이 있으실거라 생각합니다.

 

- 또 다른 문제는, 정렬기준 조건에 따라서 row가 몇번 째 데이터인지 바뀌게 되는데,

 

- 당연히 DB는 모든 경우에 따른 rownum을 가지고 있지 않기때문에 

 

- row의 수가 많아질수록 성능에 문제가 생기게 됩니다.

 

-이러한 단점을 보완해줄 수 있는 것이 커서 기반 페이지네이션 입니다.

 

 

 

3. Cursor-based Pagination (커서 기반)

 

- 음.... cursor를 포인터라고 생각하면 이해가 빠를 것 같습니다.

 

- cursor를 만들어 놓고 "이 cursor가 가리키는 것 다음 부터 n개의 데이터를 주세요" 하는 방식입니다.

 

- 오프셋 방식을 굳이 예로 들자면 "n개의 데이터를 page-1만큼 스킵하고 그 다음부터 n개의 데이터를 주세요"

  가 될 것 같네요.

 

- 만약 cursor가 int로 된 UserId라면 UserId 자체를 커서로 사용하여도 상관이 없습니다. (UserId - PK)

 

- 하지만 cursor를 회원가입 시기(CreateAt) 라고 지정한다면 어떻게 될까요?????

(데이터가 만들어진 순서대로 출력하고 싶을 때)

 

- 상황 설명을 위해 LIMIT를 사용해서 현재 가정한 테이블의 상태를 확인해보겠습니다.

  (CreateAt은 설명의 편의성을 위해서 간단한 숫자로 대체하여 표현하겠습니다.)

 

SELECT UserId,CreateAt FROM membership 
ORDER BY CreateAt ASC LIMIT 4

 

UserId CreateAt
123 1
124 2
120 3
114 4

 

- 전혀 문제가 없어 보입니다. 하지만 만약에 UserId가 "114"와 동시에 가입한 115,116,117이 있을경우

 

- cursor를 CreateAt으로 설정할 경우 cursor>4 인 리스트를 다음페이지에 불러올 텐데 그럴경우

 

- 115,116,117의 데이터는 누락이 발생합니다.

 

- 여기서 알 수 있는 것은

 

- "커서 기반 페이지네이션을 위해서는 정렬 기준에 포함되는 필드 중 하나이상은 반드시 고유값을 가져야 한다는 것 "

 

- 설명을 위해 테이블의 상황을 살짝 수정하겠습니다. 아래와 같습니다.
  (말이 좀 안되지만 age는 중복값이 없는 고유값이라고 가정을 좀 하겠습니다.)

UserId CreateAt Age
123 1 17
124 2 18
120 3 19
114 4 20

- 이 다음 페이지의 데이터를 얻기 위해서는 다음과 같은 쿼리를 날리면 됩니다.

 

SELECT UserId,CreateAt,Age FROM membership
WHERE (CreateAt >4) or (CreateAt=4 and Age>20)
ORDER BY CreateAt ASC, Age ASC LIMIT 4

- 이렇게 날리면 CreateAt이 4 인 115,116,117도 누락되지 않습니다.

 

- 문제가 모두 해결된 것 처럼 보이지만 여기서도 제가 생각하는 문제점 중 하나는

 

- 이렇게 조건절에 필요사항이 달림에 따라서 클라이언트 측에서도 이를 이해하고

 

- ORDER BY에 달려있는 필드 들을 이해하고 이에 해당하는 값들을 요청시 마다 보내야 하는데

 

- 당연히 클라이언트 측은 그걸 좋아하지 않고 원치 않습니다

 

- 그러므로 WHERE절에 걸리는 조건들을 이용해서 고유한 값인 CURSOR(커서)를 만들면 됩니다.

 

- 간단한 예시로 CreateAt이 최대 6자라고 하고 AGE가 최대 3자 라고 가정하면,

  

- 두 가지를 합쳐서 "4" + "17" => "000004017" 이런 식으로 가공하여 CURSOR 값으로 사용을 하는 것입니다.

 

- 커서를 기준으로 출력 후 , 그 다음 리스트는 이 커서 기준으로 다음부터 데이터를 가져오면

 

- 위의 문제들이 해결된다는 것을 알 수 있습니다.

 

 

 

 

 

4. Offset vs Cursor

 

- 제 생각에는 왠만하면 커서를 이용하는 것이 좋은 것 같습니다만

 

- 데이터의 입력이 매우 드물고, 또는 중복되는 데이터가 나와도 크게 상관이 없는경우

 

- 데이터의 수 자체가 그리 많지 않아서 성능에 딱히 연연하지 않아도 되는 경우

 

- 등에서는 오프셋을 사용해도 크게 문제되지는 않을 것 같습니다.

 

- 하지만 유저가 접속해서 사용하는 경우 커서 기반을 사용하는 것이 좋습니다.

'Web & App > Mysql' 카테고리의 다른 글

Query문의 실행 순서  (2) 2020.10.27
mysql 외부 접속 허용  (0) 2020.08.17

+ Recent posts