๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
limit๊ณผ offset์€ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์˜ ๊ฐœ์ˆ˜๋ฅผ ์ œํ•œํ•˜๊ณ  ํŽ˜์ด์ง€๋„ค์ด์…˜์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.

limit

์กฐํšŒํ•  ๋ ˆ์ฝ”๋“œ ์ˆ˜๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

// ์ƒ์œ„ 10๊ฐœ๋งŒ
const users = await puri.table("users")
  .select({ id: "users.id", name: "users.name" })
  .orderBy("users.created_at", "desc")
  .limit(10);

// LIMIT 10

์ตœ์‹  N๊ฐœ

// ์ตœ์‹  ๊ฒŒ์‹œ๋ฌผ 20๊ฐœ
const recentPosts = await puri.table("posts")
  .select({
    id: "posts.id",
    title: "posts.title",
    created_at: "posts.created_at"
  })
  .orderBy("posts.created_at", "desc")
  .limit(20);

offset

๊ฑด๋„ˆ๋›ธ ๋ ˆ์ฝ”๋“œ ์ˆ˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

// ์ฒ˜์Œ 10๊ฐœ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๊ทธ ๋‹ค์Œ 10๊ฐœ
const users = await puri.table("users")
  .select({ id: "users.id", name: "users.name" })
  .orderBy("users.id", "asc")
  .limit(10)
  .offset(10);  // 11~20๋ฒˆ์งธ ๋ ˆ์ฝ”๋“œ

// LIMIT 10 OFFSET 10

ํŽ˜์ด์ง€๋„ค์ด์…˜

limit๊ณผ offset์„ ์กฐํ•ฉํ•˜์—ฌ ํŽ˜์ด์ง€๋„ค์ด์…˜์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ํŽ˜์ด์ง€๋„ค์ด์…˜

function getPage(page: number, pageSize: number = 20) {
  return puri.table("posts")
    .select({
      id: "posts.id",
      title: "posts.title",
      created_at: "posts.created_at"
    })
    .orderBy("posts.created_at", "desc")
    .limit(pageSize)
    .offset((page - 1) * pageSize);
}

// 1ํŽ˜์ด์ง€ (1~20)
const page1 = await getPage(1);  // offset: 0, limit: 20

// 2ํŽ˜์ด์ง€ (21~40)
const page2 = await getPage(2);  // offset: 20, limit: 20

// 3ํŽ˜์ด์ง€ (41~60)
const page3 = await getPage(3);  // offset: 40, limit: 20

์ด ๊ฐœ์ˆ˜์™€ ํ•จ๊ป˜

async function getPageWithTotal(page: number, pageSize: number = 20) {
  // ์ด ๊ฐœ์ˆ˜
  const [{ total }] = await puri.table("posts")
    .select({ total: Puri.count() })
    .where("posts.published", true);

  // ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ
  const posts = await puri.table("posts")
    .select({
      id: "posts.id",
      title: "posts.title"
    })
    .where("posts.published", true)
    .orderBy("posts.created_at", "desc")
    .limit(pageSize)
    .offset((page - 1) * pageSize);

  return {
    data: posts,
    pagination: {
      page,
      pageSize,
      total,
      totalPages: Math.ceil(total / pageSize)
    }
  };
}

์‹ค์ „ ์˜ˆ์‹œ

async function getInfiniteScroll(cursor: number = 0, limit: number = 20) {
  const posts = await puri.table("posts")
    .select({
      id: "posts.id",
      title: "posts.title",
      created_at: "posts.created_at"
    })
    .where("posts.published", true)
    .where("posts.id", ">", cursor)  // ์ปค์„œ ์ดํ›„
    .orderBy("posts.id", "asc")
    .limit(limit);

  return {
    data: posts,
    nextCursor: posts.length > 0 ? posts[posts.length - 1].id : null,
    hasMore: posts.length === limit
  };
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
const result1 = await getInfiniteScroll(0, 20);     // ์ฒซ ํŽ˜์ด์ง€
const result2 = await getInfiniteScroll(result1.nextCursor, 20);  // ๋‹ค์Œ ํŽ˜์ด์ง€

limit(0)์˜ ์˜๋ฏธ

limit(0)์€ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
// 0๊ฐœ ๋ฐ˜ํ™˜ (๋นˆ ๋ฐฐ์—ด)
const users = await puri.table("users")
  .limit(0);

// []
์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ limit(0)์€ ๊ตฌ์กฐ ํ™•์ธ์šฉ์œผ๋กœ ์‚ฌ์šฉ๋˜์ง€๋งŒ, Puri์—์„œ๋Š” ๋นˆ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์„ฑ๋Šฅ ์ตœ์ ํ™”

1. ์ธ๋ฑ์Šค ํ™œ์šฉ

// โœ… ์ข‹์Œ: ์ •๋ ฌ ์ปฌ๋Ÿผ์— ์ธ๋ฑ์Šค
await puri.table("posts")
  .orderBy("posts.created_at", "desc")  // created_at ์ธ๋ฑ์Šค ํ•„์š”
  .limit(20);

// โŒ ๋‚˜์จ: ์ธ๋ฑ์Šค ์—†๋Š” ์ปฌ๋Ÿผ ์ •๋ ฌ
await puri.table("posts")
  .orderBy("posts.content", "desc")  // ์ธ๋ฑ์Šค ์—†์Œ
  .limit(20);

2. Offset ํฌ๊ธฐ

// โš ๏ธ ์ฃผ์˜: ํฐ offset์€ ๋А๋ฆผ
await puri.table("posts")
  .limit(20)
  .offset(100000);  // 10๋งŒ ๊ฐœ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  20๊ฐœ ์กฐํšŒ (๋А๋ฆผ)

// โœ… ์ข‹์Œ: ์ปค์„œ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋„ค์ด์…˜
await puri.table("posts")
  .where("posts.id", ">", lastId)
  .limit(20);

3. COUNT(*) ์ตœ์ ํ™”

// โŒ ๋‚˜์จ: ๋งค๋ฒˆ ์ „์ฒด ์นด์šดํŠธ
const [{ total }] = await puri.table("posts").select({ total: Puri.count() });

// โœ… ์ข‹์Œ: ์บ์‹ฑ
const cachedTotal = await cache.get("posts:total");
if (!cachedTotal) {
  const [{ total }] = await puri.table("posts").select({ total: Puri.count() });
  await cache.set("posts:total", total, 300);  // 5๋ถ„ ์บ์‹œ
}

์ปค์„œ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋„ค์ด์…˜

๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ์—๋Š” offset ๋Œ€์‹  ์ปค์„œ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

Offset ๊ธฐ๋ฐ˜ (๋А๋ฆผ)

// โŒ ํฐ offset์€ ์„ฑ๋Šฅ ์ €ํ•˜
async function getPage(page: number, pageSize: number = 20) {
  return puri.table("posts")
    .select({ id: "posts.id", title: "posts.title" })
    .orderBy("posts.id", "desc")
    .limit(pageSize)
    .offset((page - 1) * pageSize);  // ํŽ˜์ด์ง€๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ๋А๋ ค์ง
}

await getPage(1000, 20);  // offset: 19980 (๋งค์šฐ ๋А๋ฆผ)

์ปค์„œ ๊ธฐ๋ฐ˜ (๋น ๋ฆ„)

// โœ… ์ปค์„œ ์‚ฌ์šฉ (ํ•ญ์ƒ ๋น ๋ฆ„)
async function getNextPage(cursor: number | null, pageSize: number = 20) {
  let query = puri.table("posts")
    .select({ id: "posts.id", title: "posts.title" })
    .orderBy("posts.id", "desc")
    .limit(pageSize);

  if (cursor !== null) {
    query = query.where("posts.id", "<", cursor);
  }

  const posts = await query;

  return {
    data: posts,
    nextCursor: posts.length > 0 ? posts[posts.length - 1].id : null,
    hasMore: posts.length === pageSize
  };
}

// ํ•ญ์ƒ ๋น ๋ฆ„ (์ธ๋ฑ์Šค ์‚ฌ์šฉ)
const page1 = await getNextPage(null, 20);
const page2 = await getNextPage(page1.nextCursor, 20);
const page100 = await getNextPage(page99.nextCursor, 20);  // ์—ฌ์ „ํžˆ ๋น ๋ฆ„

์ฃผ์˜์‚ฌํ•ญ

1. ORDER BY ํ•„์ˆ˜

// โŒ ์œ„ํ—˜: ์ •๋ ฌ ์—†์ด limit (์ˆœ์„œ ๋ถˆํ™•์‹ค)
await puri.table("users")
  .limit(10);

// โœ… ์˜ฌ๋ฐ”๋ฆ„: ์ •๋ ฌ ํ›„ limit
await puri.table("users")
  .orderBy("users.id", "asc")
  .limit(10);

2. offset์€ 0๋ถ€ํ„ฐ

// โœ… ์˜ฌ๋ฐ”๋ฆ„
.offset(0)   // ์ฒซ ๋ฒˆ์งธ ๋ ˆ์ฝ”๋“œ๋ถ€ํ„ฐ
.offset(10)  // 11๋ฒˆ์งธ ๋ ˆ์ฝ”๋“œ๋ถ€ํ„ฐ

// โŒ ์—๋Ÿฌ: ์Œ์ˆ˜ ๋ถˆ๊ฐ€
.offset(-1)  // Error: Invalid offset: must be >= 0

3. limit๋„ 0 ์ด์ƒ

// โœ… ์˜ฌ๋ฐ”๋ฆ„
.limit(0)    // ๋นˆ ๋ฐฐ์—ด
.limit(10)   // 10๊ฐœ

// โŒ ์—๋Ÿฌ: ์Œ์ˆ˜ ๋ถˆ๊ฐ€
.limit(-1)   // Error: Invalid limit: must be >= 0

4. ์ผ๊ด€๋œ ์ •๋ ฌ

// โš ๏ธ ์ฃผ์˜: ์ •๋ ฌ์ด ์ผ๊ด€๋˜์ง€ ์•Š์œผ๋ฉด ์ค‘๋ณต/๋ˆ„๋ฝ ๊ฐ€๋Šฅ
await puri.table("posts")
  .orderBy("posts.created_at", "desc")  // ๊ฐ™์€ ์‹œ๊ฐ„ ์—ฌ๋Ÿฌ ๊ฐœ ์žˆ์„ ์ˆ˜ ์žˆ์Œ
  .limit(20)
  .offset(20);  // ์ค‘๋ณต ๋˜๋Š” ๋ˆ„๋ฝ ๊ฐ€๋Šฅ

// โœ… ์ข‹์Œ: ๊ณ ์œ  ์ปฌ๋Ÿผ ์ถ”๊ฐ€
await puri.table("posts")
  .orderBy("posts.created_at", "desc")
  .orderBy("posts.id", "desc")  // ๋™์ผ ์‹œ๊ฐ„์€ ID๋กœ ๊ตฌ๋ถ„
  .limit(20)
  .offset(20);

ํƒ€์ž… ์•ˆ์ •์„ฑ

// โœ… ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ
await puri.table("users")
  .limit(10)
  .offset(0);

// โŒ ํƒ€์ž… ์—๋Ÿฌ: ์Œ์ˆ˜
await puri.table("users")
  .limit(-10);  // Error: Invalid limit

// โŒ ํƒ€์ž… ์—๋Ÿฌ: ์Œ์ˆ˜
await puri.table("users")
  .offset(-5);  // Error: Invalid offset

๋‹ค์Œ ๋‹จ๊ณ„