메인 콘텐츠로 건너뛰기

Idempotency

멱등성 있는 처리를 위해 이벤트의 id와 리소스의 resourceId를 사용하세요.
const processedEvents = new Set();

function processEvent(event) {
  // Deduplicate by event ID
  if (processedEvents.has(event.id)) {
    return;
  }

  // Process the event
  handleEvent(event);
  processedEvents.add(event.id);
}

에러 처리

항상 적절한 HTTP 상태 코드를 반환하세요.
app.post('/en/developers/webhooks/tiro', (req, res) => {
  try {
    const event = req.body;
    
    // Process the event
    processEvent(event);
    res.status(200).send('OK');
  } catch (error) {
    console.error('Webhook processing error:', error);
    res.status(500).send('Internal server error');
  }
});

재시도 처리

Tiro의 webhook 시스템은 전달에 실패한 요청을 재시도해요. 재시도를 자연스럽게 처리하세요.
  • 2xx 상태 코드 반환: 처리에 성공한 경우
  • 4xx 상태 코드 반환: 영구적인 실패인 경우 (재시도하지 않아요)
  • 5xx 상태 코드 반환: 일시적인 실패인 경우 (재시도해요)

보안

항상 webhook의 진위 여부를 검증하세요.
function verifyWebhookAuth(authHeader, expectedSecret) {
  const token = authHeader.replace('Bearer ', '');
  return token === expectedSecret;
}

app.post('/en/developers/webhooks/tiro', (req, res) => {
  const authHeader = req.headers.authorization;
  
  if (!verifyWebhookAuth(authHeader, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Unauthorized');
  }
  
  // Process webhook event
  processEvent(req.body);
  res.status(200).send('OK');
});

리소스 라우팅

효율적인 라우팅을 위해 resourceTyperesourceId를 사용하세요.
function routeEvent(event) {
  const { resourceType, resourceId } = event.data;

  switch (resourceType) {
    case 'Note':
      return handleNoteEvent(event.type, resourceId, event.data.resource);
    case 'NoteDocument':
      return handleNoteDocumentEvent(event.type, resourceId, event.data.resource);
    case 'NoteSummary':
      return handleNoteSummaryEvent(event.type, resourceId, event.data.resource);
    case 'FolderNoteRelation':
      return handleFolderNoteEvent(event.type, resourceId, event.data.resource);
    default:
      console.log('Unknown resource type:', resourceType);
  }
}