웹훅 완벽 가이드: 서명된 페이로드와 안정적인 재시도
서드파티 결제 게이트웨이나 CI/CD 서비스를 막 통합했고, 이제 실시간 알림이 필요하신가요? 웹훅이 바로 그 답입니다. 하지만 적절한 보안과 안정성 없이는 오히려 골칫거리가 될 수 있습니다. 공격자는 요청을 위조할 수 있고, 네트워크 장애는 이벤트 누락을 초래할 수 있습니다. 이 가이드에서는 서명된 페이로드로 웹훅을 보호하고, 견고한 재시도 메커니즘을 구현하는 방법을 살펴보겠습니다.
웹훅이란?
웹훅은 특정 이벤트에 의해 트리거되는 사용자 정의 HTTP 콜백입니다. 이벤트가 발생하면(예: 결제 성공) 소스 서비스가 이벤트 데이터를 담아 사용자가 제공한 URL로 HTTP POST 요청을 보냅니다. 이 푸시 모델은 폴링보다 효율적이며 실시간 반응을 가능하게 합니다.
그러나 웹훅은 두 가지 과제를 안고 있습니다. 바로 진위성(요청이 예상된 발신자로부터 왔는지 확인)과 안정성(이벤트를 놓치지 않도록 보장)입니다.
서명된 페이로드로 웹훅 보호하기
서명된 페이로드는 발신자와 수신자 간에 공유된 시크릿을 사용해 암호학적 서명을 생성합니다. 발신자는 이 서명을 헤더에 포함하고, 수신자는 이를 검증하여 페이로드의 무결성과 출처를 확인합니다.
HMAC 서명 작동 방식
HMAC(Hash-based Message Authentication Code)이 표준 접근 방식입니다. 발신자는 공유 시크릿을 사용해 요청 본문에 대한 HMAC을 계산하고, 이를 X-Signature 같은 헤더에 포함합니다. 수신자는 HMAC을 다시 계산해 비교합니다.
// 예시: Node.js에서 서명 생성하기
const crypto = require('crypto');
function generateSignature(payload, secret) {
return crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
}
// 수신 측에서 검증:
function verifySignature(payload, secret, receivedSignature) {
const expected = generateSignature(payload, secret);
// 타이밍 공격을 방지하기 위해 타이밍 안전 비교 사용
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
핵심 포인트:
- 웹훅 엔드포인트마다 강력하고 고유한 시크릿을 사용하세요.
- 정보 누출을 피하기 위해 타이밍 안전 함수로 서명을 비교하세요.
- 재전송 공격을 방지하기 위해 서명에 타임스탬프를 포함하세요(예:
timestamp + payload서명).
추가 보안 조치
- HTTPS: 전송 중 데이터를 암호화하려면 항상 HTTPS를 사용하세요.
- IP 허용 목록: 제공자가 IP 범위를 공개한다면, 해당 IP로 들어오는 요청을 제한하세요.
- 페이로드 크기 제한: DoS를 완화하기 위해 지나치게 큰 페이로드는 거부하세요.
안정적인 재시도 구현하기
네트워크는 불안정합니다. 웹훅은 타임아웃, 서버 오류 또는 일시적 장애로 인해 실패할 수 있습니다. 견고한 재시도 전략은 시스템이나 발신자에 과부하를 주지 않으면서 최종 전달을 보장합니다.
재시도 모범 사례
- 지수 백오프: 재시도 간 지연을 늘리세요(예: 1초, 2초, 4초, 8초...).
- 최대 재시도 횟수: 무한 루프를 피하기 위해 시도 횟수를 제한하세요(예: 5회).
- 데드 레터 큐: 재시도를 모두 소진한 후 실패한 이벤트를 저장해 수동으로 검사하세요.
- 멱등성: 동일한 이벤트를 여러 번 처리해도 중복 부작용이 발생하지 않도록 보장하세요.
실전 멱등성
페이로드에 고유한 이벤트 ID를 포함하세요. 수신 측에서는 처리된 ID를 (데이터베이스나 캐시에) 추적하고 중복을 무시하세요. 예를 들면:
// 멱등 처리를 위한 의사 코드
if (eventStore.exists(event.id)) {
return 200; // 이미 처리됨
}
// 이벤트 처리...
eventStore.save(event.id);
실패를 우아하게 처리하기
수신을 확인하기 위해 빠르게 2xx 상태로 응답하세요. 처리가 오래 걸린다면 이벤트를 큐에 넣고 비동기적으로 처리하세요. 재시도가 필요한 일시적 오류에만 5xx를 반환하고, 영구적 오류(예: 잘못된 페이로드)의 경우 무의미한 재시도를 막기 위해 4xx를 반환하세요.
비교: 푸시 vs. 폴링
| 측면 | 웹훅(푸시) | 폴링 |
|---|---|---|
| 지연 시간 | 거의 실시간 | 간격에 따라 다름 |
| 효율성 | 이벤트 발생 시에만 전송 | 지속적인 요청 |
| 복잡성 | 재시도/보안 로직 필요 | 단순하지만 낭비적 |
웹훅 구현 테스트하기
ngrok 같은 도구를 사용해 로컬 서버를 노출하고 웹훅 전달을 시뮬레이션하세요. 의도적으로 요청을 실패시켜 서명 검증과 재시도 동작을 확인하세요.
FAQ
서명된 웹훅 페이로드란 무엇인가요?
서명된 페이로드는 암호학적 서명(주로 HMAC)을 포함하며, 수신자가 공유 시크릿을 사용해 요청의 진위성과 무결성을 검증할 수 있게 해줍니다.
실패한 웹훅을 몇 번이나 재시도해야 하나요?
보편적인 횟수는 없지만, 지수 백오프를 사용한 3~5회 재시도가 일반적입니다. 그 이후에는 수동 검토를 위해 실패를 기록하세요.
HTTPS 없이 웹훅을 사용할 수 있나요?
기술적으로는 가능하지만 매우 안전하지 않습니다. 도청과 중간자 공격을 방지하려면 항상 HTTPS를 사용하세요.
웹훅 페이로드를 검사할 준비가 되셨나요? JSON formatter를 사용해 JSON 페이로드를 빠르게 예쁘게 출력하고 검증해 보세요.