웹훅
웹훅을 사용하면 LabelGrid 계정에서 이벤트가 발생할 때 실시간 알림을 받을 수 있습니다. 웹훅으로 워크플로를 자동화하고 외부 시스템과 연동하세요.
개발자용: API를 통해 웹훅을 프로그래밍 방식으로 관리할 수도 있습니다. 엔드포인트와 예시는 LabelGrid API 문서를 참고하세요.
웹훅 작동 방식
섹션 제목: “웹훅 작동 방식”- 웹훅을 설정합니다 - URL과 수신할 이벤트를 지정합니다
- 이벤트가 발생합니다 - 예를 들어 릴리스가 매장에 배포됩니다
- LabelGrid이 POST 요청을 보냅니다 - 서버가 이벤트 데이터를 받습니다
- 시스템이 이를 처리합니다 - 이벤트를 기반으로 워크플로를 자동화합니다
웹훅 접근하기
섹션 제목: “웹훅 접근하기”- 오른쪽 위 모서리의 프로필 아이콘을 클릭합니다
- 드롭다운 메뉴에서 Webhooks를 선택합니다
웹훅 만들기
섹션 제목: “웹훅 만들기”- Create Webhook을 클릭합니다
- 이 웹훅을 식별할 Name을 입력합니다
- 알림을 받을 URL을 입력합니다
- 이 웹훅을 트리거할 Events를 선택합니다
- Create를 클릭합니다
웹훅 시크릿
섹션 제목: “웹훅 시크릿”웹훅을 만들면 시크릿 키를 받게 됩니다. 들어오는 요청이 실제로 LabelGrid에서 온 것인지 확인하는 데 사용하세요.
- 시크릿을 안전하게 보관하세요
- 들어오는 요청의 서명을 검증하세요
- 노출되면 시크릿을 재발급하세요
사용 가능한 이벤트
섹션 제목: “사용 가능한 이벤트”웹훅이 다음 이벤트를 수신하도록 설정하세요. **이벤트 식별자(Event Identifier)**는 페이로드의 event 속성과 X-Webhook-Event 헤더에 표시되는 값입니다.
| 이벤트 식별자 | 설명 |
|---|---|
delivery.completed | 릴리스가 매장에 성공적으로 배포되었을 때 발생 |
delivery.failed | 매장 배포가 실패했을 때 발생 |
takedown.completed | 테이크다운 요청이 완료되었을 때 발생 |
release.review.status_changed | 릴리스 검수 상태가 바뀌었을 때 발생 |
release.preflight.report_ready | 대기 중인 릴리스의 Preflight QC 리포트를 가져올 수 있게 되었을 때 발생 |
stream_radar.flag_created | Stream Radar 플래그가 발생하거나, 해결된 플래그가 새로운 감지로 다시 열렸을 때 발생 |
stream_radar.flag_resolved | 감지가 멈춰 Stream Radar 플래그가 해결되었을 때 발생 |
release.distributed | 릴리스가 배포되었을 때 발생 |
payment.statement_ready | 정산서를 확인할 수 있게 되었을 때 발생 |
transcode.completed | 트랙의 오디오 트랜스코딩이 성공적으로 완료되었을 때 발생 |
transcode.failed | 트랙의 트랜스코딩이 실패하거나 미완료로 끝났을 때 발생 |
distribution.outlet.status_changed | 매장별 배포 상태가 전환될 때마다 발생 |
하나의 웹훅에 여러 이벤트를 선택할 수도 있고, 이벤트 유형별로 웹훅을 따로 만들 수도 있습니다.
이 목록은 프로그래밍 방식으로도 읽을 수 있습니다. GET /api/public/웹훅/event-types는 모든 이벤트를 해당 페이로드의 키와 타입을 설명하는 data 스키마와 함께 반환하므로, 스키마를 엄격하게 검증하는 소비자는 수신 유효성 검사를 미리 넓혀 둘 수 있습니다.
웹훅 관리
섹션 제목: “웹훅 관리”웹훅 목록 보기
섹션 제목: “웹훅 목록 보기”웹훅 목록에는 다음이 표시됩니다.
| 열 | 설명 |
|---|---|
| Name | 지정한 웹훅 이름 |
| URL | 알림이 전송되는 위치 |
| Events | 설정된 이벤트 수 |
| Status | 활성 또는 비활성 |
| Success / Fail | 성공 및 실패한 전송 횟수 |
| Last Triggered | 웹훅이 마지막으로 호출된 시점 |
웹훅 수정하기
섹션 제목: “웹훅 수정하기”- 웹훅 행에서 Edit 동작을 클릭합니다
- 이름, URL 또는 이벤트를 수정합니다
- Save를 클릭합니다
활성화 / 비활성화
섹션 제목: “활성화 / 비활성화”웹훅을 삭제하지 않고 활성 상태만 전환합니다.
- Active - 웹훅이 알림을 받습니다
- Inactive - 웹훅이 일시 중지되어 알림을 받지 않습니다
웹훅 삭제하기
섹션 제목: “웹훅 삭제하기”- 웹훅 행에서 Delete 동작을 클릭합니다
- 삭제를 확인합니다
웹훅 테스트하기
섹션 제목: “웹훅 테스트하기”프로덕션에서 웹훅에 의존하기 전에 먼저 테스트하세요.
- 웹훅에서 Test 동작을 클릭합니다
- LabelGrid이 지정한 URL로 테스트 페이로드를 보냅니다
- 엔드포인트가 이를 제대로 받아 처리하는지 확인합니다
웹훅 로그 보기
섹션 제목: “웹훅 로그 보기”웹훅 활동을 모니터링하고 문제를 진단하세요.
- 웹훅에서 View Logs 동작을 클릭합니다
- 모든 웹훅 전송 기록을 확인합니다
로그 세부 정보
섹션 제목: “로그 세부 정보”각 로그 항목에는 다음이 표시됩니다.
| 필드 | 설명 |
|---|---|
| Event Type | 이 전송을 트리거한 이벤트 |
| Response Status | 서버에서 반환한 HTTP 상태 코드 |
| Duration | 요청에 걸린 시간 |
| Attempt | 재시도 횟수 |
| Timestamp | 전송이 발생한 시점 |
웹훅 페이로드 형식
섹션 제목: “웹훅 페이로드 형식”이벤트가 발생하면 LabelGrid이 지정한 URL로 JSON 페이로드를 담은 POST 요청을 보냅니다.
{ "event": "delivery.completed", "timestamp": "2026-05-05T10:00:00+00:00", "webhook_id": "123", "data": { // Event-specific data }}timestamp 필드는 ISO 8601 형식을 사용합니다. webhook_id는 설정한 웹훅의 ID이며 X-Webhook-Id 헤더와 일치합니다.
이벤트 페이로드
섹션 제목: “이벤트 페이로드”data 객체의 구조는 event 유형에 따라 달라집니다. 아래의 모든 필드 타입은 페이로드에 직렬화된 형태의 JSON 타입입니다.
delivery.completed
섹션 제목: “delivery.completed”릴리스 배포가 매장별로 최종 성공 상태에 도달하면 매장당 한 번씩 발생합니다.
{ "event": "delivery.completed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "label_id": 321, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "status": "complete" }}| 필드 | 타입 | 설명 |
|---|---|---|
distro_queue_id | integer | 이 배포 시도의 내부 큐 ID |
release_id | integer | 배포된 릴리스 |
label_id | integer | 릴리스가 속한 레이블. 추가 조회 없이 이벤트를 라우팅할 수 있습니다 |
release_cat | string | null | 릴리스 카탈로그 참조 번호 |
outlet_id | integer | null | 대상 매장 ID |
outlet_name | string | null | 사람이 읽을 수 있는 매장 이름(예: "Spotify") |
status | string | 이 이벤트에서는 항상 "complete" |
delivery.failed
섹션 제목: “delivery.failed”릴리스 배포가 매장별로 최종 실패 상태에 도달하면 매장당 한 번씩 발생합니다. 페이로드는 delivery.completed와 같으며 message 필드가 추가됩니다.
{ "event": "delivery.failed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "label_id": 321, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "status": "error", "message": "Outlet rejected the delivery: missing ISRC." }}| 필드 | 타입 | 설명 |
|---|---|---|
status | string | error, fault, rejected, batch_exception 중 하나 |
message | string | null | 매장 또는 배포 파이프라인에서 전달한 실패 사유 |
takedown.completed
섹션 제목: “takedown.completed”테이크다운 요청이 매장별로 성공하면 매장당 한 번씩 발생합니다. 형태는 delivery.completed와 같으며 takedown: true 플래그가 추가됩니다.
{ "event": "takedown.completed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "label_id": 321, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "status": "complete", "takedown": true }}release.distributed
섹션 제목: “release.distributed”릴리스가 distributed 배포 상태로 전환될 때 릴리스당 한 번 발생합니다. distributed로 전환되는 순간에만 발생하며, 이미 배포된 릴리스를 이후에 다시 저장할 때는 발생하지 않습니다.
{ "event": "release.distributed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "release_id": 789, "label_id": 321, "release_cat": "ABC123", "release_title": "Summer EP", "delivery_status": "distributed" }}release.review.status_changed
섹션 제목: “release.review.status_changed”릴리스가 검수 상태 사이를 이동할 때마다 발생합니다.
{ "event": "release.review.status_changed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "release_id": 789, "label_id": 321, "release_cat": "ABC123", "release_title": "Summer EP", "previous_status": "to_review", "new_status": "approved" }}| 필드 | 타입 | 설명 |
|---|---|---|
previous_status | string | 이전 상태. draft, to_review, approved, rejected, require_changes, audit 중 하나 |
new_status | string | 새 상태. 동일한 값 집합 |
review_issues | array(선택) | require_changes 및 rejected 전환에서만 포함됩니다. 주의가 필요한 이슈가 담깁니다. 다른 모든 전환에서는 키 자체가 생략되므로 항상 존재한다고 가정하지 마세요 |
release.preflight.report_ready
섹션 제목: “release.preflight.report_ready”검수 전 대기 상태에 있는 릴리스의 Preflight QC 품질 리포트를 가져올 수 있게 되었을 때 발생합니다. 계정에 Preflight QC 애드온이 있어야 합니다.
{ "event": "release.preflight.report_ready", "timestamp": "2026-07-07T10:00:00+00:00", "webhook_id": "123", "data": { "release_id": 789, "label_id": 321, "release_cat": "ABC123", "release_title": "Summer EP", "generated_at": "2026-07-07T09:58:12+00:00", "profile": { "name": "quality_report", "version": 2 }, "counts": { "blocking": 1, "informational": 2, "requires_feedback": 1 } }}| 필드 | 타입 | 설명 |
|---|---|---|
release_id | integer | 리포트가 속한 릴리스 |
label_id | integer | 릴리스가 속한 레이블 |
release_cat | string | null | 릴리스 카탈로그 참조 번호 |
release_title | string | null | 릴리스 제목 |
generated_at | string | 검사가 완료된 시점(ISO 8601). 품질 리포트 엔드포인트의 report.generated_at과 일치합니다 |
profile | object | 카운트를 산출한 품질 프로필: {name, version} |
counts | object | 집계 카운트만: {blocking, informational, requires_feedback}. requires_feedback 카운트는 나머지 두 개와 겹칩니다 |
stream_radar.flag_created
섹션 제목: “stream_radar.flag_created”Stream Radar 플래그가 발생할 때—완전히 새로운 플래그이거나, 이전에 해결된 플래그가 새로운 감지로 다시 열리는 경우—에 발생합니다. 계정에 Stream Radar 애드온이 있어야 합니다. transition 필드가 두 경우를 구분합니다. 새 플래그는 published, 다시 활성화된 플래그는 reopened입니다.
{ "event": "stream_radar.flag_created", "timestamp": "2026-07-07T10:00:00+00:00", "webhook_id": "123", "data": { "flag_id": 4501, "dsp": "spotify", "isrc": "USRC12345678", "release_id": 789, "track_id": 654, "severity": "high", "status": "active", "transition": "published", "first_detected_at": "2026-07-06T00:00:00+00:00", "last_detected_at": "2026-07-07T00:00:00+00:00", "estimated_affected_streams": 12500, "published_at": "2026-07-07T09:58:12+00:00", "resolved_at": null }}| 필드 | 타입 | 설명 |
|---|---|---|
flag_id | integer | 플래그의 안정적인 식별자. Stream Radar 엔드포인트의 id와 일치합니다 |
dsp | string | 패턴이 관찰된 플랫폼(예: spotify) |
isrc | string | 관련된 녹음물의 ISRC |
release_id | integer | 녹음물이 속한 릴리스 |
track_id | integer | null | ISRC가 트랙 중 하나에 명확하게 매핑될 때의 해당 트랙 |
severity | string | low, medium, high 중 하나 |
status | string | 이 이벤트에서는 active |
transition | string | 새 플래그는 published, 해결된 플래그가 다시 활성화된 경우는 reopened |
first_detected_at | string | null | 이 트랙과 플랫폼에서 패턴이 처음 관찰된 시점(ISO 8601) |
last_detected_at | string | null | 가장 최근 감지 시점(ISO 8601) |
estimated_affected_streams | integer | null | 관련된 스트림 수의 추정치 |
published_at | string | 플래그가 처음 발생한 시점(ISO 8601) |
resolved_at | string | null | 플래그가 활성 상태인 동안에는 null |
stream_radar.flag_resolved
섹션 제목: “stream_radar.flag_resolved”감지가 멈춰 Stream Radar 플래그가 해결될 때 발생합니다. Stream Radar 애드온이 있어야 합니다. stream_radar.flag_created와 동일한 필드를 가지며(transition 제외), status는 resolved로 설정되고 resolved_at이 채워집니다.
{ "event": "stream_radar.flag_resolved", "timestamp": "2026-07-14T10:00:00+00:00", "webhook_id": "123", "data": { "flag_id": 4501, "dsp": "spotify", "isrc": "USRC12345678", "release_id": 789, "track_id": 654, "severity": "high", "status": "resolved", "first_detected_at": "2026-07-06T00:00:00+00:00", "last_detected_at": "2026-07-12T00:00:00+00:00", "estimated_affected_streams": 18700, "published_at": "2026-07-07T09:58:12+00:00", "resolved_at": "2026-07-14T09:55:03+00:00" }}payment.statement_ready
섹션 제목: “payment.statement_ready”정산서가 생성되어 확인할 수 있게 되면 발생합니다.
{ "event": "payment.statement_ready", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "payment_request_id": 1024, "invoice_number": "INV-2026-001", "period": "2026-04-30", "amount": 1234.56, "total_due_usd": 1234.56, "currency": "USD" }}| 필드 | 타입 | 설명 |
|---|---|---|
payment_request_id | integer | 내부 결제 요청 ID |
invoice_number | string | 정산서 인보이스 참조 번호 |
period | string | null | 기간 종료일(ISO 8601 날짜, YYYY-MM-DD) |
amount | number | currency 기준 정산 금액 |
total_due_usd | number | USD로 환산한 정산 합계 |
currency | string | ISO 4217 통화 코드(기본값 USD) |
transcode.completed 및 transcode.failed
섹션 제목: “transcode.completed 및 transcode.failed”트랙의 오디오 트랜스코딩이 끝났을 때 발생합니다. transcode.completed는 성공 시, transcode.failed는 트랜스코딩이 실패하거나 미완료로 끝났을 때 발생합니다. 두 이벤트는 동일한 페이로드 형태를 공유합니다.
{ "event": "transcode.completed", "timestamp": "2026-07-07T10:00:00+00:00", "webhook_id": "123", "data": { "release_id": 789, "label_id": 321, "track_id": 654, "transcoder_queue_id": 987, "status": "complete", "status_message": "transcode_complete", "files": [ { "asset_type_id": 2, "status": "complete" } ] }}| 필드 | 타입 | 설명 |
|---|---|---|
release_id | integer | 트랙이 속한 릴리스 |
label_id | integer | 릴리스가 속한 레이블 |
track_id | integer | 트랜스코딩된 트랙 |
transcoder_queue_id | integer | 트랜스코딩 내부 큐 ID |
status | string | 원시 큐 상태: complete, error, incomplete 중 하나 |
status_message | string | 안전한 열거형 사유 코드: transcode_complete, transcode_error, transcode_incomplete 중 하나 |
files | array | 트랙의 파일별 세부 정보: 트랜스코딩된 파일마다 {asset_type_id, status} |
distribution.outlet.status_changed
섹션 제목: “distribution.outlet.status_changed”매장별 배포 상태 전환이 일어날 때마다 발생합니다(예: scheduled → transcoding → batched → complete). delivery.completed, delivery.failed, takedown.completed가 다루는 최종 상태뿐 아니라 모든 전환이 대상입니다. 이 이벤트는 설계상 전송량이 많습니다. 매장별 진행 과정을 전부 파악하려는 경우에만 구독하세요.
{ "event": "distribution.outlet.status_changed", "timestamp": "2026-07-07T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "label_id": 321, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "previous_status": "transcoding", "status": "batched" }}| 필드 | 타입 | 설명 |
|---|---|---|
distro_queue_id | integer | 이 배포의 내부 큐 ID |
release_id | integer | 배포 중인 릴리스 |
label_id | integer | 릴리스가 속한 레이블 |
release_cat | string | null | 릴리스 카탈로그 참조 번호 |
outlet_id | integer | null | 대상 매장 ID |
outlet_name | string | null | 사람이 읽을 수 있는 매장 이름 |
previous_status | string | null | 직전 상태. 행에 인식 가능한 직전 상태가 없으면 null |
status | string | 새 상태 |
웹훅 서명 검증
섹션 제목: “웹훅 서명 검증”모든 웹훅 전송에는 서명이 포함되어 있어 실제로 LabelGrid에서 온 것인지 확인할 수 있습니다. 이벤트를 처리하기 전에 항상 서명을 검증하세요.
요청 헤더
섹션 제목: “요청 헤더”모든 웹훅 POST 요청에는 다음 헤더가 포함됩니다.
| 헤더 | 설명 |
|---|---|
X-Webhook-Signature | 원본 요청 본문의 HMAC-SHA256 값(소문자 16진수, 알고리즘 접두사 없음) |
X-Webhook-Timestamp | 전송의 ISO 8601 타임스탬프(본문의 timestamp 속성과 같은 값) |
X-Webhook-Event | 이벤트 식별자(예: delivery.completed) |
X-Webhook-Id | 전송을 받는 웹훅의 ID |
User-Agent | LabelGrid-Webhooks/1.0 |
Content-Type | application/json |
알고리즘
섹션 제목: “알고리즘”- 알고리즘: HMAC-SHA256
- 인코딩: 소문자 16진수
- 접두사: 없음. 값은
sha256=...가 아니라 16진수 다이제스트 그 자체입니다 - 서명 대상: 원본 JSON 요청 본문 전체(본문 자체에 타임스탬프가 속성으로 포함되어 있으므로, 타임스탬프도 암묵적으로 서명됩니다)
검증 절차
섹션 제목: “검증 절차”- JSON 파싱이나 변환에 앞서 원본 요청 본문을 읽으세요. 파싱한 JSON을 다시 직렬화하면 바이트가 달라져 서명이 깨질 수 있습니다.
HMAC-SHA256(raw_body, your_webhook_secret)을 계산하고 소문자 16진수 다이제스트를 구하세요.- 일정 시간 비교(constant-time comparison)로
X-Webhook-Signature와 비교하세요. - (권장)
X-Webhook-Timestamp가 재전송 허용 시간을 넘긴 경우 요청을 거부하세요. 5분을 권장합니다.
PHP 예시
섹션 제목: “PHP 예시”$rawBody = file_get_contents('php://input');$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
if (! hash_equals($expected, $signature)) { http_response_code(401); exit('Invalid signature');}
// Reject deliveries older than 5 minutesif (abs(time() - strtotime($timestamp)) > 300) { http_response_code(401); exit('Stale delivery');}
$payload = json_decode($rawBody, true);// ... process the eventhttp_response_code(200);Node.js 예시
섹션 제목: “Node.js 예시”const crypto = require('crypto');
// Express: capture raw body BEFORE any JSON middlewareapp.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { const rawBody = req.body; // Buffer const signature = req.header('X-Webhook-Signature') || ''; const timestamp = req.header('X-Webhook-Timestamp') || '';
const expected = crypto .createHmac('sha256', webhookSecret) .update(rawBody) .digest('hex');
const sigBuf = Buffer.from(signature, 'hex'); const expBuf = Buffer.from(expected, 'hex');
if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) { return res.status(401).send('Invalid signature'); }
if (Math.abs(Date.now() - new Date(timestamp).getTime()) > 5 * 60 * 1000) { return res.status(401).send('Stale delivery'); }
const payload = JSON.parse(rawBody.toString('utf8')); // ... process the event res.sendStatus(200);});Python 예시
섹션 제목: “Python 예시”import hmac, hashlibfrom datetime import datetime, timezone
raw_body = request.get_data() # Flask: bytes, before any JSON parsingsignature = request.headers.get('X-Webhook-Signature', '')timestamp = request.headers.get('X-Webhook-Timestamp', '')
expected = hmac.new( webhook_secret.encode('utf-8'), raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature): return ('Invalid signature', 401)
delivery_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))if abs((datetime.now(timezone.utc) - delivery_time).total_seconds()) > 300: return ('Stale delivery', 401)
return ('', 200) # process the event, then ack자주 빠지는 함정
섹션 제목: “자주 빠지는 함정”- 해싱 전에 본문을 다시 직렬화하는 것. JSON을 자동으로 파싱하는 프레임워크(Express의
express.json(), Laravel의 기본 요청 본문 처리)는 원본 바이트를 잃어버립니다. 원본 본문을 먼저 확보하세요. - 일정 시간 비교가 아닌 비교(
==,===)를 사용하는 것. 타이밍 공격에 취약합니다. 항상hash_equals(PHP),crypto.timingSafeEqual(Node),hmac.compare_digest(Python) 또는 사용 언어의 동등한 함수를 사용하세요. sha256=접두사를 기대하는 것. 헤더 값은 접두사 없이 16진수 다이제스트뿐입니다.- 타임스탬프 검사를 건너뛰는 것. 검사하지 않으면 유출된 서명이 무기한 재전송될 수 있습니다.
한도 및 신뢰성
섹션 제목: “한도 및 신뢰성”요청 한도
섹션 제목: “요청 한도”| 한도 | 값 |
|---|---|
| 요청 타임아웃 | 10초 |
| 최대 페이로드 크기 | 64 KB |
| 사용자당 최대 웹훅 수 | 10개 |
엔드포인트가 10초 이내에 응답하지 않으면 해당 전송은 실패로 처리되어 재시도됩니다.
재시도 일정
섹션 제목: “재시도 일정”엔드포인트가 2xx 이외의 상태를 반환하거나 타임아웃되면 LabelGrid은 지수 백오프 방식으로 재시도합니다.
| 시도 | 재시도 전 대기 시간 |
|---|---|
| 1 → 2 | 30초 |
| 2 → 3 | 1분 |
| 3 → 4 | 2분 |
| 4 → 5 | 4분 |
| 5 → 6 | 8분 |
| 6 → 7 | 16분 |
| 7 → 8 | 32분 |
| 8 → 9 | 64분 |
| 9 → 10 | 128분 |
각 간격에는 0~30초의 지터(jitter)가 포함됩니다. 10번의 시도(누적 약 4.5시간) 후에도 실패하면 해당 전송은 영구 실패로 기록되며 더 이상 재시도하지 않습니다.
자동 비활성화
섹션 제목: “자동 비활성화”웹훅 엔드포인트가 계속 실패하면(중간에 성공한 전송이 하나도 없이 전송 실패가 반복되면) LabelGrid는 이벤트를 받을 수 없는 것이 분명한 엔드포인트에 대한 재시도를 멈추기 위해 웹훅을 자동으로 비활성화합니다. 실패 횟수는 전송이 성공할 때마다 초기화되므로, 가끔 발생하는 일시적인 문제로는 웹훅이 비활성화되지 않습니다. 오직 지속적이고 끊김 없는 실패만 비활성화로 이어집니다.
이런 방식으로 웹훅이 비활성화되면 해당 소유자에게 이메일이 발송됩니다. 이메일에는 웹훅 이름과 해당 엔드포인트 URL, 그리고 비활성화를 유발한 실패 유형(예: 연결 시간 초과 또는 반복된 HTTP 오류)이 안내됩니다.
다시 활성화하는 것은 셀프서비스입니다. 엔드포인트를 고친 뒤 프로필 → Webhooks에서 웹훅을 다시 켜세요. 비활성화된 웹훅을 다시 활성화하면 실패 횟수가 초기화됩니다. 웹훅 목록에는 각 웹훅의 활성 상태와 현재 실패 횟수가 표시되므로 문제가 있는 엔드포인트를 한눈에 확인할 수 있습니다.
신뢰성을 높이는 모범 사례
섹션 제목: “신뢰성을 높이는 모범 사례”- 2xx 응답을 빠르게(10초 이내) 반환하세요
- 응답을 보낸 뒤 데이터를 비동기로 처리하세요
- 모든 요청의 서명을 검증하세요(웹훅 서명 검증 참고)
- 웹훅 목록에서 실패 횟수를 모니터링하세요
- 누락된 이벤트를 조사할 때는 전송 로그를 확인하세요
활용 사례
섹션 제목: “활용 사례”자동 알림
섹션 제목: “자동 알림”- 릴리스가 공개되면 Slack 메시지 보내기
- 배포가 실패하면 팀에 이메일 보내기
- 내부 대시보드 업데이트
워크플로 자동화
섹션 제목: “워크플로 자동화”- 릴리스가 배포되면 마케팅 캠페인 시작하기
- 새 콘텐츠가 준비되면 웹사이트 업데이트하기
- 외부 프로젝트 관리 도구로 상태 동기화하기
모니터링 및 알림
섹션 제목: “모니터링 및 알림”- 배포 실패 시 즉시 알림 받기
- 배포 진행 상황을 실시간으로 추적하기
- 검수 상태 변경 모니터링하기
문제 해결
섹션 제목: “문제 해결”웹훅이 이벤트를 받지 못할 때
섹션 제목: “웹훅이 이벤트를 받지 못할 때”- 상태 확인 - 웹훅이 Active 상태인가요?
- URL 확인 - 엔드포인트에 인터넷에서 접근할 수 있나요?
- 이벤트 확인 - 올바른 이벤트가 선택되어 있나요?
- 로그 검토 - 기록된 오류가 있나요?
실패 횟수가 많을 때
섹션 제목: “실패 횟수가 많을 때”- 엔드포인트 확인 - 200 OK를 반환하고 있나요?
- 응답 시간 확인 - 타임아웃 안에 응답하고 있나요?
- 오류 메시지 검토 - 무엇이 실패하고 있나요?
- 수동 테스트 - 테스트 웹훅을 보내 보세요
시크릿 재발급
섹션 제목: “시크릿 재발급”웹훅 시크릿이 노출된 경우:
- 웹훅 설정에서 Regenerate Secret을 클릭합니다
- 새 시크릿으로 애플리케이션을 업데이트합니다
- 이전 시크릿은 즉시 작동을 멈춥니다
도움이 필요하신가요?
섹션 제목: “도움이 필요하신가요?”웹훅에 관해 궁금한 점이 있으면 지원팀에 문의하세요.