아래와 같은 환경으로 serverless 환경을 구성하기 위한 예제이다.


┌─────────────┐      ┌── lambda ──┐      ┌──────────┐
│ api-gateway ├──────┤   nodejs   ├──────┤ dynamodb │
└─────────────┘      └────────────┘      └──────────┘

dynamodb

dynamodb 는 아래와 같이 구성히고 샘플 데이터도 사전에 넣어 두자.

  • tableName : SampleTableName
  • partition key : id
  • sort key : startDate

 SampleTableName
         ┌─── Sort Key
┌─────┬──┴────────┬──────────┬──────────────────┐
│ id  │ startDate │ endDate  │  contents        │
├─────┼───────────┼──────────┼──────────────────┤
│0001 │20210101   │20210115  │ first contents    │
├─────┼───────────┼──────────┼──────────────────┤
│0002 │20210102   │20210220  │ second contents  │
└─┬───┴───────────┴──────────┴──────────────────┘
  └─ Partition Key

lambda

중간에 있는 lambda 예제는 아래와 같이 구성한다.


const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();

const tableName = 'SampleTableName';

const healthPath = '/health'
const noticePath = '/notice'

exports.handler = async function(event) {
  let response;
  switch (true) {
    case event.httpMethod === 'GET' && event.path === healthPath:
      response = buildResponse(200, 'ok');
      break;
    case event.httpMethod === 'GET' && event.path === noticePath && event.queryStringParameters !== null:
      response = getNotice(event.queryStringParameters.id, event.queryStringParameters.startDate);
      break;
    case event.httpMethod === 'GET' && event.path === noticePath:
      response = searchNotice();
      break;
    case event.httpMethod === 'POST' && event.path === noticePath:
      response = saveNotice(JSON.parse(event.body));
      break;
    case event.httpMethod === 'PATCH' && event.path === noticePath:
      const requestBody = JSON.parse(event.body);
      response = modifyNotice(requestBody.id, requestBody.startDate, requestBody.updateKey, requestBody.updateValue);
      break;
    case event.httpMethod === 'DELETE' && event.path === noticePath:
      response = deleteNotice(JSON.parse(event.body).id, JSON.parse(event.body).startDate);
      break;
    default:
      response = buildResponse(404, 'something wrong!');
  }
  return response;
}

async function deleteNotice(id, startDate) {
  const params = {
    TableName: tableName,
    Key: {
      id: id,
      startDate: startDate
    },
    ReturnValues: 'ALL_OLD'
  }
  return await dynamodb.delete(params).promise().then((response) => {
    const body = {
      Operation: 'DELETE',
      Message: 'SUCCESS',
      Item: response
    }
    return buildResponse(200, body);
  }, (error) => {
    console.error('삭제 오류 발생:', error);
  });
}

async function modifyNotice(id, startDate, updateKey, updateValue) {
  const params = {
    TableName: tableName,
    Key: {
      id: id,
      startDate: startDate
    },
    UpdateExpression: `set ${updateKey} = :value`,
    ExpressionAttributeValues: {
      ':value': updateValue
    },
    ReturnValues: 'UPDATED_NEW'
  }
  return await dynamodb.update(params).promise().then((response) => {
    const body = {
      Operation: 'UPDATE',
      Message: 'SUCCESS',
      Item: response
    }
    return buildResponse(200, body);
  }, (error) => {
    console.error('수정 에러', error);
  });
}

async function getNotice(id, startDate) {
  const params = {
    TableName: tableName,
    Key: {
      id: id,
      startDate: startDate
    }
  }
  return await dynamodb.get(params).promise().then((response) => {
    return buildResponse(200, response.Item)
  }, (error)=> {
    console.error('찾고자 하는 데이터가 없음', error);
  });
}

async function searchNotice() {
  const params = {
    TableName: tableName
  }
  const allNotice = await scanDynamoRecords(params, []);
  const body = {
    notices: allNotice
  }
  return buildResponse(200, body);
}

async function scanDynamoRecords(scanParams, itemArray) {
  try {
    const dynamoData = await dynamodb.scan(scanParams).promise();
    itemArray = itemArray.concat(dynamoData.Items);
    if (dynamoData.LastEvaluatedKey) {
      scanParams.ExclusiveStartKey = dynamoData.LastEvaluatedKey;
      return await scanDynamoRecords(scanParams, itemArray)
    }
    return itemArray;
  } catch (error) {
    console.error('조회 오류 발생', error);
  }
}

async function saveNotice(requestBody) {
  const params = {
    TableName: tableName,
    Item: requestBody
  }
  return await dynamodb.put(params).promise().then(() => {
    const body = {
      Operation: 'SAVE',
      Message: 'SUCCESS',
      Item: requestBody
    }
    return buildResponse(200, body);
  }, (error) => {
    console.error('저장 오류 발생', error);
  });
}

function buildResponse(statusCode, body) {
  return {
    statusCode: statusCode,
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  }
}

api gateway

이제 api gateway 를 아래와 같이 구성하고 각 메소드는 위의 lambda 와 연결한다. (주의할 것은 아래 것임)

  • Integration type : Lambda Function
  • Use Lambda Proxy integration : V
  • Lambda Region : lambda-region
  • Lambda Function : my-lambda-name

/
  /health
    GET
    OPTIONS
  /notice
    DELETE
    GET
    OPTIONS
    PATCH
    POST

api gateway 테스트를 수행하여 확인하면 된다.