01 SOFTWARE
Guide

Webhooks

데이터 변경 이벤트 수신

Webhooks

데이터 변경 시 서버로 이벤트를 전송받을 수 있습니다.

개요

Webhook을 설정하면 다음 이벤트 발생 시 지정된 URL로 POST 요청이 전송됩니다:

  • create - 새 문서 생성
  • update - 문서 수정

Webhook 설정

01.software Admin Panel에서 테넌트 설정의 Webhook URL을 지정합니다.

https://your-domain.com/api/webhook

Webhook Payload

{
  "collection": "products",
  "operation": "create",
  "data": {
    "id": "product-id",
    "title": "상품명",
    "price": 10000,
    ...
  }
}

SDK로 Webhook 처리

기본 핸들러

// app/api/webhook/route.ts
import { handleWebhook } from '@01.software/sdk'

export async function POST(request: Request) {
  return handleWebhook(request, async (event) => {
    console.log('Collection:', event.collection)
    console.log('Operation:', event.operation)
    console.log('Data:', event.data)

    // 이벤트 처리 로직
    if (event.collection === 'orders' && event.operation === 'create') {
      // 새 주문 처리
      await sendOrderNotification(event.data)
    }
  })
}

타입 안전한 핸들러

특정 컬렉션에 대한 타입이 지정된 핸들러를 생성할 수 있습니다.

// app/api/webhook/route.ts
import { handleWebhook, createTypedWebhookHandler } from '@01.software/sdk'

// Orders 전용 핸들러
const handleOrderWebhook = createTypedWebhookHandler(
  'orders',
  async (event) => {
    // event.data 타입이 Order로 추론됩니다
    console.log('Order Number:', event.data.orderNumber)
    console.log('Status:', event.data.status)
    console.log('Total:', event.data.totalAmount)

    if (event.operation === 'create') {
      await sendOrderConfirmation(event.data.email)
    }
  }
)

export async function POST(request: Request) {
  return handleWebhook(request, handleOrderWebhook)
}

여러 컬렉션 처리

// app/api/webhook/route.ts
import { handleWebhook } from '@01.software/sdk'

export async function POST(request: Request) {
  return handleWebhook(request, async (event) => {
    switch (event.collection) {
      case 'orders':
        await handleOrderEvent(event)
        break
      case 'products':
        await handleProductEvent(event)
        break
      case 'posts':
        await handlePostEvent(event)
        break
      default:
        console.log('Unhandled collection:', event.collection)
    }
  })
}

async function handleOrderEvent(event: WebhookEvent<'orders'>) {
  if (event.operation === 'create') {
    await sendOrderNotification(event.data)
  }
}

async function handleProductEvent(event: WebhookEvent<'products'>) {
  if (event.operation === 'update') {
    await revalidatePath(`/products/${event.data.id}`)
  }
}

async function handlePostEvent(event: WebhookEvent<'posts'>) {
  if (event.operation === 'create') {
    await revalidatePath('/blog')
  }
}

사용 사례

주문 알림

const handleOrderWebhook = createTypedWebhookHandler(
  'orders',
  async (event) => {
    if (event.operation === 'create') {
      // 이메일 발송
      await sendEmail({
        to: event.data.email,
        subject: `주문 확인 - ${event.data.orderNumber}`,
        body: `주문이 접수되었습니다.`
      })

      // Slack 알림
      await sendSlackNotification({
        channel: '#orders',
        message: `새 주문: ${event.data.orderNumber}`
      })
    }
  }
)

재고 동기화

const handleProductWebhook = createTypedWebhookHandler(
  'products',
  async (event) => {
    if (event.operation === 'update') {
      // 외부 시스템과 재고 동기화
      await syncInventory({
        sku: event.data.sku,
        stock: event.data.stock
      })
    }
  }
)

캐시 무효화

import { revalidatePath, revalidateTag } from 'next/cache'

const handlePostWebhook = createTypedWebhookHandler(
  'posts',
  async (event) => {
    // 페이지 캐시 무효화
    revalidatePath('/blog')
    revalidatePath(`/blog/${event.data.slug}`)

    // 태그 기반 캐시 무효화
    revalidateTag('posts')
  }
)

응답 형식

Webhook 핸들러는 자동으로 적절한 응답을 반환합니다:

성공

{
  "success": true,
  "message": "Webhook processed successfully"
}

실패

{
  "success": false,
  "error": "Invalid webhook event"
}

보안

프로덕션 환경에서는 Webhook 요청의 출처를 검증하는 것이 좋습니다.

IP 화이트리스트

export async function POST(request: Request) {
  const ip = request.headers.get('x-forwarded-for')

  if (!allowedIPs.includes(ip)) {
    return new Response('Forbidden', { status: 403 })
  }

  return handleWebhook(request, handler)
}

서명 검증

export async function POST(request: Request) {
  const signature = request.headers.get('x-webhook-signature')
  const body = await request.text()

  if (!verifySignature(body, signature)) {
    return new Response('Invalid signature', { status: 401 })
  }

  return handleWebhook(
    new Request(request.url, { ...request, body }),
    handler
  )
}

다음 단계

On this page