Node.js에서 Azure Table Storage 또는 Azure Cosmos DB for Table을 사용하는 방법

적용 대상: 테이블

이 문서의 내용은 Azure Table Storage와 Azure Cosmos DB for Table에 적용됩니다. API for Table은 처리량 최적화 테이블, 글로벌 배포 및 자동 보조 인덱스를 제공하는 테이블 스토리지용 프리미엄 제품입니다.

이 문서에서는 테이블을 만들고, 데이터를 저장하고, 해당 데이터에 대해 CRUD 작업을 수행하는 방법을 보여 줍니다. 샘플은 Node.js로 작성되었습니다.

Azure 서비스 계정 만들기

Azure Table Storage 또는 Azure Cosmos DB를 사용하여 테이블을 작업할 수 있습니다. 이러한 두 서비스에서 테이블 제공 간의 차이점에 대한 자세한 내용은 API for Table 개요를 참조하세요. 사용하려는 서비스에 대해 계정을 만들어야 합니다. 다음 섹션에서는 Azure Table Storage와 Azure Cosmos DB 계정을 모두 만드는 방법을 보여줍니다. 단, 둘 중 하나만 사용할 수 있습니다.

Azure Storage 계정 만들기

Azure Storage 계정을 가장 쉽게 만드는 방법은 Azure Portal을 사용하는 것입니다. 자세한 내용은 스토리지 계정 만들기를 참조하십시오.

Azure PowerShell 또는 Azure CLI를 사용하여 Azure Storage 계정을 만들 수도 있습니다.

또한 이번에 스토리지 계정을 만들지 않으려는 경우 Azure Storage Emulator를 사용하여 로컬 환경에서 코드를 실행하고 테스트할 수 있습니다. 자세한 내용은 개발 및 테스트에 Azure Storage Emulator 사용을 참조하세요.

Azure Cosmos DB for Table 계정 만들기

Azure Cosmos DB for Table 계정을 만드는 지침은 데이터베이스 계정 만들기를 참조하세요.

Table Storage에 액세스하도록 애플리케이션 구성

Azure Storage 또는 Azure Cosmos DB를 사용하려면 스토리지 REST 서비스와 통신하는 편리한 라이브러리 집합이 포함되어 있는 Node.js용 Azure Tables SDK가 필요합니다.

NPM(Node Package Manager)을 사용하여 패키지 설치

  1. PowerShell(Windows), Terminal(Mac) 또는 Bash(Unix) 등과 같은 명령줄 인터페이스를 사용하여 애플리케이션을 만든 폴더로 이동합니다.
  2. 명령 창에 다음을 입력합니다.
   npm install @azure/data-tables
  1. ls 명령을 수동으로 실행하여 node_modules 폴더가 만들어졌는지 확인할 수 있습니다. 해당 폴더 안에 테이블에 액세스하는 데 필요한 라이브러리가 포함된 @azure/data-tables 패키지가 있습니다.

패키지 가져오기

애플리케이션에서 아래 코드를 server.js 파일의 맨 위에 추가합니다.

const { TableServiceClient, TableClient, AzureNamedKeyCredential, odata } = require("@azure/data-tables");

Azure Table service에 연결

Azure 스토리지 계정 또는 Azure Cosmos DB for Table 계정에 연결할 수 있습니다. 사용 중인 계정 유형에 따라 공유 키 또는 연결 문자열을 가져옵니다.

공유 키에서 Table service 클라이언트 만들기

Azure 모듈은 Azure Storage 계정 또는 Azure Cosmos DB에 연결하는 데 필요한 정보에 대해 환경 변수 AZURE_ACCOUNT 및 AZURE_ACCESS_KEY 및 AZURE_TABLES_ENDPOINT를 읽습니다. 이러한 환경 변수가 설정되어 있지 않은 경우 TableServiceClient를 호출할 때 계정 정보를 지정해야 합니다. 예를 들어, 다음 코드는 TableServiceClient 개체를 만듭니다.

const endpoint = "<table-endpoint-uri>";
const credential = new AzureNamedKeyCredential(
  "<account-name>",
  "<account-key>"
);

const tableService = new TableServiceClient(
  endpoint,
  credential
);

연결 문자열에서 Table service 클라이언트 만들기

Azure Cosmos DB 또는 스토리지 계정 연결을 추가하려면 TableServiceClient 개체를 만들고 계정 이름, 기본 키 및 엔드포인트를 지정합니다. Azure Cosmos DB 계정 또는 스토리지 계정에 대한 Azure Portal의 설정>연결 문자열에서 이 값을 복사할 수 있습니다. 예시:

const tableService = TableServiceClient.fromConnectionString("<connection-string>");

테이블 만들기

createTable를 호출하면 지정된 이름을 사용하는 테이블이 없는 경우 새 테이블을 만듭니다. 다음 예제에서는 'mytable'이라는 새 테이블(없는 경우)을 만듭니다.

await tableService.createTable('<table-name>');

테이블 클라이언트 만들기

테이블과 상호 작용하려면 TableServiceClient를 만드는 데 사용한 것과 동일한 자격 증명을 사용하여 TableClient 개체를 만들어야 합니다. TableClient에는 대상 테이블의 이름도 필요합니다.

const tableClient = new TableClient(
  endpoint,
  '<table-name>',
  credential
);

테이블에 엔터티 추가

엔터티를 추가하려면 먼저 엔터티 속성을 정의하는 개체를 만듭니다. 모든 엔터티에는 엔터티의 고유 식별자인 partitionKeyrowKey를 포함해야 합니다.

  • partitionKey - 엔터티가 저장된 파티션을 확인합니다.
  • rowKey - 파티션에 있는 엔터티를 고유하게 식별합니다.

partitionKeyrowKey는 모두 문자열 값이어야 합니다.

다음은 엔터티를 정의하는 경우의 예입니다. dueDateDate 형식으로 정의됩니다. 유형 지정은 선택적이며 지정하지 않을 경우 유형이 유추됩니다.

const task = {
  partitionKey: "hometasks",
  rowKey: "1",
  description: "take out the trash",
  dueDate: new Date(2015, 6, 20)
};

참고 항목

각 레코드에는 엔터티가 삽입되거나 업데이트될 경우 Azure에서 설정되는 Timestamp 필드도 있습니다.

테이블에 엔터티를 추가하려면 엔터티 개체를 createEntity 메서드에 전달합니다.

let result = await tableClient.createEntity(task);
    // Entity create

작업이 성공하면 resultETag와 작업에 대한 정보가 포함됩니다.

예제 응답:

{ 
  clientRequestId: '94d8e2aa-5e02-47e7-830c-258e050c4c63',
  requestId: '08963b85-1002-001b-6d8c-12ae5d000000',
  version: '2019-02-02',
  date: 2022-01-26T08:12:32.000Z,
  etag: `W/"datetime'2022-01-26T08%3A12%3A33.0180348Z'"`,
  preferenceApplied: 'return-no-content',
  'cache-control': 'no-cache',
  'content-length': '0'
}

엔터티 업데이트

updateEntityupsertEntity 메서드의 다양한 모드

  • 병합: 기존 엔터티를 바꾸지 않고 엔터티의 속성을 업데이트하여 엔터티를 업데이트합니다.
  • 바꾸기: 전체 엔터티를 교체하여 기존 엔터티를 업데이트합니다.

다음 예제에서는 upsertEntity를 사용하여 엔터티를 업데이트하는 방법을 보여 줍니다.

// Entity doesn't exist in table, so calling upsertEntity will simply insert the entity.
let result = await tableClient.upsertEntity(task, "Replace");

업데이트할 엔터티가 존재하지 않으면 업데이트 작업이 실패하므로 존재 여부에 관계없이 엔터티를 저장하려는 경우 upsertEntity를 사용합니다.

업데이트 작업이 성공할 경우 result 값에는 업데이트된 엔터티의 Etag 가 포함됩니다.

엔터티 그룹 작업

서버에서 원자성 처리를 수행하도록 여러 작업을 일괄적으로 제출하는 것이 좋은 경우도 있습니다. 이를 수행하려면 작업 배열을 만들고 TableClientsubmitTransaction 메서드에 전달합니다.

다음 예제에서는 두 엔터티를 일괄적으로 제출하는 방법을 보여 줍니다.

const task1 = {
  partitionKey: "hometasks",
  rowKey: "1",
  description: "Take out the trash",
  dueDate: new Date(2015, 6, 20)
};
const task2 = {
  partitionKey: "hometasks",
  rowKey: "2",
  description: "Wash the dishes",
  dueDate: new Date(2015, 6, 20)
};

const tableActions = [
  ["create", task1],
  ["create", task2]
];

let result = await tableClient.submitTransaction(tableActions);
    // Batch completed

일괄 처리 작업에 성공할 경우 result 값에는 일괄 처리의 각 작업에 대한 정보가 포함됩니다.

키를 통해 엔터티 검색

PartitionKeyRowKey를 기반으로 특정 항목을 반환하려면 getEntity 메서드를 사용합니다.

let result = await tableClient.getEntity("hometasks", "1")
  .catch((error) => {
    // handle any errors
  });
  // result contains the entity

이 작업이 완료되면 result에 엔터티가 포함됩니다.

엔터티 집합 쿼리

다음 예에서는 PartitionKey가 'hometasks'인 상위 5개 항목을 반환하고 테이블의 모든 엔터티를 나열하는 쿼리를 작성합니다.

const topN = 5;
const partitionKey = "hometasks";

const entities = tableClient.listEntities({
  queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` }
});

let topEntities = [];
const iterator = entities.byPage({ maxPageSize: topN });

for await (const page of iterator) {
  topEntities = page;
  break;
}

// Top entities: 5
console.log(`Top entities: ${topEntities.length}`);

// List all the entities in the table
for await (const entity of entities) {
console.log(entity);
}

엔터티 속성 하위 집합 쿼리

테이블 쿼리에서는 엔터티에서 일부 필드만 검색할 수 있습니다. 따라서 대역폭이 줄어들며 특히 큰 엔터티의 경우 쿼리 성능이 향상될 수 있습니다. select 절을 사용하여 반환할 필드의 이름을 전달합니다. 예를 들어, 다음 쿼리에서는 descriptiondueDate 필드만 반환합니다.

const topN = 5;
const partitionKey = "hometasks";

const entities = tableClient.listEntities({
  queryOptions: { filter: odata`PartitionKey eq ${partitionKey}`,
                  select: ["description", "dueDate"]  }
});

let topEntities = [];
const iterator = entities.byPage({ maxPageSize: topN });

for await (const page of iterator) {
  topEntities = page;
  break;
}

엔터티 삭제

파티션 및 행 키를 사용하여 엔터티를 삭제할 수 있습니다. 이 예제에서 task1 개체는 삭제할 엔터티의 rowKeypartitionKey 값을 포함합니다. 그런 다음 개체는 deleteEntity 메서드에 전달됩니다.

const tableClient = new TableClient(
  tablesEndpoint,
  tableName,
  new AzureNamedKeyCredential("<accountName>", "<accountKey>")
);

await tableClient.deleteEntity("hometasks", "1");
    // Entity deleted

참고 항목

항목을 삭제할 때 항목이 다른 프로세스에서 수정되지 않았는지 확인할 수 있도록 ETag를 사용하는 것이 좋습니다. ETag 사용 방법에 대한 자세한 내용은 엔터티 업데이트 를 참조하세요.

테이블 삭제

다음 코드는 스토리지 계정에서 테이블을 삭제합니다.

await tableClient.deleteTable(mytable);
        // Table deleted

연속토큰 사용

많은 양의 결과를 얻기 위해 테이블을 쿼리하는 경우 연속 토큰을 찾습니다. 당신이 인식하지 못한 쿼리에 대한 사용 가능한 많은 양의 데이터가 있을 경우 연속토큰의 유무를 인식하지 못하도록 작성하지마세요.

연속 토큰이 있을 경우 continuationToken 속성을 설정하는 엔터티를 쿼리하는 동안 결과 개체가 반환됩니다. 그 다음 파티션 및 테이블 엔터티 간에 이동을 계속하기 위해 쿼리를 수행 할 경우 이것을 사용할 수 있습니다.

쿼리할 때, 쿼리개체 인스턴스와 콜백 함수 간에 continuationToken 매개 변수를 제공할 수 있습니다.

let iterator = tableClient.listEntities().byPage({ maxPageSize: 2 });
let interestingPage;

const page = await tableClient
   .listEntities()
   .byPage({ maxPageSize: 2, continuationToken: interestingPage })
   .next();

 if (!page.done) {
   for (const entity of page.value) {
     console.log(entity.rowKey);
   }
 }

공유 액세스 서명 작업

SAS(공유 액세스 서명)는 스토리지 계정 이름이나 키를 제공하지 않으면서 테이블에 세분화된 액세스 권한을 안전하게 제공하는 방법입니다. SAS는 모바일 앱에서 레코드를 쿼리하는 경우와 같이 데이터에 대해 제한된 액세스를 제공하는 경우에 자주 사용합니다.

클라우드 기반 서비스와 같은 신뢰할 수 있는 애플리케이션은 TableClientgenerateTableSas를 사용하여 SAS를 생성하고 모바일 앱과 같이 신뢰할 수 없거나 신뢰가 약한 애플리케이션에 제공합니다. SAS는 SAS가 유효한 시작 및 종료 날짜와 SAS 소유자에게 부여되는 액세스 수준을 설명하는 정책을 사용하여 생성됩니다.

다음 예에서는 SAS 보유자가 테이블을 쿼리('r')할 수 있도록 하는 새로운 공유 액세스 정책을 생성합니다.

const tablePermissions = {
    query: true
// Allows querying entities
};

// Create the table SAS token
const tableSAS = generateTableSas('mytable', cred, {
  expiresOn: new Date("2022-12-12"),
  permissions: tablePermissions
});

그런 다음 클라이언트 애플리케이션은 AzureSASCredential과 함께 SAS를 사용하여 테이블에 대해 작업을 수행합니다. 다음 예에서는 테이블을 연결하고 쿼리를 수행합니다. tableSAS 형식은 SAS(공유 액세스 서명)를 사용하여 Azure Storage 리소스에 대한 제한된 액세스 권한 부여 문서를 참조하세요.

// Note in the following command, tablesUrl is in the format: `https://<your_storage_account_name>.table.core.windows.net` and the tableSAS is in the format: `sv=2018-03-28&si=saspolicy&tn=mytable&sig=9aCzs76n0E7y5BpEi2GvsSv433BZa22leDOZXX%2BXXIU%3D`;

const tableService = new TableServiceClient(tablesUrl, new AzureSASCredential(tableSAS));
const partitionKey = "hometasks";

const entities = tableService.listTables({
  queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` }
});

SAS가 쿼리 액세스만으로 생성되었기 때문에 엔터티를 삽입, 업데이트 또는 삭제하려고 하면 오류가 반환됩니다.

Access Control 목록

ACL(Access Control 목록)을 사용하여 SAS에 액세스 정책을 설정할 수도 있습니다. 이 방법은 여러 클라이언트에서 테이블에 액세스하게 하면서 각 클라이언트에 서로 다른 액세스 정책을 제공하려는 경우에 유용합니다.

ACL은 각 정책에 ID가 연결된 액세스 정책 배열을 사용하여 구현됩니다. 다음 예에서는 'user1'와 'user2'에 대해 하나씩, 두 개의 정책을 정의합니다.

var sharedAccessPolicy = [{
  id:"user1",
  accessPolicy:{
    permission: "r" ,
    Start: startsOn,
    Expiry: expiresOn,
  }},
  {
  id:"user2",
  accessPolicy:{
    permissions: "a",
    Start: startsOn,
    Expiry: expiresOn,
  }},
]

다음 예에서는 hometasks 테이블에 대한 현재 ACL을 가져온 다음 setAccessPolicy를 사용하여 새 정책을 추가합니다. 이 접근 방식을 통해 다음을 수행할 수 있습니다.

tableClient.getAccessPolicy();
tableClient.setAccessPolicy(sharedAccessPolicy);

ACL이 설정된 후에 정책의 ID를 기반으로 SAS를 만들 수 있습니다. 다음 예에서는 'user2'에 대해 새 SAS를 만듭니다.

tableSAS = generateTableSas("hometasks",cred,{identifier:'user2'});

다음 단계

자세한 내용은 다음 리소스를 참조하세요.