개요
AWS는 두 가지 주요 데이터베이스 서비스를 제공합니다: **RDS(Relational Database Service)**와 DynamoDB.
RDS는 전통적인 관계형 데이터베이스를 관리형 서비스로 제공하며, DynamoDB는 NoSQL 키-값 스토어로 대규모 확장성을 제공합니다. 이 가이드에서는 두 서비스의 핵심 개념, 동작 원리, 그리고 프로젝트에 맞는 데이터베이스를 선택하는 방법을 알아봅니다.
Part 1: AWS RDS 이해하기
RDS란?
Amazon RDS(Relational Database Service)는 클라우드에서 관계형 데이터베이스를 쉽게 설정, 운영, 확장할 수 있게 해주는 관리형 서비스입니다.
지원하는 데이터베이스 엔진
RDS는 다음 6가지 인기 있는 데이터베이스 엔진을 지원합니다:
- MySQL - 가장 널리 사용되는 오픈소스 데이터베이스
- PostgreSQL - 고급 기능을 제공하는 오픈소스 데이터베이스
- MariaDB - MySQL의 커뮤니티 기반 포크
- Oracle - 엔터프라이즈급 상용 데이터베이스
- SQL Server - Microsoft의 관계형 데이터베이스
- Amazon Aurora - AWS가 개발한 MySQL/PostgreSQL 호환 고성능 데이터베이스
핵심 특징
1. 완전 관리형 서비스
RDS는 다음 작업을 자동으로 처리합니다:
- 자동 백업: 매일 자동으로 전체 백업 수행
- 소프트웨어 패칭: 데이터베이스 엔진 업데이트 자동 적용
- 장애 복구: Multi-AZ 배포로 자동 페일오버
- 모니터링: CloudWatch를 통한 성능 지표 추적
# RDS 인스턴스 생성 예시 (AWS CLI)
aws rds create-db-instance \
--db-instance-identifier mydb \
--db-instance-class db.t3.micro \
--engine mysql \
--master-username admin \
--master-user-password mypassword \
--allocated-storage 20 \
--backup-retention-period 7 \
--multi-az2. 확장성
수직 확장 (Vertical Scaling): 인스턴스 타입을 변경하여 CPU, 메모리 증가
# 인스턴스 타입 변경
aws rds modify-db-instance \
--db-instance-identifier mydb \
--db-instance-class db.t3.medium \
--apply-immediately읽기 복제본 (Read Replicas): 읽기 성능 향상을 위한 복제본 생성
# 읽기 복제본 생성
aws rds create-db-instance-read-replica \
--db-instance-identifier mydb-replica \
--source-db-instance-identifier mydb3. 보안
- VPC 격리: 데이터베이스를 프라이빗 서브넷에 배치
- 암호화: 저장 데이터 및 전송 중 데이터 암호화
- IAM 통합: AWS IAM을 통한 접근 제어
// Node.js에서 RDS MySQL 연결 예시
const mysql = require('mysql2/promise');
async function connectToRDS() {
const connection = await mysql.createConnection({
host: 'mydb.abc123.us-east-1.rds.amazonaws.com',
port: 3306,
user: 'admin',
password: process.env.DB_PASSWORD,
database: 'myapp',
ssl: {
rejectUnauthorized: true
}
});
console.log('RDS 연결 성공');
return connection;
}
// 쿼리 실행
async function getUsers() {
const connection = await connectToRDS();
try {
const [rows] = await connection.execute(
'SELECT * FROM users WHERE active = ?',
[true]
);
console.log('사용자 목록:', rows);
return rows;
} catch (error) {
console.error('쿼리 실패:', error);
throw error;
} finally {
await connection.end();
}
}RDS의 장점
- ✅ 익숙한 SQL: 기존 SQL 지식과 도구를 그대로 사용
- ✅ ACID 트랜잭션: 강력한 일관성과 데이터 무결성 보장
- ✅ 복잡한 쿼리: JOIN, 집계, 서브쿼리 등 복잡한 쿼리 지원
- ✅ 관계형 데이터: 테이블 간 관계를 명확하게 정의
- ✅ 성숙한 생태계: 풍부한 도구와 라이브러리
RDS의 한계
- ⚠️ 확장 제약: 단일 인스턴스의 컴퓨팅 한계 존재
- ⚠️ 비용: 대용량 데이터에서 스토리지 비용 증가
- ⚠️ 복잡성: 스키마 설계 및 인덱스 최적화 필요
- ⚠️ 응답 지연: 초고속 응답이 필요한 경우 부적합
Part 2: DynamoDB 이해하기
DynamoDB란?
Amazon DynamoDB는 완전 관리형 NoSQL 데이터베이스 서비스로, 어떤 규모에서도 일관된 한 자릿수 밀리초 응답 시간을 제공합니다.
핵심 개념
테이블과 항목
DynamoDB의 데이터 구조:
테이블 (Table)
└── 항목 (Item) - RDS의 행(Row)과 유사
└── 속성 (Attribute) - RDS의 열(Column)과 유사중요한 차이점: RDS와 달리 각 항목이 서로 다른 속성을 가질 수 있습니다 (스키마리스).
기본 키 (Primary Key)
DynamoDB는 두 가지 타입의 기본 키를 지원합니다:
1. 파티션 키 (Partition Key)
- 단일 속성으로 구성
- 데이터 분산의 기준
// 파티션 키만 사용하는 테이블 예시
{
"userId": "user123", // 파티션 키
"name": "홍길동",
"email": "hong@example.com"
}2. 복합 키 (Composite Key)
- 파티션 키 + 정렬 키(Sort Key)
- 같은 파티션 키를 가진 항목들을 정렬 키로 정렬
// 복합 키 사용 예시 (주문 테이블)
{
"userId": "user123", // 파티션 키
"orderId": "order-456", // 정렬 키
"amount": 50000,
"status": "completed",
"createdAt": "2024-01-15T10:30:00Z"
}읽기 일관성 모델
최종 일관된 읽기 (Eventually Consistent Read):
- 기본 옵션
- 빠르고 비용 효율적
- 최신 데이터가 아닐 수 있음 (일반적으로 1초 이내 반영)
강력한 일관성 읽기 (Strongly Consistent Read):
- 항상 최신 데이터 보장
- 비용이 2배
- 모든 리전에서 지원되지 않음
// AWS SDK를 사용한 DynamoDB 작업
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, GetCommand, PutCommand, QueryCommand } = require('@aws-sdk/lib-dynamodb');
const client = new DynamoDBClient({ region: 'us-east-1' });
const docClient = DynamoDBDocumentClient.from(client);
// 1. 항목 추가 (PutItem)
async function createUser(userId, userData) {
const command = new PutCommand({
TableName: 'Users',
Item: {
userId,
name: userData.name,
email: userData.email,
createdAt: new Date().toISOString()
}
});
try {
await docClient.send(command);
console.log('사용자 생성 완료:', userId);
} catch (error) {
console.error('사용자 생성 실패:', error);
throw error;
}
}
// 2. 항목 조회 (GetItem)
async function getUser(userId) {
const command = new GetCommand({
TableName: 'Users',
Key: { userId },
ConsistentRead: true // 강력한 일관성 읽기
});
try {
const response = await docClient.send(command);
return response.Item;
} catch (error) {
console.error('사용자 조회 실패:', error);
throw error;
}
}
// 3. 쿼리 (Query) - 파티션 키로 여러 항목 조회
async function getUserOrders(userId) {
const command = new QueryCommand({
TableName: 'Orders',
KeyConditionExpression: 'userId = :userId',
ExpressionAttributeValues: {
':userId': userId
},
ScanIndexForward: false // 정렬 키 기준 내림차순 정렬
});
try {
const response = await docClient.send(command);
return response.Items;
} catch (error) {
console.error('주문 조회 실패:', error);
throw error;
}
}
// 4. 조건부 업데이트
async function updateUserEmail(userId, newEmail, currentEmail) {
const command = new UpdateCommand({
TableName: 'Users',
Key: { userId },
UpdateExpression: 'SET email = :newEmail',
ConditionExpression: 'email = :currentEmail', // 조건: 현재 이메일이 일치할 때만
ExpressionAttributeValues: {
':newEmail': newEmail,
':currentEmail': currentEmail
}
});
try {
await docClient.send(command);
console.log('이메일 업데이트 완료');
} catch (error) {
if (error.name === 'ConditionalCheckFailedException') {
console.error('이메일이 이미 변경되었습니다');
}
throw error;
}
}용량 모드
1. 온디맨드 (On-Demand)
- 언제 사용: 트래픽 예측이 어려운 경우
- 과금 방식: 실제 요청 수에 따라 과금
- 장점: 자동 확장, 관리 불필요
- 단점: 예측 가능한 트래픽에서 비용 증가
// 온디맨드 테이블 생성
const createTableCommand = {
TableName: 'Users',
BillingMode: 'PAY_PER_REQUEST', // 온디맨드 모드
KeySchema: [
{ AttributeName: 'userId', KeyType: 'HASH' }
],
AttributeDefinitions: [
{ AttributeName: 'userId', AttributeType: 'S' }
]
};2. 프로비저닝 (Provisioned)
- 언제 사용: 안정적이고 예측 가능한 트래픽
- 과금 방식: 설정한 읽기/쓰기 용량에 따라 과금
- 장점: 비용 최적화 가능
- 단점: 용량 계획 및 조정 필요
// 프로비저닝 모드 테이블 생성
const createTableCommand = {
TableName: 'Users',
BillingMode: 'PROVISIONED',
ProvisionedThroughput: {
ReadCapacityUnits: 5, // 초당 5회 읽기
WriteCapacityUnits: 5 // 초당 5회 쓰기
},
KeySchema: [
{ AttributeName: 'userId', KeyType: 'HASH' }
],
AttributeDefinitions: [
{ AttributeName: 'userId', AttributeType: 'S' }
]
};글로벌 보조 인덱스 (GSI)
다른 속성으로 쿼리하기 위한 인덱스:
// GSI를 사용한 이메일 검색
const createTableWithGSI = {
TableName: 'Users',
BillingMode: 'PAY_PER_REQUEST',
KeySchema: [
{ AttributeName: 'userId', KeyType: 'HASH' }
],
AttributeDefinitions: [
{ AttributeName: 'userId', AttributeType: 'S' },
{ AttributeName: 'email', AttributeType: 'S' }
],
GlobalSecondaryIndexes: [
{
IndexName: 'EmailIndex',
KeySchema: [
{ AttributeName: 'email', KeyType: 'HASH' }
],
Projection: {
ProjectionType: 'ALL' // 모든 속성 포함
}
}
]
};
// GSI를 사용한 쿼리
async function getUserByEmail(email) {
const command = new QueryCommand({
TableName: 'Users',
IndexName: 'EmailIndex',
KeyConditionExpression: 'email = :email',
ExpressionAttributeValues: {
':email': email
}
});
const response = await docClient.send(command);
return response.Items[0]; // 첫 번째 항목 반환
}DynamoDB의 장점
- ✅ 무제한 확장성: 자동으로 데이터와 트래픽 분산
- ✅ 빠른 응답: 일관된 한 자릿수 밀리초 응답 시간
- ✅ 서버리스: 인프라 관리 불필요
- ✅ 고가용성: 99.99% 가용성 SLA
- ✅ 유연한 스키마: 항목마다 다른 속성 가질 수 있음
DynamoDB의 한계
- ⚠️ 제한된 쿼리: JOIN, 복잡한 집계 불가능
- ⚠️ 학습 곡선: NoSQL 모델링 패턴 학습 필요
- ⚠️ 비용: 소규모에서는 RDS보다 비쌀 수 있음
- ⚠️ 트랜잭션 제약: 제한적인 트랜잭션 지원 (최대 25개 항목)
Part 3: RDS vs DynamoDB 비교
상세 비교표
| 특성 | RDS | DynamoDB |
|---|---|---|
| 데이터 모델 | 관계형 (테이블, 행, 열) | NoSQL (키-값, 문서) |
| 쿼리 언어 | SQL | AWS SDK API |
| 스키마 | 고정 스키마 (사전 정의 필요) | 스키마리스 (유연함) |
| 확장성 | 수직 확장 (인스턴스 크기) | 수평 확장 (자동 파티셔닝) |
| 트랜잭션 | 완전한 ACID | 제한적 (최대 25개 항목) |
| JOIN | ✅ 지원 | ❌ 미지원 |
| 인덱스 | B-Tree, Hash 등 다양 | GSI, LSI |
| 응답 시간 | 수십 밀리초 | 한 자릿수 밀리초 |
| 백업 | 자동 백업, 스냅샷 | 자동 백업, PITR |
| 관리 | 관리형 (일부 설정 필요) | 완전 관리형 |
| 비용 모델 | 인스턴스 시간 + 스토리지 | 요청 수 + 스토리지 |
성능 비교
RDS 성능 특성
// RDS에서 사용자와 주문 조회 (JOIN 사용)
async function getUserWithOrders(userId) {
const query = `
SELECT
u.user_id, u.name, u.email,
o.order_id, o.amount, o.created_at
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id = ?
ORDER BY o.created_at DESC
`;
// 응답 시간: 일반적으로 20-50ms
const [rows] = await connection.execute(query, [userId]);
return rows;
}DynamoDB 성능 특성
// DynamoDB에서 같은 데이터 조회 (2번의 쿼리 필요)
async function getUserWithOrders(userId) {
// 1. 사용자 조회 (1-5ms)
const user = await docClient.send(new GetCommand({
TableName: 'Users',
Key: { userId }
}));
// 2. 주문 조회 (1-5ms)
const orders = await docClient.send(new QueryCommand({
TableName: 'Orders',
KeyConditionExpression: 'userId = :userId',
ExpressionAttributeValues: { ':userId': userId },
ScanIndexForward: false
}));
// 총 응답 시간: 일반적으로 2-10ms
return {
...user.Item,
orders: orders.Items
};
}Part 4: 선택 가이드
RDS를 선택해야 하는 경우
✅ 이런 경우에 RDS 사용
1. 복잡한 관계형 데이터
// 예: 전자상거래 시스템
// 사용자 ↔ 주문 ↔ 상품 ↔ 카테고리 등 복잡한 관계- 다중 테이블 JOIN이 빈번한 경우
- 데이터 간 참조 무결성이 중요한 경우
2. 기존 SQL 애플리케이션 마이그레이션
- 기존 MySQL, PostgreSQL 애플리케이션을 클라우드로 이전
- 최소한의 코드 변경으로 마이그레이션
3. 복잡한 쿼리 및 분석
-- 예: 월별 매출 분석
SELECT
DATE_FORMAT(created_at, '%Y-%m') as month,
category,
COUNT(*) as order_count,
SUM(amount) as total_amount
FROM orders
JOIN products ON orders.product_id = products.id
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 6 MONTH)
GROUP BY month, category
ORDER BY month DESC, total_amount DESC;4. ACID 트랜잭션이 필수적인 경우
// 예: 금융 거래
async function transferMoney(fromAccount, toAccount, amount) {
const connection = await getConnection();
try {
await connection.beginTransaction();
// 1. 출금
await connection.execute(
'UPDATE accounts SET balance = balance - ? WHERE id = ?',
[amount, fromAccount]
);
// 2. 입금
await connection.execute(
'UPDATE accounts SET balance = balance + ? WHERE id = ?',
[amount, toAccount]
);
await connection.commit();
} catch (error) {
await connection.rollback();
throw error;
}
}적합한 사용 사례:
- 전자상거래 플랫폼
- ERP 시스템
- CRM 시스템
- 금융 애플리케이션
- 콘텐츠 관리 시스템 (CMS)
DynamoDB를 선택해야 하는 경우
✅ 이런 경우에 DynamoDB 사용
1. 대규모 트래픽과 데이터
// 예: IoT 데이터 수집
// 초당 수만~수백만 건의 쓰기가 필요한 경우
async function logSensorData(deviceId, data) {
await docClient.send(new PutCommand({
TableName: 'SensorLogs',
Item: {
deviceId,
timestamp: Date.now(),
...data
}
}));
}2. 키-값 조회가 주된 액세스 패턴
// 예: 사용자 세션 관리
async function getSession(sessionId) {
// 1-5ms의 빠른 응답
const response = await docClient.send(new GetCommand({
TableName: 'Sessions',
Key: { sessionId }
}));
return response.Item;
}3. 서버리스 아키텍처
// Lambda + DynamoDB 조합
export const handler = async (event) => {
const userId = event.requestContext.authorizer.claims.sub;
// 사용자 데이터 조회
const user = await docClient.send(new GetCommand({
TableName: 'Users',
Key: { userId }
}));
return {
statusCode: 200,
body: JSON.stringify(user.Item)
};
};4. 글로벌 확장
- DynamoDB 글로벌 테이블로 다중 리전 복제
- 낮은 지연 시간의 글로벌 애플리케이션
적합한 사용 사례:
- 모바일 앱 백엔드
- 게임 리더보드
- IoT 데이터 저장
- 실시간 채팅 애플리케이션
- 사용자 세션 저장
- 쇼핑 카트
하이브리드 접근 방식
많은 실제 애플리케이션은 두 서비스를 함께 사용합니다:
// 예: 전자상거래 플랫폼
//
// RDS 사용:
// - 제품 카탈로그 (복잡한 관계, JOIN 필요)
// - 주문 관리 (트랜잭션 필수)
// - 재고 관리 (정확성 중요)
//
// DynamoDB 사용:
// - 사용자 세션
// - 쇼핑 카트
// - 제품 조회 캐시 (빠른 응답)
// - 사용자 활동 로그
// RDS: 주문 생성 (트랜잭션)
async function createOrder(orderData) {
const connection = await getRDSConnection();
try {
await connection.beginTransaction();
// 주문 생성
const [result] = await connection.execute(
'INSERT INTO orders (user_id, total_amount) VALUES (?, ?)',
[orderData.userId, orderData.totalAmount]
);
// 재고 차감
for (const item of orderData.items) {
await connection.execute(
'UPDATE inventory SET quantity = quantity - ? WHERE product_id = ?',
[item.quantity, item.productId]
);
}
await connection.commit();
return result.insertId;
} catch (error) {
await connection.rollback();
throw error;
}
}
// DynamoDB: 쇼핑 카트 관리 (빠른 읽기/쓰기)
async function updateCart(userId, cartItems) {
await docClient.send(new PutCommand({
TableName: 'ShoppingCarts',
Item: {
userId,
items: cartItems,
updatedAt: new Date().toISOString()
}
}));
}
// DynamoDB: 제품 뷰 카운트 (높은 쓰기 처리량)
async function incrementProductView(productId) {
await docClient.send(new UpdateCommand({
TableName: 'ProductStats',
Key: { productId },
UpdateExpression: 'ADD viewCount :inc',
ExpressionAttributeValues: {
':inc': 1
}
}));
}Part 5: 마이그레이션 고려사항
RDS에서 DynamoDB로 마이그레이션
데이터 모델링 재설계
RDS 스키마:
-- 정규화된 관계형 스키마
CREATE TABLE users (
user_id INT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
amount DECIMAL(10,2),
created_at TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);DynamoDB 모델 (비정규화):
// 사용자 항목
{
"PK": "USER#123",
"SK": "PROFILE",
"userId": 123,
"name": "홍길동",
"email": "hong@example.com"
}
// 주문 항목 (같은 테이블에 저장)
{
"PK": "USER#123",
"SK": "ORDER#456",
"orderId": 456,
"amount": 50000,
"createdAt": "2024-01-15T10:30:00Z"
}
// 단일 쿼리로 사용자와 모든 주문 조회 가능
const result = await docClient.send(new QueryCommand({
TableName: 'AppData',
KeyConditionExpression: 'PK = :pk',
ExpressionAttributeValues: {
':pk': 'USER#123'
}
}));DynamoDB에서 RDS로 마이그레이션
스키마 정규화
// DynamoDB의 비정규화된 데이터
{
"orderId": "order-123",
"userId": "user-456",
"userName": "홍길동", // 중복 데이터
"userEmail": "hong@example.com", // 중복 데이터
"items": [
{
"productId": "prod-789",
"productName": "노트북", // 중복 데이터
"quantity": 1
}
]
}
// RDS로 정규화
-- users 테이블
INSERT INTO users (user_id, name, email) VALUES ('user-456', '홍길동', 'hong@example.com');
-- products 테이블
INSERT INTO products (product_id, name) VALUES ('prod-789', '노트북');
-- orders 테이블
INSERT INTO orders (order_id, user_id) VALUES ('order-123', 'user-456');
-- order_items 테이블
INSERT INTO order_items (order_id, product_id, quantity)
VALUES ('order-123', 'prod-789', 1);Part 6: 비용 최적화
RDS 비용 최적화
// 1. 예약 인스턴스 사용 (최대 75% 절감)
// AWS Console에서 1년 또는 3년 약정
// 2. 적절한 인스턴스 크기 선택
// CloudWatch 메트릭으로 사용률 모니터링
// 3. 스토리지 최적화
// - GP3로 전환 (GP2 대비 20% 저렴)
// - 불필요한 스냅샷 삭제
// 4. 읽기 복제본 활용
// 읽기 전용 쿼리를 복제본으로 분산
const readConnection = await mysql.createConnection({
host: 'mydb-replica.abc123.us-east-1.rds.amazonaws.com', // 읽기 복제본
// ...
});
// 5. Aurora Serverless v2 고려
// 사용량에 따라 자동 확장DynamoDB 비용 최적화
// 1. 온디맨드 vs 프로비저닝 선택
// 예측 가능한 트래픽: 프로비저닝 모드 + 오토스케일링
// 2. TTL(Time To Live) 활용
async function createSession(sessionId, data) {
await docClient.send(new PutCommand({
TableName: 'Sessions',
Item: {
sessionId,
...data,
ttl: Math.floor(Date.now() / 1000) + (24 * 60 * 60) // 24시간 후 자동 삭제
}
}));
}
// 3. 프로젝션 최소화
// 필요한 속성만 조회
const result = await docClient.send(new QueryCommand({
TableName: 'Users',
ProjectionExpression: 'userId, #n, email', // 필요한 필드만
ExpressionAttributeNames: {
'#n': 'name'
},
// ...
}));
// 4. 배치 작업 활용
// 단일 요청 대신 배치로 처리 (비용 절감)
const batchWrite = new BatchWriteCommand({
RequestItems: {
'Users': items.map(item => ({
PutRequest: { Item: item }
}))
}
});
// 5. DAX(DynamoDB Accelerator) 고려
// 빈번한 읽기 작업에 캐싱 레이어 추가Part 7: 모니터링 및 문제 해결
RDS 모니터링
// CloudWatch 메트릭 주요 지표
//
// - CPUUtilization: CPU 사용률 (>80% 지속 시 확장 고려)
// - DatabaseConnections: 연결 수
// - FreeableMemory: 사용 가능한 메모리
// - ReadLatency/WriteLatency: 읽기/쓰기 지연 시간
// - ReadIOPS/WriteIOPS: 초당 I/O 작업
// 슬로우 쿼리 로그 활성화
const enableSlowQueryLog = {
DBInstanceIdentifier: 'mydb',
CloudwatchLogsExportConfiguration: {
EnableLogTypes: ['slowquery']
}
};
// 일반적인 성능 문제 해결
async function optimizeQuery() {
// ❌ 인덱스 없이 전체 테이블 스캔
const slowQuery = `
SELECT * FROM orders
WHERE created_at > '2024-01-01'
`;
// ✅ 인덱스 추가
await connection.execute(`
CREATE INDEX idx_orders_created_at
ON orders(created_at)
`);
// ✅ 필요한 컬럼만 선택
const fastQuery = `
SELECT order_id, amount, created_at
FROM orders
WHERE created_at > '2024-01-01'
`;
}DynamoDB 모니터링
// CloudWatch 메트릭 주요 지표
//
// - ConsumedReadCapacityUnits/ConsumedWriteCapacityUnits
// - ThrottledRequests: 제한된 요청 수 (0이어야 함)
// - SystemErrors: 시스템 오류
// - UserErrors: 사용자 오류 (잘못된 요청)
// 일반적인 문제: 핫 파티션
// ❌ 나쁜 설계: 날짜를 파티션 키로 사용
{
"date": "2024-01-15", // 모든 오늘 데이터가 같은 파티션에
"eventId": "evt-123",
// ...
}
// ✅ 좋은 설계: 높은 카디널리티의 파티션 키
{
"deviceId": "device-abc-123", // 디바이스별로 분산
"timestamp": 1705315200,
// ...
}
// 재시도 로직 구현
async function writeWithRetry(item, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
await docClient.send(new PutCommand({
TableName: 'MyTable',
Item: item
}));
return;
} catch (error) {
if (error.name === 'ProvisionedThroughputExceededException') {
// 지수 백오프로 재시도
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, i) * 100)
);
} else {
throw error;
}
}
}
throw new Error('Max retries exceeded');
}의사결정 플로우차트
시작
│
├─ 복잡한 JOIN이 필요한가?
│ ├─ Yes → RDS
│ └─ No → 다음
│
├─ ACID 트랜잭션이 필수인가?
│ ├─ Yes → RDS
│ └─ No → 다음
│
├─ 초당 수천~수만 건의 요청?
│ ├─ Yes → DynamoDB
│ └─ No → 다음
│
├─ 키-값 조회가 주된 패턴?
│ ├─ Yes → DynamoDB
│ └─ No → 다음
│
├─ 복잡한 분석 쿼리 필요?
│ ├─ Yes → RDS
│ └─ No → 다음
│
├─ 완전 서버리스 아키텍처?
│ ├─ Yes → DynamoDB
│ └─ No → RDS 또는 DynamoDB추가 학습 자료
공식 문서
-
RDS:
-
DynamoDB:
권장 학습 경로
- 기초: AWS 공식 문서 시작하기
- 실습: AWS Free Tier로 직접 테스트
- 심화: Re:Invent 세션 및 워크샵
- 실전: 작은 프로젝트로 실제 적용
관련 AWS 서비스
- Amazon Aurora Serverless: RDS의 서버리스 옵션
- DynamoDB Streams: 변경 데이터 캡처
- AWS Database Migration Service: 데이터베이스 마이그레이션
- Amazon ElastiCache: 인메모리 캐싱 (RDS 성능 향상)
- DAX: DynamoDB 전용 캐싱
결론
RDS와 DynamoDB는 각각 고유한 장점을 가진 훌률한 데이터베이스 서비스입니다:
RDS를 선택하세요:
- 복잡한 관계형 데이터와 쿼리
- ACID 트랜잭션이 필수
- 기존 SQL 애플리케이션 마이그레이션
DynamoDB를 선택하세요:
- 대규모 확장성과 빠른 응답 시간
- 키-값 기반 간단한 액세스 패턴
- 서버리스 아키텍처
많은 경우, 두 서비스를 함께 사용하는 하이브리드 접근 방식이 최선의 선택이 될 수 있습니다. 각 데이터의 특성과 액세스 패턴을 분석하여 적절한 데이터베이스를 선택하세요.
버전 정보:
- 작성일: 2026-01-27
- AWS SDK 버전: v3 (JavaScript)
- 대상 독자: 초급~중급 개발자