Engineering
Next.js 웹훅 멱등성 설계: 중복 결제와 중복 처리 사고를 막는 실전 운영법
Mike•발행일 2026년 2월 26일•수정일 2026년 2월 27일•읽는 시간 약 1분
💡 Key Takeaways
- 멱등성은 if 문이 아니라 DB 유니크 키와 상태 전이 규칙으로 완성됩니다.
- 이벤트 수신 즉시 로그 저장을 먼저 해야 장애 분석과 재처리가 쉬워집니다.
- 중복 이벤트는 200으로 응답해 재전송 폭주를 막아야 합니다.
- 동시 2회 유입, 순서 역전 유입 테스트를 CI에 넣어야 사고를 줄일 수 있습니다.
Next.js 웹훅 멱등성 설계: 중복 결제와 중복 처리 사고를 막는 실전 운영법
웹훅 연동은 "한 번 오겠지"라고 생각하는 순간부터 위험합니다. 실제 운영에서는 타임아웃, 재시도, 네트워크 지연으로 같은 이벤트가 두 번 이상 들어오는 게 정상입니다. 중복 처리 사고는 대체로 코드 복잡도 때문이 아니라 처리 순서를 명확히 정하지 않아서 생깁니다.
1) 중복 판정 키부터 고정
현장에서 가장 안전했던 조합은 아래 3개입니다.
provider_event_id(공급자 고유 ID)event_type(예:payment.succeeded)resource_id(우리 주문/구독 ID)
이 3개를 유니크 인덱스로 묶으면 "무엇이 중복인지"가 코드가 아니라 스키마에 박힙니다.
2) 처리 순서 (이 순서가 핵심)
- 서명 검증
- 원본 페이로드 저장
- 유니크 인덱스로 중복 판정
- 상태 전이 검증 (
pending -> paid -> refunded같은 규칙) - 도메인 반영
- 처리 결과 기록
중복으로 판정되면 에러를 내지 말고 200으로 끝내는 게 좋습니다. 그래야 공급자 재시도가 줄고 운영 소음도 줄어듭니다.
3) Next.js 최소 흐름 예시
// app/api/webhooks/provider/route.ts
const inserted = await insertWebhookEvent(eventKey)
if (!inserted) {
return NextResponse.json({ ok: true, duplicate: true })
}
await applyDomainTransitionOrThrow(payload)
await markWebhookHandled(eventKey)
return NextResponse.json({ ok: true })
핵심은 "처리 전 기록"입니다. 나중에 "왜 중복 처리됐는지"를 추적할 수 있어야 합니다.
4) 운영 체크리스트
- 최근 1시간 중복 유입 비율
- 서명 검증 실패율
- 상태 전이 거부 건수
- 수동 재처리 큐 적체량
배포 전에 동시 2회 유입 테스트와 순서 역전 테스트를 돌리면 대부분의 큰 사고를 미리 잡을 수 있습니다.