© 2025 anveloper.dev
GitHub·LinkedIn·Contact

목차

  • 아키텍처: 외부 API 의존
  • 상품 동기화: 본사 → Store
  • 동기화 흐름
  • 상품 상세: optionTree
  • 카테고리 자동 생성
  • 주문 동기화: 양방향
  • 이벤트 발송 (Store → 본사)
  • 동기화 API (본사 → Store)
  • 데이터 매핑
  • 알림 발송 시스템
  • 채널 분기
  • 발송 시점
  • 발송 원칙
  • 알림톡 템플릿
  • 수신자 관리
  • 재발송
  • 마무리
포스트 목록으로 돌아가기

외부 API 연동: 상품 동기화와 알림 발송

2026-02-04
API
Integration
Sync
Notification
Kakao

외부 API 연동: 상품 동기화와 알림 발송

아키텍처: 외부 API 의존

이 플랫폼의 가장 큰 특징은 상품·주문 정보를 자체 DB에 저장하지 않는다는 점이다. 상품명, 가격, 이미지는 본사 API에서 실시간 조회하고, DB에는 테넌트 설정과 참조 정보만 저장한다.

[클라이언트] ↔ [스토어 앱] ↔ [본사 API]
                   ↓
             [MariaDB]
         (설정/참조 정보만)

단, 주문 시점의 상품명과 가격은 OrderItem에 스냅샷으로 저장한다.

상품 동기화: 본사 → Store

동기화 흐름

관리자가 "상품 동기화" 버튼을 클릭하면, 본사 API에서 상품 목록을 페이지 단위로 가져온다.

[스토어 앱]                          [본사 API]
    │  GET /products?limit=20            │
    │ ──────────────────────────────────>│
    │  { products, hasMore, nextCursor } │
    │ <──────────────────────────────────│
    │                                    │
    │  [Product 테이블에 저장]            │
    │                                    │
    │  POST /products/mappings           │
    │  { mappings: [{ seqno, id }] }     │
    │ ──────────────────────────────────>│
    │  [본사에서 storeProductId 저장]      │
    │                                    │
    │  hasMore=true → 다음 페이지        │
    └── (반복) ──────────────────────────┘

핵심은 **매핑(mapping)**이다. Store에서 생성한 Product ID(cuid)를 본사에 전달하여, 양쪽 시스템에서 상호 참조할 수 있게 한다.

상품 상세: optionTree

고객이 옵션을 선택할 때 사용하는 핵심 데이터 구조다. 옵션 조합에 따른 SKU와 가격을 중첩 트리로 표현한다.

{
  "optionOrder": ["사이즈", "색상"],
  "optionTree": {
    "S": {
      "화이트": { "sku": "TS-S-W", "price": 20000 },
      "블랙": { "sku": "TS-S-B", "price": 20000 }
    },
    "2XL": {
      "화이트": { "sku": "TS-2XL-W", "price": 22000 }
    }
  }
}

"S" 사이즈에 "네이비"가 없으면 트리에 포함되지 않으므로, 선택 불가능한 옵션 조합을 자연스럽게 제외할 수 있다.

카테고리 자동 생성

동기화 시 본사 상품의 categoryName이 Store에 없으면 Category가 자동 생성된다. 관리자는 생성된 카테고리의 다국어 이름과 순서만 관리하면 된다.

주문 동기화: 양방향

주문 데이터는 두 가지 방식으로 동기화된다.

방식방향용도
이벤트 발송Store → 본사주문 생성/상태 변경 시 실시간 알림
동기화 API본사 → Store본사가 주문 데이터를 폴링으로 조회

이벤트 발송 (Store → 본사)

주문 생성 시 본사에 전체 주문 정보를 전송하고, 본사에서 발급한 extOrderSeqno를 저장한다.

// 주문 생성 API (app/api/orders/route.ts)
const order = await prisma.order.create({ ... });
 
// 본사에 이벤트 발송 (fire-and-forget)
sendOrderCreatedEvent(order).catch(console.error);

이벤트 발송 실패 시에도 주문 생성은 성공으로 처리한다 (사용자 경험 우선). 실패한 주문은 Order.syncedAt이 null로 남아있고, 본사가 동기화 API로 나중에 가져간다.

재시도 정책: 최대 3회, 지수 백오프 (1초, 2초, 4초).

동기화 API (본사 → Store)

본사가 Store의 주문 데이터를 조회하거나 상태를 변경할 수 있는 API를 제공한다.

GET  /api/sync/orders          # 주문 목록 (since 파라미터로 변경분만)
GET  /api/sync/orders/detail   # 주문 상세
PATCH /api/sync/orders/mapping # 매핑 정보 업데이트 (이벤트 실패 복구)
PATCH /api/sync/orders/status  # 상태 역방향 변경

API Key 인증(Authorization: Bearer {STORE_API_KEY})을 사용한다.

데이터 매핑

양쪽 시스템의 ID를 연결하는 매핑이 핵심이다.

Store본사설명
Order.id (cuid)-Store 내부용
Order.orderNumberORDER_NUM주문번호
Order.extOrderSeqnoorderSeqno본사 주문 번호
OrderItem.itemNumberSHOP_DETAIL_NO아이템 번호

알림 발송 시스템

채널 분기

수신자의 identifier 유형에 따라 자동 분기한다.

  • 전화번호 → 카카오톡 알림톡 (Lunasoft API)
  • 이메일 → 이메일 (SMTP)

발송 시점

이벤트고객작업자캐셔
주문 접수 (Payment PAID)OO-
상품 준비 완료 (Order READY)O-O

발송 원칙

  • 비동기(fire-and-forget): 알림 실패가 주문 응답에 영향 없음
  • Promise.allSettled: 다수 수신자 중 일부 실패해도 나머지 처리
  • 발송 이력은 NotificationLog에 기록

알림톡 템플릿

카카오 비즈니스 채널에 등록한 템플릿 ID를 상수로 관리한다.

상수용도
TEMPLATE_ORDER_CREATED주문 접수 - 고객용
TEMPLATE_ORDER_CREATED_WORKER주문 접수 - 작업자용
TEMPLATE_ORDER_COMPLETED제작 완료 - 고객용
TEMPLATE_ORDER_COMPLETED_CASHIER제작 완료 - 캐셔용

알림 URL은 커스텀 도메인이 검증(domainVerified)된 경우 https://{domain}/my/{orderNumber}, 아니면 https://store.example.com/{tenant}/my/{orderNumber}를 사용한다.

수신자 관리

NotificationRecipient 모델로 작업자/캐셔 수신자를 관리한다.

model NotificationRecipient {
  id         String  @id @default(cuid())
  tenantId   String
  role       NotificationRecipientRole  // WORKER | CASHIER
  identifier String  // 전화번호 또는 이메일
  isActive   Boolean // 수신 ON/OFF
  @@unique([tenantId, role, identifier])
}

수신자 추가 시 인증번호 검증을 거친다 (전화번호 → 알림톡, 이메일 → 이메일로 인증번호 발송). 역할별 최대 10명.

재발송

READY 상태 주문에 한해 알림톡 재발송이 가능하다. 주문 목록에서 개별 또는 체크박스 일괄 재발송을 지원한다.

마무리

외부 API 의존 전략의 장단점은 명확하다. 상품 정보를 이중으로 관리할 필요가 없어 시스템이 간결해지지만, API 장애 시 상품 정보를 표시할 수 없다. 이를 보완하기 위해 주문 시점 스냅샷과 양방향 동기화를 구현했다.

알림 시스템은 "실패해도 괜찮다"는 원칙이 중요하다. 알림 발송 실패가 주문 생성을 막아서는 안 되고, 수신자 중 일부가 실패해도 나머지는 정상 처리되어야 한다. Promise.allSettled와 fire-and-forget 패턴이 이를 가능하게 한다.