“이런 것도 Redis로 하네” 싶은 것들
지난 편까지가 캐시였다. 캐시가 Redis의 입구라면, 이번 편 패턴들은 Redis를 좀 써본 뒤에 “아 이거 Redis로 하면 되는구나” 하고 무릎을 치게 되는 것들이다. 공통점이 하나 있다. 직접 코드로 짜면 골치 아픈 걸, Redis의 원자적 명령 몇 줄로 끝낸다는 것. 자주 만나는 네 가지를 본다.
1. 세션 저장소 — 서버가 두 대 이상이 되는 순간
서버 한 대일 땐 로그인 세션을 서버 메모리에 둬도 아무 문제 없다. 문제는 트래픽이 늘어 서버를 여러 대로 늘리는 순간 터진다. 로그인은 A 서버에서 했는데 다음 요청이 B로 가면, B는 그 세션을 모른다. 로그인이 풀린다.
이때 세션을 Redis에 몰아두면 모든 서버가 같은 곳을 보니 어디로 요청이 가도 똑같이 처리된다. 스케일 아웃하는 순간 거의 반드시 마주치는 패턴이다.
HSET session:abc123 userId 1 role "admin"
EXPIRE session:abc123 1800 # 30분 후 자동 로그아웃
세션은 필드가 여럿이니 Hash로 담고, EXPIRE로 자동 만료(=자동 로그아웃)까지 공짜로 얻는다. Spring이면 spring-session-data-redis가 이 과정을 통째로 대신해줘서, 세팅만 하면 기존 세션 코드를 거의 안 건드리고 Redis로 옮겨간다.
2. Rate Limiting — “1분에 5번만”
API 남용을 막거나 선착순을 만들 때 쓴다. 핵심은 #1에서 본 INCR과 EXPIRE의 조합이다.
count = INCR rate:user:1
if count == 1: EXPIRE rate:user:1 60 # 창(window)이 처음 열릴 때만 TTL
if count > 5: "너무 많은 요청" 에러
INCR이 원자적이라 동시에 요청이 쏟아져도 카운트가 정확하다. 첫 요청일 때(count == 1)만 60초 TTL을 걸어두면, 60초 뒤 키가 사라지며 카운트가 알아서 0으로 리셋된다. “1분에 5번"이 이 몇 줄로 끝난다. 이걸 DB로 하려 들면 시간 컬럼 비교에 정리 배치까지 붙어 훨씬 지저분해진다.
이건 가장 단순한 고정 창(fixed window) 방식이다. 창 경계에서 순간적으로 두 배가 허용되는 약점이 있는데(59초에 5번, 다음 1초에 또 5번), 처음엔 이걸로 충분하다. 더 빡세게 막아야 하면 슬라이딩 윈도우나 토큰 버킷을 찾아보면 된다.
3. 분산 락 — 동시에 하나만 실행 (그리고 그 함정)
여러 서버가 같은 작업을 동시에 하면 안 될 때가 있다. 주문 하나 정산을 두 서버가 동시에 잡으면 중복 정산이 나는 식이다. 이때 분산 락으로 “이 작업은 지금 나만 한다"를 보장한다.
# NX = 키가 없을 때만 SET (획득 성공/실패를 원자적으로 판정)
SET lock:order:100 "server-A" NX EX 10
# 성공(OK) → 내가 락 소유, 작업 후 DEL
# 실패(nil) → 이미 다른 서버가 잡고 있음
NX(없을 때만)와 EX(만료)를 한 명령에 같이 거는 게 핵심이다. EX를 빼면, 락을 잡은 서버가 작업 도중 죽었을 때 락이 영영 안 풀려서 전체가 멈춘다.
여기서 솔직하게 말하면, 분산 락은 겉보기보다 훨씬 까다롭다. 작업이 10초를 넘겨 락이 먼저 만료돼 버리면, 그 사이 다른 서버가 새 락을 잡는데 원래 서버가 뒤늦게 끝나면서 남의 락을 DEL로 지워버릴 수 있다. 이런 경계 케이스가 한둘이 아니다. 그래서 나는 학습용으로 원리는 위처럼 이해하되, 실무에서는 직접 구현하지 말고 Redisson 같은 검증된 라이브러리를 쓰라고 권한다. Redisson은 작업이 길어지면 락을 자동 연장(watchdog)하고, 자기 락만 안전하게 푸는 처리를 이미 다 해뒀다. 바퀴를 다시 발명하다 다칠 필요 없다.
4. 실시간 랭킹 — Sorted Set 하나면 끝
게임 점수판, 인기글, 실시간 검색어는 사실 전부 같은 문제다. “점수 순으로 정렬된 목록에서 상위 N개랑, 특정 대상의 순위를 빠르게.” #2에서 본 Sorted Set이 정확히 이걸 위해 있다.
ZINCRBY ranking:today 1 "post:100" # 조회될 때마다 점수 +1
ZREVRANGE ranking:today 0 9 WITHSCORES # 오늘의 인기글 TOP 10
ZREVRANK ranking:today "post:100" # 이 글의 현재 순위
“오늘의 랭킹"처럼 기간이 있으면 키에 날짜를 박고(ranking:2026-07-20) TTL을 걸어두면 지난 날짜 랭킹은 알아서 정리된다. 배치로 옛날 데이터 지울 필요가 없다. 이런 소소한 게 쌓여서 Redis가 편한 거다.
마치며
네 패턴을 관통하는 건 결국 하나다. 원래 애플리케이션 코드로 복잡하게 짜야 할 것을 Redis가 원자적 명령으로 단순하게 만든다. 세션 공유, 횟수 제한, 동시성 제어, 실시간 정렬 — 하나하나 직접 구현하면 다 골치 아픈 문제인데 명령 몇 줄로 접힌다.
마지막 편에서는 이 모든 걸 Spring Boot에서 실제로 어떻게 붙이는지, 그리고 운영에 올리기 전 마지막으로 확인할 체크리스트를 정리하며 시리즈를 닫는다.