

# Node.js 및 DAX
<a name="DAX.client.run-application-nodejs-3"></a>

# Node.js에 대한 기본 클라이언트 구성
<a name="DAX-client-config-JS"></a>

DAX JavaScript SDK 클라이언트를 구성할 때 다양한 파라미터를 사용자 지정하여 성능, 연결 처리 및 오류 복원력을 최적화할 수 있습니다. 다음 표에는 제한 시간 값, 재시도 메커니즘, 자격 증명 관리 및 상태 모니터링 옵션을 포함하여 클라이언트가 DAX 클러스터와 상호 작용하는 방식을 제어하는 기본 구성 설정이 요약되어 있습니다. 자세한 내용은 [DynamoDBClient Operations](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/)를 참조하세요.


**DAX JS SDK 클라이언트 기본값**  

| 파라미터 | 유형 | 설명 | 
| --- | --- | --- | 
|  `region` 선택 사항  |  `string`  |  DAX 클라이언트에 사용할 AWS 리전입니다(예: 'us-east-1'). 환경 변수를 통해 제공되지 않는 경우 필수 파라미터입니다.  | 
|  `endpoint` 필수  |  `string`  | SDK가 연결되는 클러스터의 엔드포인트입니다. 예시: 암호화되지 않음 – dax-cluster-name.region.amazonaws.com 암호화됨 - daxs://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com  | 
|  `requestTimeout` 기본값 6,000ms  |  `number`  | 이는 클라이언트가 DAX의 응답을 기다리는 최대 시간을 정의합니다.  | 
|  `writeRetries` 기본값 1  |  `number`  | 실패한 쓰기 요청에 대한 재시도 횟수입니다.  | 
|  `readRetries` 기본값 1  |  `number`  | 실패한 읽기 요청에 대한 재시도 횟수입니다.  | 
|  `maxRetries` 기본값 1  |  `number`  | 실패한 요청에 대한 최대 재시도 횟수입니다. readRetries/writeRetries가 설정된 경우 readRetries 및 writeRetries의 구성 세트가 maxRetries보다 우선합니다.  | 
|  `connectTimeout` 기본값 10,000ms  |  `number`  | 클러스터 노드에 대한 연결을 설정하기 위한 제한 시간(밀리초)입니다.  | 
|  `maxConcurrentConnections` 기본값 100  |  `number`  | 클라이언트 인스턴스가 DAX 클러스터의 노드당 생성할 수 있는 총 동시 연결 수를 제한합니다.  | 
|  `maxRetryDelay` 기본값 7,000ms  |  `number`  | DAX 서버에서 `waitForRecoveryBeforeRetrying` 플래그를 true로 설정하여 복구가 필요하다고 알리면 클라이언트는 재시도 전에 일시 중지됩니다. 이러한 복구 기간 동안 `maxRetryDelay` 파라미터는 재시도 간의 최대 대기 시간을 결정합니다. 이 복구별 구성은 DAX 서버가 복구 모드인 경우에만 적용됩니다. 다른 모든 시나리오의 경우 재시도 동작은 재시도 횟수를 기반으로 하는 지수형 지연(`writeRetries`, `readRetries` 또는 `maxRetries` 파라미터에 의해 결정됨) 또는 예외 유형에 따른 즉시 재시도라는 두 가지 패턴 중 하나를 따릅니다.  | 
|  `credentials` 선택 사항  |  `[AwsCredentialIdentity](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-credential-providers/)` \$1 `[AwsCredentialIdentityProvider](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-credential-providers/)`  |  요청을 인증하는 데 사용할 AWS 자격 증명입니다. 이는 AwsCredentialIdentity 또는 AwsCredentialIdentityProvider로 제공될 수 있습니다. 제공되지 않은 경우 AWS SDK는 기본 자격 증명 공급자 체인을 자동으로 사용합니다. 예: `\$1 accessKeyId: 'AKIA...', secretAccessKey: '...', sessionToken: '...' \$1` \$1 @default는 AWS 자격 증명 공급자 체인을 사용합니다.  | 
|  `healthCheckInterval` 기본값 5,000ms  |  `number`  | 클러스터 상태 확인 간의 간격(밀리초)입니다. 간격이 작을수록 더 자주 확인합니다.  | 
|  `healthCheckTimeout` 기본값 1,000ms  |  `number`  | 상태 확인이 완료되기 위한 제한 시간(밀리초)입니다.  | 
|  `skipHostnameVerification` 기본값 false  |  `boolean`  |  TLS 연결의 호스트 이름 확인을 건너뜁니다. 이는 암호화되지 않은 클러스터에는 영향을 주지 않습니다. 기본값은 호스트 이름 확인을 수행하는 것입니다. 이를 True로 설정하면 확인을 건너뜁니다. 이 설정을 끄면 연결 중인 클러스터를 인증할 수 없다는 점을 이해해야 합니다.  | 
|  `unhealthyConsecutiveErrorCount` 기본값 5  |  `number`  | 상태 확인 간격 내에 노드 비정상 신호를 보내는 데 필요한 연속 오류 수를 설정합니다.  | 
|  `clusterUpdateInterval` 기본값 4,000ms  |  `number`  | 멤버십 변경에 대한 클러스터 멤버 폴링 간격을 반환합니다.  | 
|  `clusterUpdateThreshold` 기본값 125  |  `number`  | 멤버십 변경에 대해 클러스터가 폴링되지 않는 임계값을 반환합니다.  | 
|  `credentailProvider` 선택 사항 \$1 기본값 Null  |  `[AwsCredentialIdentityProvider](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-credential-providers/)`  | DAX에 대한 요청을 인증하는 데 사용되는 AWS 자격 증명에 대한 사용자 정의 공급자입니다.  | 


**DaxDocument에 대한 페이지 매김 구성**  

| 이름 | Type | 세부 정보 | 
| --- | --- | --- | 
|  `client`  |  DaxDocument  |  DaxDocument 유형의 인스턴스입니다.  | 
|  `pageSize`  |  number  |  페이지당 항목 수를 결정합니다.  | 
|  `startingToken` 선택  |  any  |  이전 응답의 LastEvaluatedKey를 후속 요청에 사용할 수 있습니다.  | 

페이지 매김 사용에 대한 내용은 [TryDax.js](DAX.client.tutorial-TryDax.md) 섹션을 참조하세요.

# DAX Node.js SDK V3로 마이그레이션
<a name="DAX.client.run-application-nodejs-3-migrating"></a>

이 마이그레이션 가이드는 기존 DAX Node.js 애플리케이션을 전환하는 데 도움이 됩니다. 새로운 SDK에는 Node.js 18 이상이 필요하며 DynamoDB Accelerator 코드를 구성하는 방법에 몇 가지 중요한 변경 사항이 도입되었습니다. 이 가이드에서는 구문 변경, 새 가져오기 방법, 업데이트된 비동기 프로그래밍 패턴 등 주요 차이점을 안내합니다.

## V2 Node.js DAX 사용량
<a name="DAX.client.run-application-nodejs-3-migrating-V2-usage"></a>

```
const AmazonDaxClient = require('amazon-dax-client');
const AWS = require('aws-sdk');

var region = "us-west-2";

AWS.config.update({
  region: region,
});

var client = new AWS.DynamoDB.DocumentClient();

if (process.argv.length > 2) {
  var dax = new AmazonDaxClient({
    endpoints: [process.argv[2]],
    region: region,
  });
  client = new AWS.DynamoDB.DocumentClient({ service: dax });
}

// Make Get Call using Dax
var params = {
    TableName: 'TryDaxTable',
    pk: 1,
    sk: 1
}
client.get(params, function (err, data) {
    if (err) {
        console.error(
            "Unable to read item. Error JSON:",
            JSON.stringify(err, null, 2)
          );
    } else {
        console.log(data);
    }
});
```

## V3 Node.js DAX 사용량
<a name="DAX.client.run-application-nodejs-3-migrating-V3-dax-usage"></a>

DAX Node.js 사용의 경우 V3 노드 버전 18 이상이 기본 버전입니다. 노드 18으로 이동하려면 다음을 사용합니다.

```
import { DaxDocument } from '@amazon-dax-sdk/lib-dax';
import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';

let client: DynamoDBDocument | DaxDocument = DynamoDBDocument.from(
  new DynamoDBClient({ region: 'us-west-2' })
);

if (process.argv.length > 2) {
  client = new DaxDocument({
    endpoints: [process.argv[2]],
    region: 'us-west-2',
  });
}

const params = {
  TableName: 'TryDaxTable',
  Key: { pk: 1, sk: 1 },
};

try {
  const results = await client.get(params);
  console.log(results);
} catch (err) {
  console.error(err);
}
```

Node.js v3.x용 DAX SDK는 [Node.js v3.x용 AWS SDK](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/)와 호환됩니다. Node.js v3.x용 DAX SDK는 [집계된](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/#high-level-concepts) 클라이언트 사용을 지원합니다. DAX는 기본 클라이언트 만들기를 지원하지 않습니다. 지원되지 않는 기능에 대한 자세한 내용은 [AWS SDK V3와 동등하지 않은 기능](#DAX.client.run-application-nodejs-3-not-in-parity) 섹션을 참조하세요.

다음 단계에 따라 Amazon EC2 인스턴스에서 Node.js 샘플 애플리케이션을 실행합니다.

**DAX에 대한 Node.js 샘플을 실행하려면**

1. 다음과 같이 Amazon EC2 인스턴스에서 Node.js를 설치합니다.

   1. 노드 버전 관리자(`nvm`)를 설치합니다.

      ```
      curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
      ```

   1. nvm을 사용하여 Node.js를 설치합니다.

      ```
      nvm install 18
      ```

   1. nvm을 사용하여 노드 18 사용

      ```
      nvm use 18
      ```

   1. Node.js가 설치되었고 올바르게 실행되는지 테스트합니다.

      ```
      node -e "console.log('Running Node.js ' + process.version)"
      ```

      다음 메시지가 표시되어야 합니다.

      `Running Node.js v18.x.x`

1. 노드 패키지 관리자(`npm`)를 사용하여 DaxDocument Node.js 클라이언트를 설치합니다.

   ```
   npm install @amazon-dax-sdk/lib-dax
   ```

## TryDax 샘플 코드
<a name="DAX.client.run-application-nodejs-3-TryDax-sample-code"></a>

DynamoDB Accelerator(DAX)의 성능 이점을 평가하려면 다음 단계에 따라 표준 DynamoDB와 DAX 클러스터 간의 읽기 작업 시간을 비교하는 샘플 테스트를 실행합니다.

1. 작업 영역을 설정하고 `lib-dax`를 종속 항목으로 설치한 후 [TryDax.js](DAX.client.tutorial-TryDax.md)를 프로젝트에 복사합니다.

1. DAX 클러스터에 대해 프로그램을 실행합니다. DAX 클러스터의 엔드포인트를 정의하려면 다음 중 하나를 선택합니다.
   +  **Using the DynamoDB console(DynamoDB 콘솔 사용)** - DAX 클러스터를 선택합니다. 다음 예제와 같이 클러스터 엔드포인트가 콘솔에 표시됩니다.

     ```
     dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com
     ```
   + **AWS CLI 사용** - 다음 명령을 입력합니다.

     ```
     aws dax describe-clusters --query "Clusters[*].ClusterDiscoveryEndpoint"
     ```

     다음 예제와 같이 클러스터 엔드포인트가 출력에 표시됩니다.

     ```
     {
         "Address": "my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com",
         "Port": 8111,
         "URL": "dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com"
     }
     ```

1. 클러스터 엔드포인트를 명령줄 파라미터로 지정하여 프로그램을 실행합니다.

   ```
   node TryDax.js dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com
   ```

   다음과 유사한 출력 화면이 표시되어야 합니다.

   ```
   Attempting to create table; please wait...
   Successfully created table. Table status: ACTIVE
   Writing data to the table...
   Writing 20 items for partition key:  1
   Writing 20 items for partition key:  2
   Writing 20 items for partition key:  3
   ...
   Running GetItem Test
           Total time: 153555.10 µs - Avg time: 383.89 µs
           Total time: 44679.96 µs - Avg time: 111.70 µs
           Total time: 36885.86 µs - Avg time: 92.21 µs
           Total time: 32467.25 µs - Avg time: 81.17 µs
           Total time: 32202.60 µs - Avg time: 80.51 µs
   Running Query Test
           Total time: 14869.25 µs - Avg time: 2973.85 µs
           Total time: 3036.31 µs - Avg time: 607.26 µs
           Total time: 2468.92 µs - Avg time: 493.78 µs
           Total time: 2062.53 µs - Avg time: 412.51 µs
           Total time: 2178.22 µs - Avg time: 435.64 µs
   Running Scan Test
           Total time: 2395.88 µs - Avg time: 479.18 µs
           Total time: 2207.16 µs - Avg time: 441.43 µs
           Total time: 2443.14 µs - Avg time: 488.63 µs
           Total time: 2038.24 µs - Avg time: 407.65 µs
           Total time: 1972.17 µs - Avg time: 394.43 µs
   Running Pagination Test
   Scan Pagination
   [
     { pk: 1, sk: 1, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 2, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 3, someData: 'XXXXXXXXXX' }
   ]
   [
     { pk: 1, sk: 4, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 5, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 6, someData: 'XXXXXXXXXX' }
   ]
   ...
   Query Pagination
   [
     { pk: 1, sk: 1, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 2, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 3, someData: 'XXXXXXXXXX' }
   ]
   [
     { pk: 1, sk: 4, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 5, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 6, someData: 'XXXXXXXXXX' }
   ]
   ...
   Attempting to delete table; please wait...
   Successfully deleted table.
   ```

   타이밍 정보를 기록해 둡니다. `GetItem`, `Query`, `Scan` 테스트에 필요한 마이크로초의 수입니다.

1. 이 경우 DAX 클러스터에 대해 프로그램을 실행했습니다. 이제 프로그램을 다시 실행하는데, 이번에는 DynamoDB 클러스터에 대해 실행합니다.

1. 이제 프로그램을 다시 실행합니다. 이번에는 클러스터 엔드포인트 URL을 명령줄 파라미터로 지정합니다.

   ```
   node TryDax.js
   ```

   출력을 확인하여 타이밍 정보를 기록해 둡니다. `GetItem`, `Query` 및 `Scan`에 대한 경과 시간은 DAX가 DynamoDB보다 현저히 적어야 합니다.

## AWS SDK V3와 동등하지 않은 기능
<a name="DAX.client.run-application-nodejs-3-not-in-parity"></a>
+ [기본](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/#high-level-concepts) 클라이언트 - Dax Node.js V3는 기본 클라이언트를 지원하지 않습니다.

  ```
  const dynamoDBClient = new DynamoDBClient({ region: 'us-west-2' });
  const regularParams = {
      TableName: 'TryDaxTable',
      Key: {
          pk: 1,
          sk: 1
      }
  };
  // The DynamoDB client supports the send operation.
  const dynamoResult = await dynamoDBClient.send(new GetCommand(regularParams));
  
  // However, the DaxDocument client does not support the send operation.
  const daxClient = new DaxDocument({
      endpoints: ['your-dax-endpoint'],
      region: 'us-west-2',
  });
  
  const params = {
      TableName: 'TryDaxTable',
      Key: {
          pk: 1,
          sk: 1
      }
  };
  
  // This will throw an error - send operation is not supported for DAX. Please refer to documentation.
  const result = await daxClient.send(new GetCommand(params));
  console.log(result);
  ```
+ [미들웨어 스택](https://aws.amazon.com/blogs/developer/middleware-stack-modular-aws-sdk-js/) - Dax Node.js V3는 미들웨어 함수 사용을 지원하지 않습니다.

  ```
  const dynamoDBClient = new DynamoDBClient({ region: 'us-west-2' });
  // The DynamoDB client supports the middlewareStack.
  dynamoDBClient.middlewareStack.add(
    (next, context) =>> async (args) => {
      console.log("Before operation:", args);
      const result = await next(args);
      console.log("After operation:", result);
      return result;
    },
    {
      step: "initialize", // or "build", "finalizeRequest", "deserialize"
      name: "loggingMiddleware",
    }
  );
  
  // However, the DaxDocument client does not support the middlewareStack.
  const daxClient = new DaxDocument({
      endpoints: ['your-dax-endpoint'],
      region: 'us-west-2',
  });
  
  // This will throw an error - custom middleware and middlewareStacks are not supported for DAX. Please refer to documentation.
  daxClient.middlewareStack.add(
    (next, context) => async (args) => {
      console.log("Before operation:", args);
      const result = await next(args);
      console.log("After operation:", result);
      return result;
    },
    {
      step: "initialize", // or "build", "finalizeRequest", "deserialize"
      name: "loggingMiddleware",
    }
  );
  ```

# TryDax.js
<a name="DAX.client.tutorial-TryDax"></a>

```
import { DynamoDB, waitUntilTableExists, waitUntilTableNotExists } from "@aws-sdk/client-dynamodb";
import { DaxDocument, daxPaginateScan, daxPaginateQuery } from "@amazon-dax-sdk/lib-dax";
import { DynamoDBDocument, paginateQuery, paginateScan } from "@aws-sdk/lib-dynamodb";

const region = "us-east-1";
const tableName = "TryDaxTable";

// Determine the client (DynamoDB or DAX)
let client = DynamoDBDocument.from(new DynamoDB({ region }));
if (process.argv.length > 2) {
  client = new DaxDocument({ region, endpoint: process.argv[2] });
}

// Function to create table
async function createTable() {
  const dynamodb = new DynamoDB({ region });
  const params = {
    TableName: tableName,
    KeySchema: [
      { AttributeName: "pk", KeyType: "HASH" },
      { AttributeName: "sk", KeyType: "RANGE" },
    ],
    AttributeDefinitions: [
      { AttributeName: "pk", AttributeType: "N" },
      { AttributeName: "sk", AttributeType: "N" },
    ],
    ProvisionedThroughput: { ReadCapacityUnits: 25, WriteCapacityUnits: 25 },
  };

  try {
    console.log("Attempting to create table; please wait...");
    await dynamodb.createTable(params);
    await waitUntilTableExists({ client: dynamodb, maxWaitTime: 30 }, { TableName: tableName });
    console.log("Successfully created table. Table status: ACTIVE");
  } catch (err) {
    console.error("Error in table creation:", err);
  }
}

// Function to insert data
async function writeData() {
  console.log("Writing data to the table...");
  const someData = "X".repeat(10);
  for (let ipk = 1; ipk <= 20; ipk++) {
    console.log("Writing 20 items for partition key: ", ipk)
    for (let isk = 1; isk <= 20; isk++) {
      try {
        await client.put({ TableName: tableName, Item: { pk: ipk, sk: isk, someData } });
      } catch (err) {
        console.error("Error inserting data:", err);
      }
    }
  }
}

// Function to test GetItem
async function getItemTest() {
  console.log("Running GetItem Test");
  for (let i = 0; i < 5; i++) {
    const startTime = performance.now();
    const promises = [];
    for (let ipk = 1; ipk <= 20; ipk++) {
      for (let isk = 1; isk <= 20; isk++) {
        promises.push(client.get({ TableName: tableName, Key: { pk: ipk, sk: isk } }));
      }
    }
    await Promise.all(promises);
    const endTime = performance.now();
    const iterTime = (endTime - startTime) * 1000;
    const totalTime = iterTime.toFixed(2).padStart(3, ' ');
    const avgTime = (iterTime / 400).toFixed(2).padStart(3, ' ');
    console.log(`\tTotal time: ${totalTime} \u00B5s - Avg time: ${avgTime} \u00B5s`);
  }
}

// Function to test Query
async function queryTest() {
  console.log("Running Query Test");
  for (let i = 0; i < 5; i++) {
    const startTime = performance.now();
    const promises = [];
    for (let pk = 1; pk <= 5; pk++) {
      const params = {
        TableName: tableName,
        KeyConditionExpression: "pk = :pkval and sk between :skval1 and :skval2",
        ExpressionAttributeValues: { ":pkval": pk, ":skval1": 1, ":skval2": 2 },
      };
      promises.push(client.query(params));
    }
    await Promise.all(promises);
    const endTime = performance.now();
    const iterTime = (endTime - startTime) * 1000;
    const totalTime = iterTime.toFixed(2).padStart(3, ' ');
    const avgTime = (iterTime / 5).toFixed(2).padStart(3, ' ');
    console.log(`\tTotal time: ${totalTime} \u00B5s - Avg time: ${avgTime} \u00B5s`);
  }
}

// Function to test Scan
async function scanTest() {
  console.log("Running Scan Test");
  for (let i = 0; i < 5; i++) {
    const startTime = performance.now();
    const promises = [];
    for (let pk = 1; pk <= 5; pk++) {
      const params = {
        TableName: tableName,
        FilterExpression: "pk = :pkval and sk between :skval1 and :skval2",
        ExpressionAttributeValues: { ":pkval": pk, ":skval1": 1, ":skval2": 2 },
      };
      promises.push(client.scan(params));
    }
    await Promise.all(promises);
    const endTime = performance.now();
    const iterTime = (endTime - startTime) * 1000;
    const totalTime = iterTime.toFixed(2).padStart(3, ' ');
    const avgTime = (iterTime / 5).toFixed(2).padStart(3, ' ');
    console.log(`\tTotal time: ${totalTime} \u00B5s - Avg time: ${avgTime} \u00B5s`);
  }
}

// Function to test Pagination
async function paginationTest() {
  console.log("Running Pagination Test");
  console.log("Scan Pagination");
  const scanParams = { TableName: tableName };
  const paginator = process.argv.length > 2 ? daxPaginateScan : paginateScan;
  for await (const page of paginator({ client, pageSize: 3 }, scanParams)) {
    console.log(page.Items);
  }

  console.log("Query Pagination");
  const queryParams = {
    TableName: tableName,
    KeyConditionExpression: "pk = :pkval and sk between :skval1 and :skval2",
    ExpressionAttributeValues: { ":pkval": 1, ":skval1": 1, ":skval2": 10 },
  };
  const queryPaginator = process.argv.length > 2 ? daxPaginateQuery : paginateQuery;
  for await (const page of queryPaginator({ client, pageSize: 3 }, queryParams)) {
    console.log(page.Items);
  }
}

// Function to delete the table
async function deleteTable() {
  const dynamodb = new DynamoDB({ region });
  console.log("Attempting to delete table; please wait...")
  try {
    await dynamodb.deleteTable({ TableName: tableName });
    await waitUntilTableNotExists({ client: dynamodb, maxWaitTime: 30 }, { TableName: tableName });
    console.log("Successfully deleted table.");
  } catch (err) {
    console.error("Error deleting table:", err);
  }
}

// Execute functions sequentially
(async function () {
  await createTable();
  await writeData();
  await getItemTest();
  await queryTest();
  await scanTest();
  await paginationTest();
  await deleteTable();
})();
```