콘텐츠 대표 이미지 - DynamoDB를 활용한 서버리스 아키텍처 완전 정복 가이드
🛠️ DB / 서버 · 프로그램개발

DynamoDB를 활용한 서버리스 아키텍처 완전 정복 가이드

AWS DynamoDB + Lambda + API Gateway로 진짜 서버리스 시스템 만들어보기 🚀

야 솔직히 말해서 요즘 백엔드 개발자들 사이에서 "서버리스" 얘기 안 나오면 좀 뒤처진 느낌 들지 않음? ㅋㅋㅋ
근데 막상 서버리스 하려고 하면 DB는 뭐 써야 하지? 어떻게 연결하지? 이런 고민이 생기잖아.
오늘은 그 고민 완전히 해결해줄게. AWS DynamoDB를 중심으로 서버리스 아키텍처를 어떻게 구축하는지 처음부터 끝까지 다 뜯어볼 거야. 🔥

진짜 실무에서 쓰는 패턴들, 주의해야 할 함정들, 최적화 꿀팁까지 다 담았으니까 끝까지 읽어봐. 분량 좀 되는데 그만큼 알차다고 ㅋㅋ


서버리스 아키텍처 전체 흐름 클라이언트 웹 / 앱 모바일 HTTP API Gateway 라우팅 / 인증 Rate Limiting CORS 처리 트리거 Lambda 비즈니스 로직 이벤트 처리 자동 스케일링 CRUD DynamoDB NoSQL DB 자동 스케일링 서버리스 친화 S3 버킷 파일 저장소 SNS / SQS 메시지 큐 CloudWatch 모니터링 ✨ 서버 관리 없이 완전 자동화된 확장 가능한 아키텍처

🤔 서버리스가 뭔데? 진짜로

"서버리스"라는 단어 들으면 "서버가 없는 거야?" 이렇게 생각하는 사람들 있는데, 그건 아니야 ㅋㅋㅋ
서버는 당연히 있어. 근데 개발자가 서버를 직접 관리하지 않아도 된다는 게 핵심이야.

기존 방식이랑 비교해보면 이해가 빠름:

구분 전통적 서버 방식 서버리스 방식
서버 관리 직접 EC2 인스턴스 관리 AWS가 알아서 관리
스케일링 수동 또는 Auto Scaling 설정 자동으로 무한 확장
비용 서버 켜있는 시간만큼 과금 실제 실행된 시간만큼만 과금
운영 부담 OS 패치, 보안 업데이트 필요 코드만 신경쓰면 됨
콜드 스타트 없음 있음 (최적화 필요)

서버리스의 핵심 철학은 "인프라 걱정은 클라우드에 맡기고, 비즈니스 로직에만 집중하자"야.
AWS에서 서버리스 아키텍처를 구성하는 주요 서비스들은 이렇게 있어:

⚡ AWS Lambda 🗄️ DynamoDB 🌐 API Gateway 📦 S3 🔔 SNS 📨 SQS 🔐 Cognito 📊 CloudWatch 🚀 AppSync ⚙️ Step Functions

이 중에서 오늘 우리가 집중할 건 DynamoDB야. 서버리스 아키텍처에서 DB 선택이 얼마나 중요한지, 왜 DynamoDB가 서버리스랑 찰떡궁합인지 알아볼 거야.


🗄️ DynamoDB가 뭔지 제대로 알아보자

DynamoDB는 AWS에서 만든 완전 관리형 NoSQL 데이터베이스야.
2012년에 출시됐고, 아마존 내부에서도 쇼핑카트, 세션 관리 등 핵심 서비스에 쓰이고 있어.

근데 왜 서버리스에서 DynamoDB를 쓰냐고? 이유가 명확해:

💡 DynamoDB가 서버리스에 찰떡인 이유
1
완전 관리형 (Fully Managed)
서버 프로비저닝, 패치, 백업 전부 AWS가 해줌. 개발자는 테이블 설계와 쿼리에만 집중하면 돼.
2
자동 스케일링
트래픽이 갑자기 10배 뛰어도 DynamoDB는 자동으로 처리 용량을 늘려줘. Lambda랑 완벽하게 시너지.
3
연결 풀 문제 없음
RDS 같은 관계형 DB는 Lambda에서 쓸 때 연결 풀 고갈 문제가 생기는데, DynamoDB는 HTTP 기반 API라 이 문제가 없어.
4
밀리초 단위 응답
단일 자릿수 밀리초(single-digit millisecond) 응답 속도를 보장해. 서버리스 함수의 짧은 실행 시간과 잘 맞아.
5
온디맨드 요금제
실제 읽기/쓰기 요청 수만큼만 비용 내면 돼. Lambda의 실행 횟수 과금 방식이랑 철학이 같아.
📐 DynamoDB 핵심 개념 정리

DynamoDB를 제대로 쓰려면 이 개념들은 무조건 알아야 해:

개념 설명 RDB 비교
Table 데이터를 저장하는 기본 단위 Table
Item 테이블 내 하나의 데이터 레코드 Row
Attribute Item의 각 데이터 필드 Column
Partition Key 데이터를 분산 저장하는 기본 키 Primary Key
Sort Key 같은 파티션 내 정렬 기준 키 Composite Key
GSI 다른 속성으로 쿼리하기 위한 인덱스 Secondary Index
LSI 같은 파티션 키, 다른 정렬 키 인덱스 Local Index
💡 꿀팁! DynamoDB에서 가장 중요한 건 Partition Key 설계야. 파티션 키가 얼마나 고르게 분산되느냐에 따라 성능이 완전히 달라져. 특정 파티션에 데이터가 몰리면 "핫 파티션" 문제가 생기거든. 이건 나중에 더 자세히 다룰게.

DynamoDB 테이블 구조 이해하기 Partition Key Sort Key Attribute 1 Attribute 2 Attribute 3 USER#user001 PROFILE 홍길동 hong@email.com 2024-01-15 USER#user001 ORDER#2024-001 노트북 1,500,000원 배송완료 USER#user001 ORDER#2024-002 마우스 85,000원 배송중 USER#user002 PROFILE 김철수 kim@email.com 2024-02-20 PRODUCT#prod001 INFO 게이밍 키보드 120,000원 재고 50개 🔑 Single Table Design 여러 엔티티를 하나의 테이블에 저장하는 패턴 DynamoDB 권장 방식 📊 파티션 분산 저장 Partition Key 해시값으로 여러 파티션에 자동 분산 고성능 병렬 처리 가능 ⚡ Sort Key 활용 같은 파티션 내에서 범위 쿼리, 정렬 가능 begins_with, between 지원

🏗️ 서버리스 아키텍처 설계 원칙

DynamoDB 쓰기 전에 서버리스 아키텍처 자체를 어떻게 설계해야 하는지 먼저 알아야 해.
잘못 설계하면 나중에 유지보수가 지옥이 되거든 ㅋㅋㅋ

🎯 서버리스 설계 핵심 원칙 5가지
1️⃣ 이벤트 드리븐 (Event-Driven) 아키텍처
서버리스는 이벤트가 발생할 때만 함수가 실행돼. HTTP 요청, DB 변경, 파일 업로드, 스케줄 등 다양한 이벤트 소스를 활용해야 해.
2️⃣ 단일 책임 원칙 (Single Responsibility)
Lambda 함수 하나는 하나의 일만 해야 해. "유저 생성", "주문 처리", "알림 발송" 이런 식으로 기능별로 분리. 함수가 너무 커지면 콜드 스타트도 느려지고 유지보수도 힘들어져.
3️⃣ 무상태 (Stateless) 설계
Lambda 함수는 실행될 때마다 새로운 컨테이너에서 돌아갈 수 있어. 그래서 함수 내부에 상태를 저장하면 안 돼. 모든 상태는 DynamoDB, S3, ElastiCache 같은 외부 저장소에 저장해야 해.
4️⃣ 멱등성 (Idempotency) 보장
같은 요청이 여러 번 들어와도 결과가 동일해야 해. 네트워크 오류로 재시도가 발생할 수 있거든. DynamoDB의 조건부 쓰기(Conditional Write)를 활용하면 멱등성 구현이 쉬워져.
5️⃣ 최소 권한 원칙 (Least Privilege)
각 Lambda 함수에는 필요한 최소한의 IAM 권한만 부여해야 해. "유저 조회" 함수에 DynamoDB 전체 권한 주는 건 보안상 위험해. 테이블별, 작업별로 세밀하게 권한 설정해야 함.
📦 프로젝트 구조 설계

실제 서버리스 프로젝트를 어떻게 구조화하는지 보여줄게. Serverless Framework 기준이야:


# 프로젝트 디렉토리 구조

my-serverless-app/

├── serverless.yml          # 서버리스 설정 파일

├── package.json

├── src/

│   ├── functions/           # Lambda 함수들

│   │   ├── users/

│   │   │   ├── create.js    # 유저 생성

│   │   │   ├── get.js       # 유저 조회

│   │   │   ├── update.js    # 유저 수정

│   │   │   └── delete.js    # 유저 삭제

│   │   ├── orders/

│   │   │   ├── create.js

│   │   │   └── list.js

│   │   └── products/

│   │       └── search.js

│   ├── lib/                 # 공통 유틸리티

│   │   ├── dynamodb.js      # DynamoDB 클라이언트

│   │   ├── response.js      # HTTP 응답 헬퍼

│   │   └── validator.js     # 입력값 검증

│   └── models/              # 데이터 모델

│       ├── user.js

│       └── order.js

└── tests/                   # 테스트 파일

⚙️ DynamoDB 테이블 설계 심화

DynamoDB에서 제일 중요한 게 뭐냐고? 바로 테이블 설계야.
RDB처럼 나중에 ALTER TABLE 해서 컬럼 추가하는 게 자유롭지 않아.
처음에 잘 설계해야 나중에 고생 안 해 ㅋㅋㅋ

🔑 키 설계 전략
Partition Key 설계 원칙

파티션 키는 데이터를 여러 파티션에 고르게 분산시키는 역할을 해.
좋은 파티션 키의 조건:

✅ 카디널리티(Cardinality)가 높아야 함 - 값의 종류가 많을수록 좋아
✅ 균등하게 분산되어야 함 - 특정 값에 요청이 몰리면 안 돼
✅ 자주 쿼리되는 패턴을 반영해야 함

❌ 나쁜 예시: 성별(M/F), 상태(active/inactive) → 값이 너무 적어서 핫 파티션 발생
✅ 좋은 예시: 사용자 ID, 주문 ID, 상품 ID → 값이 많고 고르게 분산
📊 Single Table Design 패턴

DynamoDB의 권장 패턴은 Single Table Design이야.
여러 엔티티를 하나의 테이블에 저장하는 방식인데, 처음엔 이상해 보여도 이게 맞아 ㅋㅋ
이유는 DynamoDB에서 JOIN이 없기 때문에, 관련 데이터를 같은 파티션에 저장해서 한 번의 쿼리로 가져오는 게 효율적이거든.


// Single Table Design 예시

// 테이블명: MyApp

// PK: pk (Partition Key), SK: sk (Sort Key)



// 유저 프로필

{

"pk": "USER#user001",

"sk": "PROFILE",

"name": "홍길동",

"email": "hong@email.com",

"createdAt": "2024-01-15T09:00:00Z"

}



// 유저의 주문 (같은 파티션에 저장!)

{

"pk": "USER#user001",

"sk": "ORDER#2024-001",

"productName": "노트북",

"amount": 1500000,

"status": "DELIVERED"

}



// 유저의 모든 주문 조회 (단 한 번의 쿼리로!)

const params = {

TableName: 'MyApp',

KeyConditionExpression: 'pk = :pk AND begins_with(sk, :prefix)',

ExpressionAttributeValues: {

':pk': 'USER#user001',

':prefix': 'ORDER#'

}

}
🔍 GSI (Global Secondary Index) 활용

"이메일로 유저 찾고 싶은데?" 같은 상황에서 GSI가 필요해.
기본 파티션 키가 아닌 다른 속성으로 쿼리할 수 있게 해주는 인덱스야.


// GSI 설정 예시 (serverless.yml)

resources:

Resources:

MyAppTable:

Type: AWS::DynamoDB::Table

Properties:

TableName: MyApp

BillingMode: PAY_PER_REQUEST  # 온디맨드 과금

AttributeDefinitions:

- AttributeName: pk

AttributeType: S

- AttributeName: sk

AttributeType: S

- AttributeName: email  # GSI용 속성

AttributeType: S

- AttributeName: gsi1pk  # GSI 파티션 키

AttributeType: S

KeySchema:

- AttributeName: pk

KeyType: HASH

- AttributeName: sk

KeyType: RANGE

GlobalSecondaryIndexes:

- IndexName: EmailIndex

KeySchema:

- AttributeName: email

KeyType: HASH

Projection:

ProjectionType: ALL  # 모든 속성 복사

- IndexName: GSI1

KeySchema:

- AttributeName: gsi1pk

KeyType: HASH

- AttributeName: sk

KeyType: RANGE

Projection:

ProjectionType: ALL
⚠️ 주의! GSI는 최대 20개까지 생성 가능하지만, 각 GSI마다 추가 비용이 발생해. 그리고 GSI는 기본 테이블과 별도로 데이터를 복사해서 저장하기 때문에 쓰기 비용도 늘어나. 꼭 필요한 경우에만 만들어야 해!

⚡ AWS Lambda + DynamoDB 실전 연동

이제 진짜 코드 레벨로 들어가보자. Lambda에서 DynamoDB를 어떻게 쓰는지 실전 예제로 보여줄게.
Node.js 기준으로 설명할 거야. (Python도 비슷한 패턴이니까 참고해봐)

🔧 DynamoDB 클라이언트 설정

AWS SDK v3 기준으로 설명할게. v2랑 문법이 좀 달라서 헷갈릴 수 있으니까 주의해!


// src/lib/dynamodb.js

import { DynamoDBClient } from '@aws-sdk/client-dynamodb';

import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';



// 클라이언트 생성 (Lambda 컨테이너 재사용을 위해 모듈 레벨에서 생성)

const client = new DynamoDBClient({

region: process.env.AWS_REGION || 'ap-northeast-2'

});



// DocumentClient: JS 객체를 자동으로 DynamoDB 타입으로 변환

export const dynamoDB = DynamoDBDocumentClient.from(client, {

marshallOptions: {

removeUndefinedValues: true,  // undefined 값 자동 제거

convertEmptyValues: false

}

});



export const TABLE_NAME = process.env.TABLE_NAME || 'MyApp';
💡 성능 꿀팁! DynamoDB 클라이언트를 Lambda 핸들러 함수 밖에서 생성하면 Lambda 컨테이너가 재사용될 때 클라이언트도 재사용돼. 이렇게 하면 매 요청마다 새로 연결을 맺는 오버헤드를 줄일 수 있어. 콜드 스타트 이후 웜 스타트에서 성능이 확 올라감!
✏️ CRUD 작업 구현

// src/functions/users/create.js - 유저 생성

import { PutCommand } from '@aws-sdk/lib-dynamodb';

import { dynamoDB, TABLE_NAME } from '../../lib/dynamodb.js';

import { v4 as uuidv4 } from 'uuid';



export const handler = async (event) => {

try {

const body = JSON.parse(event.body);

const userId = uuidv4();

const now = new Date().toISOString();



const item = {

pk: `USER#${userId}`,

sk: 'PROFILE',

userId,

name: body.name,

email: body.email,

createdAt: now,

updatedAt: now,

// GSI용 속성

gsi1pk: `EMAIL#${body.email}`

};



await dynamoDB.send(new PutCommand({

TableName: TABLE_NAME,

Item: item,

// 이미 존재하면 에러 발생 (멱등성 보장)

ConditionExpression: 'attribute_not_exists(pk)'

}));



return {

statusCode: 201,

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify({ userId, message: '유저 생성 완료!' })

};

} catch (error) {

if (error.name === 'ConditionalCheckFailedException') {

return { statusCode: 409, body: JSON.stringify({ message: '이미 존재하는 유저' }) };

}

return { statusCode: 500, body: JSON.stringify({ message: error.message }) };

}

};

// src/functions/users/get.js - 유저 조회

import { GetCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';

import { dynamoDB, TABLE_NAME } from '../../lib/dynamodb.js';



// 유저 ID로 조회

export const getById = async (event) => {

const { userId } = event.pathParameters;



const result = await dynamoDB.send(new GetCommand({

TableName: TABLE_NAME,

Key: {

pk: `USER#${userId}`,

sk: 'PROFILE'

}

}));



if (!result.Item) {

return { statusCode: 404, body: JSON.stringify({ message: '유저 없음' }) };

}



return { statusCode: 200, body: JSON.stringify(result.Item) };

};



// 유저의 모든 주문 조회 (Sort Key 범위 쿼리)

export const getUserOrders = async (event) => {

const { userId } = event.pathParameters;



const result = await dynamoDB.send(new QueryCommand({

TableName: TABLE_NAME,

KeyConditionExpression: 'pk = :pk AND begins_with(sk, :prefix)',

ExpressionAttributeValues: {

':pk': `USER#${userId}`,

':prefix': 'ORDER#'

},

ScanIndexForward: false  // 최신 주문부터 (내림차순)

}));



return { statusCode: 200, body: JSON.stringify(result.Items) };

};

// src/functions/users/update.js - 유저 수정

import { UpdateCommand } from '@aws-sdk/lib-dynamodb';

import { dynamoDB, TABLE_NAME } from '../../lib/dynamodb.js';



export const handler = async (event) => {

const { userId } = event.pathParameters;

const body = JSON.parse(event.body);



const result = await dynamoDB.send(new UpdateCommand({

TableName: TABLE_NAME,

Key: {

pk: `USER#${userId}`,

sk: 'PROFILE'

},

// 동적으로 업데이트할 필드 설정

UpdateExpression: 'SET #name = :name, updatedAt = :updatedAt',

ExpressionAttributeNames: {

'#name': 'name'  // 예약어 충돌 방지

},

ExpressionAttributeValues: {

':name': body.name,

':updatedAt': new Date().toISOString()

},

// 존재하는 아이템만 업데이트

ConditionExpression: 'attribute_exists(pk)',

ReturnValues: 'ALL_NEW'  // 업데이트 후 값 반환

}));



return { statusCode: 200, body: JSON.stringify(result.Attributes) };

};

🌐 API Gateway 설정 및 연동

Lambda 함수를 외부에서 호출하려면 API Gateway가 필요해.
HTTP 요청을 받아서 Lambda로 전달하고, Lambda의 응답을 다시 클라이언트에게 보내주는 역할이야.
Serverless Framework로 설정하는 방법 보여줄게:


# serverless.yml

service: my-serverless-app



provider:

name: aws

runtime: nodejs20.x

region: ap-northeast-2

stage: ${opt:stage, 'dev'}

environment:

TABLE_NAME: ${self:service}-${self:provider.stage}

iam:

role:

statements:

- Effect: Allow

Action:

- dynamodb:GetItem

- dynamodb:PutItem

- dynamodb:UpdateItem

- dynamodb:DeleteItem

- dynamodb:Query

- dynamodb:BatchGetItem

- dynamodb:BatchWriteItem

Resource:

- !GetAtt MyAppTable.Arn

- !Sub '${MyAppTable.Arn}/index/*'  # GSI 포함



functions:

createUser:

handler: src/functions/users/create.handler

events:

- httpApi:

path: /users

method: POST



getUser:

handler: src/functions/users/get.getById

events:

- httpApi:

path: /users/{userId}

method: GET



getUserOrders:

handler: src/functions/users/get.getUserOrders

events:

- httpApi:

path: /users/{userId}/orders

method: GET



updateUser:

handler: src/functions/users/update.handler

events:

- httpApi:

path: /users/{userId}

method: PUT
🔐 API Gateway 인증 설정

실제 서비스에서는 인증이 필수야. AWS Cognito나 JWT 토큰으로 인증을 구현할 수 있어:


# JWT Authorizer 설정 (serverless.yml)

provider:

httpApi:

authorizers:

jwtAuthorizer:

type: jwt

identitySource: $request.header.Authorization

issuerUrl: https://cognito-idp.ap-northeast-2.amazonaws.com/{userPoolId}

audience:

- {clientId}



functions:

getUser:

handler: src/functions/users/get.getById

events:

- httpApi:

path: /users/{userId}

method: GET

authorizer:

name: jwtAuthorizer  # 인증 적용!

DynamoDB 성능 최적화 전략 ⚡ DAX 캐싱 DynamoDB Accelerator 마이크로초 응답속도 읽기 집약적 워크로드 에 최적화 최대 10배 빠름 📦 배치 작업 BatchGetItem BatchWriteItem 한 번에 최대 25개 아이템 처리 가능 API 호출 횟수 감소 🎯 프로젝션 최적화 필요한 속성만 조회 ProjectionExpression 네트워크 전송량 감소 읽기 비용 절감 비용 + 속도 개선 🔄 페이지네이션 Limit 파라미터 활용 LastEvaluatedKey로 커서 기반 페이징 대용량 데이터 처리 메모리 효율 극대화 🚨 핫 파티션 방지 전략 1. 파티션 키에 랜덤 접미사 추가 (샤딩) 2. 시간 기반 파티션 키 사용 시 버킷팅 적용 3. 쓰기 샤딩: pk = "PRODUCT#001_shard_3" 4. DAX로 읽기 부하 분산 💰 비용 최적화 전략 1. 온디맨드 vs 프로비저닝 모드 선택 2. TTL 설정으로 만료 데이터 자동 삭제 3. GSI 프로젝션 타입 최소화 4. Scan 대신 Query 사용 (비용 차이 큼) 🎯 올바른 최적화 전략으로 성능과 비용 두 마리 토끼를 잡자!

🚀 성능 최적화 실전 기법
⚡ 콜드 스타트 최소화

서버리스의 고질적인 문제인 콜드 스타트. Lambda 함수가 처음 실행될 때 컨테이너를 새로 띄우는 시간이 걸리는데, 이게 수백 ms에서 수 초까지 걸릴 수 있어.
DynamoDB 연동에서 콜드 스타트를 줄이는 방법들:

1. 번들 사이즈 최소화
Lambda 패키지가 클수록 콜드 스타트가 느려져. AWS SDK v3는 모듈별로 import할 수 있어서 필요한 것만 가져오면 돼.

// ❌ 나쁜 예 - 전체 SDK import
import AWS from 'aws-sdk';

// ✅ 좋은 예 - 필요한 것만 import
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';

import { PutCommand } from '@aws-sdk/lib-dynamodb';
2. Provisioned Concurrency 활용
중요한 함수에는 Provisioned Concurrency를 설정해서 미리 컨테이너를 띄워놓을 수 있어.
비용이 추가되지만 콜드 스타트가 완전히 없어져. 결제 API 같은 지연에 민감한 기능에 적합해.
3. Lambda SnapStart (Java 전용)
Java Lambda의 경우 SnapStart를 활용하면 초기화 스냅샷을 저장해서 콜드 스타트를 90% 이상 줄일 수 있어.
📊 DynamoDB 쿼리 최적화

// ❌ 나쁜 예 - Scan 사용 (전체 테이블 스캔, 비용 폭탄!)

const badQuery = await dynamoDB.send(new ScanCommand({

TableName: TABLE_NAME,

FilterExpression: 'email = :email',

ExpressionAttributeValues: { ':email': 'user@email.com' }

}));



// ✅ 좋은 예 - GSI를 활용한 Query (특정 파티션만 조회)

const goodQuery = await dynamoDB.send(new QueryCommand({

TableName: TABLE_NAME,

IndexName: 'EmailIndex',  // GSI 사용!

KeyConditionExpression: 'email = :email',

ExpressionAttributeValues: { ':email': 'user@email.com' }

}));



// ✅ 프로젝션으로 필요한 속성만 가져오기

const projectedQuery = await dynamoDB.send(new GetCommand({

TableName: TABLE_NAME,

Key: { pk: 'USER#user001', sk: 'PROFILE' },

ProjectionExpression: 'userId, #name, email',  // 필요한 것만!

ExpressionAttributeNames: { '#name': 'name' }

}));
🔄 DynamoDB Streams + Lambda 이벤트 처리

DynamoDB Streams는 테이블의 변경 사항을 실시간으로 캡처해서 Lambda를 트리거할 수 있어.
이걸 활용하면 이벤트 드리븐 아키텍처를 완성할 수 있어:


// DynamoDB Streams 트리거 Lambda 함수

export const streamHandler = async (event) => {

for (const record of event.Records) {

const { eventName, dynamodb } = record;



if (eventName === 'INSERT') {

// 새 유저 생성 시 환영 이메일 발송

const newItem = dynamodb.NewImage;

if (newItem.sk.S === 'PROFILE') {

await sendWelcomeEmail(newItem.email.S);

}

}



if (eventName === 'MODIFY') {

// 주문 상태 변경 시 알림 발송

const oldItem = dynamodb.OldImage;

const newItem = dynamodb.NewImage;

if (newItem.sk.S.startsWith('ORDER#') &&

oldItem.status?.S !== newItem.status?.S) {

await sendOrderStatusNotification(newItem);

}

}



if (eventName === 'REMOVE') {

// 데이터 삭제 시 감사 로그 기록

await logDeletion(dynamodb.OldImage);

}

}

};



# serverless.yml에 Streams 트리거 설정

functions:

streamProcessor:

handler: src/functions/stream.streamHandler

events:

- stream:

type: dynamodb

arn: !GetAtt MyAppTable.StreamArn

batchSize: 100

startingPosition: LATEST

bisectBatchOnFunctionError: true
💡 DynamoDB Streams 활용 사례
✅ 실시간 알림 발송 (이메일, 푸시 알림)
✅ 검색 인덱스 동기화 (Elasticsearch, OpenSearch)
✅ 캐시 무효화 (Redis, ElastiCache)
✅ 감사 로그 (Audit Log) 기록
✅ 다른 서비스로 데이터 복제

💰 비용 최적화 전략

서버리스의 장점 중 하나가 비용 효율성인데, 잘못 쓰면 오히려 비용이 폭발할 수 있어 ㅋㅋㅋ
DynamoDB 비용 구조를 이해하고 최적화하는 방법을 알아보자.

💳 DynamoDB 요금 구조
항목 온디맨드 모드 프로비저닝 모드
읽기 (RCU) 100만 요청당 약 $0.25 RCU당 시간당 $0.00013
쓰기 (WCU) 100만 요청당 약 $1.25 WCU당 시간당 $0.00065
스토리지 GB당 월 $0.25 GB당 월 $0.25
적합한 경우 트래픽 예측 불가, 초기 서비스 트래픽 예측 가능, 안정적 서비스

※ 위 가격은 서울 리전(ap-northeast-2) 기준 대략적인 수치야. 실제 가격은 AWS 공식 사이트에서 확인해봐.

🗑️ TTL(Time To Live) 설정

만료된 데이터를 자동으로 삭제해주는 TTL 기능. 세션 데이터, 임시 토큰, 캐시 데이터 등에 활용하면 스토리지 비용을 크게 줄일 수 있어:


// TTL 설정 예시 - 세션 데이터 (24시간 후 자동 삭제)

const sessionItem = {

pk: `SESSION#${sessionId}`,

sk: 'DATA',

userId: userId,

token: accessToken,

createdAt: new Date().toISOString(),

// TTL: Unix timestamp (초 단위)

// 현재 시간 + 24시간

ttl: Math.floor(Date.now() / 1000) + (24 * 60 * 60)

};



// serverless.yml에서 TTL 속성 지정

TimeToLiveSpecification:

AttributeName: ttl

Enabled: true
⚠️ TTL 주의사항! TTL로 삭제된 아이템은 즉시 삭제되지 않아. 만료 후 최대 48시간 내에 삭제돼. 그래서 TTL 필드를 조회 시 필터 조건으로 추가하는 게 좋아:
FilterExpression: 'attribute_not_exists(ttl) OR ttl > :now'
📈 Auto Scaling 설정

프로비저닝 모드를 쓸 때는 Auto Scaling을 설정해서 트래픽에 따라 자동으로 용량을 조절할 수 있어:


# serverless.yml - Auto Scaling 설정

resources:

Resources:

MyAppTable:

Type: AWS::DynamoDB::Table

Properties:

BillingMode: PROVISIONED

ProvisionedThroughput:

ReadCapacityUnits: 5   # 최소값

WriteCapacityUnits: 5  # 최소값



ReadScalingTarget:

Type: AWS::ApplicationAutoScaling::ScalableTarget

Properties:

MaxCapacity: 100   # 최대 RCU

MinCapacity: 5     # 최소 RCU

ResourceId: !Sub 'table/${MyAppTable}'

ScalableDimension: dynamodb:table:ReadCapacityUnits

ServiceNamespace: dynamodb



ReadScalingPolicy:

Type: AWS::ApplicationAutoScaling::ScalingPolicy

Properties:

PolicyType: TargetTrackingScaling

TargetTrackingScalingPolicyConfiguration:

TargetValue: 70  # 70% 사용률 유지

PredefinedMetricSpecification:

PredefinedMetricType: DynamoDBReadCapacityUtilization

🔒 보안 설정 완전 정복

서버리스라고 보안을 소홀히 하면 안 돼! 오히려 더 신경 써야 할 부분들이 있어.

🛡️ IAM 역할 최소 권한 설정

# 함수별 세밀한 권한 설정 예시

functions:

readOnlyFunction:

handler: src/functions/users/get.handler

iamRoleStatements:  # 함수별 IAM 역할

- Effect: Allow

Action:

- dynamodb:GetItem

- dynamodb:Query

Resource:

- !GetAtt MyAppTable.Arn

- !Sub '${MyAppTable.Arn}/index/*'



writeFunction:

handler: src/functions/users/create.handler

iamRoleStatements:

- Effect: Allow

Action:

- dynamodb:PutItem

- dynamodb:UpdateItem

Resource: !GetAtt MyAppTable.Arn
🔐 환경 변수 보안 관리

API 키, DB 연결 정보 같은 민감한 정보는 절대 코드에 하드코딩하면 안 돼!
AWS Systems Manager Parameter Store나 Secrets Manager를 활용해:


# serverless.yml - SSM Parameter Store 활용

provider:

environment:

TABLE_NAME: ${self:service}-${self:provider.stage}

# SSM에서 값 가져오기

JWT_SECRET: ${ssm:/myapp/${self:provider.stage}/jwt-secret}

SMTP_PASSWORD: ${ssm:/myapp/${self:provider.stage}/smtp-password~true}  # ~true: 암호화된 값



// Lambda 코드에서 Secrets Manager 사용

import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';



const secretsClient = new SecretsManagerClient({ region: 'ap-northeast-2' });



// 캐싱으로 매 요청마다 Secrets Manager 호출 방지

let cachedSecret = null;

const getSecret = async () => {

if (cachedSecret) return cachedSecret;

const response = await secretsClient.send(new GetSecretValueCommand({

SecretId: 'myapp/production/api-keys'

}));

cachedSecret = JSON.parse(response.SecretString);

return cachedSecret;

};
🌐 VPC 설정 (필요한 경우)

DynamoDB는 기본적으로 인터넷을 통해 접근하지만, 보안이 중요한 경우 VPC Endpoint를 설정해서 트래픽이 AWS 내부 네트워크를 통해서만 흐르게 할 수 있어.
단, Lambda를 VPC에 넣으면 콜드 스타트가 느려질 수 있으니 트레이드오프를 고려해야 해.


📊 모니터링 및 디버깅

서버리스 환경에서 문제가 생기면 어디서 뭐가 잘못됐는지 찾기가 쉽지 않아.
제대로 된 모니터링 체계를 갖추는 게 진짜 중요해 ㅋㅋ

📝 구조화된 로깅

// src/lib/logger.js - 구조화된 로깅 유틸리티

export const logger = {

info: (message, data = {}) => {

console.log(JSON.stringify({

level: 'INFO',

message,

timestamp: new Date().toISOString(),

requestId: process.env.AWS_REQUEST_ID,

...data

}));

},

error: (message, error, data = {}) => {

console.error(JSON.stringify({

level: 'ERROR',

message,

errorMessage: error.message,

errorStack: error.stack,

timestamp: new Date().toISOString(),

...data

}));

}

};



// 사용 예시

logger.info('유저 생성 완료', { userId, email });

logger.error('DynamoDB 쓰기 실패', error, { userId, operation: 'createUser' });
📈 CloudWatch 메트릭 설정

DynamoDB와 Lambda 모두 CloudWatch에 자동으로 메트릭을 보내줘.
꼭 모니터링해야 할 핵심 메트릭들:

서비스 메트릭 알람 기준
Lambda Duration 타임아웃의 80% 초과 시
Lambda Errors 에러율 1% 초과 시
Lambda Throttles 스로틀링 발생 시
DynamoDB ConsumedReadCapacityUnits 프로비저닝의 80% 초과 시
DynamoDB SystemErrors 0보다 클 때
DynamoDB SuccessfulRequestLatency P99 > 100ms 시
API Gateway 5XXError 에러율 0.1% 초과 시
🔍 AWS X-Ray 분산 추적

여러 Lambda 함수와 DynamoDB가 연결된 복잡한 아키텍처에서 성능 병목을 찾으려면 X-Ray가 필수야:


# serverless.yml - X-Ray 활성화

provider:

tracing:

lambda: true      # Lambda X-Ray 활성화

apiGateway: true  # API Gateway X-Ray 활성화



// Lambda 코드에서 X-Ray SDK 사용

import AWSXRay from 'aws-xray-sdk-core';

import { DynamoDBClient } from '@aws-sdk/client-dynamodb';



// X-Ray로 DynamoDB 호출 추적

const client = AWSXRay.captureAWSv3Client(new DynamoDBClient({

region: 'ap-northeast-2'

}));

🧪 테스트 전략

서버리스 환경에서 테스트하는 게 좀 까다롭긴 한데, 제대로 된 테스트 전략이 있으면 훨씬 편해져.

🔬 단위 테스트 (Unit Test)

// tests/users/create.test.js

import { mockClient } from 'aws-sdk-client-mock';

import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';

import { handler } from '../../src/functions/users/create.js';



// DynamoDB 클라이언트 모킹

const ddbMock = mockClient(DynamoDBDocumentClient);



describe('유저 생성 Lambda', () => {

beforeEach(() => {

ddbMock.reset();

});



it('유저를 성공적으로 생성해야 함', async () => {

ddbMock.on(PutCommand).resolves({});



const event = {

body: JSON.stringify({ name: '홍길동', email: 'hong@test.com' })

};



const result = await handler(event);



expect(result.statusCode).toBe(201);

const body = JSON.parse(result.body);

expect(body.userId).toBeDefined();

});



it('중복 유저 생성 시 409 반환해야 함', async () => {

ddbMock.on(PutCommand).rejects({

name: 'ConditionalCheckFailedException'

});



const result = await handler({ body: JSON.stringify({ name: '홍길동', email: 'hong@test.com' }) });

expect(result.statusCode).toBe(409);

});

});
🌐 로컬 개발 환경 구축

매번 AWS에 배포해서 테스트하면 시간도 오래 걸리고 비용도 나와.
로컬에서 개발하는 방법들:

1. DynamoDB Local
AWS에서 제공하는 로컬 DynamoDB. Docker로 쉽게 실행 가능해:

docker run -p 8000:8000 amazon/dynamodb-local

코드에서 엔드포인트만 바꿔주면 돼:
const client = new DynamoDBClient({ endpoint: 'http://localhost:8000' });
2. Serverless Offline
Serverless Framework 플러그인으로 Lambda와 API Gateway를 로컬에서 실행:

npm install --save-dev serverless-offline serverless-dynamodb-local

serverless offline start
3. AWS SAM Local
AWS에서 공식 제공하는 로컬 테스트 도구. Docker 기반으로 실제 Lambda 환경과 유사하게 테스트 가능:

sam local start-api
sam local invoke FunctionName --event event.json

🏆 실전 아키텍처 패턴

이론은 충분히 배웠으니까 실제로 많이 쓰이는 아키텍처 패턴들을 알아보자.
이런 패턴들은 재능넷 같은 플랫폼에서도 실제로 활용할 수 있는 것들이야.

🛒 이커머스 주문 처리 패턴

// 주문 생성 - 트랜잭션으로 원자성 보장

import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb';



export const createOrder = async (userId, productId, quantity) => {

const orderId = uuidv4();

const now = new Date().toISOString();



// 트랜잭션: 주문 생성 + 재고 감소를 원자적으로 처리

await dynamoDB.send(new TransactWriteCommand({

TransactItems: [

{

// 1. 주문 아이템 생성

Put: {

TableName: TABLE_NAME,

Item: {

pk: `USER#${userId}`,

sk: `ORDER#${orderId}`,

orderId, productId, quantity,

status: 'PENDING',

createdAt: now

},

ConditionExpression: 'attribute_not_exists(pk)'

}

},

{

// 2. 재고 감소 (재고 부족 시 트랜잭션 전체 롤백)

Update: {

TableName: TABLE_NAME,

Key: { pk: `PRODUCT#${productId}`, sk: 'INFO' },

UpdateExpression: 'SET stock = stock - :qty',

ConditionExpression: 'stock >= :qty',  // 재고 체크!

ExpressionAttributeValues: { ':qty': quantity }

}

}

]

}));



return orderId;

};
🔔 실시간 알림 시스템 패턴

// DynamoDB Streams → Lambda → SNS → 사용자 알림

import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';



const snsClient = new SNSClient({ region: 'ap-northeast-2' });



export const notificationHandler = async (event) => {

for (const record of event.Records) {

if (record.eventName !== 'MODIFY') continue;



const newImage = record.dynamodb.NewImage;

const oldImage = record.dynamodb.OldImage;



// 주문 상태 변경 감지

if (newImage.sk?.S?.startsWith('ORDER#') &&

oldImage.status?.S !== newImage.status?.S) {



const message = {

userId: newImage.pk.S.replace('USER#', ''),

orderId: newImage.sk.S.replace('ORDER#', ''),

oldStatus: oldImage.status?.S,

newStatus: newImage.status.S,

timestamp: new Date().toISOString()

};



// SNS로 알림 발행

await snsClient.send(new PublishCommand({

TopicArn: process.env.ORDER_NOTIFICATION_TOPIC_ARN,

Message: JSON.stringify(message),

MessageAttributes: {

eventType: {

DataType: 'String',

StringValue: 'ORDER_STATUS_CHANGED'

}

}

}));

}

}

};
🔄 CQRS 패턴 구현

Command Query Responsibility Segregation - 읽기와 쓰기를 분리하는 패턴이야.
DynamoDB Streams를 활용해서 쓰기 모델과 읽기 모델을 분리할 수 있어:

CQRS with DynamoDB 흐름:

1️⃣ Command (쓰기): API Gateway → Lambda → DynamoDB (메인 테이블)
2️⃣ DynamoDB Streams: 변경 이벤트 캡처
3️⃣ 프로젝션 Lambda: 이벤트를 받아서 읽기 최적화 모델로 변환
4️⃣ Query (읽기): 읽기 최적화된 GSI나 별도 테이블에서 조회

이 패턴을 쓰면 복잡한 집계 쿼리도 미리 계산해서 저장해놓을 수 있어서 읽기 성능이 극적으로 향상돼.

🚨 흔한 실수와 함정들

DynamoDB + 서버리스 조합에서 많이들 하는 실수들 정리해줄게.
나도 처음에 이것들 때문에 많이 고생했거든 ㅋㅋㅋ

❌ 실수 1: Scan 남발
DynamoDB에서 Scan은 전체 테이블을 읽어. 데이터가 많아질수록 비용이 폭발하고 느려져.
항상 Query를 사용하고, 필요하면 GSI를 추가해야 해.
❌ 실수 2: 파티션 키를 날짜로 설정
날짜를 파티션 키로 쓰면 특정 날짜에 모든 트래픽이 몰려서 핫 파티션이 생겨.
날짜는 Sort Key로 쓰고, 파티션 키는 사용자 ID나 카테고리 같은 걸 써야 해.
❌ 실수 3: Lambda에서 DB 연결 매번 새로 생성
DynamoDB 클라이언트를 핸들러 함수 안에서 생성하면 매 요청마다 새로 만들어져.
모듈 레벨에서 한 번만 생성하면 Lambda 컨테이너 재사용 시 클라이언트도 재사용돼.
❌ 실수 4: 에러 처리 없이 DynamoDB 호출
DynamoDB 호출은 언제든 실패할 수 있어. ProvisionedThroughputExceededException, ThrottlingException 등
지수 백오프(Exponential Backoff)로 재시도 로직을 구현해야 해.
❌ 실수 5: 큰 아이템 저장
DynamoDB 아이템 하나의 최대 크기는 400KB야. 이미지나 큰 파일은 S3에 저장하고 DynamoDB에는 S3 URL만 저장해야 해.
❌ 실수 6: Lambda 타임아웃 설정 무시
기본 Lambda 타임아웃은 3초야. DynamoDB 쿼리가 복잡하거나 재시도가 필요하면 타임아웃에 걸릴 수 있어.
작업 특성에 맞게 타임아웃을 설정해야 해 (최대 15분).
🔧 재시도 로직 구현

// 지수 백오프 재시도 유틸리티

export const withRetry = async (fn, maxRetries = 3) => {

let lastError;

for (let attempt = 0; attempt < maxRetries; attempt++) {

try {

return await fn();

} catch (error) {

lastError = error;

// 재시도 가능한 에러인지 확인

const retryableErrors = [

'ProvisionedThroughputExceededException',

'ThrottlingException',

'RequestLimitExceeded',

'ServiceUnavailable'

];



if (!retryableErrors.includes(error.name)) {

throw error;  // 재시도 불가능한 에러는 즉시 throw

}



// 지수 백오프: 100ms, 200ms, 400ms...

const delay = Math.pow(2, attempt) * 100;

await new Promise(resolve => setTimeout(resolve, delay));

}

}

throw lastError;

};



// 사용 예시

const result = await withRetry(() =>

dynamoDB.send(new PutCommand({ TableName, Item }))

);

🌟 고급 기능 활용
📡 DynamoDB PartiQL

PartiQL은 SQL과 유사한 쿼리 언어로 DynamoDB를 조회할 수 있게 해줘.
SQL에 익숙한 개발자들이 DynamoDB를 더 쉽게 쓸 수 있도록 AWS가 2020년에 도입했어:


import { ExecuteStatementCommand } from '@aws-sdk/client-dynamodb';



// PartiQL로 조회

const result = await dynamoDB.send(new ExecuteStatementCommand({

Statement: "SELECT * FROM MyApp WHERE pk = 'USER#user001'"

}));



// 조건부 업데이트

await dynamoDB.send(new ExecuteStatementCommand({

Statement: "UPDATE MyApp SET status = 'SHIPPED' WHERE pk = 'USER#user001' AND sk = 'ORDER#001'"

}));
💡 PartiQL 주의사항! PartiQL은 편리하지만 내부적으로 Scan을 사용할 수 있어서 성능에 주의해야 해. 프로덕션에서는 기존 DynamoDB API를 쓰는 게 더 안전해. PartiQL은 개발/디버깅 용도로 활용하는 게 좋아.
🔄 DynamoDB Export to S3

DynamoDB 데이터를 S3로 내보내서 Athena로 분석하거나 백업할 수 있어.
Point-in-Time Recovery(PITR)가 활성화된 테이블에서 사용 가능해:


// DynamoDB Export 설정 (AWS CLI)

aws dynamodb export-table-to-point-in-time \

--table-arn arn:aws:dynamodb:ap-northeast-2:123456789:table/MyApp \

--s3-bucket my-backup-bucket \

--s3-prefix dynamodb-exports/ \

--export-format DYNAMODB_JSON



# 내보낸 데이터를 Athena로 분석

CREATE EXTERNAL TABLE dynamodb_export (

Item struct<pk:struct<S:string>, sk:struct<S:string>, name:struct<S:string>>

)

ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'

LOCATION 's3://my-backup-bucket/dynamodb-exports/';
🌍 글로벌 테이블 (Multi-Region)

전 세계 사용자를 대상으로 하는 서비스라면 DynamoDB Global Tables를 활용해서 여러 리전에 데이터를 자동으로 복제할 수 있어.
각 리전에서 읽기/쓰기가 가능하고, 충돌은 "last writer wins" 방식으로 해결돼.

Global Tables 특징:
  • ✅ 멀티 리전 자동 복제 (보통 1초 이내)
  • ✅ 각 리전에서 로컬 읽기/쓰기 가능
  • ✅ 리전 장애 시 자동 페일오버
  • ✅ 온디맨드 모드에서만 사용 가능 (2023년 이후)
  • ⚠️ 비용이 일반 테이블보다 높음 (복제 비용 추가)

📋 배포 및 CI/CD 파이프라인

서버리스 앱을 어떻게 배포하고 자동화할지도 중요해.
GitHub Actions로 CI/CD 파이프라인 구축하는 방법 보여줄게:


# .github/workflows/deploy.yml

name: Deploy Serverless App



on:

push:

branches: [main]

pull_request:

branches: [main]



jobs:

test:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v3

- uses: actions/setup-node@v3

with:

node-version: '20'

- run: npm ci

- run: npm test



deploy-dev:

needs: test

runs-on: ubuntu-latest

if: github.event_name == 'pull_request'

steps:

- uses: actions/checkout@v3

- uses: actions/setup-node@v3

with:

node-version: '20'

- run: npm ci

- name: Configure AWS credentials

uses: aws-actions/configure-aws-credentials@v2

with:

aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}

aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

aws-region: ap-northeast-2

- name: Deploy to dev

run: npx serverless deploy --stage dev



deploy-prod:

needs: test

runs-on: ubuntu-latest

if: github.ref == 'refs/heads/main'

steps:

- uses: actions/checkout@v3

- uses: actions/setup-node@v3

with:

node-version: '20'

- run: npm ci

- name: Configure AWS credentials

uses: aws-actions/configure-aws-credentials@v2

with:

aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}

aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

aws-region: ap-northeast-2

- name: Deploy to production

run: npx serverless deploy --stage prod

서버리스 + DynamoDB 프로덕션 체크리스트 🏗️ 설계 단계 ✅ Single Table Design 적용 여부 검토 ✅ 파티션 키 카디널리티 충분한지 확인 ✅ 쿼리 패턴 기반 인덱스 설계 ✅ GSI 최소화 (필요한 것만) ✅ 아이템 크기 400KB 이하 확인 ✅ 핫 파티션 방지 전략 수립 ✅ TTL 적용 가능한 데이터 식별 ⚡ Lambda 최적화 ✅ 모듈 레벨 DynamoDB 클라이언트 생성 ✅ 번들 사이즈 최소화 (SDK v3 활용) ✅ 적절한 타임아웃 설정 ✅ 지수 백오프 재시도 로직 구현 ✅ 환경 변수로 설정값 관리 ✅ 멱등성 보장 (조건부 쓰기 활용) ✅ 구조화된 로깅 구현 🔒 보안 ✅ IAM 최소 권한 원칙 적용 ✅ API Gateway 인증/인가 설정 ✅ 민감 정보 SSM/Secrets Manager 저장 ✅ CORS 설정 검토 ✅ 입력값 검증 (Injection 방지) ✅ Rate Limiting 설정 📊 모니터링 ✅ CloudWatch 알람 설정 ✅ X-Ray 분산 추적 활성화 ✅ DynamoDB Streams 모니터링 ✅ 에러 알림 (Slack, 이메일 등) ✅ 비용 예산 알람 설정 ✅ PITR (Point-in-Time Recovery) 활성화 🚀 이 체크리스트를 통과하면 프로덕션 준비 완료!

🎯 마무리: 서버리스 여정을 시작하며

와 진짜 길었다 ㅋㅋㅋ 여기까지 읽어줬으면 진짜 고마워!
DynamoDB + Lambda + API Gateway 조합으로 서버리스 아키텍처를 구축하는 것, 이제 어느 정도 감이 왔지?

핵심을 다시 정리하면:

🔑 핵심 요약

1️⃣ DynamoDB는 서버리스의 베스트 파트너 - 완전 관리형, 자동 스케일링, HTTP 기반 API로 연결 풀 문제 없음

2️⃣ 테이블 설계가 전부 - Single Table Design, 올바른 파티션 키 선택, 쿼리 패턴 기반 인덱스 설계

3️⃣ Scan은 금지 - 항상 Query를 사용하고, 필요하면 GSI 추가

4️⃣ 이벤트 드리븐으로 설계 - DynamoDB Streams를 활용한 비동기 처리

5️⃣ 보안과 모니터링은 필수 - IAM 최소 권한, CloudWatch 알람, X-Ray 추적

6️⃣ 비용 최적화 항상 고려 - TTL, 적절한 과금 모드 선택, 프로젝션 최소화

서버리스는 처음엔 낯설게 느껴질 수 있어. 근데 한번 익숙해지면 진짜 생산성이 폭발해.
서버 관리에 쓰던 시간을 전부 비즈니스 로직 개발에 쏟을 수 있거든.

혹시 서버리스 개발 관련해서 더 배우고 싶거나, 실제 프로젝트에 도움이 필요하다면 재능넷에서 AWS 서버리스 전문가들의 도움을 받아볼 수도 있어. 다양한 개발 재능을 가진 분들이 활동하고 있으니까 참고해봐! 😊

DynamoDB 설계 관련해서 더 깊이 파고 싶다면 Alex DeBrie의 "The DynamoDB Book"을 강력 추천해. 서버리스 아키텍처 전반에 대해서는 AWS Well-Architected Framework의 서버리스 렌즈 문서도 꼭 읽어봐. 공식 문서인데 진짜 알차거든 ㅋㅋ

자, 이제 직접 만들어봐! 코드 짜다가 막히는 거 있으면 AWS 공식 문서랑 이 글 다시 보면서 해결해봐. 화이팅! 🚀

💡 추천 학습 리소스
📚 AWS DynamoDB 공식 문서: docs.aws.amazon.com/dynamodb
📚 The DynamoDB Book - Alex DeBrie
📚 AWS Well-Architected Framework - Serverless Lens
📚 Serverless Framework 공식 문서: serverless.com/framework/docs
🎥 AWS re:Invent DynamoDB 세션 (YouTube에서 무료로 볼 수 있어!)
댓글 작성

이 글에 대한 여러분의 생각을 들려주세요

댓글 0
댓글을 불러오는 중입니다.

아직 댓글이 없습니다.