실무로 배우는 백엔드 핵심 인프라: Redis, Kafka, ElastiCache
"서비스가 느려요", "알림이 안 와요", "데이터가 유실됐어요"
이런 문제들을 해결하는 핵심 기술, Redis와 Kafka를 실무 관점에서 정리했습니다.
들어가며: 왜 이 기술들을 알아야 할까?
현대 백엔드 시스템을 설계하다 보면 필연적으로 마주하는 두 가지 문제가 있습니다.
- "어떻게 더 빠르게 응답할까?" → Redis
- "어떻게 안전하게 데이터를 흘려보낼까?" → Kafka
이 글에서는 이 두 기술이 각각 어떤 문제를 해결하는지, 그리고 실무에서 어떻게 함께 사용되는지 알아보겠습니다.
1. Redis: 초고속 인-메모리 데이터 저장소
Redis는 무엇인가?
Redis(Remote Dictionary Server)는 메모리 기반의 Key-Value 저장소입니다.
디스크가 아닌 RAM에 데이터를 저장하기 때문에 sub-millisecond 수준의 빠른 응답속도를 자랑합니다.
핵심 특징
1) 다양한 자료구조 지원
String: 단순 문자열
List: 순서가 있는 데이터
Set: 중복 없는 집합
Hash: 필드-값 쌍
Sorted Set: 점수 기반 정렬2) 영속성(Persistence) 지원
메모리 기반이지만 데이터를 디스크에 저장할 수 있습니다.
| 방식 | 설명 | 사용 사례 |
|---|---|---|
| RDB | 특정 시점의 스냅샷 저장 | 캐시 중심 서비스 |
| AOF | 모든 쓰기 작업을 로그로 기록 | 데이터 정합성이 중요한 경우 |
| RDB + AOF | 두 방식 혼합 | ElastiCache 기본 설정 |
3) Pub/Sub 기능
간단한 메시지 브로커 역할을 수행할 수 있습니다. 하지만 한계가 명확합니다.
Redis Pub/Sub의 구조적 한계:
- ❌ 메시지 저장하지 않음 (구독자가 없으면 소멸)
- ❌ 재처리 불가능
- ❌ Consumer 장애 시 복구 불가
- ⚠️ 순서 보장 약함
따라서 Redis Pub/Sub은:
- ✅ 실시간 알림
- ✅ WebSocket 보조
- ✅ 내부 이벤트 브로드캐스트
정도로만 사용하는 것이 적절합니다.
2. Redis를 "캐시"로 사용할 때의 핵심 전략
실무에서 Redis를 가장 많이 사용하는 방식은 데이터베이스 앞단 캐시입니다.
Cache Aside 패턴 (가장 많이 사용)
Client → API Server
↓
Redis 조회 (Cache Hit?)
↓ (Miss)
DB 조회
↓
Redis에 저장 (TTL 설정)장점:
- 캐시 장애 시에도 서비스 가능
- 필요한 데이터만 캐싱 (Lazy Loading)
예시 코드 (FastAPI):
async def get_user_info(user_id: str):
# 1. Redis 조회
cached = await redis.get(f"user:{user_id}")
if cached:
return json.loads(cached)
# 2. Cache Miss → DB 조회
user = await db.query(User).filter(User.id == user_id).first()
# 3. Redis에 저장 (TTL: 5분)
await redis.setex(
f"user:{user_id}",
300,
json.dumps(user.dict())
)
return user
기타 캐시 전략
| 패턴 | 설명 | 사용 시기 |
|---|---|---|
| Write Through | DB 쓰기 시 Redis도 동시 갱신 | 읽기가 매우 많은 경우 |
| Write Back | Redis에 먼저 쓰고 비동기로 DB 반영 | 쓰기 성능이 중요한 경우 |
TTL & Eviction Policy
Redis는 메모리가 무한하지 않습니다. 메모리 초과 시 처리 정책:
maxmemory-policy 설정:
- volatile-lru: TTL이 있는 키 중 가장 오래된 것 삭제
- allkeys-lru: 모든 키 중 가장 오래된 것 삭제
- volatile-ttl: TTL이 가장 짧은 키 삭제
- noeviction: 삭제하지 않고 에러 반환실무 팁:
- 반드시 TTL 설정 (안 하면 메모리 부족으로 장애)
- 모니터링 필수 (메모리 사용률 80% 넘으면 위험)
3. Redis의 운영 모드: Replication vs Cluster
Replication (복제)
Master (Write)
↓ (복제)
Replica (Read)
Replica (Read)특징:
- Master-Replica 구조
- 읽기 부하 분산
- Auto Failover 지원 (ElastiCache)
적합한 경우:
- 단순 캐시 용도
- 데이터 크기가 크지 않음
- 읽기가 많은 서비스
Cluster (클러스터)
Shard 1: 0-5460
Shard 2: 5461-10922
Shard 3: 10923-16383특징:
- 데이터를 여러 노드에 분산 (Sharding)
- 16,384개 슬롯으로 자동 분배
- 대규모 트래픽 처리
적합한 경우:
- 대규모 키 저장 (수억 개 이상)
- 단일 인스턴스 메모리 한계
- 높은 쓰기 처리량 필요
주의사항:
- 다중 키 연산 제한 (
MGET등) - 운영 복잡도 증가
4. Kafka: 분산 이벤트 스트리밍 플랫폼
Kafka는 무엇인가?
Kafka는 대용량 데이터를 안전하게 흘려보내는 파이프라인입니다.
핵심 개념
Producer → Topic → Consumer
Producer (데이터 생성)
↓
Topic (메시지 저장소)
├─ Partition 0
├─ Partition 1
└─ Partition 2
↓
Consumer (데이터 소비)Consumer Group: Kafka의 핵심 강점
Topic: order-event
├─ Partition 0 → Consumer A (Group: payment)
├─ Partition 1 → Consumer B (Group: payment)
└─ Partition 2 → Consumer C (Group: payment)
같은 메시지를
→ Group: analytics (독립적으로 소비)
→ Group: logging (독립적으로 소비)Consumer Group의 의미:
- 같은 Group ID = 메시지 분산 처리 (Queue)
- 다른 Group ID = 메시지 재소비 가능 (Pub/Sub)
이것이 "Kafka가 Queue이면서 Pub/Sub인 이유"입니다.
Offset 관리
Topic: user-event
Partition 0: [msg0] [msg1] [msg2] [msg3] [msg4]
↑
Consumer Offset: 2- Consumer가 어디까지 읽었는지 저장
- 장애 발생 시 마지막 Offset부터 재시작
- 데이터 유실 방지의 핵심
Delivery Semantics (전달 보장)
| 방식 | 의미 | 트레이드오프 |
|---|---|---|
| At-most-once | 최대 1회 (손실 가능) | 빠름, 중복 없음 |
| At-least-once | 최소 1회 (중복 가능) | 기본 설정, 안전 |
| Exactly-once | 정확히 1회 | 느림, 트랜잭션 필요 |
실무에서는:
- 대부분 At-least-once 사용
- 중복 처리는 Consumer에서 Idempotency로 해결
5. Redis vs Kafka: 언제 무엇을 쓸까?
명확한 차이점
| 특징 | Redis Pub/Sub | Kafka |
|---|---|---|
| 메시지 보관 | ❌ 즉시 소멸 | ✅ 설정된 기간 보관 |
| 재처리 | ❌ 불가능 | ✅ Offset으로 재처리 |
| 장애 복구 | ❌ 메시지 유실 | ✅ 자동 복구 |
| 순서 보장 | ⚠️ 약함 | ✅ Partition 단위 보장 |
| 처리량 | 낮음 | 매우 높음 |
| 응답 속도 | 매우 빠름 | 빠름 |
선택 기준
Redis를 선택해야 할 때:
- 캐싱 (가장 중요)
- 실시간 상태 관리 (세션, 카운터)
- 간단한 실시간 알림
- Sub-millisecond 응답 필요
Kafka를 선택해야 할 때:
- 이벤트 로그 저장
- 마이크로서비스 간 통신
- 데이터 파이프라인
- 재처리 가능성 필요
- 대용량 스트림 처리
핵심 인사이트:
Redis는 "지금 바로" 필요한 속도를 위한 것이고,
Kafka는 "나중에라도" 안전하게 처리하기 위한 것입니다.
6. ElastiCache: AWS의 관리형 캐싱 서비스
Redis vs Memcached
ElastiCache는 Redis와 Memcached 두 엔진을 지원합니다.
| 비교 항목 | Redis | Memcached |
|---|---|---|
| 자료구조 | 다양 (List, Set, Hash 등) | 단순 String만 |
| Persistence | ✅ 지원 (RDB, AOF) | ❌ 미지원 |
| Replication | ✅ Master-Replica | ❌ 미지원 |
| 고가용성 | ✅ Failover | ❌ 없음 |
| 멀티쓰레딩 | ❌ 싱글쓰레드 | ✅ 멀티코어 활용 |
결론: 거의 대부분 Redis 선택
Memcached를 선택할 유일한 경우:
- 정말 단순한 캐싱만 필요
- 멀티코어 극한 활용이 필수
ElastiCache 실무 운영 팁
필수 설정
✅ Multi-AZ 활성화
✅ Auto Failover 활성화
✅ 백업 활성화 (중요 데이터인 경우)
⚠️ Redis Cluster 모드 신중하게 선택주의사항
- TTL 없는 Key 폭증
# ❌ 위험 await redis.set(f"temp:{uuid}", data) # TTL 없음!
✅ 안전
await redis.setex(f"temp:{uuid}", 3600, data) # 1시간 후 자동 삭제
2. **메모리 모니터링**
- CloudWatch 메트릭: `DatabaseMemoryUsagePercentage`
- 80% 넘으면 알림 설정 필수
3. **비용 최적화**
- Reserved Instance (1년/3년 약정 시 최대 55% 할인)
- 적절한 인스턴스 타입 선택
### ElastiCache vs 직접 운영
| 항목 | ElastiCache | Self-hosted |
|------|-------------|-------------|
| **운영 난이도** | 쉬움 | 어려움 |
| **비용** | 상대적 고가 | 저렴 |
| **커스터마이징** | 제한적 | 자유로움 |
| **Failover** | 자동 | 직접 구현 |
**초기 스타트업/프로젝트 → ElastiCache 추천**
---
## 7. 실무 아키텍처: Redis + Kafka의 시너지
### 패턴 1: 이벤트 기반 캐시 갱신
[User Action]
↓
[API Service]
↓
┌──────────────┐
│ Kafka │ ← 이벤트 영구 보존
│ Topic: event │
└──────────────┘
↓
[Consumer Service]
↓
┌──────────────┐
│ Redis │ ← 최신 상태 캐시
└──────────────┘
↓
[DB Sync]
**시나리오: 실시간 순위 시스템 (Leaderboard)**
```python
# Producer: 점수 이벤트 발생
await kafka_producer.send('score-event', {
'user_id': 'user123',
'score': 1500,
'timestamp': datetime.now()
})
# Consumer: Kafka에서 읽어 Redis 업데이트
async def update_leaderboard():
async for msg in kafka_consumer:
score_event = json.loads(msg.value)
await redis.zadd(
'leaderboard',
{score_event['user_id']: score_event['score']}
)
# API: Redis에서 순위 조회 (초고속)
async def get_top_10():
return await redis.zrevrange('leaderboard', 0, 9, withscores=True)장점:
- API는 Redis만 보면 됨 (DB 부하 제로)
- Kafka에 이벤트 로그 영구 보존
- 장애 시 Kafka에서 재구성 가능
패턴 2: AI 파이프라인 분리
[User Upload]
↓
[API Service]
↓
Kafka Topic: ai-task
↓
[AI Worker]
- OCR
- LLM
- Analysis
↓
Redis: 결과 캐싱
↓
[DB 저장]특징:
- AI 처리가 느려도 API는 즉시 응답
- Worker 증설로 처리량 확장
- 결과는 Redis에 캐싱되어 빠른 재조회
패턴 3: 알림 시스템
[게시글 작성]
↓
Kafka: notification-event
↓
[Notification Service]
├─ Redis: 읽지 않은 알림 카운트
├─ DB: 알림 로그
└─ Push: 실시간 푸시코드 예시:
# 이벤트 발생
await kafka_producer.send('notification-event', {
'type': 'new_comment',
'user_id': 'user456',
'message': '새 댓글이 달렸습니다.'
})
# Consumer: 알림 처리
async def process_notification():
async for msg in kafka_consumer:
noti = json.loads(msg.value)
# 1. Redis: 읽지 않은 알림 카운트 증가
await redis.incr(f"unread:{noti['user_id']}")
# 2. DB: 알림 저장
await db.execute(
"INSERT INTO notifications ...",
noti
)
# 3. Push: 실시간 전송
await push_service.send(noti['user_id'], noti['message'])
8. 실전 선택 가이드
단계별 도입 로드맵
Phase 1: MVP 단계
✅ Redis (캐싱, 세션)
❌ Kafka (오버엔지니어링)이유:
- 초기에는 복잡한 이벤트 처리보다 빠른 응답속도가 중요
- Kafka 운영 부담이 큼
Phase 2: 기능 확장
✅ Redis (기존 + 실시간 데이터)
✅ Kafka (알림, 로그 수집)도입 시점:
- 마이크로서비스로 분리 시작
- 이벤트 로그 필요
- 비동기 작업 증가
Phase 3: 스케일링
✅ Redis Cluster
✅ Kafka Consumer 분리
✅ Analytics 파이프라인기술 선택 Decision Tree
데이터 저장이 필요한가?
└─ YES → 읽기 속도가 중요한가?
└─ YES → Redis
└─ NO → DB
└─ NO → 이벤트 전달이 필요한가?
└─ YES → 재처리가 필요한가?
└─ YES → Kafka
└─ NO → Redis Pub/Sub9. 면접에서 자주 나오는 질문
Q1: Redis가 싱글쓰레드인데 어떻게 빠른가요?
답변:
Redis는 싱글쓰레드이지만:
1. 메모리 연산 (디스크 I/O 없음)
2. I/O Multiplexing (epoll/kqueue)
3. Lock-free 자료구조
Context Switching 비용 없이 순차 처리하므로
오히려 더 빠름. 멀티쓰레드는 Memcached의 강점.Q2: Cache Stampede는 어떻게 해결하나요?
답변:
캐시 만료 시 동시 다발적 DB 요청 방지:
1. Lock 방식
- 첫 요청만 DB 조회
- 나머지는 대기
2. Early Expiration
- 실제 TTL보다 일찍 갱신
3. Probabilistic Early Expiration
- 확률적으로 조기 갱신Q3: Kafka vs RabbitMQ 차이는?
답변:
Kafka: 로그 기반, 높은 처리량, 메시지 보관
RabbitMQ: 메모리 기반, 낮은 지연, ACK 중심
Kafka → 데이터 파이프라인, 로그 수집
RabbitMQ → 작업 큐, 즉각 처리마치며: 아키텍처는 트레이드오프다
이 글에서 다룬 기술들은 은탄환(Silver Bullet)이 아닙니다.
- Redis는 빠르지만 메모리 제약이 있습니다
- Kafka는 안전하지만 운영 복잡도가 높습니다
- ElastiCache는 편하지만 비용이 더 듭니다
중요한 것은 "내 서비스에 정말 필요한가?"를 끊임없이 질문하는 것입니다.
"Redis와 Kafka는 경쟁 관계가 아니라,
속도와 안정성을 분리하는 역할 분담 관계다."
현대 아키텍처의 핵심은 어느 하나가 최고라기보다,
Redis를 통한 빠른 데이터 접근과
Kafka를 통한 안정적인 데이터 흐름을
적재적소에 배치하는 설계 능력입니다.
참고 자료
📌 다음 글 예고
- Redis 고급 활용: Lua Script, Pipeline, Transaction
- Kafka Streams를 활용한 실시간 데이터 처리
- ElastiCache 모니터링 & 트러블슈팅