> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Solana DAS API 페이지네이션: 대용량 데이터 세트 효율적 조회

> Solana DAS API의 페이지 기반 및 키셋 페이지네이션. 커서 및 범위 기반 전략과 병렬 쿼리를 통해 대규모 데이터 세트를 효율적으로 반복.

## 개요

DAS API 메소드는 호출당 최대 1,000개의 레코드를 반환합니다. 더 많은 데이터를 가져오기 위해 페이지네이션을 사용하여 여러 번 호출하고 데이터를 페이지별로 탐색합니다. Helius는 페이지 기반 및 키셋 페이지네이션 두 가지 메커니즘을 지원합니다.

페이지 기반 페이지네이션은 시작하기에 가장 간단한 방법입니다. 키셋 페이지네이션은 대규모(50만 개 이상) 데이터 세트를 효율적으로 쿼리하기 위한 고급 사용자용입니다.

## 사용 시기

* **페이지 기반** — 정적 뷰, 대시보드 및 대부분의 일상 쿼리. 쉽고 직관적입니다.
* **키셋(커서 또는 범위)** — 페이지 기반 탐색이 느려지는 대규모 데이터 세트(전체 컬렉션, 50만 개 이상 자산).
* **병렬 키셋** — 주소 범위를 분할하여 전체 컬렉션을 스캔하는 가장 빠른 옵션입니다.

## 정렬 옵션

결과를 `sortBy` 필드를 사용하여 다른 필드로 정렬할 수 있습니다:

| 값               | 정렬 기준          | 추천 여부 |
| --------------- | -------------- | ----- |
| `id`            | 이진 자산 ID(기본값)  | 예     |
| `created`       | 자산 생성 날짜       | 예     |
| `recent_action` | 자산 마지막 업데이트 날짜 | 아니오   |
| `none`          | 정렬 없음          | 아니오   |

정렬을 비활성화하면 가장 빠른 결과를 얻을 수 있지만 데이터가 정렬되지 않기 때문에 페이지네이션 시 일관되지 않은 결과를 얻을 수 있습니다.

## 페이지 기반 페이지네이션

페이지 번호와 페이지당 항목 수를 지정합니다. 다음 페이지로 이동하려면 페이지 번호를 증가시킵니다. 대부분의 사용 사례에 대해 쉽고 직관적이며 빠릅니다.

<Accordion title="예제">
  ```javascript theme={"system"}
  const url = `https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY`

  const example = async () => {
      let page = 1;
      let items = [];
      while (true) {
          const response = await fetch(url, {
              method: 'POST',
              headers: {
                  'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                  jsonrpc: '2.0',
                  id: 'my-id',
                  method: 'searchAssets',
                  params: {
                      grouping: ['collection', '5PA96eCFHJSFPY9SWFeRJUHrpoNF5XZL6RrE1JADXhxf'],
                      page: page,
                      limit: 1000,
                      sortBy: { sortBy: 'id', sortDirection: 'asc' },
                  },
              }),
          });
          const { result } = await response.json();
          if (result.items.length == 0) {
              console.log('No items remaining');
              break;
          } else {
              console.log(`Processing results from page ${page}`);
              items.push(...result.items);
              page += 1;
          }
      }
      console.log(`Got ${items.length} total items`);
  };
  example();
  ```
</Accordion>

페이지를 사용하는 경우 데이터베이스는 다음 페이지에 도달할 때까지 모든 항목을 탐색해야 합니다. 예를 들어, 페이지 크기가 1,000인 페이지 100을 요청하면 데이터베이스는 데이터를 반환하기 전에 첫 100만 레코드를 탐색해야 합니다. 이러한 이유로 대규모 데이터 세트에는 페이지 기반 페이지네이션이 권장되지 않으며, 키셋 페이지네이션이 더욱 적합합니다.

## 키셋 페이지네이션

데이터 세트를 필터링하는 조건을 제공하여 페이지를 정의합니다. 예를 들어, "ID가 X보다 크고 Y보다 작은 모든 자산을 가져옵니다." 각 호출 시 X 또는 Y를 수정하여 전체 데이터 세트를 탐색합니다. 키셋 페이지네이션에는 두 가지 방법이 있습니다:

1. **커서 기반** — 사용이 쉬우나 유연성이 적습니다.
2. **범위 기반** — 더 복잡하지만 매우 유연합니다.

키셋 페이지네이션은 `id`으로 정렬할 때만 지원됩니다.

### 커서 기반

페이지네이션 매개변수 없이 DAS 쿼리를 수행하면 커서를 반환합니다. 이전에 중단한 지점에서 계속하기 위해 커서를 DAS API에 다시 전달합니다.

<Accordion title="예제">
  ```typescript theme={"system"}
  const url = `https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY`

  const example = async () => {
      let items = [];
      let cursor;
      while (true) {
          let params = {
              grouping: ['collection', '5PA96eCFHJSFPY9SWFeRJUHrpoNF5XZL6RrE1JADXhxf'],
              limit: 1000,
              sortBy: { sortBy: 'id', sortDirection: 'asc' },
          } as any;
          if (cursor != undefined) {
              params.cursor = cursor;
          }
          const response = await fetch(url, {
              method: 'POST',
              headers: {
                  'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                  jsonrpc: '2.0',
                  id: 'my-id',
                  method: 'searchAssets',
                  params: params,
              }),
          });
          const { result } = await response.json();
          if (result.items.length == 0) {
              console.log('No items remaining');
              break;
          } else {
              console.log(`Processing results for cursor ${cursor}`);
              cursor = result.cursor;
              items.push(...result.items);
          }
      }
      console.log(`Got ${items.length} total items`);
  };
  example();
  ```
</Accordion>

현재 작성 시점에서는 커서가 응답의 마지막 자산 ID이지만, 커서 설계는 유연하며 모든 문자열을 지원할 수 있습니다.

### 범위 기반

범위 전반을 쿼리하려면 `before` 및/또는 `after`를 지정하십시오. 쿼리는 본질적으로 "X 이후이지만 Y 이전의 모든 자산을 가져옵니다." 각 호출 시 `before` 또는 `after` 매개변수를 업데이트하여 데이터 세트를 탐색합니다.

<Accordion title="예제">
  ```javascript theme={"system"}
  const url = `https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY`

  const example = async () => {
      // Two NFTs from the Tensorian collection.
      // The "start" item has a lower asset ID (in binary) than the "end" item.
      // We will traverse in ascending order.
      let start = '6CeKtAYX5USSvPCQicwFsvN4jQSHNxQuFrX2bimWrNey';
      let end = 'CzTP4fUbdfgKzwE6T94hsYV7NWf1SzuCCsmJ6RP1xsDw';
      let sortDirection = 'asc';
      let after = start;
      let before = end;
      let items = [];

      while (true) {
          const response = await fetch(url, {
              method: 'POST',
              headers: {
                  'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                  jsonrpc: '2.0',
                  id: 'my-id',
                  method: 'searchAssets',
                  params: {
                      grouping: ['collection', '5PA96eCFHJSFPY9SWFeRJUHrpoNF5XZL6RrE1JADXhxf'],
                      limit: 1000,
                      after: after,
                      before: before,
                      sortBy: { sortBy: 'id', sortDirection: sortDirection },
                  },
              }),
          });
          const { result } = await response.json();
          if (result.items.length == 0) {
              console.log('No items remaining');
              break;
          } else {
              console.log(`Processing results with (after: ${after}, before: ${before})`);
              after = result.items[result.items.length - 1].id;
              items.push(...result.items);
          }
      }
      console.log(`Got ${items.length} total items`);
  };
  example();
  ```
</Accordion>

## 키셋으로 병렬 쿼리(고급)

대규모 데이터 세트(예: 전체 압축 NFT 컬렉션)를 쿼리하는 고급 사용자는 성능을 위해 키셋 기반 페이지네이션을 사용해야 합니다. 다음 예제는 Solana 주소 범위를 분할하고 `before`/`after` 매개변수를 사용하여 병렬로 쿼리하는 방법을 보여줍니다. 이 방법은 빠르고 효율적이며 안전합니다.

<Accordion title="예제">
  아래 예제에서는 Tensorian 컬렉션(약 10k 레코드)을 전체 스캔합니다. Solana 주소 공간을 8개의 범위로 분할하고 동시에 해당 범위를 스캔합니다. 이는 다른 방법보다 훨씬 빠릅니다.

  ```typescript theme={"system"}
  import base58 from 'bs58';

  const url = `https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY`

  const main = async () => {
      let numParitions = 8;
      let partitons = partitionAddressRange(numParitions);
      let promises = [];
      for (const [i, partition] of partitons.entries()) {
          let [s, e] = partition;
          let start = bs58.encode(s);
          let end = bs58.encode(e);
          console.log(`Parition: ${i}, Start: ${start}, End: ${end}`);

          let promise: Promise<number> = new Promise(async (resolve, reject) => {
              let current = start;
              let totalForPartition = 0;
              while (true) {
                  const response = await fetch(url, {
                      method: 'POST',
                      headers: {
                          'Content-Type': 'application/json',
                      },
                      body: JSON.stringify({
                          jsonrpc: '2.0',
                          id: 'my-id',
                          method: 'searchAssets',
                          params: {
                              grouping: ['collection', '5PA96eCFHJSFPY9SWFeRJUHrpoNF5XZL6RrE1JADXhxf'],
                              limit: 1000,
                              after: current,
                              before: end,
                              sortBy: { sortBy: 'id', sortDirection: 'asc' },
                          },
                      }),
                  });
                  const { result } = await response.json();
                  totalForPartition += result.items.length;
                  console.log(`Found ${totalForPartition} total items in parition ${i}`);
                  if (result.items.length == 0) {
                      break;
                  } else {
                      current = result.items[result.items.length - 1].id;
                  }
              }
              resolve(totalForPartition);
          });
          promises.push(promise);
      }
      let results = await Promise.all(promises);
      let total = results.reduce((a, b) => a + b, 0);
      console.log(`Got ${total} total items`);
  };

  // Function to convert a BigInt to a byte array
  function bigIntToByteArray(bigInt: bigint): Uint8Array {
      const bytes = [];
      let remainder = bigInt;
      while (remainder > 0n) {
          // use 0n for bigint literal
          bytes.unshift(Number(remainder & 0xffn));
          remainder >>= 8n;
      }
      while (bytes.length < 32) bytes.unshift(0); // pad with zeros to get 32 bytes
      return new Uint8Array(bytes);
  }

  function partitionAddressRange(numPartitions: number) {
      let N = BigInt(numPartitions);

      // Largest and smallest Solana addresses in integer form.
      // Solana addresses are 32 byte arrays.
      const start = 0n;
      const end = 2n ** 256n - 1n;

      // Calculate the number of partitions and partition size
      const range = end - start;
      const partitionSize = range / N;

      // Calculate partition ranges
      const partitions: Uint8Array[][] = [];
      for (let i = 0n; i < N; i++) {
          const s = start + i * partitionSize;
          const e = i === N - 1n ? end : s + partitionSize;
          partitions.push([bigIntToByteArray(s), bigIntToByteArray(e)]);
      }

      return partitions;
  }

  main();
  ```
</Accordion>

## 다음 단계

<CardGroup cols={3}>
  <Card title="자산 검색" icon="magnifying-glass" href="/docs/ko/das/search">
    소유자, 컬렉션 및 tokenType으로 자산을 필터링합니다.
  </Card>

  <Card title="자산 가져오기(NFTs)" icon="image" href="/docs/ko/das/get-nfts">
    NFT, 컬렉션, 에디션 및 증명을 검색합니다.
  </Card>

  <Card title="DAS API 참조" icon="code" href="/docs/ko/api-reference/das">
    모든 DAS 메소드에 대한 전체 스키마.
  </Card>
</CardGroup>
