brunch

매거진 개발개발

You can make anything
by writing

C.S.Lewis

by 워니 Jan 20. 2018

Node.js로 Amazon DynamoDB 사용하기

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

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


#데이터베이스 #DB #개발 #AWS #아마존 #NoSQL 

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari