웹훅이 오지 않을 때

순서대로 확인하세요 - 전송 로그, 이벤트 이름, 방화벽, 서명. Deliveries 탭을 보면 Didit이 실제로 보냈는지 여부를 바로 알 수 있어 문제를 절반으로 좁혀줍니다.

Short answer

먼저 대상의 Deliveries 탭을 확인하세요. Didit이 전송을 시도조차 하지 않았다면 구독이나 대상 설정 문제입니다. 시도는 했지만 실패했다면 응답 코드가 원인을 알려줍니다. 404는 해당 URL에서 라우트에 접근할 수 없었다는 뜻이고, 서명 불일치는 잘못된 바이트를 해시한 것입니다.

#1단계: Didit이 실제로 전송을 시도했는가

API & Webhooks에서 대상을 열고 Deliveries 탭을 확인하세요. 모든 시도는 응답과 함께 개별적으로 기록됩니다.

대상별 전송 상태를 보여주는 Didit 콘솔의 웹훅 페이지
  1. Last sent를 보면 Didit이 애초에 시도했는지 알 수 있습니다.
  2. View stats는 해당 대상의 시도와 실패 횟수를 보여줍니다.
  3. 테스트 전송으로 엔드포인트와 이벤트 자체를 분리해서 확인할 수 있습니다.
  4. 계속 실패하는 대상은 수정하는 동안 비활성화할 수 있습니다.
누락된 이벤트와 실패하는 엔드포인트를 구분하는 데 필요한 모든 것이 이 페이지에 있습니다.

이 확인 하나로 문제를 절반으로 좁힐 수 있습니다.

  • 시도 기록 없음 → 해당 대상에 대해 이벤트가 아예 생성되지 않았습니다. 2단계로 이동하세요.
  • 시도 기록 있음, 실패 → Didit은 전송했지만 여러분 쪽이 거부했거나 수신하지 못했습니다. 3단계로 이동하세요.
  • 시도 기록 있음, 2xx → 정상적으로 전달되었습니다. 문제는 전송이 아니라 여러분의 핸들러 안에 있습니다.

#2단계: 시도 기록이 없는 경우

가능성이 높은 순서대로 정리하면 다음과 같습니다.

  1. 이벤트가 구독되어 있지 않습니다. 와일드카드는 없으며 - 모든 이벤트 계열을 명시적으로 나열해야 합니다. 그리고 존재하지 않는 이름(session.status.updated, kyc.completed)을 구독하면 아무것도 받지 못한 채 조용히 지나갑니다. 이벤트 목록과 대조해 확인하세요.
  2. 잘못된 애플리케이션. 대상은 애플리케이션에 속합니다. 세션이 대상과 다른 애플리케이션에서 실행된다면 어떤 이벤트도 도달하지 않습니다. 모든 것이 올바르게 설정된 것처럼 보이는데도 문제가 생기는 가장 흔한 원인입니다.
  3. 실제로 아무것도 바뀌지 않았습니다. 웹훅은 변경이 있을 때 발생합니다. 상태가 그대로인 세션이나, 임계값을 넘는 결과가 없었던 AML 모니터링 재검토는 정상적으로 이벤트를 만들지 않습니다.
  4. 기다리는 상태가 아직 발생하지 않았습니다. In Progress 상태의 세션은 아직 끝나지 않았습니다. 세션이 끝나지 않을 때를 참고하세요.

#3단계: 전송을 시도했지만 실패한 경우

404는 요청이 도달했지만 해당 위치에 라우트가 없었다는 뜻입니다. 다음을 확인하세요.

  • 후행 슬래시를 포함한 정확한 경로. /hook/hook/으로 리디렉션하는 프레임워크는 정상 작동하던 엔드포인트를 404나 본문 유실로 바꿔버릴 수 있습니다.
  • URL이 공개적으로 접근 가능한지. 인터넷에서 접근할 수 없는 로컬이나 스테이징 호스트는 이런 식으로 실패합니다.
  • 앱 앞단의 프록시, 로드 밸런서, 경로 기반 라우터가 해당 경로를 다른 곳으로 보내고 있지는 않은지.

5xx는 여러분의 핸들러가 예외를 던졌다는 뜻입니다. 실제로 무엇을 받았는지 확인할 수 있도록 파싱 전에 원본 본문을 로그로 남기세요.

타임아웃은 충분히 빠르게 응답하지 못했다는 뜻입니다. 먼저 2xx를 반환하고, 처리는 나중에 하세요.

아무 응답도 없음 / 연결 거부는 여러분의 엣지가 차단했다는 뜻입니다. Didit은 고정 IP 18.203.201.92에서 DiditWebhook/2.0 User-Agent로 전송합니다. Cloudflare나 기본 차단(default-deny) 정책의 WAF 뒤에 있다면, 수신 호스트명에 대해 해당 IP를 허용하세요.

#흔한 사례: 자동 전송은 404, 그런데 Resend는 성공

이 사례는 이름을 붙일 만큼 자주 나옵니다. 자동 전송이 404로 실패한 뒤, 같은 이벤트에서 Resend를 클릭하면 성공합니다.

이 조합은 페이로드와 엔드포인트 모두 문제가 없다는 뜻이므로, 차이는 내용이 아니라 타이밍이나 경로에 있습니다. 다음을 확인하세요.

  • 원래 전송 시점에 있었던 배포나 재시작. 앱이 다시 살아난 뒤라 Resend는 나중에 성공합니다.
  • 플랫폼의 타임아웃을 초과한 콜드 스타트 - 서버리스에서 느린 첫 호출일 때 흔합니다.
  • 두 시도 사이에 바뀐 경로 기반 라우팅, 또는 일부 요청에만 매칭되는 규칙.
  • 여러분의 엣지에 있는 속도 제한이나 봇 방지가 수동 재전송은 무더기가 아니라 단독으로 도착해서 통과시켰을 가능성.

Deliveries 탭에는 두 시도가 타임스탬프와 함께 모두 남아 있으므로, 해당 시각의 여러분 쪽 배포 및 오류 로그와 비교해 보세요.

#서명 검증 실패

거의 항상 다음 세 가지 중 하나입니다.

  1. 재직렬화된 JSON을 해시했습니다. 받은 그대로의 원본 요청 본문 바이트를 HMAC 처리하세요. 파싱 후 다시 문자열로 만들면 공백과 키 순서가 바뀌어 서명이 일치하지 않습니다. 대부분의 프레임워크는 원본 본문을 얻으려면 별도 설정이 필요합니다.
  2. 잘못된 시크릿. 서명 시크릿은 대상별로 다르며, API 키가 아닙니다. 두 개의 대상은 서로 다른 두 개의 시크릿을 가집니다.
  3. 잘못된 인코딩 가정. 시크릿을 문자열 그대로 사용하는지 아니면 먼저 디코딩해야 하는지 확실하지 않다면 추측하지 말고, 서명 검증 참고 자료를 정확히 따르고, 디버깅하는 동안 원본 본문을 로그로 남겨 비교하세요.
Important

서명 불일치를 검증 자체를 건너뛰는 방식으로 "해결"하지 마세요. 검증하지 않는 웹훅 엔드포인트는 URL을 알아낸 누구로부터도 위조된 승인을 받아들이게 되며, 디버깅용 지름길이 계정 탈취 경로로 바뀝니다.

#두 번의 재시도는 큐가 아닙니다

5xx, 404, 타임아웃, 연결 실패가 발생하면 Didit은 두 번 재시도합니다 - 약 1분 후, 그리고 다시 4분 후 - 그 후에는 전송을 포기합니다. 엔드포인트가 그보다 오래 다운되어 있었다면 해당 이벤트는 영구적으로 사라집니다.

재조정 경로를 마련하세요. 시작할 때 아직 최종 상태를 알지 못하는 세션에 대해 결정 엔드포인트를 폴링하세요. 웹훅은 빠른 경로로, 폴링은 대비책으로 다루세요.

#실제 인증 없이 테스트하기

대상 페이지의 Try Webhook은 선택한 종류 - 승인, 거절, 검토 대기, KYB, 엔티티, 트랜잭션 - 의 완전한 형태의 이벤트를 전송합니다. 실제 세션이 여기에 의존하기 전에 엔드포인트, 서명 검증, 핸들러가 제대로 작동하는지 이를 통해 확인하세요.

샌드박스는 이것의 나머지 절반입니다. 샌드박스 세션은 "environment": "sandbox"가 포함된 실제 웹훅을 발생시키므로, 비용 없이 전체 경로를 처음부터 끝까지 시험해 볼 수 있습니다. 샌드박스에서 테스트하기를 참고하세요.