Node.js와 Redis를 활용한 멱등 API

발행: (2026년 2월 11일 오전 10:28 GMT+9)
5 분 소요
원문: Dev.to

Source: Dev.to

Overview

Cover image for Idempotent APIs in Node.js with Redis

분산 시스템은 실패한다. 요청이 재시도된다. 웹훅이 두 번 도착한다. 클라이언트가 타임아웃되고 다시 시도한다. 원래 한 번만 실행돼야 할 작업이 여러 번 실행돼서 고객에게 이중 청구가 되거나 같은 이벤트가 다섯 번 처리될 수 있다. **멱등성(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:
0 조회
Back to Blog

관련 글

더 보기 »

Server Components는 SSR이 아니다!

SSR vs. React Server Components 개발 세계에서 React Server Components(RSC)는 종종 또 다른 형태의 Server‑Side Rendering(SSR)으로 오해받는다. 두 가지 모두…

📦Redux란 무엇인가?

프론트엔드 개발을 배우고 있다면, 특히 React와 함께라면 Redux에 대해 들어봤을 것입니다. 처음에는 혼란스러워 보일 수 있지만, 핵심 아이디어는 간단합니다....