Supabase Queues면 충분한데, QStash는 왜 쓸까?

서버리스 프로젝트에 Worker를 붙일 때 QStash가 반드시 필요한 것은 아니다. 이미 Supabase를 사용하고 있다면 PostgreSQL 기반의 Supabase Queues로 작업을 저장하고, Vercel 함수가 메시지를 가져가 처리하는 구조를 만들 수 있다.

그렇다면 자연스럽게 질문이 생긴다.

Supabase Queues만으로 충분하다면 QStash는 무엇이 더 강력한가?

짧게 답하면 HTTP Worker로 작업을 전달하고 운영하는 기능은 QStash가 더 풍부하다. 반면 데이터베이스와 Queue를 한곳에서 다루는 단순함은 Supabase Queues가 더 낫다. 어느 제품이 절대적으로 상위라기보다 강점을 발휘하는 지점이 다르다.

먼저 공통점부터 살펴보자

두 서비스 모두 사용자 요청에서 오래 걸리는 작업을 분리하는 데 사용할 수 있다.

사용자 주문
↓
주문 데이터 저장
↓
Queue에 작업 등록
↓
사용자에게 접수 응답

잠시 후
Queue → Worker → 후처리 완료

이메일 발송, 알림, 보고서 생성, 외부 시스템 연동처럼 주문 결과를 즉시 결정하지 않는 작업을 Queue로 넘기면 사용자는 후처리가 끝날 때까지 기다리지 않아도 된다.

차이는 Queue와 Worker가 만나는 방법에서 시작된다.

가장 큰 차이: Push와 Pull

QStash는 Worker를 호출한다

QStash는 메시지를 받으면 지정한 HTTPS 주소로 전달한다.

Vercel 주문 API
↓ 작업 등록
QStash
↓ HTTP 요청
Vercel Worker API

Worker는 계속 실행되며 메시지를 확인할 필요가 없다. QStash가 호출할 때만 Vercel 함수가 실행된다. 실패 응답이나 네트워크 오류가 발생하면 QStash가 설정에 따라 다시 호출한다.

서버리스 함수처럼 요청이 있을 때만 실행되는 환경과 자연스럽게 맞는 Push 방식이다.

Supabase Queues는 Worker가 가져간다

Supabase Queues는 PostgreSQL 안에 메시지를 보관하는 Pull 방식 Queue다.

Vercel 주문 API
↓ 작업 등록
Supabase Queues
↑ 메시지 읽기
Vercel Worker API

Queue가 Worker를 직접 호출하지 않는다. Worker를 주기적으로 깨우는 Cron이나 상시 실행 소비자가 필요하다. 예를 들어 Supabase Cron이 10초마다 Vercel Worker API를 호출하고, Worker가 Queue에서 메시지를 읽는 식이다.

서비스 공급자는 줄지만 “누가 Worker를 깨우는가”와 “얼마나 자주 메시지를 가져오는가”를 직접 결정해야 한다.

QStash가 강력한 이유

QStash는 메시지 보관보다 HTTP 전달 과정 전체에 초점을 맞춘 서비스다.

자동 재시도와 지연 전략

대상 Worker가 오류 상태를 반환하거나 응답하지 않으면 QStash가 다시 전달한다. 재시도 횟수를 지정할 수 있고 기본적으로 지수 백오프를 사용하며, 필요하면 재시도 간격도 조정할 수 있다.

Supabase Queues에서도 재처리는 가능하다. Worker가 메시지를 읽으면 visibility timeout 동안 다른 소비자에게 보이지 않고, 성공 후 삭제하지 못하면 시간이 지난 뒤 다시 보인다. 그러나 최대 시도 횟수, 재시도 간격, 최종 실패 처리 정책은 소비자 코드에서 설계해야 한다.

Dead Letter Queue

계속 실패한 QStash 메시지는 DLQ에서 조회하고 다시 시도하거나 삭제할 수 있다. 실패한 작업을 정상 Queue와 분리해 조사할 수 있다는 뜻이다.

Supabase Queues는 메시지 삭제와 archive 기능을 제공하지만, 몇 번 실패한 메시지를 별도 보관하고 관리자에게 알릴지는 애플리케이션 규칙으로 만들어야 한다.

유량과 동시 실행 제어

QStash Flow Control은 Worker에 동시에 전달할 요청 수와 일정 시간 동안의 전달 속도를 제한한다.

동시에 최대 3개
1분에 최대 20개

외부 API가 한 번에 받을 수 있는 요청 수가 제한돼 있거나 Worker가 과부하되지 않도록 보호해야 할 때 유용하다. Supabase Queues에서는 한 번에 읽을 메시지 수와 Worker 실행 수를 조정해 같은 효과를 직접 구성해야 한다.

예약, 콜백, 중복 방지

QStash는 메시지 지연 전달, 예약 실행, 성공·실패 콜백, 전달 로그, 중복 방지 ID 같은 기능을 제공한다. 특히 동일한 중복 방지 ID는 일정 시간 동안 다시 발행되지 않게 할 수 있다.

여러 HTTP 엔드포인트를 호출하고 결과를 추적해야 하는 비동기 시스템이라면 이런 기능이 구현량을 크게 줄인다.

Supabase Queues가 더 유리한 지점

기능 목록은 QStash가 길지만, Supabase Queues에도 분명한 장점이 있다.

서비스가 두 개면 충분하다

Vercel과 Supabase를 이미 사용한다면 별도의 메시징 공급자를 추가하지 않아도 된다.

Vercel
├─ 화면
├─ 주문 API
└─ Worker API

Supabase
├─ PostgreSQL
├─ Queues
└─ Cron

환경변수, 권한, 대시보드, 장애 상태를 확인할 공급자가 줄어든다. 작은 프로젝트에서는 기능 수보다 이 단순함이 더 큰 장점일 수 있다.

주문 저장과 메시지 등록을 함께 묶기 좋다

Supabase Queues는 PostgreSQL 확장 기능인 pgmq를 기반으로 한다. 주문 INSERT와 Queue 메시지 등록 함수를 같은 PostgreSQL 트랜잭션 안에서 호출할 수 있다.

트랜잭션 시작
├─ 주문 저장
├─ Queue 메시지 등록
└─ 함께 커밋

둘 중 하나가 실패하면 모두 취소할 수 있으므로 “주문은 저장됐는데 작업 메시지는 등록되지 않은” 상태를 피하기 쉽다.

외부 QStash를 사용하면 DB 저장과 메시지 발행이 서로 다른 시스템에 걸친 두 번의 작업이다. 중간 실패까지 엄격하게 다뤄야 한다면 Outbox 패턴 같은 보완 설계가 필요할 수 있다.

메시지가 데이터베이스 가까이에 있다

Queue 메시지와 archive가 PostgreSQL에 있으므로 SQL과 익숙한 도구로 상태를 확인할 수 있다. 업무 데이터와 작업 이력을 함께 분석하려는 작은 시스템에도 편리하다.

다만 Queue 트래픽도 결국 같은 PostgreSQL 자원을 사용한다. 메시지 양과 소비자 폴링이 많아지면 주 업무 쿼리와 자원을 놓고 경쟁할 수 있으므로 “별도 청구 항목이 없다”와 “비용이 없다”를 같은 뜻으로 보면 안 된다.

기능을 표로 비교하면

비교 항목 | QStash | Supabase Queues
전달 방식 | HTTP Push | 소비자 Pull
Worker 기동 | QStash가 호출 | Cron 또는 상시 소비자 필요
재시도 | 횟수·간격을 서비스에서 관리 | visibility timeout과 소비자 코드로 관리
최종 실패 | DLQ 제공 | archive와 별도 실패 규칙 구성
동시성·속도 제한 | Flow Control 제공 | 읽기 수와 Worker 수를 직접 조정
예약·지연 | 기본 기능 | Queue 지연과 Cron 조합
중복 발행 방지 | Deduplication ID 제공 | 작업 ID와 DB 제약으로 구현
DB 작업과 원자적 결합 | 별도 시스템이므로 보완 필요 | 같은 PostgreSQL 트랜잭션 사용 가능
공급자 수 | 하나 증가 | Supabase 안에서 해결
잘 맞는 Worker | 공개 HTTPS 엔드포인트 | DB 가까이에서 동작하는 소비자

전달 보장과 처리 보장은 다르다

두 제품 모두 신뢰할 수 있는 메시지 전달 기능을 설명하지만, 외부 부작용까지 자동으로 정확히 한 번 실행해 주는 것은 아니다.

Worker가 이메일을 보낸 직후 완료 상태를 기록하기 전에 종료됐다고 해보자. Queue 입장에서는 작업이 완료됐는지 알 수 없으므로 같은 메시지가 다시 처리될 수 있다.

이메일 발송 성공
↓
완료 기록 전에 Worker 종료
↓
Queue가 메시지를 다시 전달
↓
이메일이 두 번 발송될 가능성

따라서 어느 Queue를 선택해도 Worker는 멱등하게 만들어야 한다. 작업 ID를 영구 저장하고 이미 완료된 작업은 건너뛰거나, 외부 API가 제공하는 idempotency key를 사용해야 한다.

비용도 기능과 함께 비교한다

QStash 무료 플랜은 현재 하루 1,000메시지를 제공한다. 사용량제는 전달 시도 10만 건당 1달러이며, 재시도 한 번도 별도의 전달 한 건으로 계산된다.

Supabase Queues는 기존 PostgreSQL 안에서 동작하므로 별도의 Queue 공급자 기본료를 추가하지 않을 수 있다. 대신 메시지 저장량, 폴링 쿼리, Cron과 Worker 호출이 Supabase·Vercel 사용량에 포함된다.

작은 프로젝트에서는 두 방식 모두 무료 구간에 들어올 수 있으므로 단순한 단가보다 구현하고 운영할 코드의 양을 함께 봐야 한다.

어떤 상황에 무엇을 고를까

Supabase Queues가 잘 맞는 경우

이미 Supabase를 사용하고 있다.

작업량이 작고 몇 초의 처리 지연을 허용할 수 있다.

주문 저장과 메시지 등록을 한 트랜잭션으로 묶고 싶다.

Queue를 읽고 성공 시 삭제하는 흐름을 직접 구현해 보고 싶다.

공급자 수를 최소화하는 것이 중요하다.

QStash가 잘 맞는 경우

서버리스 HTTP Worker를 즉시 호출하고 싶다.

재시도, DLQ, 지연 실행과 전달 로그가 중요하다.

외부 API마다 동시성이나 호출 속도를 제어해야 한다.

여러 서비스와 엔드포인트로 작업을 전달한다.

Queue 소비 루프와 Cron을 직접 운영하고 싶지 않다.

작은 프로젝트에서 추천하는 학습 순서

Queue 자체를 이해하는 것이 목적이라면 Supabase Queues로 먼저 구현해 보는 편이 좋다.

주문 등록
→ Queue 메시지 등록
→ Cron이 Worker 호출
→ Worker가 메시지 읽기
→ 처리 성공 후 archive

이 과정을 경험하면 visibility timeout, polling, retry, archive가 왜 필요한지 보인다.

그다음 Worker가 늘어나고 재시도 정책과 유량 제어 코드가 번거로워질 때 QStash로 바꿔 보면, 전용 메시징 서비스가 어떤 운영 부담을 대신 가져가는지 분명하게 이해할 수 있다.

결론

QStash가 더 강력한가라는 질문에는 조건부로 “그렇다”고 답할 수 있다.

HTTP Worker 전달과 운영 기능은 QStash가 더 강력하다. 자동 Push, 재시도, DLQ, 예약, 콜백, 중복 방지, 유량 제어가 준비되어 있기 때문이다.

하지만 작은 Supabase 중심 프로젝트에서는 이 모든 기능이 아직 필요하지 않을 수 있다. 같은 PostgreSQL 안에서 주문과 Queue 메시지를 다루고 공급자 수를 줄이는 것이 더 큰 가치라면 Supabase Queues가 더 좋은 선택이다.

결국 선택 기준은 기능의 개수가 아니다.

Queue를 직접 소비하는 단순함을 택할 것인가, HTTP 전달 운영을 서비스에 맡길 것인가?

이 질문에 답하면 두 제품 사이의 선택도 자연스럽게 정리된다.

참고 자료

Supabase Queues 개요

Supabase Queues Quickstart

Supabase Queues API

Supabase Cron

QStash 시작하기와 주요 기능

QStash 재시도·지연·중복 방지 설정

QStash Flow Control

QStash DLQ

QStash 가격