분산 시스템 기초 (CAP & Consistency)
분류: Layer 9 - 아키텍처 & 설계 패턴
분산 시스템 기초 (CAP & Consistency)
섹션 제목: “분산 시스템 기초 (CAP & Consistency)”1. 한 줄 정의
섹션 제목: “1. 한 줄 정의”분산 시스템은 네트워크로 연결된 여러 노드가 협력하여 하나의 서비스처럼 동작하는 시스템이며, CAP 정리는 네트워크 장애 시 일관성(Consistency)과 가용성(Availability) 중 하나를 포기해야 한다는 근본적 제약이다.
2. 왜 중요한가
섹션 제목: “2. 왜 중요한가”프론트엔드 브릿지: 브라우저 캐시에서 stale 데이터를 보여주는 경험을 한 적 있을 것이다. Cache-Control: max-age=3600으로 캐싱된 HTML이 서버에서 이미 갱신되었는데도 1시간 동안 구버전을 보여주는 현상이다. 이것이 분산 시스템에서 말하는 Eventually Consistent 데이터와 동일한 원리다. 클라이언트(브라우저)와 서버 간 데이터 일관성 문제가 서버 노드들 사이에서도 그대로 발생한다.
BackOps 엔지니어로 성장하면서 이 지식이 필요한 이유:
- AWS 서비스 선택 근거 설명: “왜 이 데이터는 DynamoDB에, 저 데이터는 Aurora에 넣었나?”를 CAP/PACELC로 설명할 수 있어야 한다.
- 장애 원인 분석: Write 후 Read 시 데이터가 없는 현상이 버그인지, Eventual Consistency 특성인지 구별해야 한다.
- 시니어 대화 가능: 시스템 설계 리뷰에서 “이 서비스는 AP 시스템인가 CP 시스템인가”라는 질문에 답해야 한다.
3. 핵심 개념
섹션 제목: “3. 핵심 개념”3.1 CAP 정리
섹션 제목: “3.1 CAP 정리”2000년 Eric Brewer가 제안하고 2002년 Gilbert & Lynch가 수학적으로 증명한 정리. 분산 시스템은 아래 세 속성 중 최대 두 가지만 동시에 보장할 수 있다.
| 속성 | 약어 | 의미 |
|---|---|---|
| Consistency | C | 모든 노드가 동시에 동일한 데이터를 본다. 읽기는 항상 최신 쓰기 결과를 반환한다. |
| Availability | A | 모든 요청이 (최신 데이터 보장 없이) 반드시 응답을 받는다. 노드가 살아있으면 응답한다. |
| Partition Tolerance | P | 네트워크 파티션(노드 간 통신 단절)이 발생해도 시스템이 계속 동작한다. |
왜 셋 중 둘만 선택 가능한가:
[노드 A] ─────────────── [노드 B] 쓰기 완료 네트워크 단절 x = 10 x = 5 (구버전)
클라이언트가 노드 B에 읽기 요청: - 응답하면(Availability 보장): 구버전 x=5 반환 → Consistency 위반 - 거부하면(Consistency 보장): 요청 실패 응답 → Availability 위반네트워크 파티션은 분산 시스템에서 회피 불가능하다. 물리 케이블은 끊어지고, AWS AZ 간 통신 지연은 반드시 발생한다. 따라서 실질적 선택은 P가 발생했을 때 C와 A 중 무엇을 포기하느냐다.
비유:
- CP 선택 = 은행 ATM. 네트워크 단절 시 “현재 서비스 불가” 안내를 하지만 잔액 불일치는 절대 허용하지 않는다.
- AP 선택 = 소셜미디어 좋아요 수. 일시적으로 노드마다 다른 숫자를 보여줘도 괜찮다. 결국엔 맞춰진다.
3.2 실제 시스템 분류
섹션 제목: “3.2 실제 시스템 분류”CP 시스템 (Consistency + Partition Tolerance)
섹션 제목: “CP 시스템 (Consistency + Partition Tolerance)”파티션 발생 시 일관성을 위해 가용성을 포기한다. 일부 요청은 오류를 반환하지만 반환하는 데이터는 항상 최신이다.
CP 시스템 동작: 네트워크 파티션 발생 │ ▼ 일관성 확인 불가 → 요청 거부 (Error 반환) ✓ 데이터 정확성 보장 ✗ 일시적 서비스 중단| 시스템 | 특징 |
|---|---|
| ZooKeeper | 분산 조율 서비스. Leader 선출 중 요청 거부. 과반수 노드 동의 후에만 쓰기 허용 |
| HBase | HDFS 위의 CP 스토어. 리전 서버 장애 시 해당 리전 읽기/쓰기 중단 |
| etcd | Kubernetes 클러스터 상태 저장소. Raft 합의 알고리즘으로 강한 일관성 |
| Aurora (MySQL/PostgreSQL) | 6개 복제본 중 4개 이상 확인 후 쓰기 커밋. Read Replica 지연 있음 |
| 티켓팅 시스템 | 같은 좌석을 두 사람이 예매하는 오버부킹을 절대 허용 불가 |
AP 시스템 (Availability + Partition Tolerance)
섹션 제목: “AP 시스템 (Availability + Partition Tolerance)”파티션 발생 시 가용성을 위해 일관성을 포기한다. 구버전 데이터를 반환할 수 있지만 요청은 항상 처리한다.
AP 시스템 동작: 네트워크 파티션 발생 │ ▼ 가능한 노드로 요청 처리 → 응답 반환 (구버전일 수 있음) ✓ 서비스 지속성 보장 ✗ 데이터 일시적 불일치 허용| 시스템 | 특징 |
|---|---|
| Cassandra | Peer-to-peer 구조. 어느 노드에나 쓰기 가능. Netflix 추천 엔진에 사용 |
| DynamoDB | 기본적으로 AP. Eventually Consistent Read가 기본값 |
| CouchDB | MVCC 기반. 충돌은 애플리케이션이 해결 |
| DNS | 레코드 갱신 후 전파 지연(TTL). 전 세계 가용성 우선 |
| 쇼핑몰 장바구니 | 네트워크 장애 중에도 장바구니 담기 가능. 나중에 동기화 |
CA 시스템 (Consistency + Availability)
섹션 제목: “CA 시스템 (Consistency + Availability)”파티션을 허용하지 않는다는 뜻으로, 실제로는 단일 노드 또는 단일 데이터센터 시스템이다. 네트워크 파티션이 발생하면 시스템 자체가 동작 불가다.
| 시스템 | 특징 |
|---|---|
| 단일 노드 RDB (MySQL, PostgreSQL) | 파티션 자체가 없어서 CA 가능 |
| 단일 AZ 배포 | AZ 장애 = 전체 장애 감수 |
실무에서 “CA 시스템을 쓰겠다”는 말은 “분산 배포를 포기하겠다”는 뜻이다. 고가용성이 필요하면 CA는 선택지가 아니다.
3.3 PACELC 정리
섹션 제목: “3.3 PACELC 정리”CAP 정리는 네트워크 파티션이 발생했을 때만 다룬다. 하지만 실제 시스템은 파티션이 없는 정상 상태에서도 성능(지연)과 일관성 간의 트레이드오프가 존재한다. 2012년 Daniel Abadi가 PACELC로 이를 보완했다.
PACELC 공식: if Partition: → Availability vs Consistency 선택 Else (정상 동작): → Latency vs Consistency 선택PACELC 분류표
섹션 제목: “PACELC 분류표”| 분류 | 의미 | 대표 시스템 | 선택 이유 |
|---|---|---|---|
| PA/EL | 파티션 시 가용성, 평상시 낮은 지연 우선 | DynamoDB(기본), Cassandra, Riak | 글로벌 서비스, 높은 처리량 필요 |
| PA/EC | 파티션 시 가용성, 평상시 일관성 우선 | Cosmos DB (일부 설정) | 가용성은 필수이나 정확성도 중요 |
| PC/EL | 파티션 시 일관성, 평상시 낮은 지연 우선 | Yahoo! PNUTS | 드문 조합 |
| PC/EC | 파티션 시 일관성, 평상시 일관성 우선 | ZooKeeper, etcd, CockroachDB | 금융, 설정 관리 등 정확성 최우선 |
Aurora의 PACELC 위치:
- Partition 시: CP → 일관성 우선
- Else: Multi-AZ 동기 복제로 EC(일관성 우선), 단 쓰기 지연이 약간 있음
- Read Replica 사용 시: EL(낮은 지연)로 설정 가능하나 복제 지연(lag) 허용 필요
DynamoDB (PA/EL): 정상 상태: 클라이언트 → 가장 가까운 노드 → 즉시 응답 (EL: 낮은 지연) 파티션 시: 클라이언트 → 살아있는 노드 → 구버전 응답 (PA: 가용성)
ZooKeeper (PC/EC): 정상 상태: 클라이언트 → Leader → 과반수 동의 → 응답 (EC: 일관성, 지연 있음) 파티션 시: 과반수 달성 불가 → 요청 거부 (PC: 일관성)3.4 Consistency 모델
섹션 제목: “3.4 Consistency 모델”“일관성”이라는 단어는 강도에 따라 여러 모델로 나뉜다. 강할수록 성능 비용이 크다.
강한 일관성 ◄────────────────────────────────► 약한 일관성 │ │Linearizable Sequential Read-Your-Writes Eventual │ │ │ │ 최강 순서 보장 내 쓰기 보장 결국 수렴Linearizable (선형화 가능)
섹션 제목: “Linearizable (선형화 가능)”가장 강한 일관성. 모든 읽기는 가장 최근에 완료된 쓰기 결과를 반환한다. 시스템 전체가 단일 CPU처럼 동작하는 것처럼 보인다.
- 사용 사례: ZooKeeper의 Znode, etcd의 분산 락
- 비용: 모든 노드 동기화 대기 → 높은 지연
Write(x=10) 완료 │ ▼Read(x) → 반드시 10 반환 (어느 노드에서 읽어도)Sequential Consistency (순차 일관성)
섹션 제목: “Sequential Consistency (순차 일관성)”각 프로세스의 연산 순서는 보장하되, 전역 실시간 순서는 보장하지 않는다. 내 관점에서의 순서는 맞지만 다른 노드와 실시간 동기화는 아니다.
- 사용 사례: 멀티코어 CPU 메모리 모델
Read-Your-Writes (자신의 쓰기 읽기 보장)
섹션 제목: “Read-Your-Writes (자신의 쓰기 읽기 보장)”내가 쓴 데이터는 내가 반드시 읽을 수 있다. 다른 클라이언트는 구버전을 볼 수 있어도, 쓴 사람은 자신의 최신 값을 본다.
- 사용 사례: 소셜미디어 프로필 업데이트 (내가 바꾼 프로필을 나는 즉시 볼 수 있어야 함)
- 구현: Session Token을 활용해 동일 레플리카에 라우팅
클라이언트 A: Write(profile="새 이름") → 성공클라이언트 A: Read(profile) → "새 이름" 반환 ✓ (Read-Your-Writes 보장)클라이언트 B: Read(profile) → "구 이름" 반환 가능 (다른 레플리카)Eventual Consistency (최종 일관성)
섹션 제목: “Eventual Consistency (최종 일관성)”충분한 시간이 지나면 모든 노드가 동일한 값으로 수렴한다. 수렴 전까지는 노드마다 다른 값을 반환할 수 있다.
- 사용 사례: DNS 전파, S3 (2020년 이전), Cassandra 기본 설정
- 장점: 높은 가용성, 낮은 지연, 수평 확장 용이
Write(x=10) → 노드 A에 반영 │ ├─ 노드 A: Read → 10 ✓ ├─ 노드 B: Read → 5 (아직 복제 중) ← Eventual Consistency └─ 노드 C: Read → 5 (아직 복제 중)
[복제 완료 후] ├─ 노드 A: Read → 10 ✓ ├─ 노드 B: Read → 10 ✓ └─ 노드 C: Read → 10 ✓3.5 AWS 서비스 분류
섹션 제목: “3.5 AWS 서비스 분류”Aurora (CP, PC/EC)
섹션 제목: “Aurora (CP, PC/EC)”Writer Instance ──── 6개 복제본 (3 AZ × 2) │ └── 쓰기: 4/6 복제본 확인 후 커밋 (강한 일관성) 읽기: Writer에서 직접 → 항상 최신 읽기: Read Replica → 복제 지연 가능 (수 ms~수 초)- CAP: CP — 파티션 시 쓰기 중단 (최소 4/6 복제본 필요)
- Consistency: 기본적으로 Strong (Writer 직접 읽기), Read Replica는 Eventual
- 선택 기준: 트랜잭션 ACID 보장 필요, 복잡한 JOIN, 금융/주문 데이터
DynamoDB (AP, PA/EL)
섹션 제목: “DynamoDB (AP, PA/EL)”DynamoDB Table ├── Eventually Consistent Read (기본) │ → 최근 완료된 쓰기가 반영 안 될 수 있음 │ → 읽기 용량 유닛 1 소비 │ └── Strongly Consistent Read (옵션) → 반드시 최신 값 반환 → 읽기 용량 유닛 2 소비 (2배 비용)- CAP: AP — 파티션 시 가용성 유지, 구버전 데이터 허용
- Consistency: 기본 Eventual, 옵션으로 Strong (2배 비용)
- 선택 기준: 높은 처리량(초당 수만 TPS), 글로벌 테이블, 세션/게임 상태
S3 (Eventual → Strong 전환 완료)
섹션 제목: “S3 (Eventual → Strong 전환 완료)”2020년 12월 이전: Eventual Consistency PUT object → 즉시 GET → 구버전 반환 가능 DELETE object → 즉시 GET → 객체 여전히 반환 가능
2020년 12월 이후: Strong Consistency (추가 비용 없음) PUT/DELETE 이후 GET → 항상 최신 상태 반환- AWS가 내부 아키텍처를 개선하여 성능 저하 없이 강한 일관성 달성
- 단, S3 Replication(Cross-Region)은 여전히 Eventually Consistent
ElastiCache (AP)
섹션 제목: “ElastiCache (AP)”Redis Cluster Mode: Primary ──복제──► Replica │ Primary 장애 시: Replica가 Primary로 승격 승격 전 기간(수 초): 읽기는 Replica에서 구버전 반환- CAP: AP — 레플리카에서 읽기 허용, 구버전 데이터 가능
- Consistency: Eventual (Primary → Replica 비동기 복제)
- 선택 기준: 세션 캐시, 빈번한 읽기 캐싱, 속도 최우선
AWS 서비스 종합 비교
섹션 제목: “AWS 서비스 종합 비교”| 서비스 | CAP | PACELC | 기본 Consistency | 강한 일관성 |
|---|---|---|---|---|
| Aurora (Writer) | CP | PC/EC | Strong | 항상 |
| Aurora (Read Replica) | CP | PA/EL | Eventual | 설정 불가 |
| DynamoDB | AP | PA/EL | Eventual | 옵션 (2배 비용) |
| S3 | AP | PA/EC | Strong (2020~) | 기본 제공 |
| ElastiCache | AP | PA/EL | Eventual | 미지원 |
| RDS Multi-AZ | CP | PC/EC | Strong | 항상 |
4. 실무 의사결정 가이드
섹션 제목: “4. 실무 의사결정 가이드””어떤 데이터베이스를 선택할까?” 체크리스트
섹션 제목: “”어떤 데이터베이스를 선택할까?” 체크리스트”Step 1: 이 데이터에 ACID 트랜잭션이 필요한가? YES → Aurora / RDS (CP 시스템) NO → Step 2로
Step 2: 초당 요청 수가 10,000 TPS 이상인가? YES → DynamoDB (AP, 무제한 수평 확장) NO → Step 3으로
Step 3: 데이터 불일치가 비즈니스에 치명적인가? YES (금융, 재고, 예약) → Aurora CP 선택 NO (소셜, 로그, 캐시) → DynamoDB AP 선택시나리오별 판단
섹션 제목: “시나리오별 판단”| 시나리오 | 선택 | 이유 |
|---|---|---|
| 결제 처리, 잔액 차감 | Aurora (CP) | 이중 차감 절대 불가, ACID 필수 |
| 사용자 세션 관리 | DynamoDB (AP) | 세션 만료 시 재로그인 허용, 높은 TPS |
| 상품 재고 (판매 중) | Aurora (CP) | 오버셀링 방지 |
| 상품 조회 (카탈로그) | DynamoDB (AP) | 읽기 많고 약간의 지연 허용 |
| 알림/이벤트 로그 | DynamoDB (AP) | 순서 느슨, 높은 쓰기 처리량 |
| 글로벌 사용자 프로필 | DynamoDB Global Tables (AP) | 멀티 리전, 낮은 지연 우선 |
| 분산 락 (선착순 이벤트) | Redis (또는 DynamoDB Conditional Write) | 원자적 연산 |
5. 내 업무 연결
섹션 제목: “5. 내 업무 연결”NestJS API + Aurora + DynamoDB를 함께 쓰는 경우의 아키텍처 판단:
데이터 분리 원칙
섹션 제목: “데이터 분리 원칙”[NestJS API] │ ├── 트랜잭션 필요한 데이터 → Aurora (CP) │ ├── 주문 (order) │ ├── 결제 (payment) │ └── 재고 (inventory) │ └── 높은 처리량 / 유연한 스키마 → DynamoDB (AP) ├── 사용자 세션 ├── 이벤트 로그 └── 타임라인 데이터Read Replica 사용 시 주의
섹션 제목: “Read Replica 사용 시 주의”// Aurora Read Replica에서 읽을 때 — 복제 지연 인지 필수// 주문 생성 직후 주문 목록 조회는 Writer에서 해야 함
// 잘못된 패턴:await orderRepository.create(orderData); // Writer에 쓰기return await orderReadRepo.findAll(); // Read Replica에서 조회 → 새 주문 없을 수 있음
// 올바른 패턴:await orderRepository.create(orderData); // Writer에 쓰기return await orderRepository.findAll(); // Writer에서 조회 → 항상 최신DynamoDB Consistent Read 선택
섹션 제목: “DynamoDB Consistent Read 선택”// 기본: Eventually Consistent (빠름, 저렴)const result = await dynamoClient .get({ TableName: "sessions", Key: { userId }, }) .promise();
// 강한 일관성이 필요할 때 (2배 비용)const result = await dynamoClient .get({ TableName: "sessions", Key: { userId }, ConsistentRead: true, // 읽기 용량 2배 소비 }) .promise();
// 판단 기준:// - 로그인 직후 권한 확인 → ConsistentRead: true// - 일반 프로필 조회 → 기본값 (Eventually Consistent)6. 학습 체크리스트
섹션 제목: “6. 학습 체크리스트”- CAP 정리에서 P(Partition Tolerance)가 왜 실질적으로 포기 불가능한지 설명할 수 있다
- ZooKeeper가 CP인 이유와 파티션 발생 시 어떤 동작을 하는지 설명할 수 있다
- DynamoDB의 기본 읽기가 Eventually Consistent인 이유와 Strongly Consistent Read와의 차이를 설명할 수 있다
- PACELC에서 “Else” 부분(파티션 없을 때)의 Latency vs Consistency 트레이드오프를 설명할 수 있다
- Aurora Write 후 Read Replica에서 읽을 때 데이터가 없을 수 있는 이유를 설명할 수 있다
- “이 서비스는 AP인가 CP인가”라는 질문에 선택 근거와 함께 답할 수 있다
- Strong Consistency와 Linearizable의 차이를 설명할 수 있다
6.5 트러블슈팅
섹션 제목: “6.5 트러블슈팅”트러블슈팅 1: Write 후 바로 Read 시 데이터가 없음 (Eventual Consistency 이해 부재)
섹션 제목: “트러블슈팅 1: Write 후 바로 Read 시 데이터가 없음 (Eventual Consistency 이해 부재)”증상:
POST /orders → 201 CreatedGET /orders → 방금 생성한 주문이 목록에 없음원인:
- DynamoDB의 기본 읽기는 Eventually Consistent
- Write는 특정 노드에 완료되었으나 다른 레플리카로 전파 전에 Read가 발생
- 복제 지연은 보통 수십 ms~수 초
해결:
// 방법 1: Strongly Consistent Read 사용const result = await dynamoClient .get({ Key: { orderId }, ConsistentRead: true, // 쓰기 직후 읽기 보장 }) .promise();
// 방법 2: Write 응답값을 그대로 반환 (Read 생략)const created = await orderService.create(dto);return created; // DB 재조회 불필요
// 방법 3: 동일 레플리카로 라우팅 (Read-Your-Writes)// Session 기반 sticky routing 활용예방:
- API 설계 시 Create 후 Redirect to GET 패턴을 쓸 경우 ConsistentRead 필수
- 단순 생성 응답은 DB 재조회 대신 생성된 객체 직접 반환
트러블슈팅 2: DynamoDB Strongly Consistent Read 비용 폭증
섹션 제목: “트러블슈팅 2: DynamoDB Strongly Consistent Read 비용 폭증”증상:
DynamoDB 비용이 예상의 2배 청구CloudWatch: ConsumedReadCapacityUnits 급증원인:
ConsistentRead: true는 읽기 용량 유닛(RCU)을 2배 소비- Eventually Consistent Read: 1 RCU per 4KB
- Strongly Consistent Read: 2 RCU per 4KB (동일 데이터에 2배 비용)
- 전체 API에 ConsistentRead를 일괄 적용한 경우 발생
해결:
// 비용 최적화: 필요한 경우에만 ConsistentRead 사용class OrderRepository { // 주문 생성 직후 확인 - Strong 필요 async findAfterCreate(orderId: string) { return this.get(orderId, { ConsistentRead: true }); }
// 일반 목록 조회 - Eventual 충분 async list(userId: string) { return this.query(userId, { ConsistentRead: false }); // 기본값 }}판단 기준:
- 금융 트랜잭션 직후 확인 → Strong 필수
- 대시보드, 통계, 목록 조회 → Eventual 충분 (비용 절감)
- 2배 비용이 부담되면 Aurora 전환 고려 (쓰기 후 바로 읽기가 잦은 패턴)
트러블슈팅 3: Aurora Read Replica 지연으로 인한 데이터 불일치
섹션 제목: “트러블슈팅 3: Aurora Read Replica 지연으로 인한 데이터 불일치”증상:
사용자가 프로필을 수정하고 새로고침 → 이전 데이터가 보임주문 완료 후 주문 목록 → 새 주문 없음원인:
- Aurora Read Replica는 Writer와 비동기 복제
- 일반적으로 수 ms 지연이지만 부하 시 수 초까지 증가 가능
replica_lag모니터링 항목으로 확인 가능
해결:
// TypeORM 예시: 읽기 전략 선택@Injectable()class OrderService { constructor( @InjectRepository(Order) private repo: Repository<Order>, @InjectDataSource("reader") private readerConn: DataSource, ) {}
// 쓰기 직후 조회 → Writer 사용 async createAndGet(dto: CreateOrderDto) { const order = await this.repo.save(dto); return this.repo.findOne({ where: { id: order.id } }); // Writer }
// 일반 목록 조회 → Reader 사용 가능 async list(userId: string) { return this.readerConn.getRepository(Order).find({ where: { userId } }); // Reader Replica }}모니터링:
-- Aurora Read Replica 지연 확인SHOW SLAVE STATUS\G-- Seconds_Behind_Master 값이 0~1 이면 정상-- 5 이상이면 부하 증가 또는 네트워크 문제
-- CloudWatch 지표-- AuroraReplicaLag (ms)-- AuroraReplicaLagMaximum예방:
- 쓰기 직후 읽기가 필요한 API는 Writer 엔드포인트 고정
- Read Replica는 리포트, 검색, 집계 쿼리에만 사용
AuroraReplicaLagCloudWatch 알람 설정 (임계값: 2000ms)
7. 다음 학습 단계
섹션 제목: “7. 다음 학습 단계”이 문서를 이해했다면 다음 주제로 이어진다:
- 분산 트랜잭션 (Saga 패턴): AP 시스템에서 트랜잭션 보장하는 방법 → MSA Patterns 문서 참고
- Consensus 알고리즘 (Raft, Paxos): CP 시스템이 내부적으로 일관성을 유지하는 방법
- CRDT (Conflict-free Replicated Data Types): Eventual Consistency에서 충돌 자동 해결
- Vector Clock / Versioning: DynamoDB Conditional Write로 충돌 감지
- 분산 락: Redis
SETNX, DynamoDB Conditional Write로 선착순 처리
8. 추천 리소스
섹션 제목: “8. 추천 리소스”- CAP Theorem Explained - AlgoMaster — 도식과 실사례가 풍부한 CAP 입문서
- PACELC Theorem - Wikipedia — 원 제안자 Daniel Abadi의 논리 원문 포함
- Amazon S3 Strong Consistency (AWS 공식) — S3가 2020년 강한 일관성으로 전환한 배경 설명
- Consistency and Partition Tolerance: CAP vs PACELC - ByteByteGo — 시각적 다이어그램으로 비교
- Why Strong Consistency? - Marc Brooker (AWS) — AWS 수석 엔지니어가 쓴 Strong Consistency의 실용적 가치
9. 예상 시나리오 출력
섹션 제목: “9. 예상 시나리오 출력”시나리오 1: DynamoDB Eventual Consistency 재현
섹션 제목: “시나리오 1: DynamoDB Eventual Consistency 재현”// 테스트: Write 직후 Eventual Consistent Readasync function demonstrateEventualConsistency() { const userId = "user-123";
// Write: 세션 생성 await dynamoClient .put({ TableName: "UserSessions", Item: { userId, status: "active", updatedAt: Date.now() }, }) .promise();
// Read (Eventually Consistent - 기본값) const result1 = await dynamoClient .get({ TableName: "UserSessions", Key: { userId }, // ConsistentRead: false (기본값) }) .promise();
// 실제 발생하는 동작: // - 90% 이상의 경우: result1.Item.status === 'active' (운 좋게 최신 노드 할당) // - 드물게: result1.Item === undefined (구버전 노드 할당, 아직 복제 안 됨) // - 복제 지연이 길수록 undefined 발생 빈도 증가
console.log("Eventually Consistent Read:", result1.Item);
// Read (Strongly Consistent) const result2 = await dynamoClient .get({ TableName: "UserSessions", Key: { userId }, ConsistentRead: true, }) .promise();
// 항상 보장되는 동작: // - result2.Item.status === 'active' (최신 쓰기 반영 보장) console.log("Strongly Consistent Read:", result2.Item);}실제 발생하는 동작 설명:
ConsistentRead: false(기본): DynamoDB가 임의의 레플리카에서 읽는다. 쓰기 직후라면 새 값이 아직 전파 안 된 레플리카에서 읽힐 수 있다.ConsistentRead: true: DynamoDB가 쓰기 완료를 확인한 노드들에서 읽는다. 항상 최신 값을 반환하지만 RCU 2배 소비.
시나리오 2: Aurora Write/Read 분리 패턴
섹션 제목: “시나리오 2: Aurora Write/Read 분리 패턴”// Aurora Multi-AZ: Writer는 항상 최신, Reader는 지연 가능@Injectable()class ProductService { // 재고 차감 (CP 필요) → Writer async decrementInventory(productId: string, qty: number) { return this.writerDataSource.transaction(async (em) => { const product = await em.findOne(Product, { where: { id: productId }, lock: { mode: "pessimistic_write" }, // 비관적 락 });
if (product.stock < qty) { throw new BadRequestException("재고 부족"); }
product.stock -= qty; return em.save(product); }); }
// 상품 목록 조회 (AP 허용) → Reader async listProducts(categoryId: string) { // Read Replica 사용 - 수 ms 지연 허용 return this.readerDataSource .getRepository(Product) .find({ where: { categoryId, isActive: true } }); // 실제 발생: 재고가 실시간보다 수 ms 지연될 수 있음 // 허용 가능: 상품 목록은 약간의 stale 데이터 무방 }}실제 발생하는 동작 설명:
writerDataSource: Aurora Writer 엔드포인트. 모든 쓰기와 정확성이 중요한 읽기에 사용. 항상 최신 데이터.readerDataSource: Aurora Reader 엔드포인트(Read Replica). 수 ms~수 초의 복제 지연 존재. 목록 조회, 검색 등 stale 데이터가 허용되는 곳에 사용.
10. 요약
섹션 제목: “10. 요약”| 개념 | 핵심 한 줄 |
|---|---|
| CAP 정리 | 분산 시스템에서 네트워크 장애(P) 시 일관성(C)과 가용성(A) 중 하나를 포기해야 한다 |
| CP 시스템 | 장애 시 응답 거부, 데이터는 항상 정확. ZooKeeper, etcd, Aurora |
| AP 시스템 | 장애 시도 응답, 구버전 데이터 허용. Cassandra, DynamoDB, ElastiCache |
| PACELC | CAP를 확장: 정상 상태에서도 지연(Latency)과 일관성(Consistency) 트레이드오프 존재 |
| Eventual Consistency | 시간이 지나면 수렴 보장, 즉시는 아님. DynamoDB 기본값 |
| Strong Consistency | 쓰기 직후 읽기 항상 최신값. Aurora Writer, DynamoDB ConsistentRead |
| 실무 선택 | ACID+복잡한 쿼리 → Aurora, 높은 TPS+유연한 스키마 → DynamoDB |
| 핵심 함정 | Write 후 바로 Read Replica/DynamoDB 기본 읽기 → Eventual Consistency로 인한 불일치 주의 |