본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] Redis를 메시지 브로커로 활용하는 3가지 방법과 캐시 사용 시의 결정적 차이 및 해결 방안

by Papa Martino V 2026. 3. 20.
728x90

Redis(Remote Dictionary Server)
Redis (Remote Dictionary Server)

 

 

안녕하세요. 오늘은 파이썬(Python) 환경에서 분산 시스템을 구축할 때 가장 빈번하게 고려되는 Redis(Remote Dictionary Server)에 대해 심도 있게 다뤄보겠습니다. 흔히 Redis를 '빠른 캐시 메모리'로만 알고 계시지만, 실전 마이크로서비스 아키텍처(MSA)에서는 이를 강력한 메시지 브로커(Message Broker)로 활용합니다. 단순히 데이터를 저장하는 캐시와 달리, 메시지를 전달하고 흐름을 제어하는 브로커로 사용할 때는 설계 철학 자체가 달라져야 합니다. 본 포스팅에서는 Redis를 브로커로 쓸 때 발생하는 데이터 유실 가능성, 가용성 문제, 그리고 이를 해결하기 위한 구체적인 Python 구현 코드와 아키텍처 전략을 100% 실무 관점에서 설명해 드립니다.


1. Redis 캐시 vs 메시지 브로커: 개념의 결정적 차이

Redis를 캐시로 사용할 때는 '데이터가 사라져도 원본 DB에서 다시 읽어오면 된다'는 전제가 깔려 있습니다. 하지만 메시지 브로커로 사용할 때는 메시지의 전달 보장(Delivery Guarantee)이 생명입니다. 이 관점의 차이가 시스템의 안정성을 결정짓습니다.

비교 항목 캐시(Cache) 활용 메시지 브로커(Message Broker) 활용
주 목적 데이터 읽기 성능 향상 및 DB 부하 감소 서비스 간 비동기 통신 및 데이터 스트리밍
데이터 휘발성 LRU/LFU 알고리즘에 의한 자동 삭제 허용 소비(Consume) 전까지 데이터 보존 필수
주요 장애 지점 Cache Miss로 인한 DB 병목 메시지 유실 및 컨슈머 처리 지연(Backlog)
대표 모델 Key-Value Store Pub/Sub, List(LPUSH/BRPOP), Streams

2. Redis를 브로커로 활용할 때 반드시 주의해야 할 3가지 리스크

첫째, 메모리 오버플로우와 Eviction 정책

Redis는 인메모리 데이터베이스입니다. 메시지 생산 속도가 소비 속도보다 빠를 경우 메모리가 가득 차게 됩니다. 이때 캐시 설정인 maxmemory-policyallkeys-lru로 되어 있다면, 아직 처리되지 않은 중요한 메시지가 메모리 확보를 위해 강제로 삭제되는 대참사가 발생합니다.

둘째, 비정상 종료 시 데이터 유실 (Persistence)

기본적으로 Redis는 비동기 스냅샷(RDB)이나 AOF 방식으로 데이터를 저장합니다. 메시지를 브로커에 넣자마자 서버가 다운되면, 마지막 저장 시점 이후의 메시지는 영구히 사라집니다. 이를 방지하려면 appendfsync always 설정이 필요하지만, 이 경우 Redis의 최대 장점인 성능이 급격히 저하됩니다.

셋째, Pub/Sub 모델의 'Fire and Forget' 특성

Redis Pub/Sub은 메시지를 보낸 후 수신자가 없으면 그대로 버립니다. 즉, 컨슈머가 잠시 네트워크 장애로 연결이 끊긴 사이에 발행된 메시지는 다시는 받을 수 없습니다. 이 문제를 해결하려면 Redis Streams나 List 구조를 활용한 구현이 강제됩니다.


3. Python 실전 예제: 안정적인 메시지 브로커 구현 (List 기반)

가장 대중적이고 안정적인 Reliable Queue 패턴을 Python 코드로 구현해 보겠습니다. RPOPLPUSH (또는 최신 버전의 LMOVE) 명령어를 사용하여 메시지를 처리함과 동시에 백업 큐에 보관함으로써 유실을 방지합니다.

Sample Example: Reliable Producer & Consumer


import redis
import time
import uuid

# Redis 연결 설정
client = redis.StrictRedis(host='localhost', port=6379, db=0, decode_responses=True)

def produce_task(task_data):
    """메시지를 큐에 적재하는 프로듀서"""
    task_id = str(uuid.uuid4())
    payload = f"{task_id}:{task_data}"
    client.lpush("task_queue", payload)
    print(f"[Producer] Task Enqueued: {payload}")

def consume_task():
    """메시지 유실 방지 로직이 포함된 컨슈머"""
    print("[Consumer] Waiting for tasks...")
    while True:
        # task_queue에서 꺼내서 processing_queue로 동시에 이동 (Atomicity 보장)
        # 처리 중 장애 발생 시 processing_queue에 데이터가 남아있어 복구 가능
        task = client.brpoplpush("task_queue", "processing_queue", timeout=5)
        
        if task:
            print(f"[Consumer] Processing: {task}")
            try:
                # 실제 비즈니스 로직 수행 (예: 이메일 발송, 데이터 분석)
                time.sleep(1) 
                
                # 처리가 완료되면 작업 완료 큐에서 삭제
                client.lrem("processing_queue", 1, task)
                print(f"[Consumer] Done & Removed: {task}")
            except Exception as e:
                print(f"[Consumer] Error processing task: {e}")
                # 재시도 로직 등을 여기에 추가
        else:
            print("[Consumer] No task found, polling...")

if __name__ == "__main__":
    # 테스트를 위한 샘플 데이터 생성
    produce_task("Send Welcome Email")
    produce_task("Process Payment #1024")
    
    # 컨슈머 실행
    consume_task()

4. 고가용성을 위한 Redis Streams 활용법

Redis 5.0부터 도입된 Streams는 Kafka와 유사한 기능을 제공합니다. 소비자 그룹(Consumer Groups)을 지원하여 메시지 수신 확인(ACK) 프로세스를 처리할 수 있습니다. 메시지 브로커로서의 기능을 극대화하려면 반드시 Streams를 검토해야 합니다.

  • 메시지 ID 추적: 각 메시지는 밀리초 단위의 타임스탬프 기반 ID를 가집니다.
  • 보류 리스트(PEL): 읽어갔지만 ACK를 보내지 않은 메시지를 추적하여 재처리가 가능합니다.
  • 데이터 영속성: List보다 구조화된 형태로 저장되어 관리가 용이합니다.

5. 결론: Redis 브로커 도입 전 체크리스트

시스템을 설계할 때 아래 4가지 질문에 "YES"라고 답할 수 없다면, Redis보다는 RabbitMQ나 Kafka 도입을 고려해야 합니다.

  1. 메모리 용량이 충분한가? (Eviction 발생 시 메시지 삭제 위험)
  2. 100% 데이터 무결성이 아닌 성능이 우선인가? (초당 수만 건의 처리 속도)
  3. Consumer Group 관리가 단순해도 괜찮은가?
  4. Redis Persistence(AOF/RDB) 설정이 최적화되었는가?

Redis는 파이썬 생태계와 매우 궁합이 잘 맞는 도구입니다. 하지만 '캐시'로 쓸 때의 관성대로 '브로커'를 운영한다면 예상치 못한 데이터 유실로 인해 비즈니스에 큰 타격을 입을 수 있습니다. 위에서 설명한 brpoplpush 패턴이나 Streams 기능을 적재적소에 활용하여 견고한 시스템을 구축하시기 바랍니다.


참고 문헌 및 출처

  • Redis Official Documentation: https://redis.io/docs/manual/patterns/distributed-queues/
  • Python-redis (redis-py) GitHub Repository: https://github.com/redis/redis-py
  • Real Python - Using Redis With Python: https://realpython.com/python-redis/
  • High Performance Message Queues (Architecture Paper): IEEE Software Architecture Journal 2024
728x90