database

BullMQ Overview

BullMQ란 무엇인가요?

BullMQ는 Redis를 기반으로 한 강력한 Node.js 작업 큐 라이브러리로, 이메일 전송, 이미지 처리, 데이터 분석과 같은 시간이 오래 걸리는 작업을 비동기적으로 처리하는 데 유용합니다. 이를 통해 서버 부하를 줄이고, 서비스의 응답 속도를 크게 향상시킬 수 있습니다.


BullMQ는 기존 Bull 라이브러리를 개선한 버전으로, TypeScript 지원이 강화되었으며, 성능과 안정성이 더욱 향상되었습니다. 대규모 분산 작업을 효율적으로 처리할 수 있도록 설계되어 있으며, 다양한 기능을 사용할 수 있습니다.

BullMQ 설치하기

BullMQ는 Redis를 저장소로 사용하기 때문에 Redis가 먼저 설치되어 있어야 합니다. BullMQ는 npm이나 pnpm을 통해 설치가 가능합니다.

BullMQ
pnpm add bullmq ioredis

BullMQ의 주요 개념

BullMQ를 이해하려면 다음 세 가지 개념을 알아두어야 합니다.

  • Queue(큐): 실행할 작업(Job)을 저장하는 공간입니다. 여러 개의 작업이 대기하며, 순차적으로 처리됩니다.
  • Worker(워커): 큐에서 작업을 가져와 실제로 실행하는 프로세스입니다.
  • Job(작업): 개별적으로 큐에 추가된 하나의 처리 단위입니다. 예를 들어 이메일 전송 요청이 하나의 Job이 될 수 있습니다.

BullMQ의 주요 특징

  • 고성능: 효율적인 Redis 기반 설계로 빠른 작업 처리가 가능합니다.
  • TypeScript 지원: 강력한 타입 안전성을 제공하여 개발 생산성을 높입니다.
  • 작업 재시도 및 일정 예약: 실패한 작업의 자동 재시도 및 예약 실행이 가능합니다.
  • 수평 확장: 여러 개의 작업 처리 노드를 쉽게 확장할 수 있습니다.
  • 이벤트 기반 구조: 작업의 상태 변화를 실시간으로 추적할 수 있습니다.

큐 등록 예제

이제 간단한 예시를 통해 BullMQ를 실제로 어떻게 사용하는지 살펴보겠습니다.

BullMQ
import { Queue } from 'bullmq'
import IORedis from 'ioredis'
 
const connection = new IORedis()
const emailQueue = new Queue('emailQueue', { connection })
 
async function addEmailJob() {
  await emailQueue.add('sendEmail', { email: 'user@example.com' })
  console.log('이메일 작업을 성공적으로 등록했습니다.')
}
 
addEmailJob()
  • IORedis를 사용하여 Redis에 연결합니다.
  • Queue 클래스를 이용해 emailQueue라는 큐를 생성합니다.
    • Queue의 첫 번째 인자는 큐의 이름입니다. 여러 개의 큐를 운영할 경우, 각 큐를 구별하기 위해 고유한 이름을 설정해야 합니다.
    • 두 번째 인자로 설정 객체를 전달하며, 여기서 connection을 통해 Redis 연결 정보를 지정합니다.
    • 이 큐는 특정 작업을 대기하고 있다가 Worker에 의해 순차적으로 실행됩니다.
  • addEmailJob 함수에서 emailQueue.add를 호출하여 작업을 추가합니다.
    • 첫 번째 인자는 작업의 이름(sendEmail)입니다.
    • 두 번째 인자는 작업과 함께 전달할 데이터(이메일 정보)입니다.
  • 작업이 성공적으로 추가되면 콘솔에 메시지를 출력합니다.

큐에 등록된 작업은, 이후에 Worker를 통해 실행됩니다.

작업을 처리하는 예제(Worker)

BullMQ
import { Worker } from 'bullmq'
import IORedis from 'ioredis'
 
const connection = new IORedis()
 
const emailWorker = new Worker(
  'emailQueue',
  async (job) => {
    console.log(`작업 처리 중: 작업 ID ${job.id}, 작업 유형 ${job.name}`)
    // 이메일 전송 로직을 여기에 구현합니다.
  },
  { connection },
)
 
emailWorker.on('completed', (job) => {
  console.log(`${job.id} 작업이 성공적으로 완료되었습니다.`)
})
 
emailWorker.on('failed', (job, err) => {
  console.error(`${job.id} 작업 처리에 실패했습니다. 오류: ${err.message}`)
})
  • IORedis를 사용하여 Redis에 연결합니다.
  • Worker 클래스를 이용해 emailQueue 큐에서 작업을 가져와 실행하는 워커를 생성합니다.
    • 첫 번째 인자는 처리할 큐의 이름(emailQueue)입니다.
    • 두 번째 인자는 작업을 처리하는 함수이며, job 객체를 인자로 받아 처리할 내용을 정의할 수 있습니다.
    • 세 번째 인자로 connection 설정을 전달하여 Redis 연결을 지정합니다.
  • 작업이 완료되었을 때 이벤트 리스너를 설정합니다.
    • emailWorker.on('completed', callback): 작업이 정상적으로 완료되었을 때 실행됩니다.
    • emailWorker.on('failed', callback): 작업이 실패했을 때 실행되며, 오류 메시지를 출력합니다.

Worker는 Queue에 등록된 작업을 자동으로 가져와 실행하므로, 별도의 작업 실행 요청 없이도 백그라운드에서 지속적으로 실행됩니다.

작업 상태 모니터링하기

BullMQ는 작업의 현재 상태를 쉽게 모니터링할 수 있습니다. 작업은 waiting(대기 중), active(실행 중), completed(완료됨), failed(실패함), delayed(지연됨) 등의 상태를 가질 수 있습니다. 각 상태에 따라 적절한 처리를 수행하여 안정적인 작업 관리를 할 수 있습니다.

BullMQ
async function checkJobStatus(jobId) {
  const job = await emailQueue.getJob(jobId)
  if (job) {
    console.log(`작업 ID ${jobId}의 상태: ${await job.getState()}`)
  } else {
    console.log(`작업 ID ${jobId}를 찾을 수 없습니다.`)
  }
}
BullMQ
async function checkJobCounts() {
  const counts = await emailQueue.getJobCounts()
  console.log('현재 작업 상태:', counts)
}
 
checkJobCounts()

실무에서 유용한 BullMQ 활용 팁

  • 재시도(Retry) 전략: 작업 처리 실패 시 자동으로 몇 번까지 재시도할지 설정할 수 있습니다.
  • 지연 작업(Delayed jobs): 특정 시간 이후에 작업이 실행되도록 설정할 수 있습니다.
  • 동시성(Concurrency) 제어: 동시에 처리하는 작업 수를 제한하여 서버 자원을 효과적으로 관리할 수 있습니다.

정리

BullMQ를 활용하면 Node.js 기반 애플리케이션의 성능을 효과적으로 높이고 시스템의 안정성을 향상할 수 있습니다. 비동기 작업을 효율적으로 관리하고, 서버 리소스를 최적화하여 더 나은 서비스를 제공할 수 있습니다.

참고자료