Node.js · 2026-02-28

RabbitMQ로 이벤트 기반 아키텍처 구현

RabbitMQ와 Node.js를 이용해 이벤트 기반 아키텍처를 이해하고 설계하는 방법, 핵심 개념과 실무 코드 예제 및 운영 고려사항

작성일 : 2026-02-28 ㆍ 작성자 : 관리자
post
목차

소개

이 글은 RabbitMQ를 중심으로 이벤트 기반 아키텍처를 설계하고 Node.js로 구현하는 흐름을 정리한다. 초급자도 이해할 수 있도록 개념부터 코드까지 단계적으로 설명하며, 실무에서 자주 쓰이는 패턴과 운영상 고려사항을 함께 다룬다.

이벤트 기반 아키텍처 개요

이벤트 기반 아키텍처란?

이벤트 기반 아키텍처는 시스템 구성 요소들이 이벤트로 느슨하게 결합되어 통신하는 방식이다. 서비스 간 직접 호출을 줄이고 비동기 메시징을 통해 확장성과 복원력을 높인다. 비동기 처리와 느슨한 결합이 핵심 원리다.

왜 RabbitMQ를 선택하는가

RabbitMQ는 AMQP 표준을 준수하는 메시지 브로커다. 다양한 라우팅 옵션과 안정성 기능(ACK, 재전송, 내구성)을 제공한다. 특히 복잡한 라우팅과 Pub/Sub 패턴 구현에 유리하다.

핵심 개념 정리

교환기(Exchange), 큐(Queue), 바인딩(Binding)

교환기는 메시지를 받아 라우팅 규칙에 따라 큐로 보낸다. 큐는 소비자가 메시지를 꺼내 처리하는 버퍼다. 바인딩은 교환기와 큐를 연결하는 규칙이다. 교환기 타입에는 direct, fanout, topic, headers 등이 있다.

ACK, 내구성, prefetch

ACK는 소비자가 메시지 처리 완료를 브로커에 알리는 메커니즘이다. 내구성 설정을 통해 브로커 재시작 시 메시지 보존이 가능하다. prefetch는 소비자에게 동시에 전달되는 미처리 메시지 수를 제한한다.

Node.js 환경에서 RabbitMQ 시작하기

설치와 연결

Node.js에서 주로 사용하는 라이브러리는 amqplib이다. 간단한 연결 코드는 아래와 같다. 이 예제는 기본 연결과 채널 생성을 보여준다.

const amqp = require('amqplib');

async function connect() {
  const conn = await amqp.connect('amqp://localhost');
  const ch = await conn.createChannel();
  return { conn, ch };
}

module.exports = { connect };

간단한 퍼블리셔 예제

다음은 fanout 교환기를 이용한 Pub/Sub 예제의 퍼블리셔 코드다. fanout은 바인딩된 모든 큐에 메시지를 브로드캐스트한다.

const { connect } = require('./rabbit');

async function publish() {
  const { conn, ch } = await connect();
  const exchange = 'logs';
  await ch.assertExchange(exchange, 'fanout', { durable: false });
  const msg = JSON.stringify({ event: 'user.created', data: { id: 1, name: 'Alice' } });
  ch.publish(exchange, '', Buffer.from(msg));
  setTimeout(() => conn.close(), 500);
}

publish().catch(console.error);

간단한 컨슈머 예제

컨슈머는 익명 큐를 생성해 교환기에 바인딩한다. 이렇게 하면 여러 컨슈머가 동시에 같은 메시지를 받을 수 있다.

const { connect } = require('./rabbit');

async function consume() {
  const { conn, ch } = await connect();
  const exchange = 'logs';
  await ch.assertExchange(exchange, 'fanout', { durable: false });
  const q = await ch.assertQueue('', { exclusive: true });
  await ch.bindQueue(q.queue, exchange, '');
  ch.consume(q.queue, msg => {
    if (msg) {
      const content = JSON.parse(msg.content.toString());
      console.log('Received', content);
      ch.ack(msg);
    }
  }, { noAck: false });
}

consume().catch(console.error);

Pub/Sub와 라우팅 패턴

direct과 topic

direct 교환기는 라우팅 키가 일치하는 큐로 메시지를 보낸다. topic은 와일드카드를 사용해 더 세밀한 라우팅이 가능하다. 예를 들어 'user.*'나 'order.#' 같은 패턴을 통해 이벤트를 분류할 수 있다.

실무 적용 예

  • 이벤트 셜럽 및 로깅: fanout으로 여러 로거에 동일 메시지 전달
  • 업무 분할: direct/topic으로 서비스별 책임 분리
  • 작업 큐: 작업 처리량 제어 및 재시도 로직 적용

운영 및 성능 고려사항

내구성 및 메시지 보존

내구성이 필요한 메시지는 교환기와 큐를 durable로 설정하고 메시지를 persistent로 보내야 한다. 그렇지 않으면 브로커 재시작 시 데이터 손실 위험이 있다.

ACK와 재시도 전략

처리가 실패하면 메시지를 재큐(requeue)할지, 데드레터 교환기로 보낼지 정책을 정해야 한다. 네트워크 오류나 예외 발생 시 중복 처리에 대비한 idempotency 설계도 필요하다.

모니터링과 스케일링

큐 길이, 소비자 처리율, 메시지 처리 시간 등을 모니터링해야 한다. 필요 시 컨슈머 수를 늘리거나 prefetch 값을 조정해 처리량을 조절한다.

결론

Node.js와 RabbitMQ의 조합은 이벤트 기반 아키텍처 구현에 적합하다. 기본 개념과 코드 예제를 바탕으로 Pub/Sub, 라우팅, 내구성, ACK 정책을 설계하면 실무에 적용 가능한 시스템을 만들 수 있다. 시작은 단순한 퍼블리셔와 컨슈머에서 출발해 운영 요구에 따라 확장하는 접근이 실용적이다.

Node.js RabbitMQ 사용법 RabbitMQ Pub/Sub Node 예제 AMQP Node.js 튜토리얼 이벤트 기반 아키텍처 메시지 브로커 amqplib 예제 RabbitMQ 운영 Pub/Sub 패턴