조회수 8037
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-sdk2. 실행// app.jsvar 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.jsvar 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.jsvar 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.jsvar 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.jsvar 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.jsvar 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.jsvar 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 countervar 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.jsvar 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 countervar 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.jsvar 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.jsvar 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.jsExpressionAttributeNames는 이름을 교체함. 이를 사용하는 이유는 year가 DynamoDB에서 예약어이기 때문. KeyConditionExpression을 포함해 어떤 표현식에서도 사용할 수 없으므로 표현식 속성 이름인 #yr을 사용하여 이를 지칭ExpressionAttributeValues는 값을 교체함. 이를 사용하는 이유는 KeyConditionExpresssion을 포함해 어떤 표현식에서도 리터럴을 사용할 수 없기 때문. 표현식 속성 값인 :yyyy를 사용해 지칭* 위의 프로그램은 기본 키 속성으로 테이블을 쿼리하는 방법. DynamoDB에서 1개 이상의 보조 인덱스를 테이블에 생성하여 그 인덱스로 테이블을 쿼리하는 것과 동일한 방식으로 쿼리 작업 가능. 보조 인덱스는 키가 아닌 속성에 대한 쿼리를 허용하여 애플리케이션에 더 많은 유연성을 부여함한 해 동안 개봉한 모든ㄴ 영화 중에 특정 제목을 지닌 영화year 1992에 개봉한 영화 중에 title이 "A"부터 "L"까지의 알파벳으로 시작하는 영화를 모두 조회합니다.// QueryTitle.jsvar 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.jsvar 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.jsProjectionExpression은 스캔 결과에서 원하는 속성만 지정FilterExpression은 조건을 만족하는 항목만 반환하도록 조건을 지정. 다른 항목들은 모두 무시됨테이블 삭제// DeleteTable.jsvar 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