> ## 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.

# 커스텀 스크립트

> Initialization 및 On cart update 스크립트 슬롯으로 Aftersell Cart에서 커스텀 JavaScript를 실행하세요.

커스텀 스크립트를 사용하면 [Cart SDK](/ko/aftersell/cart/sdk-overview)를 사용해 장바구니에 대해 직접 만든 JavaScript를 실행할 수 있어요. 카트 에디터의 **Cart settings → Custom script**에서 추가하며, 드롭다운으로 두 슬롯 사이를 전환해요: **Initialization**과 **On cart update**예요.

이 에디터에는 `<script>` 태그 없이 일반 JavaScript를 작성하세요. **On cart update**에는 시작 템플릿을 복원하는 **Reset to default** 작업이 있지만, **Initialization**에는 없으므로 지우기 전에 직접 사본을 보관하세요.

<Note>
  머천트가 예전에 스크립트로 처리하던 많은 것들이 이제 기본 제공 설정이 되었어요. 먼저 [스크립트를 작성하기 전에](/ko/aftersell/cart/sdk-use-cases#before-you-write-a-script)를 확인하세요: 설정은 장바구니 리디자인 후에도 계속 작동하지만, 스크립트는 그렇지 않을 수 있어요.
</Note>

<div id="which-slot-to-use">
  ## 어떤 슬롯을 사용할까요
</div>

|           | Initialization                                                                                                                                                  | On cart update                      |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| **실행 시점** | 장바구니가 로드될 때 한 번.                                                                                                                                                | 첫 로드 이후 장바구니가 변경될 때마다.              |
| **작성 범위** | 스크립트 전체.                                                                                                                                                        | 핸들러 본문만. `cart_updated` 래퍼는 잠겨 있어요. |
| **용도**    | 동작을 한 번 등록하기: [`configure`](/ko/aftersell/cart/sdk-configure), [`events.on`](/ko/aftersell/cart/sdk-events), [`hooks.register*`](/ko/aftersell/cart/sdk-hooks). | 장바구니의 현재 내용에 대해 다시 평가해야 하는 규칙.      |
| **예시**    | 라인 변환으로 무료 사은품 라인 숨기기.                                                                                                                                          | 지출 기준에 맞춰 무료 사은품 동기화 유지하기.          |

<div id="initialization">
  ## Initialization
</div>

**Initialization** 스크립트는 **장바구니가 로드될 때 한 번** 실행돼요. 장바구니 동작 구성, 이벤트 구독, 훅 등록 등 설정 작업의 진입점이에요. [SDK](/ko/aftersell/cart/sdk-overview)는 `window.aftersell.cart`로 사용할 수 있어요.

여기서 하는 설정 호출([`configure(...)`](/ko/aftersell/cart/sdk-configure), [`events.on(...)`](/ko/aftersell/cart/sdk-events), [`hooks.*`](/ko/aftersell/cart/sdk-hooks))은 장바구니가 완전히 부팅되기 전이라도 스크립트 상단에서 안전하게 호출할 수 있어요. 버퍼링되었다가 부팅되면 적용돼요. 장바구니를 읽거나 변경하는 액션([`addItem`](/ko/aftersell/cart/sdk-actions#additemvariantid-quantity)이나 [`getCart`](/ko/aftersell/cart/sdk-actions#getcart) 등)은 [`ready()`](/ko/aftersell/cart/sdk-overview#ready) 안이나 이벤트 핸들러 안에서 실행해야 해요.

이 슬롯은 세 가지 **주석 처리된** 예시로 시작해요 — 추가할 때마다 드로어 열기, `cart_loaded`에 반응하기, 무료 사은품 라인 숨기기 — 따라서 손대지 않은 Initialization 스크립트는 아무것도 하지 않아요. 하나의 주석을 해제해 시도하거나 교체하세요.

이 슬롯의 자연스러운 형태는 **이벤트가 개입하지 않는 일회성 등록**이에요: 동작을 한 번 등록하면 장바구니가 그 이후로 적용해요. 총액을 변경하지 않고 드로어에서 무료 사은품 라인을 숨기는 것이 기본 제공되는 예시예요:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerLineTransform((line) => {
  if (line.finalLinePrice === 0) line.setHidden(true);
});
```

[`registerLineTransform`](/ko/aftersell/cart/sdk-hooks#registerlinetransform)은 렌더링되는 모든 라인에 대해 실행되고, `setHidden`은 표시 전용이므로 라인은 장바구니에 남아 총액에 계속 포함되며 드로어에만 표시되지 않아요. 변환으로 할 수 있는 더 많은 일은 [장바구니 라인 숨기기 및 라벨 변경](/ko/aftersell/cart/sdk-use-case-hide-lines)을 참고하세요.

장바구니를 읽는 액션은 `ready()` 안에 넣으세요:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.ready().then(() => {
  const state = window.aftersell.cart.actions.getCart();
  console.log('Cart loaded with', state.itemCount, 'items');
});
```

장바구니의 DOM에 접근할 때도 동일한 대기가 필요하며, [`shadowRoot`](/ko/aftersell/cart/sdk-overview#shadowroot)가 필요해요: 장바구니는 shadow root 안에 렌더링되므로 `document.querySelector`는 드로어 안의 아무것도 볼 수 없어요.

<Tip>
  장바구니가 로드되기 **전에** 마켓, 국가, 통화에 따라 분기하나요? 대신 [`context`](/ko/aftersell/cart/sdk-overview#context)를 읽으세요. `ready()` 없이 동기적으로 사용할 수 있으므로, 규칙이 적용되지 않는 구매자에 대해서는 핸들러 등록 자체를 건너뛸 수 있어요.
</Tip>

<div id="on-cart-update">
  ## On cart update
</div>

**On cart update** 스크립트는 장바구니가 변경될 때마다 실행돼요. `cart_updated` 구독을 감싼 잠긴 래퍼이므로 본문만 편집하며, 코드는 업데이트된 `cart`를 받아요.

이 슬롯은 **장바구니가 변경될 때마다 다시 평가해야 하는** 규칙을 위한 것이에요. 무료 사은품 기준이 대표적인 사례예요(\$75 이상 구매 시 무료 토트백 증정). 답이 현재 내용에 달려 있고, 내용이 언제 바뀌는지 다른 방법으로는 알 수 없기 때문이에요:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (cart) => {
  const GIFT_VARIANT_ID = 1234567890;
  const THRESHOLD = 7500;   // $75.00, in cents

  let giftLine = null;
  let subtotal = 0;
  (cart.items ?? []).forEach((line) => {
    if (line.variantId === GIFT_VARIANT_ID) giftLine = line;
    else subtotal += line.finalLinePrice;   // the gift itself never counts toward the threshold
  });

  const shouldHaveGift = subtotal >= THRESHOLD;
  const hasGift = Boolean(giftLine);

  // Bail when the cart already matches. This is the part that matters: adding or
  // removing an item fires cart_updated again, so without this check the handler
  // re-enters itself forever.
  if (shouldHaveGift === hasGift) return;

  if (shouldHaveGift) window.aftersell.cart.actions.addItem(GIFT_VARIANT_ID, 1);
  else window.aftersell.cart.actions.removeItem(giftLine.key);
});
```

<div id="keeping-the-cart-in-a-desired-state">
  ### 장바구니를 원하는 상태로 유지하기
</div>

`if (shouldHaveGift === hasGift) return;` 줄이 이 코드를 안전하게 만들며, 장바구니를 원하는 상태로 유지하는 모든 스크립트에 일반화돼요. 이 슬롯은 장바구니 변경에 반응하는 동시에 변경을 일으키므로, 모든 `addItem`이나 `removeItem`이 슬롯을 다시 진입시켜요. 원하는 상태를 기술하고, 현재 상태와 비교하고, 이미 일치하면 일찍 반환하세요. 그러면 핸들러가 무한 반복하지 않고 한 번의 실행으로 수렴해요. 피해야 할 보호 장치 없는 버전과 페이로드가 읽기 전용인 이유는 [두 가지 규칙](/ko/aftersell/cart/sdk-events#the-two-rules)을 참고하세요.

느린 스토어에서는 모듈 수준의 진행 중(in-flight) 플래그도 유지하는 것이 좋아요. 그러면 두 개의 빠른 변경이 첫 번째 추가가 완료되기 전에 모두 추가를 시작하는 일이 없어요.

<Note>
  `cart_updated`는 첫 로드 **이후의** 변경에만 발생하므로([이벤트 타이밍](/ko/aftersell/cart/sdk-events#cart_updated)), 이 슬롯의 스크립트는 페이지 로드 시 이미 조건을 충족하는 장바구니를 조정하지 않아요. 두 경우를 모두 처리하는 버전은 **Initialization** 슬롯에서 같은 함수로 `cart_loaded`와 `cart_updated`를 구독하세요. [기준 도달 시 무료 사은품 자동 추가](/ko/aftersell/cart/sdk-use-case-free-gift)를 참고하세요.
</Note>

<div id="when-a-script-breaks">
  ## 스크립트가 고장 났을 때
</div>

각 슬롯은 자체 샌드박스에서 실행되므로, 고장 난 **Initialization** 스크립트가 **On cart update**의 실행을 막을 수 없고, 어느 쪽도 장바구니 자체를 망가뜨릴 수 없어요.

하지만 슬롯 안에서는 실행이 **첫 번째 오류에서 멈춰요**. 그 줄 아래의 모든 것은 건너뛰어지므로, 그 아래에 있는 `configure`, `events.on`, `hooks.register*`는 등록되지 않아요. 코드가 맞아 보이는데 "핸들러가 절대 실행되지 않는" 경우의 일반적인 원인이에요.

장바구니는 브라우저 콘솔에서 실패한 줄을 알려주고, 각 슬롯은 자체 파일명(`aftersell-cart-init.js`와 `aftersell-cart-cart-update.js`)으로 실행되므로, DevTools의 Sources 패널에서 둘 중 하나를 열어 중단점을 설정할 수 있어요. 정확한 메시지와 콘솔에 표시되지 않는 훅 실패를 잡아내는 디버그 채널은 [디버깅](/ko/aftersell/cart/sdk-overview#debugging)을 참고하세요.

`cart_loaded`는 [늦게 구독한 쪽에도 재생](/ko/aftersell/cart/sdk-events#cart_loaded)되므로 등록 순서는 전혀 중요하지 않아요. 가장 안전한 구조는 먼저 모든 것을 등록한 다음, 위험한 작업은 핸들러 안에서 수행하는 것이에요. 그러면 throw가 해당 핸들러에만 격리돼요.

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

* **[Cart SDK](/ko/aftersell/cart/sdk-overview)**: 커스텀 스크립트는 SDK 코드를 실행하는 방법이에요. 전체 API는 [configure](/ko/aftersell/cart/sdk-configure), [events](/ko/aftersell/cart/sdk-events), [actions](/ko/aftersell/cart/sdk-actions), [hooks](/ko/aftersell/cart/sdk-hooks) 레퍼런스를, 핸들러가 받는 데이터의 구조는 [장바구니 객체](/ko/aftersell/cart/sdk-cart-object)를, 바로 사용할 수 있는 스니펫은 [사용 사례](/ko/aftersell/cart/sdk-use-cases)를 참고하세요.
* **[커스텀 코드 블록](/ko/aftersell/cart/custom-code-blocks)**: 장바구니에 마크업을 추가할 때 사용해요. Custom code 블록의 HTML 모드는 JavaScript를 실행하지 **않으므로**, 로직에는 커스텀 스크립트(또는 블록의 React 모드)를 사용하세요.
