> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 개요

> Aftersell Cart SDK의 작동 방식: 전역 진입점, API의 네 가지 구성 요소, 로드 시점, 그리고 안전하게 코드를 실행하는 방법.

**Cart SDK**는 스토어프런트의 Aftersell Cart를 위한 JavaScript API예요. 카트의 동작 방식을 변경하고, 쇼핑객의 행동에 반응하고, 코드에서 카트의 내용을 읽거나 변경할 수 있어요.

SDK 코드는 [커스텀 스크립트](/ko/aftersell/cart/custom-scripts)를 통해 실행하거나, 자체 UI를 렌더링하는 블록의 경우 [커스텀 코드 블록](/ko/aftersell/cart/custom-code-blocks)의 React 모드를 통해 실행해요.

<Note>
  머천트가 SDK에 요청하는 것들 중 상당수는 이미 설정으로 제공되고 있어요. 스크립트를 작성하기 전에 [카트 블록](/ko/aftersell/cart/blocks-overview), [마켓/국가/통화별 조건](/ko/aftersell/cart/blocks-overview#show-or-hide-by-market-country-or-currency), 또는 [카트 설정](/ko/aftersell/cart/cart-settings)으로 이미 가능한지 확인해 보세요. 이런 기능들은 카트 리디자인 후에도 계속 작동하지만, 스크립트는 그렇지 않을 수 있어요.
</Note>

<div id="the-global-entry-point">
  ## 전역 진입점
</div>

모든 것은 하나의 전역 객체에서 시작돼요:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart
```

<Note>
  **이 문서의 모든 스니펫은 `window.aftersell.cart`를 전체로 작성**하기 때문에, 어떤 스니펫이든 붙여넣기만 하면 그 자체로 작동해요. 한 번 별칭을 지정한 뒤(`const cart = window.aftersell.cart;`) 그 이후로 `cart`를 사용하는 것도 완전히 유효하며, 카트가 로드되기 전에도 안전해요. 다만 스니펫을 줄여 쓸 때는 그 줄을 꼭 포함하세요. `cart`만 단독으로 쓰면 `cart is not defined` 오류가 발생해요.
</Note>

네 가지 구성 요소가 실제 작업을 담당해요:

<Columns cols={2}>
  <Card title="Configure" icon="sliders" href="/ko/aftersell/cart/sdk-configure">
    카트의 동작 방식을 설정해요: 드로어가 열리는 시점, 금액 표시 형식, Aftersell이 장바구니 담기를 가로챌지 여부.
  </Card>

  <Card title="Events" icon="tower-broadcast" href="/ko/aftersell/cart/sdk-events">
    일어나는 일에 반응해요: 카트가 로드됨, 상품이 추가됨, 드로어가 열림, 체크아웃이 클릭됨.
  </Card>

  <Card title="Actions" icon="wand-magic-sparkles" href="/ko/aftersell/cart/sdk-actions">
    카트를 읽고 변경해요: 카트 열기, 상품 추가, 수량 업데이트, 현재 상태 읽기.
  </Card>

  <Card title="Hooks" icon="plug" href="/ko/aftersell/cart/sdk-hooks">
    카트 자체의 작동 방식을 변경해요: 라인 숨기기 또는 레이블 변경, 순서 재정렬, 추가 데이터 첨부, 장바구니 담기 제어.
  </Card>
</Columns>

<Note>
  스크립트가 장바구니 담기에서 더 이상 실행되지 않는다면 [장바구니 담기 가로채기](/ko/aftersell/cart/add-to-cart-interception)부터 시작하세요. Aftersell이 왜 추가를 대신 처리하는지, 그리고 폼을 제외하는 모든 방법을 설명해요.
</Note>

그리고 세 가지 작은 멤버가 있어요:

| 멤버           | 용도                                       |
| ------------ | ---------------------------------------- |
| `ready()`    | 카트가 처음 로드되면 resolve되는 Promise예요.         |
| `context`    | 서버에서 렌더링된 구매자 컨텍스트로, 동기적으로 읽을 수 있어요.     |
| `shadowRoot` | 카트의 shadow root로, 드로어 내부 요소를 쿼리할 때 사용해요. |

<div id="events-actions-or-hooks">
  ## 이벤트, 액션, 아니면 훅?
</div>

이 세 가지는 혼동하기 쉬운데, 잘못 선택하는 것이 스크립트가 작성자의 의도대로 작동하지 않는 가장 흔한 이유예요:

| 원하는 것…               | 사용할 것   | 예시                          |
| -------------------- | ------- | --------------------------- |
| *무언가 일어났을 때* 코드 실행   | **이벤트** | 상품이 추가되면 분석 이벤트를 전송해요.      |
| 카트에 *들어 있는 내용* 변경    | **액션**  | 총액이 \$50을 넘으면 무료 사은품을 추가해요. |
| *카트의 작동 방식이나 렌더링* 변경 | **훅**   | 무료 사은품 라인을 드로어에서 숨겨요.       |

가장 중요한 차이점: **액션은 쇼핑객의 실제 카트**(그리고 총액)를 변경하는 반면, **훅은 렌더링되는 내용만** 변경해요. 훅으로 라인을 숨기면 카트와 총액에는 그대로 남아 있고, 액션으로 제거하면 실제로 사라져요.

<div id="how-and-when-it-loads">
  ## 로드 방식과 시점
</div>

카트는 두 단계로 로드되며, SDK는 순서를 신경 쓸 필요가 없도록 설계되어 있어요:

1. 작은 **스텁**이 `window.aftersell.cart`를 즉시 생성하므로 항상 존재해요.
2. 전체 SDK가 곧이어 로드되어 스텁을 그 자리에서 업그레이드하므로, 이전에 캡처한 참조도 계속 작동해요.

이에 따라 호출은 두 가지 범주로 나뉘어요:

<Columns cols={2}>
  <Card title="설정 호출: 즉시 안전" icon="circle-check">
    `configure(...)`, `events.on(...)`, 그리고 모든 `hooks.register*` 호출. 부팅 전에는 버퍼링되었다가 SDK가 로드되면 순서대로 재생돼요. 스크립트 맨 위에 두세요.
  </Card>

  <Card title="액션: ready()를 기다리세요" icon="clock">
    `actions.*` 아래의 모든 것. `ready()` 안이나 이벤트 핸들러 안에서 실행하세요. 너무 일찍 호출하면 콘솔에 경고를 남기고 아무 일도 하지 않아 안전해요: 비동기 액션은 여전히 resolve되므로 `.then()` 체인이 깨지지 않아요.
  </Card>
</Columns>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Set-up: fine at the top level, before the cart has loaded.
window.aftersell.cart.configure({ open_on_add_to_cart: 'always' });

window.aftersell.cart.events.on('item_added', (payload) => {
  console.log('Added', payload.item.title);
});

// Actions: wait until the cart is ready.
window.aftersell.cart.ready().then(() => {
  const state = window.aftersell.cart.actions.getCart();
  console.log(state.itemCount, 'items');
});
```

<div id="ready">
  ### ready()
</div>

`ready()`는 첫 번째 카트 로드가 \*\*완료(settle)\*\*되면 resolve되는 Promise를 반환해요. 성공뿐 아니라 실패 시에도 resolve되므로, 불안정한 연결 상태의 쇼핑객이 있어도 스크립트가 멈춰 있는 일이 없어요. 카트가 도착했다고 가정하지 말고 `getCart()`가 `null`인지 확인하세요.

카트가 이미 로드된 후에 `ready()`를 호출하면 즉시 resolve되므로, 코드 어디에서든 일반적인 "이제 카트가 존재한다"라는 게이트로 안전하게 사용할 수 있어요.

<Tip>
  이벤트 핸들러 안에서는 `ready()`가 필요 없어요. `cart_loaded`, `cart_updated`, `item_added`가 발생하는 시점에는 카트가 이미 로드되어 있어 액션을 안전하게 호출할 수 있어요.
</Tip>

<div id="context">
  ## context
</div>

`window.aftersell.cart.context`는 서버에서 렌더링된 구매자 데이터를 담고 있으며, `ready()` 없이 동기적으로 읽을 수 있어요. 카트가 로드되기 전에 실행되어야 하는 마켓 또는 국가 분기 처리에 사용하세요.

| 필드                        | 설명                                        | 부팅 전 사용 가능               |
| ------------------------- | ----------------------------------------- | ------------------------ |
| `shopify_market`          | 구매자의 Shopify 마켓.                          | 예                        |
| `customer_country`        | 두 글자 국가 코드.                               | 예                        |
| `customer_currency`       | 활성 통화 코드.                                 | 예                        |
| `money_format`            | 스토어의 Shopify 금액 형식.                       | 예                        |
| `backend_url`             | 직접 백엔드 호스트로, 앱 프록시가 구성되지 않았을 때 폴백으로 사용돼요. | 예                        |
| `storefront_access_token` | Storefront API 호출용 토큰.                    | **아니요** — 카트가 부팅될 때 추가돼요 |

<Warning>
  `storefront_access_token`은 서버가 `cart.context`에 렌더링하지 않는 유일한 `context` 필드예요. 카트가 부팅될 때 `context`에 추가되므로, 스크립트 맨 위에서 읽으면 `undefined`가 반환돼요. 먼저 `window.aftersell.cart.ready()`를 await하세요.
</Warning>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
if (window.aftersell.cart.context.customer_country === 'CA') {
  // Canada-only behavior, decided before the cart loads.
}
```

<Note>
  마켓, 국가 또는 통화별로 다른 블록 설정을 표시하려면 스크립트 대신 [카트 에디터의 조건](/ko/aftersell/cart/blocks-overview#show-or-hide-by-market-country-or-currency)을 사용하세요. 스크립트가 필요 없어요. 전체 조건 UI는 현재 [Rewards](/ko/aftersell/cart/rewards-block#per-market-rewards)에서 제공돼요.
</Note>

<div id="shadowroot">
  ## shadowRoot
</div>

카트는 shadow root 안에서 렌더링되므로 `document.querySelector`는 **드로어 내부의 어떤 것도 볼 수 없어요**. 카트 안의 요소에 접근하려면 shadow root를 쿼리하세요:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const root = window.aftersell.cart.shadowRoot;
const button = root?.querySelector('.cart-external-checkout-button');
```

[커스텀 CSS](/ko/aftersell/cart/custom-css)에서 사용하는 것과 동일한 **공개 `cart-external-*` 클래스**를 대상으로 하세요. 이것들이 지원되는 핸들이에요. `cart-internal-*` 쌍둥이 클래스는 카트 자체의 내부 구조이므로, 대신 external 클래스를 쿼리하세요.

<Warning>
  블록, 설정, 훅으로 해결되지 않을 때만 shadow root를 사용하세요. 훅은 카트 리디자인 후에도 살아남지만, DOM 쿼리는 여러분의 코드가 직접 유지 관리해야 하는 문제가 돼요.
</Warning>

shadow root는 카트가 부팅된 후에만 존재하므로, 스크립트 맨 위가 아닌 `ready()` 안이나 이벤트 핸들러 안에서 읽으세요.

<div id="debugging">
  ## 디버깅
</div>

깨진 스크립트가 장바구니 담기나 드로어를 망가뜨려서는 절대 안 되기 때문에, SDK는 실패를 전파하지 않고 내부에 가둬요. 실패가 어디에 표시되는지는 무엇이 깨졌는지에 따라 달라요:

| 실패한 것                                              | 표시되는 곳                                  |
| -------------------------------------------------- | --------------------------------------- |
| 스크립트가 최상위 레벨에서 throw함                              | `console.error`에 해당 줄과 실행되지 않은 부분이 표시돼요 |
| [이벤트](/ko/aftersell/cart/sdk-events) 핸들러가 throw함   | `console.error`; 다른 핸들러는 계속 실행돼요        |
| [훅](/ko/aftersell/cart/sdk-hooks)이 throw함          | 조용히 처리돼요. 아래의 디버그 채널로 이동해요              |
| [액션](/ko/aftersell/cart/sdk-actions)이 카트 로드 전에 실행됨 | `console.warn`; 호출은 아무 일도 하지 않아요        |

<div id="when-your-script-throws">
  ### 스크립트가 throw할 때
</div>

커스텀 스크립트는 **첫 번째 오류에서 멈추기** 때문에, 그 줄 아래의 모든 `configure`, `events.on`, `hooks.register*`는 실행되지 않아요. 카트는 이를 명시적으로 알려줘요:

```
[aftersell-cart] Initialization script error on line 12 — 4 more line(s) did not run;
any configure/events/hooks below are unregistered.
```

분명히 등록한 핸들러가 전혀 실행되지 않을 때 찾아봐야 할 메시지가 바로 이거예요: 아마 그 줄까지 도달하지 못한 거예요. 줄 번호는 실행이 멈춘 최상위 문장의 위치이며, throw한 내부 함수가 아니에요. 브라우저의 스택을 사용할 수 없으면 추측하지 않고 생략돼요.

스크립트는 자체 파일명으로 실행되므로, DevTools에서 `aftersell-cart-init.js`와 `aftersell-cart-cart-update.js`로 표시돼요. Sources 패널에서 열어 다른 파일처럼 중단점을 설정할 수 있어요.

<div id="the-debug-channel">
  ### 디버그 채널
</div>

훅 실패는 쇼핑객이 절대 볼 수 없도록 의도적으로 콘솔에 표시되지 않아요. 대신 여기로 가요:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// After reproducing the problem, inspect what was swallowed:
window.aftersellCartDebugEvents.filter((entry) => entry.level === 'ERROR');

// Or watch them live:
window.addEventListener('aftersell-cart-debug', (event) => console.log(event.detail));
```

<div id="where-to-go-next">
  ## 다음 단계
</div>

<Columns cols={2}>
  <Card title="Configure" icon="sliders" href="/ko/aftersell/cart/sdk-configure">
    모든 옵션과 각각의 예시.
  </Card>

  <Card title="Events" icon="tower-broadcast" href="/ko/aftersell/cart/sdk-events">
    모든 이벤트, 발생 시점, 그리고 핸들러에서 하지 말아야 할 것.
  </Card>

  <Card title="Actions" icon="wand-magic-sparkles" href="/ko/aftersell/cart/sdk-actions">
    모든 액션과 각각의 스니펫.
  </Card>

  <Card title="Hooks" icon="plug" href="/ko/aftersell/cart/sdk-hooks">
    모든 훅과 등록이 조합되는 방식.
  </Card>

  <Card title="Cart 객체" icon="table-list" href="/ko/aftersell/cart/sdk-cart-object">
    카트와 라인의 구조.
  </Card>

  <Card title="사용 사례" icon="book-open" href="/ko/aftersell/cart/sdk-use-cases">
    자주 요청되는 사항에 대한 완전하고 실행 가능한 솔루션.
  </Card>
</Columns>
