이번 프로젝트에서 풀스택으로 하나의 기능을 구현하게 됐다. 이전에 MySQL을 사용했던 프로젝트에서는 쿼리문 작성이 번거롭고, 간단한 api를 만드는데에 품이 많이 든다는 느낌을 많이 받았다. 그러다 DynamoDB + AWS Lambda 조합을 사용하는 레퍼런스를 봤는데, 구조도 깔끔하고 연동도 쉬워 보여 최종적으로 이 조합을 선택했다.
Serverless 아키텍처를 사용하면 서버 관리 없이도 자동 확장이 가능하고, 데이터 CRUD 작업도 빠르게 처리할 수 있는 환경을 만들 수 있다.
구현에 들어가기 전에, 이 프로젝트에서 사용할 주요 기술들의 개념과 용도를 먼저 정리해보자!
말그대로 서버가 없다는 뜻일까? 그렇지 않다
서버리스는 클라우드 컴퓨팅의 모델 중 하나로 개발자가 서버를 직접 관리할 필요가 없는 아키텍처를 의미한다.
즉 서버가 없는 것이 아니라 서버를 구축하고 유지, 관리하며 트래픽에 맞게 스케일링할 필요가 없다는 뜻이다
하지만 서버리스 모델들은 이벤트 기반 트리거 되어 실행되기에 온디맨드(On Demand)로 과금된다. (예전에 동아리에서 서버리스로 구현된 서버를 여러명이 사용했다가 7만원이 과금 됐던 것이 떠오른다… 요청 제한과 같은 부분을 잘 세팅 해야할 듯 하다)
최대한 서버와 관련된 작업을 최소로 하고 싶기 때문에 Faas인 AWS Lambda로 서버리스를 구축해보고자 한다!
위에서 알아본대로 AWS Lambda는 Fass인 서버리스 컴퓨팅 서비스로, 사용자가 서버를 관리하지 않고도 코드를 실행할 수 있게 해준다.
Lambda는 다양한 AWS 서비스와 통합되어, 이벤트에 반응하여 자동으로 실행될 수 있다. 예를 들어, API Gateway의 HTTP 요청, S3에 파일 업로드 등의 이벤트를 통해 Lambda 함수가 실행될 수 있다.
DynamoDB는 AWS에서 제공하는 서버리스 기반 Key-Value NoSQL 데이터베이스이다.
API Gateway는 AWS에서 제공하는 서비스로, RESTful API를 생성하고 관리할 수 있게 해준다. Lambda 함수와 함께 사용하여 클라이언트와 서버 간의 통신을 중개한다.
API Gateway는 인증, 요청 제한, 데이터 변환 등의 기능을 제공하여 API의 보안성과 성능을 향상시킨다. 또한, 다양한 요청을 Lambda 함수에 매핑하여 유연한 API 구조를 구현할 수 있다.
AWS의 DynamoDB 서비스로 들어가 새로운 테이블을 생성해준다
DynamoDB 내부에는 해쉬함수가 존재해, 파티션 키는 이 해쉬함수를 거쳐 데이터를 저장할 파티션을 결정하게 된다. 따라서 동일한 파티션 키를 지닌 데이터는 물리적으로 가까운 위치에 저장된다.
그리고 정렬키를 사용하면 동일한 파티션에 저장된 데이터는 정렬키를 기준으로 순서대로 저장되게 된다.
DynamoDB의 세부설정이 주 목표는 아니기에 우선 기본적인 설정만 하고 넘어간다.
IAM Role은 특정 AWS서비스의 액세스 권한을 부여하는 보안증명서로, AWS 내 어떤 서비스를 이용할 수 있는 자격을 의미한다.
나는 다음과 같이 이번 실습에 사용할 권한이 부여된 역할을 생성해주었다
AWSLambdaBasicExecutionRole
: Lambda 함수의 실행 결과나 오류 등을 모니터링하기 위해 CloudWatch Logs에 로그를 작성할 수 있는 권한 (디버깅용)AmazonDynamoDBFullAccess
: DynamoDB 데이터베이스에 대한 모든 권한AmazonAPIGatewayInvokeFullAccess
: API Gateway에서 생성한 API를 호출할 수 있는 권한CloudWatchFullAccess
: CloudWatch는 AWS 리소스 및 애플리케이션의 로그와 성능을 모니터링하고 관리하는 서비스 (선택)
함수의 이름과 런타임 환경을 설정해준다. 그리고 실행역할로 2에서 만든 IAM Role을 세팅해준다!
이후 생성 시 나타나는 코드에디터의 index.mjs에 백엔드 로직을 작성해준다.
이때 함수이름이 handler일때 핸들러는 index.handler라고 세팅해주면된다.
아래는 기본적인 예제이다.
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);
export const handler = async (event, context) => {
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
};
참고로 아래와 같은 방식으로 aws-sdk 모듈을 불러오면 test시 ReferenceError: require is not defined in ES module scope, you can use import instead
라는 에러가 발생한다.
import AWS from 'aws-sdk';
const dynamoDB = new AWS.DynamoDB.DocumentClient();
Node.js 18 런타임 환경에서는 aws-sdk
v3가 기본 제공된다. 기존의 aws-sdk
v2 방식으로 코드를 작성하면 모듈을 찾지 못해 오류가 발생한다고 한다. 관련 글
AWS Lambda 콘솔에서는 직접 패키지를 설치할 수 없기 때문에 로컬에서 설치한 폴더를 압축해 레이어로 등록을 해주어야한다
나의 경우에는 uuid를 통해 unique id를 세팅해주기로 결정하였다. 따라서 프로젝트를 따로 uuid를 설치한 레이어를 로컬에서 세팅 후 zip으로 압축해 업로드해주어야 한다.
uuid-layer/
└── nodejs/
├── node_modules/
│ └── uuid/
└── package-lock.json
위와 같이 세팅 후 nodejs 디렉토리를 압축해준다.
압축한 파일을 통해 레이어를 만들고 Lambda 함수에 적용해준다.
routeKey로 요청 경로에 맞게 실행할 로직을 작성해준다
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; // DynamoDB 클라이언트 가져오기
import {
DynamoDBDocumentClient,
GetCommand,
PutCommand,
ScanCommand,
DeleteCommand,
UpdateCommand,
} from "@aws-sdk/lib-dynamodb"; // DocumentClient 메소드 가져오기
import { v4 as uuidv4 } from "uuid"; // UUID 생성 함수 가져오기
const ddbClient = new DynamoDBClient({});
const ddb = DynamoDBDocumentClient.from(ddbClient);
const tableName = "Todos";
export const handler = async (event) => {
let statusCode = 200;
let body;
let headers = {
"Content-Type": "application/json",
};
try {
switch (event.routeKey) {
case "GET /todos":
// 관련 로직
break;
case "GET /todos/{todoId}":
// 관련 로직
break;
case "POST /todos":
// 관련 로직
break;
case "PATCH /todos/{todoId}/content":
// 관련 로직
break;
case "DELETE /todos/{todoId}":
// 관련 로직
break;
default:
throw new Error(`지원하지 않는 경로: ${event.routeKey}`);
}
} catch (err) {
...
} finally {
body = JSON.stringify(body);
}
return {
statusCode: statusCode,
body: body,
headers: headers,
};
};
이제 마지막으로 api 경로를 지정해주면 된다!
http메서드와 리소스 경로, 그리고 해당 경로로 요청시 실행할 lambda함수를 넣어준다.
최종적으로 위와 같이 세팅이 된다. Gateway의 대시보드에서는 배포된 url을 확인할 수 있다.
API Gateway에서 CORS 설정이 가능하다. 나의 경우에는 개발 중인 포트만 허용해주었다. 필요에 따라 header도 설정해주면 좋다.
람다에서도 연결된 Gateway와 레이어를 확인할 수 있다.
정상적으로 요청이 처리되어 응답이 오고있다!
이렇게 AWS 서비스를 이용해 간단하게 서버리스 환경을 구축할 수 있었다. 구축 과정이 비교적 쉬운 편이라, 토이 프로젝트나 간단한 서비스에 적합할 것 같다.
특히 서버 관리에 신경 쓸 필요 없이, 확장성까지 자동으로 처리해주기 때문에 작은 규모의 프로젝트부터 시작해서 더 큰 서비스로 확장할 때도 유용하지 않을까 싶다! 👍
https://inpa.tistory.com/entry/WEB-🌐-서버리스ServerLess-개념-💯-총정리-BaaS-FaaS