베타 운영 중·유료 거래는 2026년 12월 17일 정식 오픈 후 시작됩니다. 지금은 상품을 미리 등록하고 둘러보실 수 있습니다.

개발 단계부터 연동

출시 후에 라이선스 체계를 붙이는 게 아니라, 개발 단계부터 새몰 연동을 코드에 넣어두면 출시일에 결제 → 발급 → 검증이 그대로 동작합니다. 샌드박스(테스트 키)로 결제 없이 전 구간을 검증하세요.

테스트 키는 결제 없이 발급되고, 주문·정산·매출 집계·Analytics에 일절 포함되지 않습니다. 실 트래픽과 완전히 분리된 검증용 통로예요.

왜 개발 단계부터인가

제품에 자체 라이선스 서버를 먼저 만들어 출시하면, 나중에 판매 채널을 붙일 때 키 체계·검증 로직을 갈아엎게 됩니다. 처음부터 제품의 활성화 지점에 verifyLicense(또는 verifyEntitlement) 한 번만 넣어두면 —

  • 라이선스 서버·키 발급·디바이스 제한을 직접 구현할 필요가 없고
  • 심사 통과 후 환경변수 하나만 교체하면 실판매가 시작되며
  • 환불·구독 해지에 따른 키 회수도 출시일부터 자동으로 처리됩니다

전체 흐름

단계하는 일어디서
1. 상품 등록자동 전달 방식(라이선스·구독 등)을 포함해 상품을 등록합니다. 심사를 통과하기 전에도 연동 시크릿과 테스트 시크릿은 바로 발급됩니다.상품 등록
2. 테스트 키 발급연동 정보 카드에서 테스트 키 발급 (활성 키 상품당 최대 5개)상품 수정 → 연동 정보
3. 개발·검증테스트 시크릿을 개발 환경변수로 두고 verify·웹훅·사용량 리포팅까지 전 구간 개발내 코드
4. 심사·출시심사를 통과하면 상품이 공개됩니다. 연동 코드는 그대로 둡니다.심사 (보통 24시간 이내)
5. 실 시크릿 전환운영 환경변수만 실 시크릿으로 교체, 테스트 키 회수배포 설정 · 라이선스 관리
자동 전달 방식을 등록하려면 플랜 조건을 맞춰야 합니다. 라이선스·인앱·구독형은 Pro부터, 나머지를 포함한 전체 방식은 Business부터 쓸 수 있습니다. 함께 고른 방식 중 조건에 못 미치는 것만 잠깁니다. (수동 전달은 연동이 필요 없어 이 가이드의 대상이 아닙니다.)

샌드박스 규칙

  • 테스트 시크릿은 test_, 테스트 키는 MF-TEST-… / mfk_test_… 접두 — 육안으로 구분됩니다
  • 테스트 시크릿 ↔ 테스트 키만 매칭 — 실 시크릿으로 테스트 키를 검증하거나 그 반대면 401로 거부됩니다
  • verify 응답에 is_test: true가 포함되어 코드에서 분기할 수 있습니다
  • 웹훅도 실 발급과 동일하게 발송됩니다 (payload에 is_test: true) — 웹훅 핸들러까지 미리 검증하세요
  • 회수·재발급은 라이선스 관리에서 실 키와 동일한 콘솔로 처리합니다
테스트 키의 요금제 이름은 테스트로 고정됩니다. 주문 없이 발급되므로 구매한 요금제가 없기 때문입니다. 그래서 요금제 이름을 키로 찾는 설정은 테스트 키에서 그대로 매칭되지 않습니다:
  • plan_features — 매칭이 안 되면 plan_features: null이 되어 기능이 열리지 않습니다. JSON에 "테스트" 항목을 함께 넣어두면 개발 중에도 플랜 게이팅을 확인할 수 있습니다.
  • sku_map — JSON에 "테스트" 항목이 있으면 테스트 키에도 같은 규칙으로 SKU가 실립니다. 없으면 skunull로 옵니다.
기능이 안 열린다고 연동을 의심하기 전에 이 두 가지를 먼저 확인하세요.

환경변수만 다르게, 코드는 동일하게

.env.development / .env.production
# 개발 — 테스트 시크릿 (상품 수정 → 연동 정보 카드)
SAEMOL_SECRET=test_xxxxxxxxxxxxxxxx

# 운영 — 실 시크릿
SAEMOL_SECRET=xxxxxxxxxxxxxxxx
activate.ts — 개발·운영 공통 코드
import { Saemol } from '@saemol/sdk'

const saemol = new Saemol({ secret: process.env.SAEMOL_SECRET! })

const result = await saemol.verifyLicense({
  licenseKey: inputKey,
  deviceId:   machineId(),
})

if (result.valid) {
  activate()
  if (result.is_test) console.info('[saemol] 샌드박스 키로 활성화됨')
}

웹훅 로컬 테스트

테스트 키 발급 시에도 실 발급과 동일한 웹훅이 발송됩니다 (payload에 is_test: true). 다만 localhost는 외부에서 접근할 수 없으므로 로컬 개발 중에는:

  • ngrok·cloudflared 같은 터널링 도구로 로컬 서버를 임시 공개 URL로 노출하고, 그 URL을 웹훅 수신 URL로 등록하세요
  • 놓친 발송은 연동 관리에서 실패 내역 확인 후 수동 재발송할 수 있습니다
  • 핸들러에서 data.is_test로 분기하면 테스트 발급이 운영 데이터에 섞이지 않습니다

서명 검증·이벤트 목록은 웹훅 가이드를 보세요.

안정성 — 캐싱과 오프라인 유예

검증 결과를 짧게 캐시하고, 네트워크 장애 시에는 마지막 성공 결과로 유예(grace)하세요. 새몰 API가 잠시 응답하지 못해도 제품이 멈추지 않습니다.

let cached: { at: number; valid: boolean } | null = null
const TTL = 5 * 60_000, GRACE = 24 * 3_600_000

async function isLicensed(key: string): Promise<boolean> {
  if (cached && Date.now() - cached.at < TTL) return cached.valid
  try {
    const r = await saemol.verifyLicense({ licenseKey: key })
    cached = { at: Date.now(), valid: r.valid }
    return r.valid
  } catch {
    // 네트워크 실패 — 마지막 성공 결과를 GRACE 시간까지 신뢰
    return cached != null && Date.now() - cached.at < GRACE ? cached.valid : false
  }
}

출시 전 체크리스트

  • 운영 배포의 SAEMOL_SECRET을 실 시크릿으로 교체했다
  • 웹훅 수신 URL이 프로덕션 서버를 가리키고 서명 검증(verifyWebhook)이 켜져 있다
  • 디바이스 제한·허용 버전·업데이트 정보 등 상품 연동 설정을 확인했다
  • 사용을 마친 테스트 키를 라이선스 관리에서 회수했다
  • 심사를 신청했다 — 심사 기준 미리 확인