Node.js와 Redis를 활용한 멱등 API
Source: Dev.to
Overview

분산 시스템은 실패한다. 요청이 재시도된다. 웹훅이 두 번 도착한다. 클라이언트가 타임아웃되고 다시 시도한다. 원래 한 번만 실행돼야 할 작업이 여러 번 실행돼서 고객에게 이중 청구가 되거나 같은 이벤트가 다섯 번 처리될 수 있다. **멱등성(idempotency)**이 해결책이다. 하지만 올바르게 구현하는 것이 어려운 부분이다.
이 글에서는 Node.js와 Redis를 사용한 멱등 API 구현 방법과 idempotency-redis 패키지가 재시도, 결제, 웹훅을 안전하게 처리하도록 돕는 방식을 보여준다.
What idempotency means for APIs
API 작업이 멱등하다는 것은 같은 멱등성 키를 사용한 여러 호출이 동일한 결과를 반환하고, 부수 효과는 한 번만 발생한다는 뜻이다.
- 멱등성 키당 한 번 실행
- 동시 요청이나 재시도 요청은 동일한 결과를 재생
- 실패도 재생 가능
이는 다음 상황에서 중요하다:
- 💳 결제
- 🔁 자동 재시도
- 🔔 웹훅
- 🧵 동시 요청
Why naive solutions fail
일반적인 접근 방식은 금방 한계에 부딪힌다:
- 인메모리 락 → 인스턴스 간에 동작하지 않음
- DB 고유 제약 → 결과를 재생하기 어려움
- Redis
SETNX→ 결과나 오류를 재생하지 않음 409 Conflict반환 → 복잡성을 클라이언트에 전가
실제로 필요한 것은 조정 + 캐싱 + 재생이며, 이는 모든 노드에서 공유되어야 한다.
Using idempotency-redis
idempotency-redis는 Redis를 기반으로 멱등 실행을 제공한다:
- 하나의 요청만이 실제 작업을 수행
- 다른 요청들은 대기하고 캐시된 결과를 재생
- 오류도 기본적으로 캐시되고 재생됨
- 여러 Node.js 인스턴스 간에 동작
Basic example
import Redis from 'ioredis';
import { IdempotentExecutor } from 'idempotency-redis';
const redis = new Redis();
const executor = new IdempotentExecutor(redis);
await executor.run('payment-123', async () => {
return chargeCustomer();
});
같은 키로 동시에 다섯 번 호출하면 함수가 한 번만 실행된다.
Real‑world use cases
Payments
결제 제공자와 클라이언트는 공격적으로 재시도한다. API는 절대 이중 청구를 해서는 안 된다.
await executor.run(`payment:${paymentId}`, async () => {
const charge = await stripe.charges.create(/* … */);
await saveToDB(charge);
return charge;
});
응답이 손실되더라도 재시도는 캐시된 결과를 재생하므로 두 번째 청구가 발생하지 않는다.
Webhooks
웹훅 제공자는 명시적으로 “이벤트가 한 번 이상 전달될 수 있다.” 라고 말한다.
await executor.run(`webhook:${event.id}`, async () => {
await processWebhook(event);
});
중복 전달? 동일한 결과. 한 번만 실행.
Retries without fear
멱등성을 적용하면 안전하게 할 수 있다:
- HTTP 재시도 활성화
- 백그라운드 작업 재시도
- 느리거나 불안정한 의존성 처리
중복 작업 없음. 레이스 컨디션도 없음.
Error handling and control
기본적으로 오류도 캐시되고 재생되어 무한 재시도를 방지한다. 필요에 따라 선택적으로 제외할 수 있다:
await executor.run(key, action, {
shouldIgnoreError: (err) => err.retryable === true
});
When to use this
다음 상황이라면 idempotency-redis를 사용하라:
- 상태를 변경하는 API를 구축할 때
- 재시도나 웹훅을 받는 경우
- 여러 Node.js 인스턴스를 운영 중일 때
- 장애 상황에서도 정확성을 보장하고 싶을 때
Learn more
- 📦 npm:
- 🐙 GitHub: