> ## 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 커스텀 코드 블록: Cart items 내부를 포함해 드로어 어디에나 직접 만든 HTML 또는 React를 추가하세요.

> **Custom code** 블록은 직접 만든 HTML 또는 React를 장바구니에 추가해요. 드로어의 어느 섹션에나 배치하거나, [**Cart items**](/ko/aftersell/cart/cart-items-block) 안에 하위 블록으로 중첩해 각 라인마다 반복되도록 할 수 있어요. 다른 블록과 달리 Content 설정과 Design 섹션이 없어요: 블록 자체가 코드이므로 **Code** 탭에서만 작업해요.

<Frame>
  <img src="https://mintcdn.com/aftersell/1Y3gBpUfxv16VGSW/images/aftersell/cart-custom-code-block-add-and-enable.gif?s=6717cc64a8765b0c06b65990f99e12ff" alt="Aftersell Cart 에디터에서 Custom code 블록을 추가하고 켜는 애니메이션 미리보기" title="Aftersell Cart 에디터에서 Custom code 블록을 추가하고 켜는 애니메이션 미리보기" width="1200" height="558" data-path="images/aftersell/cart-custom-code-block-add-and-enable.gif" />
</Frame>

<div id="add-and-turn-on-a-custom-code-block">
  ## Custom code 블록 추가 및 켜기
</div>

1. **Custom code** 블록을 아무 섹션에나 추가하거나, **Cart items** 아래 하위 블록으로 추가하세요.
2. 블록을 선택하고 **Code** 탭을 여세요.
3. **HTML** 또는 **React component**를 선택하세요. 새 블록의 기본값은 HTML이에요.
4. 코드를 작성하세요.
5. React를 선택했다면 <span style={{display:'inline-flex',alignItems:'center',gap:'4px',background:'#1C1C1C',color:'#fff',border:'1px solid #0A0A0A',borderRadius:'4px',padding:'0 6px',fontSize:'0.85em',fontWeight:500,lineHeight:'1.4em',verticalAlign:'middle'}}><svg width="8" height="9" viewBox="0 0 10 12" fill="none" aria-hidden="true" style={{display:'block'}}><path d="M1.5 1.2v9.6L8.8 6 1.5 1.2Z" stroke="#fff" strokeWidth="1.5" strokeLinejoin="round" /></svg>Compile</span>을 클릭하세요.
6. \*\*"Use custom template"\*\*을 켜세요. 이 블록에서 이 스위치는 "내 커스텀 코드를 표시"를 의미하며, 기본적으로 꺼져 있으므로 활성화하기 전까지 아무것도 렌더링되지 않아요.
7. 블록이 구매자에게 계속 표시되도록 사이드바 눈 모양 토글을 켜 두세요.

블록이 표시되려면 눈 모양 토글과 \*\*"Use custom template"\*\*이 모두 켜져 있어야 해요.

<div id="behavior">
  ## 동작 방식
</div>

* 블록은 장바구니가 로드될 때까지 아무것도 렌더링하지 않아요.
* 사이드바 눈이 꺼져 있거나, \*\*"Use custom template"\*\*이 꺼져 있거나, 코드가 비어 있거나, React 컴파일 또는 렌더링이 실패해도 아무것도 렌더링하지 않아요. 실패가 조용히 처리되므로 게시하기 전에 [미리보기](/ko/aftersell/cart/previewing-carts)에서 블록을 확인하세요.

<div id="html-mode">
  ## HTML 모드
</div>

HTML 모드는 소수의 토큰을 마크업에 치환해요. 정적이거나 토큰 기반 콘텐츠를 위한 것이지, 로직 실행을 위한 것이 아니에요.

* **인라인 `<script>` 태그는 실행되지 않으며**, HTML 모드는 **SDK나 `window`에 접근할 수 없어요.**
* 로직이 필요하면 [**React 모드**](#react-mode)나 [Cart SDK](/ko/aftersell/cart/sdk-overview)를 활용한 [커스텀 스크립트](/ko/aftersell/cart/custom-scripts)를 사용하세요.

<div id="tokens">
  ### 토큰
</div>

토큰 값은 마크업에 바로 넣을 수 있는 **포맷된 문자열**(스토어 통화 형식, `%`가 포함된 백분율, 또는 수량)이에요:

| 토큰                       | 표시 내용                                 |
| ------------------------ | ------------------------------------- |
| `{{pre_cart_total}}`     | 할인 전 장바구니 총액이에요.                      |
| `{{post_cart_total}}`    | 할인 후 장바구니 총액이에요.                      |
| `{{savings_amount}}`     | 절약된 금액(할인 전 총액에서 할인 후 총액을 뺀 값)이에요.    |
| `{{savings_percentage}}` | `%` 기호가 포함된 백분율 형태의 절약률이에요(예: `15%`). |
| `{{cart_quantity}}`      | 장바구니에 표시되는 아이템 수예요.                   |

<div id="example">
  ### 예시
</div>

```html theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
<div class="cart-external-custom-code_html">
  You saved {{savings_amount}} ({{savings_percentage}})
</div>
```

<div id="react-mode">
  ## React 모드
</div>

React 모드는 컴포넌트를 컴파일하고 장바구니 데이터와 `add-to-cart` 액션을 전달해요.

* 에디터는 래퍼를 `function CustomCode(props: CustomCodeProps) { … }`로 고정하며, 그 사이의 본문만 편집할 수 있어요.
* 블록이 표시되려면 <span style={{display:'inline-flex',alignItems:'center',gap:'4px',background:'#1C1C1C',color:'#fff',border:'1px solid #0A0A0A',borderRadius:'4px',padding:'0 6px',fontSize:'0.85em',fontWeight:500,lineHeight:'1.4em',verticalAlign:'middle'}}><svg width="8" height="9" viewBox="0 0 10 12" fill="none" aria-hidden="true" style={{display:'block'}}><path d="M1.5 1.2v9.6L8.8 6 1.5 1.2Z" stroke="#fff" strokeWidth="1.5" strokeLinejoin="round" /></svg>Compile</span>을 클릭한 다음 \*\*"Use custom template"\*\*을 켜야 해요.
* 컴포넌트에서 `useState`, `useEffect`, `useMemo`, `useRef`, `useCallback`을 사용할 수 있어요.
* HTML 모드와 달리 React는 페이지 컨텍스트에서 실행되므로, 사용 가능한 경우 `window`와 [Cart SDK](/ko/aftersell/cart/sdk-overview)를 호출할 수 있어요.
* 컴포넌트가 런타임에 오류를 발생시키면 블록은 아무것도 렌더링하지 않고 장바구니의 나머지 부분은 계속 작동해요.

<div id="props">
  ### Props
</div>

총액과 절약 금액은 통화의 [최소 단위](/ko/aftersell/cart/sdk-actions#formatmoneycents)(USD의 경우 센트)로 된 정수예요. 즉 `$12.50`은 `12.50`이 아니라 `1250`이에요. HTML 토큰과 달리 포맷된 금액 문자열이 아니에요.

| Prop                                            | 타입                          | 설명                                                                                     |
| ----------------------------------------------- | --------------------------- | -------------------------------------------------------------------------------------- |
| `cart`                                          | `AftersellCart`             | 현재 장바구니예요. [장바구니 객체 레퍼런스](/ko/aftersell/cart/sdk-cart-object)를 참고하세요.                  |
| `line`                                          | `AftersellCartLine \| null` | 블록이 Cart items 하위 블록일 때만 설정돼요(라인마다 한 번 렌더링). 섹션에서는 `null`이에요.                          |
| `preCartTotal`                                  | `number`                    | **할인 전** 장바구니 총액(Shopify의 `original_total_price`)으로, 통화의 최소 단위(예: 센트)예요.               |
| `postCartTotal`                                 | `number`                    | **할인 후** 장바구니 총액으로, 통화의 최소 단위예요.                                                       |
| `savings`                                       | `{ amount, percentage }`    | 절약 금액과 백분율이에요.                                                                         |
| `addProduct(variantId, quantity?, properties?)` | `function`                  | 상품을 장바구니에 추가하며, 이 블록의 귀속 정보가 기록되어 [분석](/ko/aftersell/cart/analytics)에서 성과를 인정받을 수 있어요. |

<div id="the-cart-and-line-shapes">
  ### cart와 line의 구조
</div>

`cart`와 `line`은 SDK가 다른 모든 곳에서 노출하는 것과 동일한 객체이므로, \*\*[장바구니 객체 레퍼런스](/ko/aftersell/cart/sdk-cart-object)\*\*에 한 번만 문서화되어 있어요: 장바구니, 라인, 번들의 모든 필드예요.

가장 자주 사용하게 될 것들: `cart.items`, `cart.itemCount`, `cart.totalPrice`, `line.title`, `line.quantity`, `line.finalLinePrice`.

이 블록에 특화된 세 가지:

* **`line`은 Cart items 하위 블록에서만 설정되며**, 이때 컴포넌트가 라인마다 한 번 렌더링돼요. 섹션에 배치되면 `line`은 `null`이고 대신 `cart.items`를 읽으세요.
* **번들 자식은 `cart.items`에 없어요.** 라인이 [번들로 그룹화](/ko/aftersell/cart/sdk-use-case-bundles)되면 앵커 라인만 나타나고, 자식은 `line.bundle.children`에 있어요.
* **[라인 변환](/ko/aftersell/cart/sdk-hooks#registerlinetransform)으로 숨겨진 라인도 거기에 없지만**, 여전히 `cart.totalPrice`에는 포함돼요.

<div id="examples">
  ### 예시
</div>

아이템 수 표시:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function CustomCode(props) {
  return (
    <div className="cart-external-custom-code_jsx">
      {props.cart.itemCount} items
    </div>
  );
}
```

Cart items 하위 블록으로 사용할 때는 상품별 콘텐츠에 `props.line`을 사용하세요. 블록은 라인마다 한 번 렌더링되며, 해당 라인의 상품과 옵션 정보가 태그돼요:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function CustomCode(props) {
  if (!props.line) return null;
  return (
    <div className="cart-external-custom-code_jsx">
      {props.line.productTitle}
      {props.line.variantTitle ? ` · ${props.line.variantTitle}` : ''}
    </div>
  );
}
```

<div id="reading-enrichment-metadata">
  ### 보강(enrichment) 메타데이터 읽기
</div>

`cart.items`의 각 아이템에는 `metadata` 필드가 있어요: [카트 인리처(cart enricher)](/ko/aftersell/cart/sdk-hooks#registercartenricher)가 채우기 전까지는 빈 객체 `{}`예요. 채워지면 인리처의 `id`를 키로 하며, 해당 라인의 상품 또는 옵션에 대한 Storefront 데이터를 담아요:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function CustomCode(props) {
  return (
    <ul>
      {(props.cart.items ?? []).map((item) => {
        const note = item.metadata?.shipping?.shippingNote;
        return (
          <li key={item.key}>
            {item.title}
            {note ? ` · ${note.value}` : ''}
          </li>
        );
      })}
    </ul>
  );
}
```

`metadata`는 항상 존재하며, 인리처의 비동기 가져오기가 완료되기 전까지 기본값은 빈 객체 `{}`예요("아직 보강되지 않음" 테스트는 `Object.keys(item.metadata).length === 0`이에요). 특정 인리처의 키를 읽을 때는 옵셔널 체이닝(`item.metadata?.enricherId`)을 사용하세요. 보강이 완료되기 전까지 그 키는 존재하지 않으니까요.

<div id="reading-discount-codes-and-line-discounts">
  ### 할인 코드와 라인 할인 읽기
</div>

`cart.discountCodes`는 장바구니에 적용된 할인 코드를 나열하고, 각 라인의 `discountAllocations`는 해당 라인에 적용된 할인을 나열해요:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function CustomCode(props) {
  const codes = props.cart.discountCodes;
  return (
    <div>
      {codes.length > 0 && (
        <p>Active discounts: {codes.join(', ')}</p>
      )}
      <ul>
        {(props.cart.items ?? []).map((item) => {
          return (
            <li key={item.key}>
              {item.title}
              {item.discountAllocations.map(
                (discount) => ` · ${discount.title} (-${(discount.amount / 100).toFixed(2)})`
              )}
            </li>
          );
        })}
      </ul>
    </div>
  );
}
```

<div id="placement-and-limits">
  ## 배치 및 제한
</div>

* **영역:** 모든 위치(상단, 본문, 하단). Cart items 하위 블록으로도 사용할 수 있어요.
* **최대 개수:** 무제한.
* **상태:** 채워진 장바구니와 빈 장바구니(섹션 블록일 때). Cart items 하위 블록일 때는 장바구니에 라인이 있을 때만 렌더링되며, 라인마다 하나의 인스턴스가 렌더링돼요.
* 잠겨 있지 않으므로 제거하거나 숨길 수 있어요.
* 블록별 Design 섹션이 없어요. 직접 만든 마크업, [**커스텀 CSS**](/ko/aftersell/cart/custom-css), 전역 [**디자인 설정**](/ko/aftersell/cart/design-settings)으로 스타일을 지정하세요.

<div id="when-to-use-custom-code-block-vs-custom-template-vs-custom-script">
  ## 커스텀 코드 블록 vs. 커스텀 템플릿 vs. 커스텀 스크립트, 언제 무엇을 쓸까요
</div>

|                                                    | 하는 일                                                                       | 사용 시점                                   | 예시                                                                                                                      |
| -------------------------------------------------- | -------------------------------------------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **커스텀 코드 블록**                                      | 직접 만든 HTML 또는 React로 *새로운* 블록을 추가해요.                                       | 기본 제공 블록이 다루지 않는 것이 필요할 때.              | 장바구니 총액에 고정 배송비를 더한 예상 총액 라인이나, 체크아웃 버튼 위의 배송 마감 카운트다운.                                                                 |
| **[커스텀 템플릿](/ko/aftersell/cart/custom-templates)** | 해당 블록의 데이터를 사용해 *기존* 블록의 렌더링을 여러분의 JSX로 대체해요.                              | 기본 블록이 거의 맞지만 다른 마크업이 필요할 때.            | 옵션 이름, 절약 금액, 수량 선택기가 한 줄에 오도록 [Product 행](/ko/aftersell/cart/cart-items-block#custom-template)을 다시 만들기.                |
| **[커스텀 스크립트](/ko/aftersell/cart/custom-scripts)**  | [Cart SDK](/ko/aftersell/cart/sdk-overview)를 통해 장바구니에 대해 JavaScript를 실행해요. | 드로어 마크업이 아닌 장바구니 전체 로직, 이벤트, 구성이 필요할 때. | \$75 이상 구매 시 무료 토트백 증정: 장바구니가 기준을 넘으면 [사은품을 추가](/ko/aftersell/cart/sdk-use-case-free-gift)하고, 구매자가 기준 아래로 내려가면 다시 제거해요. |
