우당탕탕

Redis pub/sub 메시지 큐로 써봤더니 알게 된 핵심 포인트들 본문

Database

Redis pub/sub 메시지 큐로 써봤더니 알게 된 핵심 포인트들

모찌모찝 2026. 6. 12. 13:56

저도 처음엔 ‘Redis pub/sub’가 뭔지, 이걸 메시지 큐로 쓰는 게 도대체 어떤 의미인지 전혀 몰랐어요. 근데 실제로 조금씩 코드도 짜보고, 실행 결과도 보면서 확실히 이해가 되더라고요. 이 과정에서 겪은 시행착오와 팁들을 정리해봤습니다.

이 글에서는 Redis pub/sub의 기본 개념부터, 직접 메시지 큐로 구현해본 경험담, 실행 결과 분석, 그리고 초보자가 흔히 헷갈리는 부분까지 한 번에 정리해 드릴게요.

개발 환경 / 버전 정보

실습에 사용한 Redis 버전은 Redis 7.0이며, 클라이언트로는 Node.js v18.15.0를 활용했습니다. 메시지 큐 역할을 해보기 위해 redis 공식 패키지를 사용했어요.

Redis pub/sub가 뭔지, 메시지 큐랑 왜 헷갈리는지

사실 이 부분이 가장 헷갈렸어요. Redis pub/sub는 발행자(publisher)가 채널에 메시지를 보내면, 그 채널을 구독(subscribe)한 모든 구독자에게 메시지를 전달해주는 ‘실시간 방송’ 같은 개념이에요. 그래서 특정 이벤트를 여러 구독자가 동시에 받아야 할 때 유용합니다.

반면 메시지 큐는 보통 ‘발행된 메시지를 순서대로 처리하는 통로’ 역할을 하죠. 그래서 1개 혹은 소수의 소비자가 순차적으로 메시지를 처리합니다. “pub/sub랑 메시지 큐가 왜 헷갈리지?” 싶은데, Redis는 공식적으로 메시지 큐 기능을 따로 제공하지 않고 pub/sub 기능으로도 메시지 큐처럼 쓸 수 있거든요. 단, 이 때 중요한 건 pub/sub의 단점도 명확히 알아야 한다는 점이에요.

제가 해본 Redis pub/sub 메시지 큐 기본 구현 방법

직접 구독자와 발행자를 Node.js로 만들어봤어요. 구독자는 채널을 구독하고 발행자는 메시지를 발행하죠. 코드가 궁금하실 것 같아서 최대한 쉽게 주석도 넣었습니다.

// Redis pub/sub 예제 - 발행자 (publisher.js)
const { createClient } = require('redis');

(async () => {
  const publisher = createClient();
  await publisher.connect();

  // 1초마다 메시지 발행
  let count = 0;
  setInterval(async () => {
    const message = `메시지 번호: ${++count}`;
    await publisher.publish('my_channel', message);
    console.log(`발행: ${message}`);
  }, 1000);
})();

// Redis pub/sub 예제 - 구독자 (subscriber.js)
const { createClient } = require('redis');

(async () => {
  const subscriber = createClient();
  await subscriber.connect();

  // 'my_channel' 채널 구독
  await subscriber.subscribe('my_channel', (message) => {
    console.log(`구독자1 수신: ${message}`);
  });
})();

위 코드를 돌리면 발행자가 1초마다 메시지를 보내고 구독자는 그걸 실시간으로 받아서 출력해요. 저는 이걸 보면서 '아, 이게 바로 pub/sub이구나' 싶었죠.

실행 결과와 Redis pub/sub 메시지 큐로서 한계 느낀 부분

실행 결과는 콘솔에 아래처럼 나왔어요.

발행: 메시지 번호: 1
구독자1 수신: 메시지 번호: 1
발행: 메시지 번호: 2
구독자1 수신: 메시지 번호: 2
// ... 계속 반복

처음엔 잘 되는 것 같았는데 이걸 메시지 큐로 쓰려니까 불편한 점이 많더라고요. pub/sub는 메시지를 받은 구독자가 메시지를 완료했는지를 Redis가 알 수 없어요. 그래서 메시지가 잘 처리됐는지 확인이나 재처리가 불가능하죠.

또, 만약 구독자가 연결이 끊긴 상태일 때 발행자가 메시지를 보내면 그 메시지는 사라집니다. 즉, 구독자가 나중에 다시 연결해도 못 받은 메시지는 못 받는 거예요. 이 부분이 일반적인 메시지 큐와 가장 큰 차이라고 생각합니다.

제가 삽질했던 Redis pub/sub 메시지 누락 문제 해결법

실제로 구독자가 잠시 끊긴 상태에서 메시지를 못 받아서 한참 당황했었어요. 이걸 해결할 방법으로는 Redis의 Stream 기능을 추천한대요. 왜냐면 Stream은 메시지에 ID가 붙어서 구독자가 끊겼다가 다시 연결해도 읽지 않은 메시지부터 받을 수 있어요.

하지만 처음부터 Stream을 쓰자니 너무 복잡해서, 간단하게 pub/sub로 테스트해보고 싶을 때는 발행자 쪽에서 메시지를 별도로 저장해놓고(예: Redis List에 집어넣기), 구독자가 못 받은 메시지는 거기서 다시 확인하는 방식을 썼습니다.

Redis pub/sub 메시지 큐 응용 코드 예시

이 부분이 핵심인데요, 발행할 때 메시지를 Redis List에 저장하고, 구독자가 메시지를 처리하면 List에서 해당 메시지를 제거하는 형태로 구현해봤어요.

// 발행자 예제 (publisher_with_storage.js)
const { createClient } = require('redis');

(async () => {
  const client = createClient();
  await client.connect();

  let count = 0;
  setInterval(async () => {
    const message = `메시지 번호: ${++count}`;
    // 메시지를 List에 저장
    await client.rPush('message_queue', message);
    // pub/sub로 발행
    await client.publish('my_channel', message);
    console.log(`발행 및 저장: ${message}`);
  }, 1000);
})();

// 구독자 예제 (subscriber_with_ack.js)
const { createClient } = require('redis');

(async () => {
  const client = createClient();
  await client.connect();

  await client.subscribe('my_channel', async (message) => {
    console.log(`구독 수신: ${message}`);
    // 처리 후 메시지를 List에서 제거 (처리 완료 신호)
    await client.lRem('message_queue', 1, message);
    console.log(`처리 완료: ${message}`);
  });
})();

이 방식을 쓰면 구독자가 잠시 끊겼을 때라도 리스트에 쌓인 메시지를 뒤늦게 확인하고 다시 처리할 수 있어요. 물론 이걸 완전히 자동화하려면 구독자가 재접속 시 리스트를 스캔하는 로직도 필요하죠.

자주 헷갈리는 Redis pub/sub 관련 질문들

Q. Redis pub/sub는 메시지 큐랑 완전히 다른 건가요?

A. 완전히 다르진 않지만, 큰 차이는 ‘메시지 보장’ 여부예요. pub/sub는 메시지가 구독자에게 보내지면 끝이라서 실패 시 재시도나 누락 복구가 어렵고, 메시지 큐는 보통 메시지 보장 및 순서 보장을 제공합니다.

Q. Redis 대신 RabbitMQ나 Kafka 같은 걸 써야 할까요?

A. 네, 만약 메시지 손실이나 복잡한 메시지 처리 로직이 중요하다면 RabbitMQ, Kafka 같은 전용 메시지 큐를 권장합니다. 하지만 단순 이벤트 알림이라면 Redis pub/sub도 부담 없이 쓸 수 있어요.

Q. pub/sub 성능은 어떤가요?

A. Redis 자체가 메모리 기반이라서 매우 빠릅니다. 그래서 실시간 알림, 채팅, 게임 서버 이벤트 전달 등에 자주 사용돼요. 다만 메시지 저장이나 재처리는 직접 구현해야 한다는 점 잊지 마세요.

짧게 요약하면, Redis pub/sub를 메시지 큐처럼 쓰는 건 가능하지만, 메시지 손실과 처리 보장 문제 때문에 상황에 맞게 보완책을 세워야 한다는 점이 중요하더라고요. 저처럼 처음 입문하신 분들은 우선 pub/sub 개념부터 차근차근 이해하고, 어느 정도 익숙해지면 Redis Stream이나 RabbitMQ 같은 솔루션도 살펴보는 걸 추천합니다.

Comments