Node.js로 Amazon DynamoDB 사용하기

Node.js 서버를 통해 Amazon DynamoDB  사용하기

트레바리 / 조회수 : 7579


DynamoDB 로컬 설정 (다운로드 버전)

실제 DynamoDB 웹 서비스에 액세스하지 않고 로컬에서 애플리케이션 작성 및 테스트를 할 수 있음


1. 다운로드 링크에서 DynamoDB 무료 다운로드

2. 압축 해제 후 해당 디렉터리에서 아래의 명령어로 실행

java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb

* Ctrl+C로 중지할 수 있고 중지하기 전까지 수신 요청을 처리함

* 기본적으로 8000번 포트를 사용


Node.js 용 AWS SDK 설치


1. 설치

npm install aws-sdk

2. 실행

// app.js
var AWS = require("aws-sdk");
var s3 = new AWS.S3();

// 버킷 이름은 모든 S3 사용자에게 고유한 것이어야 합니다.
var myBucket = "dynamodb.sample.wonny";
var myKey = "myBucketKey";

s3.createBucket({ Bucket: myBucket }, function(err, data) {
  if (err) {
    console.log(err);
  } else {
    params = { Bucket: myBucket, Key: myKey, Body: "Hello!" };
    s3.putObject(params, function(err, data) {
      if (err) {
        console.log(err);
      } else {
        console.log("Successfully uploaded data to myBucket/myKey");
      }
    });
  }
});
node app.js



테이블 생성

// CreateTable.js
var AWS = require("aws-sdk");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

var dynamodb = new AWS.DynamoDB();
var params = {
  TableName: "Movies",
  KeySchema: [
    { AttributeName: "year", KeyType: "HASH" }, // Partition key
    { AttributeName: "title", KeyType: "RANGE" } // Sort key
  ],
  AttributeDefinitions: [
    { AttributeName: "year", AttributeType: "N" },
    { AttributeName: "title", AttributeType: "S" }
  ],
  // 다운로드 버전인 경우 아래 코드 무시
  ProvisionedThroughput: {
    ReadCapacityUnits: 10,
    WriteCapacityUnits: 10
  }
};

dynamodb.createTable(params, function(err, data) {
  if (err) {
    console.log(
      "Unable to create table. Error JSON: ",
      JSON.stringify(err, null, 2)
    );
  } else {
    console.log(
      "Created table. Table description JSON: ",
      JSON.stringify(data, null, 2)
    );
  }
});
node CreateTable.js


샘플 데이터 로드

1. 이곳에서 샘플 데이터 파일 다운로드

데이터 형태는 아래와 같음

[
    {
        "year": 2013,
        "title": "Rush",
        "info": {
            "directors": ["Ron Howard"],
            "release_date": "2013-09-02T00:00:00Z",
            "rating": 8.3,
            "genres": [
                "Action",
                "Biography",
                "Drama",
                "Sport"
            ],
            "image_url": "http://ia.media-imdb.com/images/M/MV5BMTQyMDE0MTY0OV5BMl5BanBnXkFtZTcwMjI2OTI0OQ@@._V1_SX400_.jpg",
            "plot": "A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda.",
            "rank": 2,
            "running_time_secs": 7380,
            "actors": [
                "Daniel Bruhl",
                "Chris Hemsworth",
                "Olivia Wilde"
            ]
        }
    },
    ...
]

- year 및 title을 Movies 테이블을 위한 기본 키 속성 값으로 사용

- info의 나머지 값들은 info라는 단일 속성에 저장

- JSON을 DynamoDB 속성에 저장

2. 샘플 데이터 Movies 테이블에 로드

// LoadData.js

var AWS = require("aws-sdk");
var fs = require("fs");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

var docClient = new AWS.DynamoDB.DocumentClient();
console.log("Importing movies info DynamoDB. Please wait.");

var allMovies = JSON.parse(fs.readFileSync("moviedata.json", "utf8"));
allMovies.forEach(function(movie) {
  var params = {
    TableName: "Moves",
    Item: {
      year: movie.year,
      title: movie.title,
      info: movie.info
    }
  };

  docClient.put(params, function(err, data) {
    if (err) {
      console.error(
        "Unable to add movie",
        movie.title,
        ". Error JSON:",
        JSON.stringify(err, null, 2)
      );
    } else {
      console.log("PutItem succeeded:", movie.title);
    }
  });
});
node LoadData.js



테이블에 항목 추가

// PutItem.js

var AWS = require("aws-sdk");
var fs = require("fs");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

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

var table = "Movies";

var year = 2017;
var title = "The Big Wonny";

var params = {
  TableName: table,
  Item: {
    year: year,
    title: title,
    info: {
      plot: "Nothing happens at all.",
      rating: 0
    }
  }
};

console.log("Adding a new item...");
docClient.put(params, function(err, data) {
  if (err) {
    console.error(
      "Unable to add item. Error JSON:",
      JSON.stringify(err, null, 2)
    );
  } else {
    console.log("Added item:", JSON.stringify(data, null, 2));
  }
});
node PutItem.js

- 기본 키가 필요하므로 기본 키 (year, title) 및 info 속성 추가


항목 읽기

// GetItem.js

var AWS = require("aws-sdk");
var fs = require("fs");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

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

var table = "Movies";

var year = 2017;
var title = "The Big Wonny";

var params = {
  TableName: table,
  Key: {
    year: year,
    title: title
  }
};

docClient.get(params, function(err, data) {
  if (err) {
    console.error(
      "Unable to read item. Error JSON:",
      JSON.stringify(err, null, 2)
    );
  } else {
    console.log("GetItem succeeded:", JSON.stringify(data, null, 2));
  }
});
node GetItem.js


항목 업데이트

// UpdateItem.js

var AWS = require("aws-sdk");
var fs = require("fs");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

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

var table = "Movies";

var year = 2017;
var title = "The Big Wonny";

var params = {
  TableName: table,
  Key: {
    year: year,
    title: title
  },
  UpdateExpression: "set info.rating = :r, info.plot=:p, info.actors=:a",
  ExpressionAttributeValues: {
    ":r": 5.5,
    ":p": "Everything happens all at once.",
    ":a": ["Larry", "Moe", "Curly"]
  },
  ReturnValues: "UPDATED_NEW"
};

console.log("Updating the item...");
docClient.update(params, function(err, data) {
  if (err) {
    console.error(
      "Unable to update item. Error JSON:",
      JSON.stringify(err, null, 2)
    );
  } else {
    console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));
  }
});
node UpdateItem.js

- 지정된 항목에 대해 수행하고자 하는 모든 업데이트를 설명하기 위해 UpdateExpression을 사용

- ReturnValues 파라미터는 DynamoDB에게 업데이트된 속성("UPDATED_NEW")만 반환하도록 지시



원자성 카운터 증가시키기

update 메서드를 사용하여 다른 쓰기 요청을 방해하지 않으면서 기존 속성의 값을 증가시키거나 감소시킬 수 있음 (모든 쓰기 요청은 수신된 순서대로 적용)


실행 시 rating 속성이 1씩 증가하는 프로그램

// Increment.js

var AWS = require("aws-sdk");
var fs = require("fs");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

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

var table = "Movies";

var year = 2017;
var title = "The Big Wonny";

// Increment an atomic counter

var params = {
  TableName: table,
  Key: {
    year: year,
    title: title
  },
  UpdateExpression: "set info.rating = info.rating + :val",
  ExpressionAttributeValues: {
    ":val": 1
  },
  ReturnValues: "UPDATED_NEW"
};

console.log("Updating the item...");
docClient.update(params, function(err, data) {
  if (err) {
    console.error(
      "Unable to update item. Error JSON:",
      JSON.stringify(err, null, 2)
    );
  } else {
    console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));
  }
});
node Increment.js



항목 업데이트(조건부)

UpdateItem을 조건과 함께 사용하는 방법

조건이 true로 평가되면 업데이트가 성공하지만 그렇지 않으면 수행되지 않음

// ConditionalUpdateItem.js

var AWS = require("aws-sdk");
var fs = require("fs");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

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

var table = "Movies";

var year = 2017;
var title = "The Big Wonny";

// Increment an atomic counter

var params = {
  TableName: table,
  Key: {
    year: year,
    title: title
  },
   UpdateExpression: "remove info.actors[0]",
  ConditionExpression: "size(info.actors) > :num",
  ExpressionAttributeValues: {
    ":num": 3
  },
  ReturnValues: "UPDATED_NEW"
};

console.log("Attempting a conditional update...");
docClient.update(params, function(err, data) {
  if (err) {
    console.error(
      "Unable to update item. Error JSON:",
      JSON.stringify(err, null, 2)
    );
  } else {
    console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));
  }
});
node ConditionalUpdateItem.js

다음과 같이 작성하면 아래와 같은 에러 메시지가 표시 됨

The conditional request failed"

영화에는 3명의 배우가 있는데 배우가 3명보다 많은지를 확인하고 있어 에러가 발생

다음과 같이 수정하면 정상적으로 항목이 업데이트 됨

ConditionExpression: "size(info.actors) >= :num",



항목 삭제

// DeleteItem.js

var AWS = require("aws-sdk");
var fs = require("fs");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

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

var table = "Movies";

var year = 2017;
var title = "The Big Wonny";

var params = {
  TableName: table,
  Key: {
    year: year,
    title: title
  },
  ConditionExpression: "info.rating <= :val",
  ExpressionAttributeValues: {
    ":val": 5.0
  }
};

console.log("Attempting a conditional delete...");
docClient.delete(params, function(err, data) {
  if (err) {
    console.error(
      "Unable to update item. Error JSON:",
      JSON.stringify(err, null, 2)
    );
  } else {
    console.log("DeleteItem succeeded:", JSON.stringify(data, null, 2));
  }
});
node DeleteItem.js

다음과 같이 작성하면 아래와 같은 에러 메시지가 표시 됨

The conditional request failed

특정 영화에 대한 평점이 5보다 크기 때문에 에러가 발생

다음과 같이 수정하면 정상적으로 항목이 삭제 됨

var params = {
  TableName: table,
  Key: {
    year: year,
    title: title
  }
};



데이터 쿼리

- 파티션 키 값을 지정해야 하며, 정렬 키는 선택 사항

- 1년 동안 개봉한 모든 영화를 찾으려면 year만 지정, title을 입력하면 2014년 개봉된 "A"로 시작하는 영화를 검색하는 것과 같이 정렬 키에 대한 어떤 조건을 바탕으로 일부 영화를 검색할 수도 있음


한 해 동안 개봉한 모든 영화

// QueryYear.js

var AWS = require("aws-sdk");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

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

var params = {
  TableName: "Movies",
  KeyConditionExpression: "#yr = :yyyy",
  ExpressionAttributeNames: {
    "#yr": "year"
  },
  ExpressionAttributeValues: {
    ":yyyy": 1985
  }
};

docClient.query(params, function(err, data) {
  if (err) {
    console.error("Unable to query. Error JSON:", JSON.stringify(err, null, 2));
  } else {
    console.log("Query succeeded.");
    data.Items.forEach(function(item) {
      console.log(" -", item.year + ": " + item.title);
    });
  }
});
node QueryYear.js

ExpressionAttributeNames는 이름을 교체함. 이를 사용하는 이유는 year가 DynamoDB에서 예약어이기 때문. KeyConditionExpression을 포함해 어떤 표현식에서도 사용할 수 없으므로 표현식 속성 이름인 #yr을 사용하여 이를 지칭

ExpressionAttributeValues는 값을 교체함. 이를 사용하는 이유는 KeyConditionExpresssion을 포함해 어떤 표현식에서도 리터럴을 사용할 수 없기 때문. 표현식 속성 값인 :yyyy를 사용해 지칭


* 위의 프로그램은 기본 키 속성으로 테이블을 쿼리하는 방법. DynamoDB에서 1개 이상의 보조 인덱스를 테이블에 생성하여 그 인덱스로 테이블을 쿼리하는 것과 동일한 방식으로 쿼리 작업 가능. 보조 인덱스는 키가 아닌 속성에 대한 쿼리를 허용하여 애플리케이션에 더 많은 유연성을 부여함


한 해 동안 개봉한 모든ㄴ 영화 중에 특정 제목을 지닌 영화

year 1992에 개봉한 영화 중에 title이 "A"부터 "L"까지의 알파벳으로 시작하는 영화를 모두 조회합니다.

// QueryTitle.js

var AWS = require("aws-sdk");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

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

console.log(
  "Querying for movies from 1992 - titles A-L, with genres and lead actor"
);

var params = {
  TableName: "Movies",
  ProjectionExpression: "#yr, title, info.genres, info.actors[0]",
  KeyConditionExpression: "#yr = :yyyy and title between :letter1 and :letter2",
  ExpressionAttributeNames: {
    "#yr": "year"
  },
  ExpressionAttributeValues: {
    ":yyyy": 1992,
    ":letter1": "A",
    ":letter2": "L"
  }
};

docClient.query(params, function(err, data) {
  if (err) {
    console.error("Unable to query. Error JSON:", JSON.stringify(err, null, 2));
  } else {
    console.log("Query succeeded.");
    data.Items.forEach(function(item) {
      console.log(
        " -",
        item.year + ": " + item.title + " ... " + item.info.genres + " ... ",
        item.info.actors[0]
      );
    });
  }
});
node QueryTtiel.js



스캔

테이블의 모든 항목을 읽고 테이블의 모든 데이터를 반환

선택 사항인 filter_expression을 제공할 수 있으며 그 결과 기준이 일치하는 항목만 반환하지만 필터는 테이블 전체를 스캔한 후에만 적용됨

// Scan.js

var AWS = require("aws-sdk");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

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

var params = {
  TableName: "Movies",
  ProjectionExpression: "#yr, title, info.rating",
  FilterExpression: "#yr between :start_yr and :end_yr",
  ExpressionAttributeNames: {
    "#yr": "year"
  },
  ExpressionAttributeValues: {
    ":start_yr": 1950,
    ":end_yr": 1959
  }
};

console.log("Scanning Movies table.");
docClient.scan(params, onScan);

function onScan(err, data) {
  if (err) {
    console.error(
      "Unable to scan the table. Error JSON:",
      JSON.stringify(err, null, 2)
    );
  } else {
    // print all the movies
    console.log("Scan succeeded.");
    data.Items.forEach(function(movie) {
      console.log(
        movie.year + ": ",
        movie.title,
        "- rating:",
        movie.info.rating
      );
    });

    // continue scanning if we have more movies, because
    // scan can retrieve a maximum of 1MB of data
    if (typeof data.LastEvaluatedKey != "undefined") {
      console.log("Scanning for more...");
      params.ExclusiveStartKey = data.LastEvaluatedKey;
      docClient.scan(params, onScan);
    }
  }
}
node Scan.js

ProjectionExpression은 스캔 결과에서 원하는 속성만 지정

FilterExpression은 조건을 만족하는 항목만 반환하도록 조건을 지정. 다른 항목들은 모두 무시됨



테이블 삭제

// DeleteTable.js

var AWS = require("aws-sdk");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

var dynamodb = new AWS.DynamoDB();

var params = {
  TableName: "Movies"
};

dynamodb.deleteTable(params, function(err, data) {
  if (err) {
    console.error(
      "Unable to delete table. Error JSON:",
      JSON.stringify(err, null, 2)
    );
  } else {
    console.log(
      "Deleted table. Table description JSON:",
      JSON.stringify(data, null, 2)
    );
  }
});
node DeleteTable.js


#트레바리 #개발자 #안드로이드 #앱개발 #Node.js #백엔드 #인사이트 #경험공유 #데이터베이스 #DB #개발 #AWS #아마존 #NoSQL 


관련 스택

기업문화 엿볼 때, 더팀스

로그인

/