아무도 경고하지 않는 Webhook 실패 모드
Source: Dev.to
번역을 진행하려면 번역이 필요한 본문 텍스트를 제공해 주세요. 현재는 소스 링크만 포함되어 있어 번역할 내용이 없습니다. 텍스트를 알려주시면 바로 한국어로 번역해 드리겠습니다.
개요
Webhook 통합은 간단해 보입니다: Stripe를 엔드포인트에 연결하고 200 OK를 반환하면 끝입니다. 하지만 실제 운영 환경에서는 문제가 조용히 발생할 수 있어, 페이로드, 서명, 서버, Stripe 재시도 큐, 혹은 하위 서비스 중 어디에 문제가 있는지 추측하게 됩니다.
이 필드 가이드는 개발자들이 흔히 마주치는 웹훅 실패 유형을 다루며, 보다 빠른 디버깅 전략을 제공합니다.
엔드포인트가 200 OK를 반환하지만 핸들러가 실행되지 않음
무슨 일이 일어나고 있나요?
- 서버는 요청을 받아들이지만, 페이로드가 애플리케이션의 다른 부분으로 라우팅되어 조용히 실패합니다.
- 미들웨어가 이벤트를 필터링할 수 있습니다.
- 웹훅은 수신되지만 데이터베이스 쓰기가 실패하고 오류가 무시됩니다.
전형적인 (느린) 디버그 접근법
- 핸들러 전역에
console.log()문을 삽입합니다. - 변경 사항을 배포합니다.
- Stripe의 재시도 일정(최대 78 시간)을 기다립니다.
- 로그를 확인하고 반복합니다.
빠른 접근법
원시 웹훅 요청을 애플리케이션에 도달하기 전에 캡처합니다. 이렇게 하면 코드를 수정하지 않고도 Stripe가 보낸 내용, 엔드포인트가 반환한 내용, 그리고 시점을 정확히 확인할 수 있습니다.
API Version Mismatch
Symptom
event.data.object가 null이거나 빈 객체({})입니다.
Root cause
엔드포인트가 이벤트를 생성한 Stripe API 버전과 다른 버전을 사용하고 있습니다. Stripe는 경고를 표시하지 않으며, 단순히 생성된 버전의 페이로드를 전송합니다.
Fix
파싱이나 미들웨어가 실행되기 전에 핸들러의 가장 상단에서 원시 요청 본문을 로그에 기록하십시오.
서명 검증 오류
일반적인 시나리오
- Stripe 서명을 검증하지만 계속 실패합니다.
- 웹훅 비밀키를 재생성해도 도움이 되지 않습니다.
- 테스트를 위해 원시 페이로드를 하드코딩하면 작동합니다.
근본 원인
파싱된 본문을 검증하고 원시 본문을 검증하지 않는 것이 원인입니다. Stripe 서명은 역직렬화된 JSON 객체가 아니라 원시 요청 페이로드를 기반으로 계산됩니다.
잘못된 예시 (Node/Express)
// Wrong — bodyParser already parsed the JSON
app.post('/webhook', (req, res) => {
const sig = req.headers['stripe-signature'];
stripe.webhooks.constructEvent(req.body, sig, secret); // req.body is PARSED
});
올바른 예시
// Right — use the raw body
app.post(
'/webhook',
express.raw({ type: 'application/json' }),
(req, res) => {
const sig = req.headers['stripe-signature'];
stripe.webhooks.constructEvent(req.body, sig, secret); // req.body is RAW
}
);
Stripe 서명 오류를 해결하느라 한 시간을 보냈다면, 이 문제에 직면했을 가능성이 높습니다.
네트워크 및 인프라 문제
- 만료된 터널 – ngrok 터널이 만료되어 Stripe의 요청이 차단될 수 있습니다.
- 방화벽 차단 – 방화벽이 Stripe의 IP 범위를 차단하고 있을 수 있습니다.
- 서버 다운타임 – 프로세스가 충돌 후 재시작되지 않았을 수 있습니다.
Stripe가 전달 실패를 보고하면 서버가 이벤트를 전혀 받지 못했다는 의미입니다. 공개 접근 가능성에 대한 가시성이 없으면 Stripe가 전달 실패에 대해 이메일을 보낸 후에야 문제를 알게 됩니다.
테스트 수정을 재시도 대기 없이 확인하기
Stripe의 재시도 일정:
- 첫 번째 실패 후 1 시간
- 그 후 12 시간
- 그 후 72 시간
이는 78 시간까지 걸릴 수 있어, 프로덕션에서 수정이 정상 작동하는지 확인하는 데 시간이 많이 소요됩니다. 개발 단계에서는 이 지연이 고통스럽습니다.
우회 방법
Stripe CLI를 사용해 테스트 이벤트를 수동으로 트리거합니다. 이는 페이로드를 시뮬레이션하지만 프로덕션 네트워크 조건을 완전히 재현하지는 못합니다.
공통된 문제: 가시성 부족
위에서 언급한 모든 실패 모드는 하나의 문제를 공유합니다: 웹훅 요청에 실제로 어떤 일이 일어났는지 볼 수 없습니다.
웹훅 디버깅 도구
발신자(Stripe, GitHub 등)와 서버 사이에 배치된 전용 디버깅 엔드포인트는 다음 세 가지 핵심 기능을 제공합니다:
- Capture – 발신자가 직접 서버를 호출하는 대신 디버그 엔드포인트에 요청을 보냅니다.
- Inspect – 대시보드에서 전체 원시 페이로드, 헤더, 응답 및 타이밍을 확인합니다.
- Replay – Stripe의 재시도 스케줄을 우회하여 정확한 페이로드를 즉시 서버에 다시 전송합니다.
이 접근 방식은 실패를 몇 초 안에 포착하게 해 주어, 서버가 전송된 그대로의 데이터를 정확히 받았다는 확신을 제공합니다.
Hooklog 소개
나는 이 문제를 해결하기 위해 Hooklog을 만들었습니다. 무료(월 10 k 이벤트), 회원가입 필요 없으며 다음을 제공합니다:
- 즉시 어떤 서비스든 연결할 수 있는 고유한 웹훅 URL.
- 전체 페이로드 검사: 헤더, 본문, 응답, 타이밍.
- 클릭 한 번으로 재시도를 기다리지 않고 핸들러를 테스트할 수 있는 재생 기능.
- 엔드포인트가 4xx 또는 5xx 응답을 반환할 때 이메일 알림.
시작하기:
- 무료 티어: 월 10,000 이벤트, 3일 보관, 최대 3개의 엔드포인트. 신용카드 필요 없음.
당신의 이야기를 공유하세요
가장 최악의 웹훅 디버깅 경험은 무엇인가요? 댓글에 남겨 주세요—모두 읽습니다.