개발·연동
웹훅이란?
Webhook · HTTP callback · 이벤트 콜백 · 웹 훅
웹훅은 한 시스템에서 특정 이벤트가 발생했을 때, 등록된 URL로 HTTP POST 요청을 자동 전송해 다른 시스템에 알리는 연동 방식입니다. 받는 쪽이 주기적으로 '새 이벤트 있나요?'를 묻는 대신, 보내는 쪽이 이벤트가 생긴 순간 직접 푸시한다는 점이 차이입니다. 영어권 자료에서는 HTTP callback이라는 표현도 같이 쓰이고, Stripe·GitHub·Shopify 같은 대표 서비스가 모두 같은 패턴을 표준으로 채택하고 있습니다.
AI 전화와 AICC 환경에서는 통화 종료, 통화 요약 생성, 상담원 연결, 발신 실패 같은 이벤트가 웹훅의 단골 대상입니다. 외부 CRM, 운영 대시보드, 사내 알림 도구에 통화 데이터를 이어 붙일 때 자주 쓰입니다.

API와의 차이 — pull과 push의 비대칭
API 연동이 사용자가 필요한 시점에 데이터를 요청하는 pull 방식이라면, 웹훅은 시스템이 이벤트가 생기는 순간 데이터를 보내는 push 방식입니다. 이 비대칭 때문에 두 방식은 같은 문제를 다른 각도에서 풉니다. 정기 동기화나 복잡한 쿼리·페이지네이션·관계 탐색이 필요할 때는 API가 적합하고, 분 단위 이내의 실시간 반응이나 통화 이벤트 같은 비결정적 트리거에는 웹훅이 더 가볍습니다.
실제 시스템에서는 둘을 함께 쓰는 '훅 앤 페치(hook-and-fetch)' 패턴이 흔합니다. 웹훅 페이로드는 흔히 얇게 — 이벤트 ID와 타입 정도만 — 만들어 두고, 받는 쪽이 그 ID로 다시 API를 호출해 최신 상태를 가져옵니다. 이렇게 하면 페이로드의 stale 위험을 줄이면서 실시간성을 유지할 수 있고, 보안상 민감한 정보는 페이로드 안에 담지 않을 수 있습니다.
웹훅에는 분명한 한계도 있습니다. 단방향 알림이라는 점입니다. 외부 시스템에 무언가를 만들거나 갱신하려면 결국 API 호출이 필요합니다. 그래서 웹훅은 API를 대체하지 못합니다. API에 실시간 트리거를 더하는 보조 채널로 보면 됩니다.
At-least-once 전제 — 재시도, 중복, idempotency
산업 표준에서는 웹훅 전송을 at-least-once delivery로 정의합니다. 받는 쪽이 2xx 응답을 돌려주지 않으면 보내는 쪽은 같은 이벤트를 다시 보내고, 그 결과 같은 이벤트를 두 번 이상 받는 일이 생깁니다. 네트워크 끊김, 일시적 5xx, 타임아웃 모두 재시도의 원인이 됩니다. 따라서 받는 쪽 핸들러는 같은 이벤트를 두 번 처리해도 한 번 처리한 것과 같은 결과가 되도록(idempotent) 짜야 합니다.
재시도는 보통 exponential backoff + jitter 방식입니다. 즉시 → 30초 → 2분 → 8분 → 30분 → 2시간 → 8시간 → 24시간 같은 식으로 간격을 늘리고, 동시에 실패한 이벤트가 한꺼번에 다시 시도되지 않도록 무작위 지터를 더합니다. 5–8회 정도 시도 뒤에도 실패하면 dead letter queue(DLQ)로 보내, 운영자가 원인 파악 후 수동 또는 자동 replay하는 패턴이 표준입니다.
받는 쪽 idempotency는 보통 이벤트 ID 기반입니다. 각 이벤트마다 unique한 ID(예: `evt_abc123`)가 헤더 또는 페이로드로 같이 오므로, 받는 쪽은 Redis 또는 DB에 처리한 ID 집합을 저장하고 들어온 이벤트가 그 집합에 이미 있으면 그냥 200을 돌려주고 처리는 건너뜁니다. TTL은 보낸 쪽의 최대 재시도 윈도(보통 24~72시간)보다 길게 잡습니다.
서명 검증과 replay 방어
웹훅 엔드포인트는 인터넷에서 접근 가능한 공개 URL입니다. 서명 검증이 없으면 누구나 그 URL로 가짜 페이로드를 POST해 시스템에 거짓 이벤트를 흘릴 수 있습니다. 표준 방어는 HMAC-SHA256 서명입니다. 보내는 쪽은 공유 비밀키로 페이로드를 해싱해 헤더(`X-Hub-Signature-256`, `Stripe-Signature` 등 제공자별 이름)에 같이 보내고, 받는 쪽이 같은 비밀키로 다시 해싱해 일치 여부를 비교합니다.
구현 시 자주 빠뜨리는 디테일이 둘 있습니다. 첫째, 서명 검증은 원본 바이트(raw body)에 대해 해야 합니다. 프레임워크가 JSON을 파싱한 뒤 다시 직렬화한 문자열은 공백·키 순서 차이로 다른 해시가 나옵니다. 둘째, 비교에는 timing-safe 함수(`crypto.timingSafeEqual`, `hmac.compare_digest`)를 써야 합니다. 일반 문자열 등치 연산은 타이밍 사이드 채널을 그대로 노출합니다.
서명만으로는 replay 공격을 막을 수 없습니다. 공격자가 합법적 서명이 붙은 이벤트를 가로채 나중에 다시 보내면 검증은 통과합니다. Stripe·Coinbase 같은 제공자는 서명 페이로드 안에 timestamp(`t=`)를 포함해 같이 해싱하므로, 받는 쪽은 timestamp가 보통 5분 이내인지 확인하고 그보다 오래되면 거절합니다. 더 강한 방어가 필요하면 이벤트 ID를 nonce로 캐시해 윈도 안 재전송도 차단합니다.
더 보기
vox 웹훅 설정과 서명 검증
엔드포인트 등록·이벤트 종류·서명 키 발급
받는 쪽 운영 — 빠른 200, 비동기 처리, DLQ
받는 쪽 핸들러가 가장 먼저 지켜야 할 규칙은 빠른 200 응답입니다. 대부분의 제공자는 5~30초 안에 응답이 없으면 실패로 간주해 재시도합니다(Shopify 5초, GitHub 10초, Stripe 30초). 핸들러 안에서 DB 쓰기와 외부 API 호출을 동기로 다 처리하려 들면 응답 시간이 늘어나며 timeout과 중복 처리 위험이 함께 커집니다. 표준 패턴은 서명 검증 → 페이로드를 durable 큐에 적재 → 즉시 200 반환 → 백그라운드 워커가 비동기 처리입니다.
큐에 들어간 이벤트는 워커가 자체 retry 로직(짧은 transient 재시도 → exponential backoff → DLQ)으로 처리합니다. 같은 큐를 통화 분석, CRM 업데이트, SMS 발송처럼 여러 다운스트림이 fan-out으로 소비하면 한 다운스트림 장애가 다른 다운스트림을 막지 않습니다. 운영 대시보드에서 ingestion latency, 처리 성공률, DLQ 깊이, 서명 실패 건수를 함께 모니터링하는 것이 일반적입니다.
AI 전화 운영에서는 통화 종료, 상담원 연결, 발신 결과 같은 이벤트가 그대로 외부 시스템(CRM, 사내 운영 도구, 인바운드 콜·아웃바운드 콜 캠페인 도구)으로 흘러가는데, 페이로드에 개인정보가 섞일 수 있으므로 개인정보 마스킹 정책과 함께 설계해야 합니다.
