본문 바로가기
database/orm

페이지네이션 offset 과 cursor 차이 ( prisma )

by cactuslog 2024. 4. 30.
데이터베이스에서 전체가 아닌 특정 범위의 레코드만 조회할 경우가 있다.
주로 offset 또는 cursor를 사용한다.

 

 

offset 기반 페이지네이션

1. offset 만큼 레코드를 건너뛰고 limit 만큼 레코드를 가져온다.

 

2. 이를 orm인 prisma에서는 skip, take로 표현한다.

const results = await prisma.post.findMany({
  skip: 3,
  take: 4,
})

 

 

3. 한 페이지당 보여주어야 할 게시물이 10개이고 3번 페이지로 이동한다면 20개를 스킵하고 그 다음부터 10개의 레코드를 조회해서 보여주면 된다.

const results = await prisma.post.findMany({
  skip: 10 * ( 3 - 1 ),
  take: 10,
})

 

장점

1. 2페이지에서 5 페이지로 한번에  이동할 수 있다.

 

2. cursor기반에서는 페이지 점프가 불가능하다.

 

3. 어떤 정렬 기준으로도 페이지네이션이 가능하다.

 

4. 유니크하지 않은 이름으로 정렬된 사용자 목록에서 10페이지로 직접 이동할 수 있다.

 

5. cursor 기반에서는 유니크하고 순차적인 column을 기준으로 정렬해야하기 때문에 점프가 불가능하다.

 

6. 유니크하고 순차적인 column은 auto increment 값이나 timestamp 같은 유일하고 순서가 있는 값을 말한다.

 

단점

1. 데이터가 많을 경우 성능에 이슈가 있다.

 

2. skip: 10000, take 10 인 경우 10000개의 행을 스킵하기 위해 10000개의 행을 모두 탐색하고 그 이후의 10개를 가져와야 하기 때문이다.

 

3. 대량의 데이터를 스킵하는 과정에서 CPU와 메모리 자원을 많이 쓰게된다.

 

4. 적절한 인덱스가 존재하더라도 많은 수의 행을 스킵하는 것은 인덱스를 효율적으로 사용하지 못하게 만든다.

 

5. 인덱스는 데이터 접근을 빠르게 하지만 많은 수의 행을 스킵하는 작업은 여전히 많은 인덱스 엔트리를 탐색해야 한다.

 

 

필터링과 정렬

const results = await prisma.post.findMany({
  skip: 200,
  take: 20,
  where: {
    email: {
      contains: 'Prisma',
    },
  },
  orderBy: {
    title: 'desc',
  },
})

 

 


커서 기반 페이지네이션

1. 커서 기반 페이지네이션은 마지막으로 액세스한 행의 키 값을 기억하고, 다음 쿼리에서는 그 키 값 다음부터 시작하는 방식을 사용한다.

 

2. 마지막 키 값 다음의 행부터 직접 접근하기 때문에, 불필요한 행을 스킵하는 데 필요한 인덱스 탐색이 감소한다.

 

첫 조회는 마지막 키 값이 없으므로 cursor를 생략한다.

const firstQueryResults = await prisma.post.findMany({
  take: 4,
  where: {
    title: {
      contains: 'Prisma'
    },
  },
  orderBy: {
    id: 'asc',
  },
})

 

두번 째 조회부터는 첫 조회의 마지막 id인 29가 cursor가 된다.

마지막 id인 29번 이후부터 조회해야하기 때문에 skip: 1을 해준다.

const secondQueryResults = await prisma.post.findMany({
  take: 4,
  skip: 1, // Skip the cursor
  cursor: {
    id: 29,
  },
  where: {
    title: {
      contains: 'Prisma',
    },
  },
  orderBy: {
    id: 'asc',
  },
})

 

커서 기반 주의사항

 

1. 커서의 값을 추측해서 넣으면 알 수 없는 위치로 페이지네이션 된다.

 

2. 데이터베이스 내부의 커서를 사용하는 것이 아니다.

 

3. 존재하지 않는 커서를 사용하면 null을 반환한다.

 

4. 커서를 기준으로 정렬해야하고 커서는 auto increment값 같이 유일하고 순차적이어야 한다.

 

5. 특정 페이지로 직접 점프할 수 없다.

 

6. 20페이지로 가기 위해서는 1부터 19페이지까지 먼저 요청해야한다.

 

 

뒷 페이지로 이동하기

1. take에 음수 값을 주면 뒤로 이동할 수 있다.

 

2. 채팅과 같이 마지막 요소들이 보여지고 위로 올리면 이전 요소로 이동하는 것에 사용한다.

const firstQueryResults = await prisma.post.findMany({
  take: -4,
  skip: 1,
  cursor: {
    id: 29,
  },
  where: {
    title: {
      contains: 'Prisma'
    },
  },
  orderBy: {
    id: 'asc',
  },
})