배송 상태가 변경될 때마다 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" // 구독 시 등록한 메타데이터
  }
}

배송 상태 코드

보안: 시그니처 검증

웹훅 요청이 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)
• 로그 기록

다음 단계