개발 단계부터 연동
출시 후에 라이선스 체계를 붙이는 게 아니라, 개발 단계부터 새몰 연동을 코드에 넣어두면 출시일에 결제 → 발급 → 검증이 그대로 동작합니다. 샌드박스(테스트 키)로 결제 없이 전 구간을 검증하세요.
테스트 키는 결제 없이 발급되고, 주문·정산·매출 집계·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가 실립니다. 없으면sku가null로 옵니다.
환경변수만 다르게, 코드는 동일하게
.env.development / .env.production
# 개발 — 테스트 시크릿 (상품 수정 → 연동 정보 카드)
SAEMOL_SECRET=test_xxxxxxxxxxxxxxxx
# 운영 — 실 시크릿
SAEMOL_SECRET=xxxxxxxxxxxxxxxxactivate.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] 샌드박스 키로 활성화됨')
}웹훅 로컬 테스트
안정성 — 캐싱과 오프라인 유예
검증 결과를 짧게 캐시하고, 네트워크 장애 시에는 마지막 성공 결과로 유예(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)이 켜져 있다 - 디바이스 제한·허용 버전·업데이트 정보 등 상품 연동 설정을 확인했다
- 사용을 마친 테스트 키를 라이선스 관리에서 회수했다
- 심사를 신청했다 — 심사 기준 미리 확인