결제가 완료되는 순간 주문 상태를 바꾸고, 깃 저장소에 코드가 푸시되는 즉시 빌드를 돌리고 싶다면 어떻게 해야 할까? 서버에 1초마다 “혹시 무슨 일 생겼어?”라고 계속 물어보는 방식은 낭비가 크다. 대신 사건이 벌어진 쪽이 먼저 알려 주게 하는 것 — 이것이 웹훅의 발상이다. 이 글은 웹훅의 정의와 폴링과의 차이, 동작 원리, 시그니처 검증·재시도 같은 실전 요소, 그리고 GitHub·Stripe·Slack의 실제 사례까지 입문자 눈높이에서 정리한다. 앞서 다룬 REST API란 무엇인가, GraphQL이란 무엇인가, gRPC란 무엇인가 글과 함께 읽으면 API 생태계에서 웹훅이 어디에 자리하는지가 한눈에 잡힐 것이다.
웹훅이란 무엇인가
웹훅(Webhook)은 특정 이벤트가 발생했을 때 서버가 미리 등록된 URL로 데이터를 자동 전송하는 방식이다. 조금 더 풀어 말하면, 어떤 사건이 일어난 순간 그 사실을 실시간으로 다른 애플리케이션에 밀어 넣어 주는 HTTP 기반 통신이다. Red Hat의 정의는 이를 “애플리케이션 간에 HTTP를 통해 데이터를 자동으로 주고받는, 가볍고 이벤트 기반의 통신”이라 설명한다.
흥미로운 별명이 하나 있다. Red Hat은 웹훅을 “역방향 API(reverse API)” 또는 “푸시 API(push API)” 라고 부른다. 통신을 시작하는 책임을 클라이언트가 아니라 서버 쪽에 두기 때문이다. 다만 같은 문서는 “별명과 달리 웹훅은 API가 아니며, 둘은 함께 작동한다. 웹훅을 쓰려면 애플리케이션에 API가 있어야 한다”고 못 박는다. 웹훅은 API를 대체하는 것이 아니라 API의 빈틈 — ‘실시간 통보’ — 을 메우는 보완재인 셈이다.
핵심 흐름은 단순하다. 클라이언트가 서버에 “이런 사건이 생기면 이 주소로 알려 줘”라고 URL 하나를 등록해 두면, 서버는 그 사건이 실제로 벌어졌을 때 해당 주소로 HTTP 요청 한 번을 쏘아 보낸다. Red Hat의 표현대로 “지정한 이벤트가 서버 앱에서 일어나는 즉시 페이로드가 전송”된다.
웹훅 vs 폴링(Polling) — 무엇이 다른가
웹훅을 이해하는 가장 빠른 길은 반대 방식인 폴링(polling) 과 비교하는 것이다. 폴링은 클라이언트가 서버에게 주기적으로 “새 데이터 있어?”라고 반복해서 물어보는 방식이다. 새 메일이 왔는지 확인하려고 우편함을 5분마다 열어 보는 것에 가깝다. 반면 웹훅은 우편배달부가 우편이 도착하는 순간 초인종을 눌러 주는 것에 가깝다.
GitHub 공식 문서는 이 차이를 명확히 정리한다. 웹훅은 “데이터가 준비됐는지 확인하려고 API를 간헐적으로 호출하는 폴링과 달리, 사건이 일어나는 그대로 데이터를 받게 해 준다.” 그러면서 폴링 대비 세 가지 이점을 든다.
- 효율성: 웹훅은 “API를 폴링하는 것보다 더 적은 노력과 자원”을 요구한다. 아무 일도 없는데 헛되이 요청을 던지지 않기 때문이다.
- 확장성: 웹훅은 “API 호출보다 더 잘 확장된다.” 감시할 자원이 많아져도 쓸데없는 반복 호출로 요청 한도(rate limit)를 소진하지 않는다.
- 속도: 웹훅은 “이벤트가 발생할 때 트리거되므로 거의 실시간 업데이트”를 가능하게 한다.
정리하면 아래와 같다.
| 구분 | 웹훅(Webhook) | 폴링(Polling) |
|---|---|---|
| 통신 방향 | 서버 → 클라이언트 (푸시) | 클라이언트 → 서버 (풀) |
| 요청 시점 | 이벤트 발생 시 1회 | 주기적으로 반복 |
| 실시간성 | 거의 즉시 | 폴링 주기만큼 지연 |
| 자원 효율 | 높음 (필요할 때만) | 낮음 (빈 응답 다수) |
| 구현 난이도 | 수신 엔드포인트·검증 필요 | 상대적으로 단순 |
물론 폴링이 항상 나쁜 것은 아니다. 수신 서버를 외부에 공개할 수 없거나, 이벤트가 아주 드물게 발생한다면 단순한 폴링이 오히려 관리하기 편할 수 있다. 선택의 기준은 ‘실시간성이 얼마나 중요한가’와 ‘이벤트 빈도’다.
웹훅은 어떻게 동작하는가
웹훅의 생애 주기는 크게 세 단계로 나뉜다.
- 등록(구독): 데이터를 받고 싶은 쪽이 이벤트를 발생시키는 서비스에 자신의 수신 URL과 관심 이벤트 종류를 등록한다. 예를 들어 “결제 성공(
payment.succeeded) 이벤트가 생기면https://myapp.com/webhooks/stripe로 보내 줘”라고 설정하는 식이다. - 이벤트 발생: 등록해 둔 사건 — 결제 완료, 코드 푸시, 메시지 도착 등 — 이 실제로 일어난다.
- 전송: 서비스가 등록된 URL로 HTTP POST 요청을 보낸다. 요청 본문에는 무슨 일이 일어났는지 담은 페이로드(payload) 가 들어 있다. Stripe 문서는 “Stripe가 HTTPS를 사용해 이벤트를 Event 객체를 포함한 JSON 페이로드로 앱에 전송한다”고 설명한다.
수신 측이 받는 페이로드는 대개 아래처럼 이벤트 종류와 관련 데이터를 담은 JSON이다.
{
"id": "evt_1P2x3y4z",
"type": "payment_intent.succeeded",
"created": 1719878400,
"data": {
"object": {
"id": "pi_3P2x3y4z",
"amount": 15000,
"currency": "krw",
"status": "succeeded"
}
}
}
여기서 실무의 중요한 원칙 하나가 나온다. Stripe는 수신 엔드포인트가 “복잡한 로직을 실행하기 전에 먼저 성공 상태 코드(2xx)를 빠르게 반환”하라고 권한다. 웹훅을 보낸 쪽은 정해진 시간 안에 2xx 응답을 받지 못하면 전송이 실패한 것으로 간주하기 때문이다. 무거운 작업은 일단 응답을 돌려준 뒤 별도 큐에서 비동기로 처리하는 것이 정석이다.
웹훅 보안 — 시그니처 검증과 재시도
웹훅 URL은 인터넷에 열려 있는 공개 엔드포인트다. 누구든 그 주소로 가짜 요청을 보낼 수 있다는 뜻이다. 그래서 “이 요청이 정말 그 서비스가 보낸 것인가”를 확인하는 시그니처 검증(signature verification) 이 필수다.
가장 널리 쓰이는 방식은 HMAC(해시 기반 메시지 인증 코드) 다. 발신 측과 수신 측이 공유하는 비밀 키(signing secret)로 페이로드의 해시값을 계산해 서명으로 붙이고, 수신 측이 같은 키로 다시 계산해 서명이 일치하는지 대조한다. Stripe는 “HMAC과 SHA-256을 사용해 서명을 생성”하며, 이를 Stripe-Signature라는 HTTP 헤더에 실어 보낸다. GitHub의 배송 검증 문서도 동일하게 시크릿 토큰과 SHA-256 HMAC으로 요청의 진위를 확인하는 절차를 안내한다.
Node.js에서 Stripe 방식의 서명을 검증하는 개념 예시는 다음과 같다.
const crypto = require("crypto");
function verifySignature(payload, header, secret) {
const [t, sig] = parseStripeHeader(header); // t=타임스탬프, v1=서명
const signedPayload = `${t}.${payload}`;
const expected = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");
// 타이밍 공격 방지를 위해 상수 시간 비교
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
여기에 더해 재전송 공격(replay attack) 방어도 중요하다. Stripe는 서명에 타임스탬프를 포함시키고 “기본적으로 타임스탬프와 현재 시각 사이에 5분의 허용 오차”만 인정한다. 오래된 요청을 가로채 그대로 다시 보내도 통하지 않게 하는 장치다.
한편 웹훅은 네트워크 특성상 전송이 실패할 수 있다. 수신 서버가 잠시 죽어 있거나 응답이 느리면 요청이 유실된다. 그래서 성숙한 서비스는 재시도(retry) 정책을 둔다. Stripe는 “라이브 모드에서 최대 3일 동안 지수 백오프(exponential backoff) 방식으로 이벤트 전송을 재시도”하며, 대시보드나 CLI로 수동 재전송하는 기능도 제공한다. 수신 측은 이 재시도 때문에 같은 이벤트를 두 번 받을 수 있으므로, 이벤트 ID를 기록해 중복 처리를 막는 멱등성(idempotency) 설계를 갖춰야 한다.
실제 사례 — GitHub·Stripe·Slack
웹훅은 개념만이 아니라 오늘날 개발 현장의 접착제 역할을 한다.
- GitHub: 저장소에 push, pull request 생성, 이슈 코멘트 같은 이벤트가 일어날 때 지정한 URL로 페이로드를 보낸다. CI/CD 파이프라인이 “코드가 올라오면 자동으로 테스트·빌드·배포”를 트리거하는 것이 대표적이다.
- Stripe: 결제 성공, 구독 갱신, 환불, 분쟁 같은 비동기 결제 이벤트를 웹훅으로 통보한다. 카드사 승인이 몇 초 뒤에 떨어지는 결제 흐름에서, 폴링 없이 결과를 받아 주문 상태를 갱신할 수 있다.
- Slack: Incoming Webhooks를 통해 외부 시스템이 특정 채널로 메시지를 밀어 넣는다. 배포 알림, 장애 경보, 모니터링 리포트를 슬랙 채널에 자동으로 꽂아 넣는 봇 대부분이 이 방식이다.
세 사례의 공통점은 분명하다. “사건이 벌어지면 즉시 반응해야 하는” 자동화의 출발점마다 웹훅이 놓여 있다. Zapier의 설명처럼, 웹훅은 서로 다른 앱을 실시간으로 잇는 가장 가벼운 연결 고리다.
웹훅의 한계와 단점
균형을 위해 단점도 분명히 짚어야 한다. 웹훅이 모든 실시간 통신의 정답은 아니다.
- 수신 엔드포인트가 공개되어야 한다: 웹훅을 받으려면 인터넷에서 접근 가능한 URL이 필요하다. 로컬 개발 환경에서는 ngrok 같은 터널링 도구가 있어야 테스트할 수 있어 초기 설정이 번거롭다.
- 유실과 순서 보장 문제: 네트워크 장애로 요청이 사라질 수 있고, 여러 이벤트가 도착 순서를 그대로 지킨다는 보장이 없다. 재시도 정책과 멱등성 설계로 보완해야 한다.
- 디버깅이 까다롭다: 클라이언트가 먼저 호출하는 것이 아니라 서버가 보내 주는 요청을 기다리는 구조라, 문제가 생겼을 때 재현과 추적이 폴링보다 어렵다.
- 보안 부담: 공개 엔드포인트인 만큼 시그니처 검증을 빠뜨리면 위조 요청에 그대로 노출된다. 보안이 선택이 아니라 필수인 이유다.
정리하면, 웹훅이 주는 실시간성과 효율의 대가로 인프라·보안·안정성 측면의 책임이 수신 측에 얹힌다. “이 통신이 정말 즉각적인 반응을 요구하는가?”를 먼저 물어야 하는 이유다.
웹훅, 언제 써야 하나
정리하면, 웹훅은 이벤트가 발생하는 즉시 서버가 등록된 URL로 데이터를 밀어 보내는 ‘역방향 API’다. 폴링의 낭비 없이 거의 실시간으로 사건을 통보받을 수 있다는 것이 핵심 강점이고, 그 대가로 공개 엔드포인트·시그니처 검증·재시도 대응이라는 숙제가 따라온다.
그렇다면 언제 웹훅을 골라야 할까? 결제 완료·코드 푸시·메시지 도착처럼 사건에 즉각 반응해야 하고, 그 사건이 예측 불가능한 시점에 발생한다면 웹훅이 제격이다. 반대로 이벤트가 아주 드물거나, 수신 서버를 외부에 공개하기 어렵거나, 데이터를 원하는 시점에 직접 조회하는 편이 자연스럽다면 REST API를 주기적으로 호출하는 폴링이 더 단순한 답일 수 있다. 실제로 많은 서비스가 평소엔 웹훅으로 실시간 알림을 받되, 유실에 대비해 API 폴링을 안전망으로 병행한다.
여러분이 지금 설계 중인 연동은 즉각적인 반응이 생명인가, 아니면 원하는 때 조회하면 충분한가? 도구의 유행이 아니라 통신의 성격을 기준으로 고르는 것이 핵심이다. 기술 환경과 서비스 요구에 따라 최적의 선택은 달라질 수 있으니, 본 정리는 판단의 출발점으로 활용하길 권한다.
