SDK
기본 구현 경로에는 TypeScript SDK를 사용합니다.
프론트엔드, 서버 route, 워크플로를 01.software 규칙에 맞춰 구현할 때 SDK를 사용합니다.
설정 흐름
패키지 설치
프로젝트가 이미 사용하는 패키지 매니저로 @01.software/sdk를 추가합니다.
브라우저 클라이언트 생성
Publishable Key는 승인된 origin에서만 사용합니다. 브라우저 client는 공개 읽기와 capability 또는 customer 권한으로 승인된 Cart, Checkout, PaymentSession 작업을 지원합니다.
서버 클라이언트 생성
Secret Key는 신뢰할 수 있는 서버 코드에서 쓰기와 권한 워크플로에만 사용합니다.
검증
인계 전에 대상 프로젝트의 typecheck, lint, test, build를 실행합니다.
클라이언트 선택
| 위치 | 클라이언트 | 메모 |
|---|---|---|
| 브라우저 UI | browser client | 공개 읽기와 capability/customer 권한 commerce 작업, origin 제한 |
| 서버 route | server client | 서버 권위 작업과 권한 작업에는 @01.software/sdk/server에서 import |
| React 데이터 fetching | query helpers | 캐시와 loading 상태 명시 |
| AI 생성 코드 | 기존 프로젝트 패턴 | 새 프레임워크 계층 추가 금지 |
Collection 읽기(find/count)와 서버 전용 bulk mutation
(@01.software/sdk/server의 updateMany/removeMany)은 같은 필터 검증을
사용합니다. 값을 조용히 버리거나 바꾸지 않고 필터 의미를 보존하며,
equals: null과 not_equals: null은 Payload REST에서도 null을 유지합니다.
where 내부의 그 외 null·undefined, 빈 조건 객체(where: {} 포함),
빈 배열(and/or 포함), 10단계 초과 중첩, 원소가 1000개를 넘는 배열은
요청 전에 ValidationError를 던집니다. false, 0, 빈 문자열, Date
조건은 유효합니다. 이 읽기 입력 제한은 BREAKING (0.x minor) 변경입니다.
최상위 where: undefined는 where 생략과 같지만, 필터 내부의 undefined는
거절합니다. 조건 없는 읽기는 where를 생략하세요. 동적으로 만든 ID 목록이
비면 SDK 호출 전에 빈 결과를 반환하세요. bulk mutation은 조건이 남지 않거나
동적 목록이 비면 호출 자체를 생략하세요. Raw collection 작업은 도메인별 SDK 메서드를 대체하거나 서버의 접근 규칙을 우회하는 수단이 아닙니다.
업그레이드 전에 기존 updateMany/removeMany 호출을 검토하세요. 이전에는
HTTP 400으로 실패하던 호출이 이제 실제 갱신·삭제를 수행할 수 있습니다.
기존 HTTP 400을 mutation 안전장치로 간주하지 마세요.
큰 in/not_in 목록은 URL 길이 제한을 넘을 수 있으므로 전체 선택 조건을
보존할 수 있는 작은 배치로 처리하세요. not_in은 모든 배치에서 전체 제외
조건을 유지해야 합니다. 제외 목록을 서로 다른 mutation으로 나누면 제외되는
레코드가 달라집니다.
서버 collection 쓰기(create, update, updateMany)는 localized 필드를 쓸
콘텐츠 locale로 선택 옵션 locale(en 또는 ko)을 받으며, Payload의
locale 쿼리 파라미터로 보냅니다. 지정하지 않으면 요청은 그대로이고 서버는
플랫폼 기본 locale en으로 씁니다. 쓰기는 워크스페이스 기본 locale로 바꿔
쓰지 않으므로, ko만 켠 워크스페이스(한국어 온보딩 기본값)에서는
{ locale: 'ko' }를 넘겨야 합니다. locale을 생략했거나 켜지지 않은 locale로
쓰면 HTTP 400과 apiReason content_locale_not_enabled로 실패합니다.
Import 경계
브라우저 안전 client, commerce helper, core type에는 @01.software/sdk를
사용합니다. createServerClient는 @01.software/sdk/server에서 import하고,
React Query helper가 필요할 때만 @01.software/sdk/query를 사용합니다. 오류
가드는 client를 만드는 client/server entry에서 사용할 수 있으며, root와
@01.software/sdk/errors entry 사이에서도 동일한 오류를 판별합니다.
none으로 표시된 항목은 @01.software/sdk 외에 추가 패키지가 필요하지
않습니다.
| Import | Feature(s) | 설치할 때 |
|---|---|---|
@01.software/sdk | browser-safe createClient, commerce helpers, collection helpers, types | none |
@01.software/sdk/client | browser-safe createClient entry and SDK error guards | none |
@01.software/sdk/server | createServerClient, server-only APIs, and SDK error guards | none; secretKey 코드는 서버에 둡니다 |
@01.software/sdk/errors | SDK error classes and guards | none |
@01.software/sdk/metadata | SEO metadata extraction and generation helpers | none |
@01.software/sdk/webhook | webhook handlers, event guards, and webhook types | none |
@01.software/sdk/query | React Query hooks, cache helpers, getQueryClient | @tanstack/react-query, react, react-dom |
@01.software/sdk/realtime | RealtimeConnection, useRealtimeQuery | @tanstack/react-query, react, react-dom |
@01.software/sdk/storefront-cache | product storefront cache resource helper | none |
@01.software/sdk/embedded-admin | embedded app 브라우저 handshake와 protocol type | none |
@01.software/sdk/embedded-admin/server | assertion 검증과 JWKS 조회 | none; receiver session 생성은 서버에 둡니다 |
@01.software/sdk/ui/rich-text | RichTextContent, StyledRichTextContent | react, react-dom, @payloadcms/richtext-lexical |
@01.software/sdk/ui/form | FormRenderer | react, react-dom |
@01.software/sdk/ui/code-block | CodeBlock, highlight | react, react-dom, shiki, hast-util-to-jsx-runtime |
@01.software/sdk/ui/canvas | CanvasRenderer, CanvasFrame, useCanvas, prefetchCanvas | react, react-dom, @tanstack/react-query, @xyflow/react, quickjs-emscripten, postcss, sucrase |
@01.software/sdk/ui/canvas/server | canvas server helpers | postcss >=8 (optional peer) |
@01.software/sdk/ui/video | VideoPlayer | react, react-dom, @mux/mux-player-react |
@01.software/sdk/ui/image | Image | react, react-dom |
동영상 재생
VOD 재생은 앱 서버에서 시청자의 콘텐츠 접근 권한을 확인한 뒤
createServerClient(...).video.playback(video.id)로 URL을 발급합니다. 반환된
streamUrl을 <VideoPlayer video={video} streamUrl={streamUrl} />에 전달하고,
긴 재생이나 일시정지 뒤에는 2시간 만료 전에 갱신해야 합니다. Free 재생은 전송량을 차감하는 게이트웨이를
사용하며 월 한도 초과 시 403, 사용량 확인 불가 시 503을 반환할 수 있습니다.
기존 Mux URL 유틸리티는 전환되지 않은 유료 자산에만 사용합니다. 전환된 자산의
저장된 공개 ID는 과거 사용량 증빙용이며 재생 주소가 아닙니다.
공개 SDK 0.67.0은 아래 저장 비율 매핑을 지원합니다. 공개 0.65.1에는 이 매핑이 없습니다. 사용하는 패키지 버전의 지원 여부를 확인하세요.
VideoPlayer는 저장된 aspectRatio 값 16_9, 9_16, 4_3, 1_1을
placeholder와 ready player 모두에서 CSS 비율로 표시합니다. 미설정 시 기본값은
16/9입니다. ready player는 style.aspectRatio, placeholder는
placeholderStyle.aspectRatio로 덮어쓸 수 있으며, 각 스타일은 해당 렌더 경로에만
적용됩니다.
CanvasRenderer는 예약 shape 노드(terminator, process, decision,
io, subprocess)를 워크스페이스 카탈로그 행이나 QuickJS 템플릿 없이 1st-party SVG
도형으로 렌더링합니다.
목록에 없는 feature는 별도 peer install이 필요하지 않습니다. 전체 매핑은 SDK package README에서 확인합니다.
갤러리 이미지와 영상 아이템
브라우저에서 client.content.galleryItems.list()로 공개 갤러리를 편집 순서대로 읽습니다.
const gallery = await client.content.galleryItems.list({
gallerySlug: 'spring-lookbook',
limit: 24,
})아이템의 mediaType: 'image' | 'video'는 선택 필드이며, 이전 응답에 값이
없으면 image로 처리합니다. 이미지 아이템은 기존 image 미디어를 유지합니다.
영상 아이템은 video.availability: 'ready' | 'processing' | 'unavailable'를
포함하며, 영상 문서 id와 안전한 업로드 thumbnail을 포함할 수 있습니다.
영상 기능이 꺼졌거나 플랜에서 차단된 경우, 대상이 없거나 삭제된 경우, 처리에
실패한 경우에는 { availability: 'unavailable' }만 반환하고 아이템 위치와
페이지 정보를 유지합니다. 이미지 관계를 영상 포스터로 사용하지 않습니다.
ready는 준비 상태이며 재생 권한을 뜻하지 않습니다. 앱 서버에서 요청한 영상이
공개 갤러리와 아이템에 속하는지 확인하고 시청자 접근 정책을 적용한 다음
server.video.playback(video.id)를 호출하세요. 반환된 streamUrl을 플레이어에
전달하면 재생 승인이 현재 워크스페이스, 영상 상태, 기능과 플랜을 다시 확인합니다.
서버 자격 증명은 앱 서버에 보관합니다. 갤러리 DTO에는 재생 URL, provider ID,
토큰, 비공개 저장소 필드가 포함되지 않습니다.
도메인과 전문 연동 참조
- Customers SDK: 고객 identity, hosted 세션과 출처 인식형 그룹 참조.
- Ecommerce SDK: 마켓, 상품 수정, 주문과 storefront 캐시 참조.
- Events SDK: 이벤트의 canonical 날짜와 유료 입장권 projection 참조.
- Community SDK: 브라우저용 게시물 읽기와 nullable 상세 결과 참조.
- Payments: 결제 action 재사용, reconcile과 서버 전용 custom bridge.
- Membership: 유료 구독 준비, 서버가 전달하는 결제 키 보관 세대 조회와 서명된 청구 결과 전달.
- Embedded Admin: 고정 origin 경계에서 embedded app 세션 수신과 검증.
- External Effects: 서버 전용 외부 작업 기록, 설치 암호문 보관과 고객 연결.
유료 Membership 구독
본문과 예제: Membership.
Embedded Admin App
본문과 예제: Embedded Admin.
Collection action은 receiveEmbeddedAdminExecution과 서버 전용
verifyEmbeddedAdminExecution을 사용합니다. 전체 frameUrl을 고정하고 실제
실행 입력을 비교하세요. 필수 consumeExecution callback은 공유 durable 저장소에서
expiresAt+60까지 atomic claim만 하며 업무 부작용을 실행하지 않습니다. 검증은
await 뒤 deadline을 다시 확인하고 false·timeout·불확실 commit·오류를 거절합니다.
성공 반환 뒤에만 업무를 시작하고 앱 자체 idempotency를 적용하세요. v2는 명시적
capability 협상과 별도 audience를 사용하며 v1 identity로 fallback하지 않습니다.
Reload로 prepare의 60초 deadline을 늘릴 수 없습니다. 액션 재실행은 새 execution이며
동일 업무 결과의 재시도를 보장하지 않습니다.
오류 reason
Canonical commerce 실패는 닫힌 typed reason을 CommerceSDKError.reason에
그대로 유지합니다. 그 밖의 endpoint는 open-ended machine reason을
SDKError.apiReason에 노출합니다. 공통 오류 처리에서는
@01.software/sdk/errors의 getSDKErrorReason을 사용하면
details.reason이나 details.body.reason을 직접 검사하지 않고 두 family를
모두 읽을 수 있으며, 알 수 없는 commerce reason은 rawReason 원문으로
반환됩니다.
Raw collection 실패는 Payload 원래 body를 유지합니다. 최상위 reason이 없고
errors[0].data.code(또는 .reason)에 machine code가 있으면 그 값을
apiReason으로 노출합니다(예: content_locale_not_enabled). Typed 오류 클래스와
code는 계속 HTTP status를 따릅니다.
PortOne Standard V2 취소와 재시도
본문과 예제: Payments.
서버 전용 PortOne identity reconcile
본문과 예제: Payments.
서버 전용 커스텀 결제 bridge
본문과 예제: Payments.
멱등 mutation 응답은 exactly-once 게이트가 아닙니다
본문과 예제: Payments.
기존 품목의 기본 가격 수정
본문과 예제: Ecommerce SDK.
주문 수정 호환성
본문과 예제: Ecommerce SDK.
외부 작업 기록 (서버 전용)
본문과 예제: External Effects.
설치 자격 암호문 보관 (서버 전용)
본문과 예제: External Effects.
테넌트 컨텍스트 조회
신뢰할 수 있는 서버 워크플로에서는 서버 전용 client로 해석된 워크스페이스 feature, 활성·비활성 collection, field configuration을 조회할 수 있습니다:
import { createServerClient } from '@01.software/sdk/server'
const server = createServerClient({
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
secretKey: process.env.SOFTWARE_SECRET_KEY!,
})
const context = await server.tenant.context()
const productSchema = await server.tenant.collectionSchema('products')Collection count와 webhook configuration이 필요할 때만
server.tenant.context({ includeCounts: true })를 사용하세요. 이 모드는 추가
읽기를 수행하므로 기본 호출보다 느립니다. 이 API와 두 credential은 신뢰할 수
있는 서버 코드에만 두세요.
productSchema.collection.customFields는 optional입니다. 값이 있으면
customData에 저장되는 active tenant-defined field를 machine key, type,
optional required marker, select option 저장값으로 설명합니다. 운영자 label과
저장된 document 값은 제외되며 실제 read/write는 기존 collection 권한을 그대로
따릅니다.
출처 인식형 고객 그룹
본문과 예제: Customers SDK.
외부 Customer 연결
본문과 예제: External Effects.
고객 인증
본문과 예제: Customers SDK.
고객 셀프 탈퇴
본문과 예제: Customers SDK.
커뮤니티 공개 읽기
본문과 예제: Community SDK.
이벤트 범위와 종일 날짜
본문과 예제: Events SDK.
이벤트 입장권
본문과 예제: Events SDK.
마켓 컨텍스트
본문과 예제: Ecommerce SDK.
Analytics
웹사이트 분석에는 GA4, Plausible, Vercel Analytics 같은 외부 도구를 직접 연동하세요. SDK는 더 이상 분석 수집 헬퍼를 제공하지 않습니다.
Storefront Cache Resources
본문과 예제: Ecommerce SDK.
Next.js 매핑
본문과 예제: Ecommerce SDK.
운영 전 확인
- 워크스페이스 플랜에서 collection과 기능 사용 가능 여부를 확인합니다.
- 키 저장 위치와 회전 담당자를 확인합니다.
- 고객 노출 흐름의 실패 처리 경로를 확인합니다.
Secret Key 값을 브라우저 bundle, public env, analytics event, 생성 예제에 넣지 않습니다.
오류, 재시도와 요청 identity
번역되는 error 문구 대신 machine reason으로 분기하고 지원용 requestId를 보존하세요. 알 수 없는 reason도 처리해야 합니다. Canonical commerce는 CommerceSDKError.reason(미지 값은 rawReason), 일반 오류는 SDKError.apiReason이며 getSDKErrorReason으로 둘 다 읽습니다. 잘못된 canonical envelope나 body/header request ID 불일치는 protocol 오류입니다.
SDK 자동 실패 재시도는 safe method(GET, HEAD, OPTIONS)에만 적용되며, network·timeout 실패와 재시도 가능한 HTTP 실패(기본 상태: 408/500/502/503/504)를 SDK의 bounded retry budget 안에서 bounded backoff(기본 exponential)로 재시도합니다. GET에는 이미 이 재시도가 적용되므로 시도 횟수를 중첩시키는 바깥 재시도 루프를 추가하지 마세요. terminal 429는 자동 재시도하지 않으며 Retry-After를 따릅니다. Customer-token 401은 token_expired일 때만 refresh 1회를 허용하며, 원래 요청은 GET/HEAD/OPTIONS만 재전송하고 unsafe method는 원래 인증 오류를 반환합니다. token exchange나 unsafe mutation을 자동 재전송하지 마세요. 지원 mutation의 응답이 끊기면 원래 durable idempotencyKey를 재사용합니다. 응답 flag로 자신의 부수 효과가 중복 제거되지는 않습니다.
refresh 성공 뒤 안전한 요청의 1회 재전송은 일반 재시도 예산과 별도로 보장합니다. retry.maxRetries: 0이거나 마지막 attempt에서 만료 응답을 받아도 적용되며, 일반 재시도 예산을 초기화하지는 않습니다. 다시 401을 받으면 추가 refresh 없이 해당 응답의 typed 인증 오류와 request ID를 반환합니다.
refresh 뒤 canonical commerce 응답의 retryable이 false이거나 생략됐을 때, 또는 Retry-After가 자동 대기 상한을 넘을 때는 추가 자동 재시도 없이 해당 응답의 typed 오류와 request ID를 반환합니다.
validateAuthenticatedCustomer를 사용하는 endpoint(갱신 endpoint 자체는 제외)의 token_expired는 서명·발급자·audience·tenant·활성 고객 검사를 통과하고 access token의 만료 시점만 지났다는 뜻입니다. 폐기 검증이 활성화되어 있으면 generation·비밀번호 변경 시점·JTI 폐기 검사도 통과해야 합니다. 만료됐으며 폐기되었거나 다른 검증에 실패한 토큰은 계속 401 token_invalid이며 refresh를 호출하지 않습니다. 유효한 refresh token으로 갱신을 구성해야 하며, 갱신 endpoint 자체는 만료된 legacy bearer에 token_invalid를 반환합니다.
분할 예제는 공개 SDK 0.65.1, 필요한 곳에서 Next.js 16.3.8, Node 24.15.0을 사용합니다. 완전한 앱 세션과 배포 검사는 연결된 작업 가이드를 따르세요.
고객 적립금
템플릿 지급, getGrant()로 지급 내역 조회, 계정 조회와 회수는 server.credits, 고객 본인 조회는 브라우저 client.credits를 사용합니다. 월별 지급 recipe와 정확한 Checkout funding 협상은 고객 적립금을 확인하세요.
다음 액션
- 정확한 HTTP 계약이 필요하면 API를 엽니다.
- shell 사용 coding agent가 필요하면 CLI를 엽니다.
- hosted·shell 없는 discovery가 필요하면 MCP를 엽니다.
- 이벤트 전달이 필요하면 웹훅을 엽니다.
- 비회원 구매는 Ecommerce: Cart·Checkout·PortOne Standard V2·주문 확인을 따릅니다.
고객 혜택권
benefitVouchers는 콘솔의 교환·이용 쿠폰 API입니다. tenant-admin은 이커머스 → 교환·이용 쿠폰 종류에서 발급할 내용을 설정합니다. 종류를 등록하는 것과 고객에게 발급하는 것은 별도 단계입니다. 이 쿠폰은 직원이 주문 없이 1회 사용 처리하며 Checkout 할인·적립금에 영향을 주지 않습니다. 발급 대상과 시점은 워크스페이스 서버가 결정합니다(멤버십 webhook, 자체 cron 등). source 유일성은 중복 효과만 막으며 자격이나 일정은 검증하지 않습니다.
import { createServerClient } from '@01.software/sdk/server'
const server = createServerClient({ publishableKey, secretKey })
const { voucher, created } = await server.benefitVouchers.issue({
definitionId,
customerId,
sourceNamespace: 'member-benefits',
sourceRef: 'period:2030-01:member-opaque-id',
})
const redeemed = await server.benefitVouchers.redeem({
customerId,
voucherId: voucher.id,
requestId,
actorRef: 'staff:opaque-id',
})서버 benefitVouchers.get({ customerId, voucherId }), list({ customerId, state?, definitionId?, limit?, cursor? })는 read scope, issue, redeem, voidVoucher({ customerId, voucherId, requestId, actorRef, reason })는 write scope가 필요합니다. sk01 서버 키를 사용하고 Origin을 보내지 않습니다. 무효화 사유는 issued_in_error, benefit_revoked, customer_request, other입니다. 발급·사용에는 Ecommerce와 Customers가 모두 활성화되어야 하며, 기능을 해제해도 서버 get/list·고객 본인 목록·무효화는 유지됩니다. 콘솔 패널은 고객 상세 안에 있으므로 Customers를 끄면 고객 상세와 함께 표시되지 않습니다.
발급 결과를 모르면 같은 sourceNamespace/sourceRef와 같은 요청으로 재시도합니다. 사용·무효화는 request UUID를 한 번 생성하여 재사용합니다. POST 명령은 자동 재시도하지 않습니다. 같은 식별자를 다른 요청에 쓰면 409입니다. 사용·무효화는 되돌릴 수 없고, 만료는 DB 시각으로 판단하며 expiresAt과 같으면 만료입니다. 정정은 무효화 후 새 source identity로 발급합니다.
고객은 client.benefitVouchers.listMine({ status: 'available', limit: 20 })로 본인 목록만 읽습니다. status: 'all'은 이력을 포함하며 nextCursor로 다음 페이지를 조회합니다(최대 100건). 응답은 식별자·고객 안내·상태·시각만 포함하고 private, no-store입니다. 서버 조회는 customerId가 필수이며 다른 고객·tenant 대상은 404로 처리합니다. 콘솔 고객 상세에서는 역할에 따른 사용 처리·무효화 확인도 제공합니다.
직원 앱 서버는 사용 전에 직원 권한과 고객을 확인해야 합니다. voucherId 자체는 사용 권한이 아닙니다. actorRef는 앱이 주장한 증거이며 플랫폼이 검증한 직원 신원이나 권한 입력이 아닙니다. sourceRef·actorRef에는 개인정보 없이 불투명 식별자를 사용합니다. 발급 시 이름·안내·만료 시각을 복사하므로 정의를 수정해도 기존 쿠폰은 바뀌지 않습니다. 이력은 고객 개인정보 삭제 뒤에도 보존되고 tenant 영구 삭제를 차단합니다. 참조된 정의는 보관 처리합니다.