shop24 Docs

Storefront SDK

구매자 API 클라이언트 — createStorefrontClient 사용법

@shop24/storefront-sdk스토어프론트 API의 zod 스키마와 fetch 클라이언트를 제공합니다. 표준 응답 엔벨로프($.data)를 자동으로 언랩하고, 모든 응답을 zod 스키마로 파싱합니다.

npm install @shop24/storefront-sdk

클라이언트 생성

import { createStorefrontClient } from "@shop24/storefront-sdk";

const sdk = createStorefrontClient({
  baseUrl: "https://shop.api.avarlabs.com/storefront/v1",
  /** 테넌트 지정 → X-Store-Code 헤더. 생략하면 서버가 요청 Host의 서브도메인으로 해석 */
  storeCode: "demo",
  /** 비회원 장바구니 토큰 → X-Cart-Token 헤더 */
  cartToken: () => localStorage.getItem("cartToken"),
  /** 서버가 새 X-Cart-Token을 발급했을 때 호출 */
  onCartToken: (token) => localStorage.setItem("cartToken", token),
});

옵션 정리 (StorefrontClientOptions):

옵션타입설명
baseUrlstringAPI 베이스 URL (필수)
storeCodeTokenSource테넌트 코드 → X-Store-Code 헤더. 생략 시 서버가 Host 서브도메인으로 테넌트를 해석
accessTokenTokenSourceAuthorization: Bearer 헤더
auth{ accessToken?: string }accessToken의 별칭 (한쪽만 쓰면 됩니다)
sessionStorefrontSession세션을 넘기면 Bearer 부착·401 refresh·인증 만료 정리가 자동 배선됩니다
cartTokenTokenSource비회원 카트 토큰 → X-Cart-Token 헤더
onCartToken(token: string) => void응답의 X-Cart-Token 헤더 수신 콜백
fetchtypeof fetch커스텀 fetch (로깅 등)

TokenSourcestring | null | undefined | (() => string | null | undefined)입니다. 함수로 넘기면 매 요청마다 재평가됩니다.

Access Token 소스는 여러 옵션을 동시에 줄 경우 accessTokenauth.accessTokensession.accessToken 순으로 우선합니다.

인증 — 세션

createStorefrontSession은 로그인/회원가입, 토큰 저장, 자동 갱신을 처리합니다. 구매자는 OAuth authorize 화면 없이 login() / signup()으로 바로 토큰 페어를 받습니다.

토큰을 어디에 저장할지는 앱이 SessionStorage 어댑터로 결정합니다 (get / set / remove).

import {
  createStorefrontSession,
  createStorefrontClient,
} from "@shop24/storefront-sdk";

const session = createStorefrontSession({
  baseUrl: "https://shop.api.avarlabs.com/storefront/v1",
  storeCode: "demo",
  storage: {
    get: (key) => localStorage.getItem(key),
    set: (key, value) => localStorage.setItem(key, value),
    remove: (key) => localStorage.removeItem(key),
  },
});

await session.login({ email: "buyer@example.com", password: "buyer1234!" });
const me = await session.userinfo();

// 세션을 넘기면 클라이언트가 Bearer·401 refresh·만료 정리를 자동 처리한다
const sdk = createStorefrontClient({
  baseUrl: "https://shop.api.avarlabs.com/storefront/v1",
  storeCode: "demo",
  session,
});

StorefrontSession:

멤버설명
login(LoginRequest)로그인 후 토큰 페어를 저장
signup(SignupRequest)회원가입 후 토큰 페어를 저장
refresh()Access Token 갱신, 성공 여부(boolean) 반환. 동시 호출은 single-flight로 한 번만 나갑니다
accessToken / refreshToken현재 저장된 토큰 게터
userinfo()회원 정보(Member) 또는 null
clear()저장 키 제거 + best-effort 로그아웃
subscribe(cb)인증 상태 변경 구독, 해제 함수 반환

session을 클라이언트에 연결하면 401 응답 시 자동으로 refresh()를 1회 시도하고, 갱신에 실패하면 세션을 정리합니다. 별도의 재시도 래퍼를 만들 필요가 없습니다.

session을 클라이언트에 넘긴 경우 sdk.accessToken / sdk.refreshToken 게터와 sdk.userinfo()로 세션에 바로 접근할 수 있습니다.

세션 없이 직접 호출

세션을 쓰지 않고 sdk.auth를 직접 호출할 수도 있습니다. 이때 토큰 저장·갱신은 앱이 관리합니다.

const tokens = await sdk.auth.login({
  email: "buyer@example.com",
  password: "buyer1234!",
});
// { accessToken, refreshToken, expiresIn } — expiresIn은 서버가 반환하는 만료 시간(초)

// Refresh는 rotation 방식 — 갱신하면 이전 refreshToken은 무효화된다
const rotated = await sdk.auth.refresh(tokens.refreshToken);

await sdk.auth.logout();

signup은 약관 동의와 함께 회원가입 후 로그인과 동일한 토큰 페어를 반환합니다.

const tokens = await sdk.auth.signup({
  email: "buyer@example.com",
  password: "buyer1234!",
  name: "홍길동",
  phone: "01012345678",
  agreements: { terms: true, privacy: true, marketing: false },
});

카탈로그 — 인증 불필요

// 카테고리 트리
const categories = await sdk.catalog.listCategories();

// 상품 검색 (페이지네이션 + 필터 + 정렬)
const page = await sdk.catalog.searchProducts({
  keyword: "티셔츠",
  page: 1,
  size: 20,
  sort: "priceAsc",
});
console.log(page.totalElements, page.contents[0].name);

// 상품 상세
const product = await sdk.catalog.getProduct("prod_001");

// 리뷰 (평점/사진/정렬 필터)
const reviews = await sdk.catalog.listProductReviews("prod_001", { sort: "helpful" });

// 상품 문의 조회 / 작성
const inquiries = await sdk.catalog.listProductInquiries("prod_001", { page: 1 });
await sdk.catalog.createInquiry("prod_001", {
  content: "언제 발송되나요?",
  secret: true, // 비밀글 (기본 false)
});

장바구니 — 비회원 토큰 자동 처리

비회원이 처음 장바구니에 담으면 서버가 X-Cart-Token을 발급하고, SDK가 onCartToken 콜백으로 전달합니다. 이후 요청에는 저장된 토큰이 자동으로 실립니다.

// 담기 — 첫 호출 시 onCartToken이 호출되어 토큰이 저장된다
const cart = await sdk.cart.addItem({
  productId: "prod_001",
  optionId: "opt_001",
  quantity: 2,
});

// 조회/수정/삭제
await sdk.cart.get();
await sdk.cart.updateItem(cart.items[0].cartItemId, { quantity: 3 });
await sdk.cart.removeItem(cart.items[0].cartItemId);

체크아웃

체크아웃 세션 생성 → 결제 요청 → 결제 승인 3단계입니다.

// 1. 장바구니 항목으로 체크아웃 세션 생성
const checkout = await sdk.checkout.create({ cartItemIds: [cart.items[0].cartItemId] });

// 2. 배송지·결제수단으로 결제 파라미터 요청
const pay = await sdk.checkout.requestPayment(checkout.checkoutId, {
  shippingAddress,
  paymentMethod: "CARD",
});

// 3. PG 인증 후 받은 토큰으로 결제 승인
const result = await sdk.checkout.confirmPayment(pay.paymentId, pgToken);

주문/마이페이지

// 내 주문 목록 (상태 필터) / 단건 조회
const orders = await sdk.myOrders.list({ status: "DELIVERED" });
const order = await sdk.myOrders.get("order_001");

// 비회원 주문 조회
const guest = await sdk.myOrders.getGuestOrder("order_001", {
  phone: "01012345678",
  orderPassword: "1234",
});

// 배송 조회 / 구매 확정
await sdk.myOrders.getDelivery("oi_001");
await sdk.myOrders.decidePurchase("oi_001");

// 클레임 (취소/반품/교환)
await sdk.myClaims.create("oi_001", {
  type: "RETURN",
  reason: "CHANGE_OF_MIND",
  reasonDetail: "사이즈가 맞지 않아요",
});
const claims = await sdk.myClaims.list({ status: "REQUESTED" });
await sdk.myClaims.withdraw("claim_001");

회원 · 리뷰 · 문의

// 회원 정보 / 수정
const me = await sdk.member.me();
await sdk.member.updateMe({ name: "홍길동" });

// 배송지
const addresses = await sdk.member.listAddresses();
await sdk.member.createAddress(address);

// 찜
const wishlist = await sdk.member.listWishlist({ page: 1 });
await sdk.member.addWishlist("prod_001");
await sdk.member.removeWishlist("prod_001");

// 작성 가능한 리뷰 조회 / 작성
const writable = await sdk.reviews.listWritable();
await sdk.reviews.create("oi_001", { rating: 5, content: "배송도 빠르고 품질도 좋았습니다" });

// 내 문의
const myInquiries = await sdk.inquiries.listMine({ answered: false });

에러 처리 — ApiError

HTTP 에러는 항상 ApiError로 던져집니다. 표준 엔벨로프의 $.error(code/message/traceId)가 매핑됩니다.

import { ApiError } from "@shop24/storefront-sdk";

try {
  await sdk.cart.addItem({ productId: "prod_x", quantity: 1 });
} catch (error) {
  if (error instanceof ApiError) {
    // error.status: HTTP 상태 코드
    // error.code:   도메인 에러 코드 (예: OUT_OF_STOCK, UNAUTHORIZED)
    // error.traceId: 서버 로그 추적용
    if (error.code === "OUT_OF_STOCK") showToast("재고가 부족합니다");
  }
}

On this page