ASP.NET Core에서 중복 웹훅 이벤트를 처리하는 방법 (Idempotency 가이드)
Source: Dev.to
왜 중복 웹훅이 발생하는가
결제 게이트웨이(예: Worldpay, Stripe)는 다음과 같은 경우에 웹훅 호출을 의도적으로 재시도합니다:
- 엔드포인트가 타임아웃될 때
- 200 OK 응답을 받지 못했을 때
- 네트워크 문제가 발생했을 때
시스템이 멱등성을 보장하지 않으면 중복 이벤트는 다음과 같은 문제를 일으킬 수 있습니다:
- 유효한 결제 상태를 덮어씀
- 비즈니스 로직이 중복 실행됨
- 데이터 일관성 손상
- 재무 조정에 문제 발생
결제 시스템에서는 이러한 상황이 매우 위험합니다.
멱등성(Idempotency)이란?
멱등성은 같은 이벤트를 여러 번 처리하더라도 최종 상태가 동일하게 유지된다는 의미입니다. 즉, 이벤트를 몇 번 처리하든 결과가 변하지 않습니다.
ASP.NET Core에서의 간단한 전략
신뢰할 수 있는 한 가지 방법은 각 웹훅 이벤트 ID를 데이터베이스에 저장하고, 처리하기 전에 존재 여부를 확인하는 것입니다.
- 이벤트 ID가 이미 존재하는지 확인한다.
- 존재한다면 → 웹훅을 무시한다.
- 존재하지 않는다면 → 웹훅을 처리하고 ID를 저장한다.
public async Task HandleWebhook(WebhookEvent webhook)
{
var exists = await _context.PaymentEvents
.AnyAsync(e => e.EventId == webhook.EventId);
if (exists)
return;
var paymentEvent = new PaymentEvent
{
EventId = webhook.EventId,
PaymentId = webhook.PaymentId,
Status = webhook.Status,
CreatedAt = DateTime.UtcNow
};
_context.PaymentEvents.Add(paymentEvent);
await _context.SaveChangesAsync();
// Continue business logic safely
}
이렇게 하면 중복 재시도로 인한 상태 손상을 방지할 수 있습니다.
기본 로깅을 넘어서는 작업
프로덕션 환경에서는 다음과 같은 추가 정보를 캡처하고 싶을 수 있습니다:
- 전체 상태 전이 추적
- 이전 상태와 새로운 상태 비교
- 원시 페이로드 저장
- 출처 추적(웹훅 / 수동 / API)
구조화된 감사 로깅이 이러한 요구사항에 필수적입니다.
마무리 생각
중복 웹훅은 버그가 아니라 보장된 상황입니다.
ASP.NET Core 결제 연동에 멱등성을 구현하지 않으면 설계상 취약해집니다.
초기 개발자를 위해 이러한 시나리오를 처리하는 가벼운 감사‑로깅 컴포넌트가 제공됩니다: