Cloud & IT Cert Prep · 강의

세션 스토리지 및 순위표 패턴

ElastiCache를 사용해 애플리케이션 서버에서 HTTP 세션 상태를 분리하고, Redis 정렬 집합으로 실시간 순위표를 구현합니다.

레슨 4/413개 단계

세션 스토리지 및 순위표 패턴은(는) CoddyKit의 무료 Cloud & IT Cert Prep 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Cloud & IT Cert Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Cloud & IT Cert Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

서버 측 세션의 문제

기존 웹 애플리케이션은 서버 메모리에 세션 데이터를 저장합니다. 단일 서버에서는 문제가 없지만 수평 확장하면 문제가 발생합니다. 사용자의 다음 요청이 다른 EC2 인스턴스로 라우팅되면 해당 인스턴스에는 사용자의 세션 정보가 없으므로 사용자는 로그아웃됩니다. 고정 세션(로드 밸런서의 세션 친화성)은 이 문제를 일부 해결하지만 부하 분산의 효과를 떨어뜨립니다. 확장 가능한 해결책은 모든 인스턴스에서 접근할 수 있는 공유 저지연 저장소로 세션 상태를 옮기는 것이며, ElastiCache Redis가 바로 이러한 기능을 제공합니다.

세션 저장소로 사용하는 Redis

세션을 Redis에 저장하면 모든 애플리케이션 서버에서 1밀리초 미만의 세션 읽기, 자동 세션 만료를 위한 기본 TTL, 경쟁 조건을 방지하는 원자적 세션 업데이트, 키를 삭제하여 세션을 즉시 무효화하는 기능을 사용할 수 있습니다. 애플리케이션은 쿠키에 세션 ID를 저장하고, 각 요청에서 Redis의 세션 ID를 조회하여 세션 데이터를 가져옵니다. 모든 애플리케이션 서버가 동일한 Redis를 공유하므로 어떤 서버든 모든 사용자의 요청을 처리할 수 있습니다.

# Session storage with Redis (Python Flask example)
import redis, json, uuid
from datetime import timedelta

redis_client = redis.Redis(host='prod-redis-primary', port=6379)
SESSION_TTL = int(timedelta(hours=8).total_seconds())

def create_session(user_id):
    session_id = str(uuid.uuid4())
    session_data = {'user_id': user_id, 'logged_in': True}
    redis_client.setex(f'session:{session_id}', SESSION_TTL, json.dumps(session_data))
    return session_id

def get_session(session_id):
    data = redis_client.get(f'session:{session_id}')
    return json.loads(data) if data else None

세션 TTL과 슬라이딩 만료

고정 TTL은 활동 여부와 관계없이 세션이 생성된 후 N초가 지나면 세션을 만료시킵니다. 슬라이딩 TTL(접근할 때마다 만료 시간 연장)은 세션이 마지막 접근 후 N초가 지나면 만료되므로 사용자에게 더 편리합니다. Redis에서는 성공적으로 세션을 읽을 때마다 세션 키에 EXPIRE(또는 EXPIREAT)를 호출하여 만료 카운트다운을 재설정하는 방식으로 슬라이딩 TTL을 구현합니다. 이렇게 하면 활동 중인 사용자가 예기치 않게 로그아웃되지 않고, 비활성 세션은 자동으로 만료되어 메모리를 확보합니다.

# Sliding TTL session implementation
def get_session_with_sliding_ttl(session_id, redis_client, ttl_seconds=1800):
    session_key = f'session:{session_id}'

    # Pipeline: GET + EXPIRE in one round trip
    pipe = redis_client.pipeline()
    pipe.get(session_key)
    pipe.expire(session_key, ttl_seconds)  # Reset TTL on access
    results = pipe.execute()

    data = results[0]
    if data:
        return json.loads(data)
    return None   # Session expired or not found

Redis의 쇼핑 카트

전자상거래 쇼핑 카트는 Redis에 적합한 사용 사례입니다. 각 카트는 Redis Hash로 저장하며, 필드는 제품 SKU이고 값은 수량입니다. HINCRBY 및 HDEL과 같은 Hash 작업을 사용하면 카트 전체를 가져와 다시 기록하지 않고도 원자적으로 업데이트할 수 있습니다. 24시간 동안 방치된 카트를 만료시키도록 TTL을 설정하면 Redis는 모든 카트 추가 이벤트마다 관계형 데이터베이스를 사용하는 오버헤드 없이 빠르고 지속적인 카트 저장소를 제공합니다.

# Shopping cart operations using Redis Hash
cart_key = f'cart:{user_id}'

# Add item (or increase quantity)
# HINCRBY cart:user42 SKU-001 2
redis_client.hincrby(cart_key, 'SKU-001', 2)

# Remove item
# HDEL cart:user42 SKU-001
redis_client.hdel(cart_key, 'SKU-001')

# Get all items in cart
# HGETALL cart:user42
cart = redis_client.hgetall(cart_key)  # {b'SKU-001': b'2', b'SKU-002': b'1'}

# Set TTL for cart abandonment (24 hours)
redis_client.expire(cart_key, 86400)

리더보드 아키텍처

실시간 리더보드는 Sorted Sets(ZSETs)로 구현하는 대표적인 Redis 사용 사례입니다. 모든 플레이어 항목에는 점수가 있으며, Sorted Set은 항상 멤버를 오름차순 점수로 유지합니다. 리더보드 조회(상위 N명의 플레이어, 플레이어 순위, 특정 점수 범위의 플레이어)는 O(log n) 또는 O(log n + m)으로 처리되므로 수백만 명의 플레이어가 있어도 매우 빠릅니다. Redis Sorted Set은 복잡한 데이터베이스 조회를 작성하거나 페이지를 볼 때마다 순위를 다시 계산하지 않고도 게임, 피트니스, 소셜 순위 기능을 구현하는 기반이 됩니다.

# Real-time leaderboard with Redis Sorted Set

# Add or update a player's score
# ZADD game:weekly:leaderboard 15750 'player:alice'
redis_client.zadd('game:weekly:leaderboard', {'player:alice': 15750})

# Increment score (atomic)
# ZINCRBY game:weekly:leaderboard 500 'player:alice'
redis_client.zincrby('game:weekly:leaderboard', 500, 'player:alice')

# Get top 10 players (highest scores first)
# ZREVRANGE game:weekly:leaderboard 0 9 WITHSCORES
top_10 = redis_client.zrevrange('game:weekly:leaderboard', 0, 9, withscores=True)

플레이어 순위와 주변 플레이어

'상위 10명 표시' 외에 리더보드에서 자주 사용하는 기능으로는 플레이어의 순위 표시와 특정 플레이어 주변의 플레이어 표시가 있습니다. 두 기능 모두 Redis Sorted Set을 사용하면 간단하게 구현할 수 있습니다. ZREVRANK는 점수 내림차순에서 플레이어의 0부터 시작하는 순위를 반환합니다. 플레이어보다 순위가 5단계 높은 플레이어와 낮은 플레이어를 표시하려면 먼저 해당 플레이어의 순위를 가져온 다음, ZREVRANGE를 순위-5부터 순위+5까지 사용하면 됩니다. 이렇게 하면 Redis 명령 두 개만으로 개인화된 리더보드 화면을 제공할 수 있으므로 복잡한 SQL 윈도 함수가 필요하지 않습니다.

# Get Alice's rank (0-indexed, so add 1 for display)
# ZREVRANK game:weekly:leaderboard 'player:alice'
rank = redis_client.zrevrank('game:weekly:leaderboard', 'player:alice')
print(f'Alice is rank #{rank + 1}')

# Get 5 players above and below Alice
start = max(0, rank - 5)
end = rank + 5
nearby = redis_client.zrevrange(
    'game:weekly:leaderboard', start, end, withscores=True
)
print('Players near Alice:', nearby)

Redis를 사용한 요청 속도 제한

요청 속도 제한(클라이언트가 일정 시간 창에서 보낼 수 있는 요청 수를 제한하는 것)은 Redis의 또 다른 중요한 사용 사례입니다. 슬라이딩 윈도 알고리즘은 각 멤버가 요청 타임스탬프인 Sorted Set을 사용합니다. 요청이 들어올 때마다 윈도보다 오래된 멤버를 제거하고, 남은 멤버 수를 세고, 수가 제한을 초과하면 요청을 거부한 다음 새 타임스탬프를 추가합니다. 이 방식은 밀리초 단위의 정밀한 슬라이딩 윈도 요청 속도 제한을 구현하므로 고정 윈도 카운터보다 훨씬 정확하며 데이터베이스 오버헤드도 없습니다.

# Sliding window rate limiter (100 requests per 60 seconds)
import time

def is_rate_limited(user_id, redis_client, limit=100, window_seconds=60):
    key = f'ratelimit:{user_id}'
    now = time.time()
    window_start = now - window_seconds

    pipe = redis_client.pipeline()
    pipe.zremrangebyscore(key, '-inf', window_start)  # Remove old
    pipe.zcard(key)                                    # Count current
    pipe.zadd(key, {str(now): now})                   # Add this request
    pipe.expire(key, window_seconds)
    results = pipe.execute()

    request_count = results[1]
    return request_count >= limit  # True = rate limited

Redis를 사용한 분산 잠금

분산 잠금은 여러 애플리케이션 서버에서 공유 리소스에 배타적으로 접근할 수 있도록 조정합니다. Redis의 SET key value NX EX ttl 명령은 원자적인 잠금 획득을 제공합니다. 즉, 키가 존재하지 않을 때만 키를 설정하고(NX = Not eXists), 보유자가 충돌하더라도 교착 상태가 발생하지 않도록 TTL을 설정합니다. 작업이 완료되면 보유자가 키를 삭제합니다. Redlock 알고리즘은 여러 Redis 노드를 사용해 정족수를 구성하므로 더 견고한 분산 잠금을 제공하지만 복잡성이 증가합니다. 대부분의 사용 사례에서는 단일 Redis 노드 잠금으로 충분합니다.

# Distributed lock with Redis SET NX EX
import uuid

def acquire_lock(redis_client, resource, ttl_seconds=30):
    lock_id = str(uuid.uuid4())  # Unique ID to identify this lock holder
    key = f'lock:{resource}'
    acquired = redis_client.set(key, lock_id, nx=True, ex=ttl_seconds)
    return lock_id if acquired else None

def release_lock(redis_client, resource, lock_id):
    key = f'lock:{resource}'
    # Only delete if we still own the lock (Lua script for atomicity)
    lua = 'if redis.call("get",KEYS[1])==ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end'
    redis_client.eval(lua, 1, key, lock_id)

세션 저장소: ElastiCache와 DynamoDB 비교

ElastiCache Redis와 DynamoDB 모두 세션 데이터를 저장할 수 있지만 서로 다른 장단점이 있습니다. ElastiCache Redis: 마이크로초 단위 지연 시간, 메모리 기반(지속성을 활성화하지 않으면 휘발성), 더 단순한 데이터 모델, VPC 필요. DynamoDB: 한 자릿수 밀리초 지연 시간(DAX를 사용하면 Redis와 비슷한 수준), 유지 관리할 클러스터가 없는 완전 관리형 서비스, 기본적으로 내구성이 보장됨, Global Tables를 사용한 전역 액세스, 온디맨드 용량을 지원하는 서버리스 서비스. SAA-C03 시험에서는 마이크로초 단위 지연 시간이나 복잡한 메모리 내 작업이 강조되면 Redis를 선택합니다. 내구성, 서버리스, 전역 확장성이 강조되면 DynamoDB를 고려합니다.

Redis를 사용한 지리 공간 인덱싱

Redis에는 위도와 경도 좌표를 저장하고 주변 검색을 수행할 수 있는 내장 지리 공간 데이터 형식(GEO 명령)이 있습니다. GEOADD, GEODIST, GEORADIUS(Redis 6.2부터는 GEOSEARCH)를 사용하면 특정 지점에서 주어진 반경 안에 있는 모든 위치를 O(n + log n) 시간에 찾을 수 있습니다. 사용 사례로는 주변 운전자 찾기(차량 공유), 5km 이내의 식당 찾기, 거리를 기준으로 검색 결과 정렬하기 등이 있습니다. 별도의 지리 공간 데이터베이스가 필요하지 않으며 위치 검색을 메모리 내 처리 속도로 수행할 수 있습니다.

# Store driver locations
# GEOADD drivers 13.361389 38.115556 'driver:001'
# GEOADD drivers 15.087269 37.502669 'driver:002'

# Find all drivers within 10 km of a point
# GEOSEARCH drivers FROMLONLAT 13.5 38.1 BYRADIUS 10 km ASC COUNT 5 WITHCOORD

# Result: sorted list of driver IDs within 10 km with coordinates

고유 방문자 수 계산을 위한 HyperLogLog

HyperLogLog는 집합에 추가되는 고유 항목 수와 관계없이 고정된 메모리( Redis에서는 12KB)를 사용해 고유 요소의 개수를 추정하는 확률적 데이터 구조입니다. 표준 오차는 약 0.81%입니다. 요소를 추가할 때는 PFADD를 사용하고, 추정값을 가져올 때는 PFCOUNT를 사용합니다. 정확한 개수가 필요하지 않고 메모리 효율성이 중요할 때 일일 활성 사용자 수, 고유 페이지 조회 수, 고유 IP 주소 수를 계산하는 데 적합합니다. 수백만 개의 고유 사용자 ID를 Redis Set에 저장하면 GB 단위의 메모리를 사용하지만, HyperLogLog는 12KB만 사용합니다.

# Count unique daily visitors using HyperLogLog
date = '2024-01-15'
hll_key = f'unique_visitors:{date}'

# Track a visitor (PFADD is idempotent for the same user)
# PFADD unique_visitors:2024-01-15 'user:12345'
redis_client.pfadd(hll_key, 'user:12345')
redis_client.pfadd(hll_key, 'user:67890')
redis_client.pfadd(hll_key, 'user:12345')  # Duplicate — not counted again

# Get estimated unique visitor count
# PFCOUNT unique_visitors:2024-01-15
count = redis_client.pfcount(hll_key)
print(f'Unique visitors today (estimate): {count}')

빠른 확인

이번 레슨에서 다룬 AWS Solutions Architect(SAA-C03) 개념을 제대로 이해했는지 확인해 보세요.

레슨 요약

이번 레슨에서는 다음을 배웠습니다. Redis 세션 저장소는 모든 인스턴스가 공유 세션 상태에 서브밀리초 지연 시간으로 접근할 수 있게 하여 상태 비저장 수평 확장을 지원합니다. Redis Sorted Set은 O(log n) 순위 조회를 통해 실시간 리더보드를 구현합니다. 또한 특수한 Redis 데이터 형식(고유 개수 계산을 위한 HyperLogLog, 주변 검색을 위한 GEO, 분산 잠금)은 일반적인 아키텍처 문제를 효율적으로 해결합니다. 이것으로 ElastiCache를 사용한 캐싱 과정을 마치고, 다음에는 고가용성과 장애 허용 아키텍처를 살펴보겠습니다.

무료로 시작

AI 튜터와 함께 Cloud & IT Cert Prep을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
150
레슨
600

자주 묻는 질문

“세션 스토리지 및 순위표 패턴” 강의는 무료인가요?

네 — “세션 스토리지 및 순위표 패턴” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Cloud & IT Cert Prep 강의 전체를 잠금 해제할 수 있습니다. Cloud & IT Cert Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

“세션 스토리지 및 순위표 패턴”에서 뭘 배우나요?

ElastiCache를 사용해 애플리케이션 서버에서 HTTP 세션 상태를 분리하고, Redis 정렬 집합으로 실시간 순위표를 구현합니다. 브라우저에서 직접 실행하는 실습 코드로 Cloud & IT Cert Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Cloud & IT Cert Prep을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Cloud & IT Cert Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“세션 스토리지 및 순위표 패턴” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Cloud & IT Cert Prep 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Cloud & IT Cert Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Redis와 Memcached 비교: 적합한 엔진 선택
  2. ElastiCache Redis 복제 그룹 및 클러스터 모드
  3. 캐싱 전략: 지연 로딩 및 쓰기 관통
  4. 세션 스토리지 및 순위표 패턴
← Cloud & IT Cert Prep(으)로 돌아가기