배송 상태가 변경될 때마다 API를 폴링하는 대신, 웹훅을 사용하면 자동으로 알림을 받을 수 있습니다.
🎯 웹훅의 장점
• 실시간 알림 수신
• 배송 상태 변경 즉시 대응
• 고객 서버에서 폴링 로직 구현 불필요
• 자동으로 상태 변경 알림 수신
• 실시간 알림 수신
• 배송 상태 변경 즉시 대응
• 고객 서버에서 폴링 로직 구현 불필요
• 자동으로 상태 변경 알림 수신
웹훅 작동 원리
구독이 활성화된 동안 폴링 주기(1시간)마다 DeliveryAPI 서버가 최신 상태를 담아 여러분의 서버로 HTTP POST 요청을 보냅니다. 변경 여부와 무관하게 매번 전송되며, items[].hasChanged로 실제 변경 여부를 구분할 수 있습니다.
배송 접수 → DeliveryAPI 감지 → 웹훅 POST → 여러분의 서버 1단계: 웹훅 엔드포인트 생성
Express.js 예제
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/delivery', (req, res) => {
const { event, items } = req.body;
console.log('웹훅 이벤트:', event);
// 이벤트 타입 체크
if (event !== 'tracking.polled' && event !== 'tracking.completed') {
return res.status(400).send('Unknown event type');
}
// 각 항목별 처리
for (const item of items) {
const { trackingNumber, currentStatus, previousStatus } = item;
console.log(`상태 변경: ${previousStatus} → ${currentStatus}`);
switch(currentStatus) {
case 'PICKED_UP':
console.log('집화 완료:', trackingNumber);
break;
case 'IN_TRANSIT':
console.log('배송중:', trackingNumber);
break;
case 'OUT_FOR_DELIVERY':
console.log('배송 출발:', trackingNumber);
break;
case 'DELIVERED':
console.log('배송 완료:', trackingNumber);
break;
case 'FAILED':
console.log('배송 실패:', trackingNumber);
break;
}
}
// 200 응답 필수! (3초 이내)
res.status(200).send('OK');
});
app.listen(3000); 2단계: 웹훅 URL 등록
대시보드 또는 API로 웹훅 URL을 등록합니다.
POST https://api.deliveryapi.co.kr/v1/webhooks/endpoints
{
"url": "https://yourdomain.com/webhooks/delivery",
"name": "배송 알림 시스템"
}
// 응답
{
"isSuccess": true,
"data": {
"endpointId": "ep_xxx",
"webhookSecret": "whsec_xxx" // 이 값을 안전하게 보관!
}
} 웹훅 페이로드 구조
{
"event": "tracking.polled",
"requestId": "req_20260113_103000_a1b2c3d4e5f6a7b8",
"timestamp": "2026-01-13T10:30:00.000Z",
"summary": {
"total": 1,
"delivered": 1,
"active": 0,
"hasChanges": true
},
"items": [
{
"courierCode": "cj",
"trackingNumber": "1234567890",
"clientId": "order_12345",
"previousStatus": "IN_TRANSIT",
"currentStatus": "DELIVERED",
"hasChanged": true,
"isDelivered": true,
"trackingData": {
// 상세 배송 정보 (UnifiedTrackingResponse)
}
}
],
"metadata": {
"orderId": "ORD-12345" // 구독 시 등록한 메타데이터
}
} 배송 상태 코드
PENDING- 상품 준비중REGISTERED- 택배사 등록PICKED_UP- 집화 완료IN_TRANSIT- 배송 중OUT_FOR_DELIVERY- 배송 출발DELIVERED- 배송 완료FAILED- 배송 실패RETURNED- 반송
보안: 시그니처 검증
웹훅 요청이 DeliveryAPI에서 온 것인지 검증해야 합니다.
const crypto = require('crypto');
// ⚠️ 서명은 원본 바이트(raw body) 기준으로 계산됩니다.
// 전역 express.json() 대신 이 라우트에만 express.raw()를 적용하세요.
app.post(
'/webhooks/delivery',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const rawBody = req.body.toString('utf8'); // Buffer → 원본 JSON 문자열
const secret = process.env.WEBHOOK_SECRET;
// 서명 대상: timestamp.payload (payload는 반드시 원본 바이트)
const hash = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
const expected = Buffer.from(`sha256=${hash}`);
const received = Buffer.from(signature || '');
const valid =
expected.length === received.length &&
crypto.timingSafeEqual(expected, received);
if (!valid) {
return res.status(401).send('Invalid signature');
}
const { event, items } = JSON.parse(rawBody);
// 웹훅 처리...
res.status(200).send('OK');
}
); 재전송 정책
별도의 재시도 큐는 없습니다. 웹훅은 폴링 주기(1시간)마다 최신 상태를 담아 전송되며, 특정 전송이 실패해도 다음 폴링 사이클에 다시 전송됩니다.
💡 참고
엔드포인트가 계속 실패 응답을 반환하면 자동으로 비활성화될 수 있습니다. 안정적으로 수신하려면 3초 이내 200 응답과 idempotency(중복 이벤트 처리)가 중요합니다.
엔드포인트가 계속 실패 응답을 반환하면 자동으로 비활성화될 수 있습니다. 안정적으로 수신하려면 3초 이내 200 응답과 idempotency(중복 이벤트 처리)가 중요합니다.
✅ 베스트 프랙티스
• 3초 이내에 200 응답 반환
• 무거운 작업은 백그라운드로 처리
• 중복 이벤트 처리 (idempotency)
• 로그 기록
• 3초 이내에 200 응답 반환
• 무거운 작업은 백그라운드로 처리
• 중복 이벤트 처리 (idempotency)
• 로그 기록