웹훅 연동 방법은 상대가 폴링으로 상태를 묻기 전에 이벤트가 난 순간 HTTP로 알리는 절차입니다. 결제 완료, 배포 성공, 모델 작업 종료처럼 시점이 중요한 자동화에 잘 맞습니다. 지연을 줄이려면 구독 범위와 실패 처리를 처음부터 같이 정하는 것이 좋습니다.
아래에서는 수신 URL과 시크릿 키를 먼저 맞춘 뒤 발신 페이로드와 수신 엔드포인트, 서명 검증과 재시도·멱등성 순으로 살펴봅니다. GitHub과 Stripe 규칙을 기준으로 실무에서 자주 빠지는 검증 순서를 확인합니다. 마지막에는 운영에서 바로 점검할 항목만 모아 정리합니다.
웹훅이란 무엇이고 언제 쓰나

웹훅은 공급자가 지정한 웹훅 수신 URL로 POST를 보내 이벤트 페이로드를 전달하는 방식입니다. 클라이언트가 주기적으로 조회하지 않아도 되므로 생성형 AI 작업 완료나 구독 변경처럼 빈도가 불규칙한 알림에 유리합니다. 같은 이유로 채팅 완료 콜백이나 배치 추론 종료 통지에도 자주 쓰입니다.
반대로 상대가 공개 URL을 제공할 수 없거나 방화벽이 인바운드를 막으면 폴링이 더 단순합니다. 연동을 고를 때는 지연 허용 범위와 실패 시 재처리 비용을 함께 봅니다. 초 단위 알림이 필요하면 웹훅을 고르고 수분 지연이 괜찮으면 조회 API를 검토하면 됩니다. 내부망 워커만 있는 경우에는 아웃바운드 발신이 더 맞습니다.
웹훅 연동 전 준비 사항
(출처: Dr.M)
준비의 핵심은 웹훅 수신 URL, 웹훅 시크릿 키, 허용 이벤트 목록입니다. URL은 HTTPS만 쓰고 경로에 추측하기 쉬운 계정 식별자를 넣지 않는 편이 안전합니다. 스테이징과 운영 URL을 나누면 시험 이벤트가 실서비스 큐에 섞이지 않습니다. 토큰 순환 일정도 미리 적어 두면 만료 때 수신이 끊기지 않습니다.
시크릿은 대시보드 값과 CLI 값이 다를 수 있으니 엔드포인트마다 따로 보관합니다. Stripe도 엔드포인트 시크릿이 환경마다 다르다고 안내합니다. 로컬에서 터널로 시험할 때도 운영 시크릿을 재사용하지 마십시오. 시크릿 읽기 권한은 배포 담당자로 제한합니다.
발신 웹훅 구현 절차
발신 측은 구독한 이벤트만 보내고 본문은 JSON으로 고정하는 것이 일반적입니다. 타임아웃을 짧게 두고 2xx만 성공으로 본 뒤 그 외 응답은 재시도 큐에 넣습니다. 헤더에는 이벤트 타입과 요청 ID를 함께 실어 수신 로그와 맞추기 쉽게 합니다.
상대가 늦게 처리하더라도 발신 로그에는 요청 ID와 HTTP 상태를 남겨 추적이 가능하게 합니다. 페이로드 크기를 제한하면 수신 측 타임아웃도 줄어듭니다. Content-Type은 application/json으로 통일하고 압축은 수신 측이 받을 수 있을 때만 켭니다. 실패 응답 본문도 짧게 남겨 원인 분류에 쓰게 합니다.
이벤트 선택과 페이로드 설계
이벤트는 구독자가 실제로 분기할 단위만 고릅니다. 너무 잘게 쪼개면 구독 설정이 늘고 너무 굵으면 수신 측이 다시 필터해야 합니다. 작업 완료, 실패, 취소처럼 상태가 갈리는 지점을 우선합니다. 공급자 목록에서 실제 자동화에 쓰는 항목만 구독합니다.
이벤트 페이로드에는 이벤트 타입, 발생 시각, 리소스 ID, 변경 요약이 들어가면 수신 측이 추가 조회 없이 처리할 수 있습니다. 비밀값이나 전체 프롬프트처럼 재유출되면 위험한 필드는 빼거나 마스킹합니다. 스키마 버전 필드를 두면 이후 필드 추가가 수월합니다. 필드 이름은 한 번 정한 뒤 바꾸지 않는 것이 호환에 유리합니다.
재시도 정책 설정
네트워크 오류와 5xx에는 지수 백오프로 웹훅 재시도를 적용합니다. 4xx 중 인증 실패는 시크릿 교체 전까지 중단하는 편이 낫고 404는 구독 해제로 볼 수 있습니다. 429는 짧은 대기 후 다시 보내는 편이 안전합니다. 2xx가 늦으면 발신 측이 중복 전송을 시작할 수 있습니다.
재시도마다 같은 이벤트 ID를 유지하면 수신 측 멱등성 키가 안정됩니다. 최대 횟수와 최종 실패 알림 채널도 함께 정합니다. 재시도 간격을 문서에 밝혀 두면 수신 측 처리 시간과 맞추기 쉽습니다. 같은 본문을 무한히 보내지 않도록 실패 보관 큐를 둡니다.
수신 웹훅 엔드포인트 설계
수신 엔드포인트는 POST JSON만 받고 서명 확인 전에는 본문을 업무 로직에 넣지 않습니다. Stripe는 타임아웃을 피하려고 복잡한 처리 전에 2xx를 빠르게 반환하라고 권합니다. 검증에 실패하면 4xx로 거절해 발신 측이 재시도 여부를 판단하게 합니다. 원문 바디는 한 번만 읽고 검증과 적재에 같은 버퍼를 씁니다.
검증과 큐 적재만 동기 구간에서 끝내고 모델 호출이나 메일 발송은 비동기로 넘깁니다. 응답 본문은 짧게 두고 상태 코드로 성공 여부를 알립니다. 경로를 이벤트 종류마다 나누기보다 한 URL에서 타입 필드로 분기하는 구성이 운영이 단순합니다. 수신 로그에는 원문 일부가 아니라 해시와 이벤트 ID만 남깁니다.
서명 검증과 재시도·멱등성
서명은 시크릿과 원문 바디로 계산한 값이 헤더와 같을 때만 수락합니다. 재시도가 오면 같은 이벤트라도 네트워크상 중복이 생기므로 멱등성 키로 이미 처리한 ID는 건너뜁니다. 키는 이벤트 ID를 그대로 쓰거나 공급자명과 ID를 이어 붙여 만듭니다. 처리 중 재진입을 막으려면 키 적재를 원자적으로 수행합니다.
Stripe-Signature에는 서명된 타임스탬프가 있어 재전송을 완화하고 재시도 시 새 서명과 타임스탬프가 만들어집니다. 허용 오차를 넘긴 시각은 거절합니다. 검증 순서는 서명, 시각, 중복 확인 다음 업무 처리로 고정하는 것이 안전합니다. 창 비교는 서버 수신 시각이 아니라 헤더 타임스탬프를 기준으로 합니다.
HMAC 서명 확인
GitHub은 시크릿으로 페이로드 HMAC-SHA256 서명을 만들고 X-Hub-Signature-256에 sha256= 접두사와 함께 보냅니다. 검증은 원문으로 hex digest를 계산한 뒤 상수 시간 비교로 헤더와 맞춰야 하며 시크릿이 없으면 해당 헤더가 없습니다. 파싱한 JSON이 아니라 수신 원문으로 계산해야 공백 차이로 실패하지 않습니다.
신규 구현은 HMAC-SHA256을 쓰고 HMAC-SHA1의 X-Hub-Signature는 레거시 호환용입니다. Stripe는 Stripe-Signature와 whsec_ 시크릿으로 constructEvent에 바디를 넘깁니다. Dashboard 시크릿과 CLI 시크릿은 서로 다르므로 환경을 섞지 마십시오.
| 구분 | GitHub | Stripe |
|---|---|---|
| 서명 헤더 | X-Hub-Signature-256 | Stripe-Signature |
| 시크릿 형태 | 웹훅 시크릿 토큰 | whsec_ 엔드포인트 시크릿 |
| 권장 알고리즘 | HMAC-SHA256 | 타임스탬프 포함 서명 |
| 수신 응답 | 검증 후 2xx | 복잡한 처리 전 2xx |
웹훅 연동 실무 정리
연동을 안정적으로 끝내려면 수신 URL과 시크릿을 엔드포인트 단위로 분리하고 발신은 짧은 타임아웃과 재시도 한도를 문서화하면 됩니다. 수신은 서명 검증, 타임스탬프 창, 멱등성 키 순서를 고정한 뒤 2xx를 먼저 돌려보냅니다. 이 세 단계를 빠뜨리면 재전송과 중복 처리가 바로 생깁니다. 구독 목록은 분기하지 않는 이벤트부터 줄여 트래픽을 낮춥니다.
GitHub과 Stripe처럼 헤더 이름과 시크릿 출처가 다르므로 대시보드 값과 CLI 값을 섞지 마십시오. 로그에는 페이로드 전체가 아니라 이벤트 ID와 검증 결과만 남기는 것이 안전합니다. 운영 점검 때는 실패 큐 적체와 서명 실패 비율을 함께 봅니다. 시크릿 순환 뒤에는 이전 키를 잠시 받아 주는 기간을 두면 배포 공백이 줄어듭니다.