Redis/BullMQ 이용하여 연산 작업 따로하기

2025. 12. 23. 00:35·NestJS/개발

연산을 따로 하려고 하는 이유

이번 작업을 하고 있는 토이 프로젝트의 전체적인 동작은 다음과 같다.

  1. 클라이언트가 이미지를 업로드하면
  2. NestJS 백엔드 서버에서 이미지 원본과 WebP 변환 파일을 저장하고
  3. 변환된 이미지의 경로를 PostgreSQL 데이터베이스에 기록한다.

현재 구조에서는 파일을 하나만 올리거나 사용자가 많지 않을 경우에는 큰 문제가 발생하지 않는다. 그러나 동시에 여러 사용자가 이미지를 업로드하고 변환 작업이 이루어지면, 빨간색 네모 박스 부분을 잘 생각해야 하는데, CPU 연산이 많은 WebP 변환 과정에서 서버에 부하가 걸릴 가능성이 있다. 이 경우, NestJS 백엔드가 모든 변환 작업을 동기적으로 처리하게 되면 다음과 같은 문제가 발생할 수 있다.

  • 응답 지연: 변환이 완료될 때까지 API 응답이 지연된다.
  • 서버 과부하/다운: 동시에 많은 요청이 몰리면 CPU와 메모리가 과도하게 사용되어 서버가 불안정해질 수 있다.

이런 문제를 해결하기 위해, Redis와 BullMQ를 활용한 비동기 큐 구조를 도입하려고 한다.
즉, 업로드 요청 시 API 서버는 즉시 응답하고, 실제 이미지 변환 작업은 백엔드 워커에서 비동기적으로 처리된다.
이를 통해 서버는 안정성을 유지하면서도, 다수의 요청이 동시에 들어와도 효율적으로 처리할 수 있다. 그리고 스케일 아웃 측면에서도 아주 좋다.

  • 워커 수를 늘려서 더 많은 처리가능
  • Redis큐를 공유하면 여러 워커가 처리가능함

결론적으로 webp 변환 작업을 워커로 따로 관리하게 한다.


npm 패키지 설치하기

npm install bullmq ioredis

 

  • bullmq: 큐/워커 기능
  • ioredis: Redis 연결 라이브러리 (BullMQ 내부에서도 사용)

 

https://docs.bullmq.io/guide/connections

공식문서에 설명이 나와있다. 우리는 bullmq와 ioredis를 사용할 것이다.


Redis와 BullMQ를 사용하려는 이유

만약 관계형 데이터베이스로 job 관리를 한다고 가정해보자. 이미지를 업로드한 뒤 worker가 이를 webp로 변환해야 하는 상황에서,
이 작업 상태를 전부 RDB에서 관리하려면 보통 다음과 같은 구조가 된다.

  1. 백엔드는 작업 상태를 조회하는 API를 따로 제공하고
  2. 클라이언트 또는 워커는 이를 polling 방식으로 주기적으로 조회하며 처리할 작업이 있는지 확인해야 한다.
  3. 또한 작업 실패 시 재시도 횟수 관리, 백오프(backoff), 중복 처리 방지와 같은 로직을 모두 직접 구현해야 한다.

문제는 이런 방식이 큐의 본질과 잘 맞지 않는다는 점이다. 작업 큐는 "넣고(push) → 처리하고(pop)" 하는 선입선출(FIFO) 구조가 핵심인데, 관계형 데이터베이스는 이런 사용 패턴을 전제로 설계되지 않았다. 추가적으로 불필요한 락(lock), 잦은 조회 쿼리, 복잡한 상태 관리 테이블이 생기고 시스템이 커질수록 관리 비용이 급격히 증가한다.

 

Redis를 사용하는 이유도 여기에 있다. Redis는 메모리 기반으로 동작하기 때문에 작업을 가져오고 상태를 변경하는 연산이 매우 빠르지만 관계형 데이터베이스에서는 여러 worker가 동일한 작업을 동시에 가져가지 않도록 트랜잭션과 락을 사용해야 한다. 반면 Redis는 큐 연산 자체가 원자적으로 수행되기 때문에 별도의 락 없이도 작업을 하나의 worker에게만 안전하게 할당할 수 있다. 또한 BullMQ는 Redis를 기반으로 실패 처리, 재시도, 지연 실행 같은 기능을 기본 제공하므로 개발자는 비즈니스 로직에만 집중할 수 있다.


BullMQ - Queue(job 등록하기)

import { Queue, Worker } from 'bullmq';

const myQueue = new Queue('myqueue', {
  connection: {
    host: 'test.com',
    port: 6379,
  },
});

위와 같이 레디스 연결 주소를 넣어서 큐를 사용할수 있게 인스턴스를 생성한다.

await myQueue.add('job', { files: fileArr });

이렇게 인스턴스를 이용하여 add 해주면 된다. job이라는 이름의 작업을, 전달한 데이터와 함께 큐에 등록한다.


BullMQ - Worker (job 받아오기)

import { Queue, Worker } from 'bullmq';

const myWorker = new Worker(
  "myqueue",
  async (job) => {
    //작업코드
  },
  {
    connection: {
      host: "test.com",
      port: 6379,
    },
  }
);

이제 내부에 내가 원하는 작업을 넣으면 된다. 이제 이 코드를 실행시키면


참고

https://docs.bullmq.io/

 

What is BullMQ | BullMQ

General description of BullMQ and its features

docs.bullmq.io

 

'NestJS > 개발' 카테고리의 다른 글

실시간 채팅 서버 개발 - 02 (방 생성하기)  (0) 2026.01.18
실시간 채팅 서버 개발 - 01 (Redis 및 NestJS 셋팅)  (1) 2026.01.14
JWT - Passport 사용하기  (0) 2025.12.22
JWT - 역할 기반 관리하기  (1) 2025.12.21
JWT - 토큰 활용하기  (0) 2025.12.21
'NestJS/개발' 카테고리의 다른 글
  • 실시간 채팅 서버 개발 - 02 (방 생성하기)
  • 실시간 채팅 서버 개발 - 01 (Redis 및 NestJS 셋팅)
  • JWT - Passport 사용하기
  • JWT - 역할 기반 관리하기
나는지토
나는지토
  • 나는지토
    안녕은헬로입니다.
    나는지토
  • 전체
    오늘
    어제
    • 분류 전체보기 (27)
      • Backend Design (1)
      • NestJS (19)
        • 개발 (9)
        • 개념과 구조 정리 (10)
      • SpringBoot (0)
      • Java (4)
        • 코테 (0)
      • PostgreSQL (2)
      • Docker (1)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    nestjs/jwt
    PostgreSQL
    채팅
    커서기반 조회
    인증 가드
    토큰 검사
    ArrayList
    nestjs
    JWT
    Collections
    코딩테스트
    컨트롤러
    Redis
    BullMQ
    조회 방식
    자료구조
    서비스
    db 연결 오류
    역할 검사
    Java
  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
나는지토
Redis/BullMQ 이용하여 연산 작업 따로하기
상단으로

티스토리툴바