메시지 큐는 백엔드 시스템에서 비동기 처리를 구현하는 데 있어 매우 유용한 도구입니다. 특히 Node.js와 함께 사용했을 때, 서버의 부하를 줄이고 더 나은 성능을 제공할 수 있습니다. 이번 글에서는 Node.js로 메시지 큐를 활용하여 비동기 처리를 구현한 경험을 공유하겠습니다.

1. 메시지 큐란?

메시지 큐(Message Queue)는 프로세스 간의 메시지를 비동기적으로 전달하기 위해 사용하는 시스템입니다. 일반적으로 생산자(Producer)가 메시지를 큐에 보내고, 소비자(Consumer)가 이를 받아 처리합니다. 이를 통해 시스템의 응답성을 높이고, 비동기 처리로 인해 발생할 수 있는 병목 현상을 줄일 수 있습니다.

2. 사용할 라이브러리 및 설정

이번 프로젝트에서는 RabbitMQ라는 메시지 큐 서버와 amqplib라는 Node.js 클라이언트를 사용할 예정입니다. RabbitMQ는 AMQP(Advanced Message Queuing Protocol)을 지원하는 오픈소스 메시지 브로커로, 높은 신뢰성과 성능을 자랑합니다.

2.1 RabbitMQ 설치

먼저 RabbitMQ를 설치해야 합니다. Docker를 이용하면 간편하게 설치할 수 있습니다. 터미널에서 다음 명령어를 입력합니다.

docker run -d --hostname my-rabbit --name some-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:3-management

위 명령어를 실행하면 RabbitMQ 서버가 실행되고, 웹 관리 인터페이스에 접속하려면 http://localhost:15672로 이동합니다. 기본 로그인 정보는 guest / guest입니다.

2.2 amqplib 설치

Node.js 프로젝트를 생성한 후, amqplib 패키지를 설치합니다.

npm install amqplib

3. 메시지 생산자(Producer) 만들기

메시지 생산자는 큐에 메시지를 보내는 역할을 합니다. 아래는 간단한 생산자 코드입니다.

const amqp = require('amqplib');

async function sendMessage(msg) {
    try {
        const connection = await amqp.connect('amqp://localhost');
        const channel = await connection.createChannel();
        const queue = 'task_queue';

        await channel.assertQueue(queue, { durable: true });
        channel.sendToQueue(queue, Buffer.from(msg), { persistent: true });

        console.log(Sent: ${msg});
        await channel.close();
        await connection.close();
    } catch (error) {
        console.error('Error sending message:', error);
    }
}

// 메시지 전송
sendMessage('Hello World!');

위 코드에서 sendMessage 함수는 RabbitMQ에 연결하여 "task_queue"라는 큐에 메시지를 전송합니다. persistent: true 옵션을 설정하여 큐의 내구성을 보장합니다.

4. 메시지 소비자(Consumer) 만들기

소비자는 큐에서 메시지를 읽어 처리하는 역할을 합니다. 아래는 소비자 코드입니다.

const amqp = require('amqplib');

async function receiveMessages() {
    try {
        const connection = await amqp.connect('amqp://localhost');
        const channel = await connection.createChannel();
        const queue = 'task_queue';

        await channel.assertQueue(queue, { durable: true });
        console.log('Waiting for messages in %s. To exit press CTRL+C', queue);

        channel.consume(queue, (msg) => {
            const content = msg.content.toString();
            console.log(Received: ${content});
            channel.ack(msg);  // 메시지 처리가 완료되었음을 RabbitMQ에 알려줍니다.
        }, { noAck: false });
    } catch (error) {
        console.error('Error receiving messages:', error);
    }
}

// 메시지 수신 시작
receiveMessages();

소비자는 큐에서 메시지를 대기하고 있다가 수신된 메시지를 처리한 후 channel.ack(msg)을 호출하여 RabbitMQ에 처리 완료를 알립니다.

5. 비동기 처리의 이점

메시지 큐를 사용하면 비동기 처리의 여러 장점을 누릴 수 있습니다.

  • 서버 부하 감소: 요청을 큐에 넣고 즉시 응답을 반환하므로 서버의 부하를 줄일 수 있습니다.
  • 서버 확장성: 여러 소비자를 두어 수평 확장이 가능하므로, 처리량을 쉽게 늘릴 수 있습니다.
  • 내구성: 메시지가 큐에 지속적으로 저장되므로, 시스템 오류 시에도 데이터 손실을 방지할 수 있습니다.

6. 마지막 생각

Node.js와 메시지 큐를 활용한 비동기 처리의 경험은 매우 긍정적이었습니다. 신속한 개발과 운영의 이점을 누릴 수 있었고, 시스템의 성능을 한층 향상시킬 수 있었습니다. 비동기 처리를 원하는 백엔드 개발자라면 메시지 큐를 적극적으로 활용해 보시기를 추천합니다.

마무리

메시지 큐는 현대 웹 애플리케이션에서 비동기 처리를 구현하는 데 매우 중요한 역할을 합니다. RabbitMQ와 Node.js의 조합을 통해 비동기 처리의 이점을 경험해 보았고, 이를 통해 더 나은 아키텍처를 구축할 수 있었습니다. 이 글이 여러분에게 도움이 되었기를 바랍니다.