Shopify Scripts Deadline: 6월 30일까지 Functions로 마이그레이션

Shopify Scripts Deadline: 6월 30일까지 Functions로 마이그레이션

Shopify Scripts Deadline: 6월 30일까지 Functions로 마이그레이션

Shopify Scripts를 Functions로 마이그레이션하는 방법: 코드 튜토리얼 완벽 가이드 (2026년 에디션) — Revize 블로그 아티클 헤더

2026년 4월 16일입니다. 어제인 4월 15일은 Shopify가 Script Editor를 영구적으로 폐쇄한 날이었습니다. 이제 새로운 Script를 생성하거나 게시할 수 없습니다. 실행 중단은 75일 후인 2026년 June 30일에 적용됩니다.

지난 12개월 동안 이 마이그레이션을 "다음 스프린트"로 미뤄왔던 Shopify Plus 개발자나 Plus 스토어를 운영하는 대행사라면 지금 문제가 발생한 것입니다. "해결하면 좋은" 수준의 문제가 아닙니다. "7월 1일 자정에 체크아웃이 중단되는" 문제입니다. 대부분의 Plus 스토어는 수년에 걸쳐 5개에서 20개의 Script를 누적해 왔으며, 각 Script는 아무도 기억하지 못하는 할인 규칙, 배송 비활성화 또는 결제 게이트를 조용히 구동하고 있습니다.

이 가이드는 지난 1월에 있었으면 좋았을 기술 마이그레이션 매뉴얼입니다. 전략뿐만 아니라 실제 코드를 다룹니다. 이 글을 다 읽을 때쯤이면 Shopify CLI로 어떻게 Function을 스캐폴딩하는지, 할인, 배송 커스텀, 결제 커스텀을 위한 Rust 또는 JavaScript 로직을 어떻게 작성하는지, 태그된 고객 하위 집합을 대상으로 안전하게 테스트하는 방법과 기존 체크아웃을 중단 없이 프로덕션에 배포하는 방법을 알게 될 것입니다.

스토어에서 Scripts를 제거하고 Functions를 도입해 보겠습니다.



Developer migrating Shopify Scripts to Shopify Functions modules

요약: 60초 만에 끝내는 Scripts → Functions

한 줄 요약: Shopify Scripts(Script Editor의 Ruby 코드, Plus 전용)가 Shopify Functions(Rust 또는 JavaScript로 작성된 WebAssembly 모듈, 모든 플랜에서 사용 가능)로 대체됩니다. shopify app generate extension으로 Function을 스캐폴딩하고, 필요한 카트 데이터를 가져오는 run.graphql 쿼리를 작성한 다음, 작업(할인, 숨겨진 배송 방법 등)을 반환하는 run.rs 또는 run.js 파일을 작성합니다. 그리고 shopify app deploy로 배포한 뒤 Admin 또는 GraphQL 뮤테이션을 통해 활성화합니다. Functions는 컴파일된 WASM으로 5ms 미만의 대기 시간으로 실행되며, 모든 플랜에서 작동하고, 향후 Shopify가 지원하는 유일한 커스텀 방식입니다.

June 30일에 실제로 변경되는 사항

코드를 다루기 전에 날짜를 명확히 확인하십시오. 두 날짜 모두 중요합니다.


날짜

변경 사항

필요한 조치

2026년 April 15일 (지난 날짜)

Script Editor가 읽기 전용으로 전환되었습니다. 신규 Script 생성 및 기존 Script 편집이 불가합니다.

기존 Scripts는 여전히 실행됩니다. 지금 마이그레이션하거나 로직을 동결하십시오.

2026년 June 30일

모든 Shopify Scripts의 실행이 중단됩니다. 예외는 없습니다.

이 날짜 이전에 대체할 Function이 라이브 상태여야 합니다.

마이그레이션은 이분법적입니다. June 30일까지 Function을 배포하여 체크아웃을 계속 작동시키거나, 배포하지 못해 영향을 받는 모든 장바구니가 표준 가격, 표준 배송 요금 및 모든 활성화된 결제 수단으로 자동 기본 설정되거나 둘 중 하나입니다. 부분 점수는 없습니다. Script가 실행되거나 실행되지 않거나 둘 중 하나이며, June 30일 이후에는 실행되지 않습니다.

팁: Shopify admin에서 Settings → Checkout → Customizations Report를 여십시오. 스토어의 모든 활성 Script, 기능 및 권장되는 대체 Function 유형이 나열되어 있습니다. 거기서부터 시작하십시오.

Functions 대 Scripts: 실제 차이점


구분

Shopify Scripts (지원 종료)

Shopify Functions (대체)

언어

Ruby DSL (Shopify 전용)

Rust, JavaScript, TypeScript

런타임

Shopify 인프라의 샌드박스형 Ruby

WebAssembly (WASM) — 5ms 미만 실행

사용 가능 플랜

Plus 전용

모든 플랜 (커스텀 앱은 Plus 필요, 공개 앱은 제한 없음)

에디터

어드민 내 Script Editor

로컬 IDE + Shopify CLI

버전 관리

없음 — 라이브 편집

Git 연동 지원 — 전체 버전 제어 가능

테스트

체크아웃에서 수동 테스트

shopify app dev를 통한 로컬 개발, 미리보기 링크

배포

어드민에서 "Save" 클릭

터미널에서 shopify app deploy 실행

대상 (Targets)

Line items, shipping, payments

Discounts, Cart Transform, Validation, Delivery Customization, Payment Customization, Order Routing, Fulfillment Constraints 등

아키텍처의 전환이 중요합니다. Scripts는 "텍스트 영역에서 Ruby를 수정하는 것"이었습니다. Functions는 "실제 앱을 작성하고, 버전을 관리하고, 로컬에서 테스트하고, 실제 CI 파이프라인을 통해 배포하는 것"입니다. 이는 진입 장벽이 더 높습니다. 또한 당분간 체크아웃 로직을 위해 수행할 마지막 마이그레이션이 될 것입니다. Functions는 Scripts처럼 임시방편이 아닌 Shopify의 장기적인 약속입니다.

Scripts에 맞는 올바른 Function 유형 매핑

현재 사용 중인 모든 Script는 정확히 하나의 Function API에 매핑됩니다. 모니터에 고정해 두어야 할 대조표는 다음과 같습니다.


기존 Script 유형

기능

신규 Function API

Function Target

Line Item Script

특정 상품 / 고객 / 카트 조건에 할인 적용

Cart & Checkout Discounts API

cart.lines.discounts.generate.run

Shipping Script (할인)

카트 규칙에 기반한 무료 / 할인 배송 적용

Cart & Checkout Discounts API

cart.delivery-options.discounts.generate.run

Shipping Script (숨기기 / 이름 변경 / 순서 변경)

특정 금액 초과 시 배송비 숨기기, "Standard"를 "$50 이상 무료"로 변경 등

Delivery Customization API

cart.delivery-options.transform.run

Payment Script

B2B 고객 대상 PayPal 숨기기, $500 초과 시 COD 숨기기, 결제 수단 정렬 등

Payment Customization API

cart.payment-methods.transform.run

카트 변경 Script (드묾)

번들 상품 구성, 품목 교체

Cart Transform API

cart.transform.run

체크아웃 차단 Script

유효하지 않은 SKU 조합 시 카트 거부

Cart & Checkout Validation API

cart.validations.generate.run

Scripts가 10개인 경우, 대개 3~5개의 Functions를 빌드하게 됩니다. 여러 Scripts가 더 깔끔한 분기 로직을 통해 하나의 Function으로 통합되는 경우가 많기 때문입니다.



Shopify Functions unifying discounts, delivery, and payment customizations

사전 요구 사항: 로컬 개발 환경 설정

Function을 스캐폴딩하기 전에 로컬에 세 가지가 설치되어 있어야 합니다. 터미널에서 다음 확인 작업을 수행하십시오.

1. Node.js 18+


node --version
# Must be >= 18.0.0
node --version
# Must be >= 18.0.0

구버전인 경우 nvm을 통해 설치하거나 nodejs.org에서 다운로드하십시오.

2. Shopify CLI 3+


npm install -g @shopify/cli@latest
shopify version
# Should output 3.x or higher
npm install -g @shopify/cli@latest
shopify version
# Should output 3.x or higher

3. Rust 툴체인 (Rust로 Functions를 작성할 경우에만 해당)


curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-wasip1
cargo --version
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-wasip1
cargo --version

JavaScript Functions의 경우 Rust가 필요하지 않습니다. 팀 내에서 하나의 언어를 선택하여 유지하십시오. 혼용 시 유지관리 리소스가 늘어납니다.

4. 개발 스토어

Partner 대시보드에 로그인하여 새 개발 스토어를 만들거나 기존 스토어를 사용하십시오. 프로덕션에 적용하기 전에 이 개발 스토어에 Functions를 배포하여 검증하게 됩니다.

첫 번째 Function 스캐폴딩

CLI가 상용구 코드의 대부분을 처리합니다. 아무 디렉터리 내에서 다음과 같이 실행하십시오.


# Create a new Shopify app (skip if you already have one)
shopify app init my-checkout-functions
cd my-checkout-functions

# Generate a Function extension
shopify app generate extension
# Create a new Shopify app (skip if you already have one)
shopify app init my-checkout-functions
cd my-checkout-functions

# Generate a Function extension
shopify app generate extension

CLI가 안내 프롬프트를 표시합니다. 할인 Function의 경우 다음과 같이 선택하십시오.

  • Type: Function

  • Template: discount (또는 cart_checkout_validation, delivery_customization, payment_customization 등)

  • Language: Rust 또는 JavaScript

  • Name: volume-discount-fn과 같은 이름

그러면 다음 구성으로 extensions/volume-discount-fn/이 생성됩니다.


extensions/volume-discount-fn/
├── shopify.extension.toml      # Function config targets, build, version
├── src/
├── cart_lines_discounts_generate_run.graphql   # Input query
└── cart_lines_discounts_generate_run.rs        # Function logic
├── Cargo.toml                  # Rust dependencies (Rust only)
└── README.md
extensions/volume-discount-fn/
├── shopify.extension.toml      # Function config targets, build, version
├── src/
├── cart_lines_discounts_generate_run.graphql   # Input query
└── cart_lines_discounts_generate_run.rs        # Function logic
├── Cargo.toml                  # Rust dependencies (Rust only)
└── README.md

자주 편집하게 될 세 파일은 .toml(설정), .graphql(입력), 그리고 .rs / .js(로직)입니다. 이것이 전부입니다.

튜토리얼 1: Line Item Script 대체 (수량 할인)

기존 Script에서 카트에 특정 컬렉션 상품이 5개 이상 담겨 있을 때마다 주문 소계의 10%를 할인해 주었다고 가정해 보겠습니다. 다음은 이를 대체하는 Function 버전입니다.

단계 1.1: 구성 (shopify.extension.toml)


api_version = "2026-01"

[[extensions]]
name = "volume-discount-fn"
handle = "volume-discount-fn"
type = "function"

[[extensions.targeting]]
target = "cart.lines.discounts.generate.run"
input_query = "src/cart_lines_discounts_generate_run.graphql"
export = "cart_lines_discounts_generate_run"

[extensions.build]
command = "cargo build --target=wasm32-wasip1 --release"
path = "target/wasm32-wasip1/release/volume-discount-fn.wasm"
watch = ["src/**/*.rs"]
api_version = "2026-01"

[[extensions]]
name = "volume-discount-fn"
handle = "volume-discount-fn"
type = "function"

[[extensions.targeting]]
target = "cart.lines.discounts.generate.run"
input_query = "src/cart_lines_discounts_generate_run.graphql"
export = "cart_lines_discounts_generate_run"

[extensions.build]
command = "cargo build --target=wasm32-wasip1 --release"
path = "target/wasm32-wasip1/release/volume-discount-fn.wasm"
watch = ["src/**/*.rs"]

단계 1.2: 입력 쿼리 (src/cart_lines_discounts_generate_run.graphql)


query Input {
  cart {
    lines {
      id
      quantity
      cost {
        subtotalAmount {
          amount
        }
      }
      merchandise {
        ... on ProductVariant {
          product {
            inAnyCollection(ids: ["gid://shopify/Collection/123456789"])
          }
        }
      }
    }
  }
  discount {
    discountClasses
  }
}
query Input {
  cart {
    lines {
      id
      quantity
      cost {
        subtotalAmount {
          amount
        }
      }
      merchandise {
        ... on ProductVariant {
          product {
            inAnyCollection(ids: ["gid://shopify/Collection/123456789"])
          }
        }
      }
    }
  }
  discount {
    discountClasses
  }
}

팁: Functions는 쿼리한 데이터만 볼 수 있습니다. GraphQL을 최소한으로 유지하십시오. 생략하는 필드가 많을수록 실행 속도가 빨라지고 비용이 절감됩니다.

단계 1.3: 로직 (src/cart_lines_discounts_generate_run.rs)


use super::schema;
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function]
fn cart_lines_discounts_generate_run(
    input: schema::cart_lines_discounts_generate_run::Input,
) -> Result<schema::CartLinesDiscountsGenerateRunResult> {
    // Bail if discount class doesn't match
    let has_order_discount = input
        .discount()
        .discount_classes()
        .contains(&schema::DiscountClass::Order);

    if !has_order_discount {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // Sum quantities of items in the target collection
    let qualifying_qty: i64 = input
        .cart()
        .lines()
        .iter()
        .filter(|line| {
            if let schema::Merchandise::ProductVariant(v) = line.merchandise() {
                *v.product().in_any_collection()
            } else {
                false
            }
        })
        .map(|line| *line.quantity())
        .sum();

    if qualifying_qty < 5 {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // Apply 10% off the order subtotal
    Ok(schema::CartLinesDiscountsGenerateRunResult {
        operations: vec![schema::CartOperation::OrderDiscountsAdd(
            schema::OrderDiscountsAddOperation {
                selection_strategy: schema::OrderDiscountSelectionStrategy::First,
                candidates: vec![schema::OrderDiscountCandidate {
                    targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal(
                        schema::OrderSubtotalTarget {
                            excluded_cart_line_ids: vec![],
                        },
                    )],
                    message: Some("Volume discount: 10% off".to_string()),
                    value: schema::OrderDiscountCandidateValue::Percentage(
                        schema::Percentage { value: Decimal(10.0) }
                    ),
                    conditions: None,
                    associated_discount_code: None,
                }],
            },
        )],
    })
}
use super::schema;
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function]
fn cart_lines_discounts_generate_run(
    input: schema::cart_lines_discounts_generate_run::Input,
) -> Result<schema::CartLinesDiscountsGenerateRunResult> {
    // Bail if discount class doesn't match
    let has_order_discount = input
        .discount()
        .discount_classes()
        .contains(&schema::DiscountClass::Order);

    if !has_order_discount {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // Sum quantities of items in the target collection
    let qualifying_qty: i64 = input
        .cart()
        .lines()
        .iter()
        .filter(|line| {
            if let schema::Merchandise::ProductVariant(v) = line.merchandise() {
                *v.product().in_any_collection()
            } else {
                false
            }
        })
        .map(|line| *line.quantity())
        .sum();

    if qualifying_qty < 5 {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // Apply 10% off the order subtotal
    Ok(schema::CartLinesDiscountsGenerateRunResult {
        operations: vec![schema::CartOperation::OrderDiscountsAdd(
            schema::OrderDiscountsAddOperation {
                selection_strategy: schema::OrderDiscountSelectionStrategy::First,
                candidates: vec![schema::OrderDiscountCandidate {
                    targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal(
                        schema::OrderSubtotalTarget {
                            excluded_cart_line_ids: vec![],
                        },
                    )],
                    message: Some("Volume discount: 10% off".to_string()),
                    value: schema::OrderDiscountCandidateValue::Percentage(
                        schema::Percentage { value: Decimal(10.0) }
                    ),
                    conditions: None,
                    associated_discount_code: None,
                }],
            },
        )],
    })
}

단계 1.4: 테스트, 배포, 활성화


# Local development with hot reload
shopify app dev

# When ready, deploy
shopify app deploy

# In the GraphiQL panel that opens (press `g` in the dev terminal),
# create the automatic discount that uses your Function:
# Local development with hot reload
shopify app dev

# When ready, deploy
shopify app deploy

# In the GraphiQL panel that opens (press `g` in the dev terminal),
# create the automatic discount that uses your Function:
mutation {
  discountAutomaticAppCreate(
    automaticAppDiscount: {
      title: "Volume Discount (5+ collection items)"
      functionHandle: "volume-discount-fn"
      discountClasses: [ORDER]
      startsAt: "2026-04-16T00:00:00Z"
    }
  ) {
    automaticAppDiscount { discountId }
    userErrors { field message }
  }
}
mutation {
  discountAutomaticAppCreate(
    automaticAppDiscount: {
      title: "Volume Discount (5+ collection items)"
      functionHandle: "volume-discount-fn"
      discountClasses: [ORDER]
      startsAt: "2026-04-16T00:00:00Z"
    }
  ) {
    automaticAppDiscount { discountId }
    userErrors { field message }
  }
}

완료되었습니다. Function이 활성화되어 버전 관리되며 기존 Script를 완벽하게 대체합니다.

튜토리얼 2: Shipping Script 대체 (카트 임계값 초과 시 배송비 숨기기)

자주 사용되는 Script: "대량 구매 시 비용이 많이 드는 특송 배송을 방지하기 위해 장바구니 가격이 $500를 초과하면 Express Shipping을 숨깁니다." 다음은 Delivery Customization Function 버전입니다.

단계 2.1: 스캐폴딩


shopify app generate extension --template delivery_customization --name hide-express-fn
shopify app generate extension --template delivery_customization --name hide-express-fn

단계 2.2: 입력 쿼리 (src/run.graphql)


query Input {
  cart {
    cost {
      subtotalAmount {
        amount
      }
    }
    deliveryGroups {
      deliveryOptions {
        handle
        title
      }
    }
  }
}
query Input {
  cart {
    cost {
      subtotalAmount {
        amount
      }
    }
    deliveryGroups {
      deliveryOptions {
        handle
        title
      }
    }
  }
}

단계 2.3: 로직 (src/run.js — JavaScript 방식)


// @ts-check
/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 */

const NO_CHANGES = { operations: [] };
const THRESHOLD = 500.0;
const HIDE_TITLES = ["Express", "Overnight"];

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount);
  if (subtotal < THRESHOLD) return NO_CHANGES;

  const operations = input.cart.deliveryGroups.flatMap((group) =>
    group.deliveryOptions
      .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t)))
      .map((opt) => ({
        hide: { deliveryOptionHandle: opt.handle },
      }))
  );

  return { operations };
}
// @ts-check
/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 */

const NO_CHANGES = { operations: [] };
const THRESHOLD = 500.0;
const HIDE_TITLES = ["Express", "Overnight"];

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount);
  if (subtotal < THRESHOLD) return NO_CHANGES;

  const operations = input.cart.deliveryGroups.flatMap((group) =>
    group.deliveryOptions
      .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t)))
      .map((opt) => ({
        hide: { deliveryOptionHandle: opt.handle },
      }))
  );

  return { operations };
}

단계 2.4: Admin을 통한 활성화 (GraphQL 불필요)

Delivery Customizations에는 고유한 Admin UI가 있습니다. shopify app deploy를 수행한 후 다음 단계를 진행하십시오.

  1. Settings → Shipping and delivery로 이동합니다.

  2. 하단의 Customizations 섹션으로 스크롤합니다.

  3. Add customization을 클릭한 다음 작성한 Function을 선택합니다.

  4. 저장합니다.

숨김 규칙이 프로덕션에 적용되었습니다. Mutation 처리를 거치지 않아도 됩니다.



Shopify delivery customization Function hiding shipping option at checkout

튜토리얼 3: Payment Script 대체 (B2B 고객 대상 COD 숨기기)

기존 Script: "'B2B' 태그가 지정된 고객의 착불 결제(Cash on Delivery)를 숨깁니다." 다음은 Payment Customization 버전입니다.

단계 3.1: 스캐폴딩


shopify app generate extension --template payment_customization --name hide-cod-b2b-fn
shopify app generate extension --template payment_customization --name hide-cod-b2b-fn

단계 3.2: 입력 쿼리


query Input {
  cart {
    buyerIdentity {
      customer {
        hasTags(tags: [{ tag: "B2B" }]) {
          tag
          hasTag
        }
      }
    }
  }
  paymentMethods {
    id
    name
  }
}
query Input {
  cart {
    buyerIdentity {
      customer {
        hasTags(tags: [{ tag: "B2B" }]) {
          tag
          hasTag
        }
      }
    }
  }
  paymentMethods {
    id
    name
  }
}

단계 3.3: 로직 (src/run.js)


const NO_CHANGES = { operations: [] };

export function run(input) {
  const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0];
  const isB2B = tagCheck?.hasTag === true;
  if (!isB2B) return NO_CHANGES;

  const codMethod = input.paymentMethods.find((pm) =>
    pm.name.toLowerCase().includes("cash on delivery")
  );
  if (!codMethod) return NO_CHANGES;

  return {
    operations: [{ hide: { paymentMethodId: codMethod.id } }],
  };
}
const NO_CHANGES = { operations: [] };

export function run(input) {
  const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0];
  const isB2B = tagCheck?.hasTag === true;
  if (!isB2B) return NO_CHANGES;

  const codMethod = input.paymentMethods.find((pm) =>
    pm.name.toLowerCase().includes("cash on delivery")
  );
  if (!codMethod) return NO_CHANGES;

  return {
    operations: [{ hide: { paymentMethodId: codMethod.id } }],
  };
}

단계 3.4: 활성화

Payment Customizations 역시 아래 경로에 Admin UI가 존재합니다: Settings → Payments → Customizations. 배송 설정과 동일한 흐름으로 진행하십시오. Function을 선택하고 저장하면 완료됩니다.

함께 읽을거리 — 사후 구매(Post-Purchase)에 관하여

이곳은 Revize 블로그이므로 간략히 안내해 드립니다. Revize는 Functions가 건드릴 수 없는 부분들을 처리합니다. 주문이 완료된 후 고객이 상품을 추가하거나, 옵션을 변경하거나, 배송 주소를 수정하거나, 누락된 할인을 수동 적용하고 싶어 할 때가 있습니다. Functions는 체크아웃 시점에 작동하지만, Revize는 체크아웃 이후에 작동합니다. Functions가 장바구니에 허용될 항목을 결정한다면, Revize는 환불 후 다시 처음부터 주문을 빌드하는 불필요한 과정 없이 고객과 지원 팀이 주문 완료 후에도 상품을 간단하게 편집할 수 있도록 돕습니다. 이는 두 종류의 상점에 꼭 필요합니다. 수동 편집이 어려울 만큼 주문량이 막대한 스토어(Plus 운영사나 대규모 Advanced 스토어)와, 고객 경험이 브랜드의 핵심 가치여서 "변경 처리가 어렵습니다" 유형의 이메일이 재구매율 하락으로 연결되는 스토어입니다.

만약 마이그레이션 계획이 Scripts → Functions에만 맞추어져 있고 사후 주문 편집 영역을 고려하지 않았다면, 약 3주 뒤에 "대체 왜 이렇게 복잡하지?"라는 한계에 부딪힐 것입니다. 최근 완결된 주문 관리 가이드에 체크아웃 이후 관리를 위한 완벽한 해결책을 제시해 두었습니다.

다시 마이그레이션 안내로 돌아가겠습니다.

테스트 전략: 태그 기반 고객(Tagged Customer) 패턴

Functions는 Admin에서 켜고 깔 수 있는 별도의 "초안 모드(Draft Mode)"를 제공하지 않습니다. 이 때문에 가장 프로페셔널한 방식은 새로 개발한 Function을 특정 고객 태그 기준으로 제어하여, 기존 Script와 신규 Function을 병렬로 구동하면서 테스트 가입자들에게 정확하게 동일한 결과가 도출되는지 확인한 뒤 실 서비스에 완전히 반영하는 것입니다.

단계 1: 테스트 대상 사용자 태그 설정

Customers 메뉴에서 내부 개발 테스트 계정 2~3개에 FN-TESTER 태그를 추가합니다.

단계 2: 태그 존재 여부에 따라 Function 분기 처리


// At the top of your run function
let is_tester = input
    .cart()
    .buyer_identity()
    .and_then(|bi| bi.customer())
    .map(|c| c.has_any_tag())
    .unwrap_or(&false);

if !*is_tester {
    return Ok(default_result);  // Fall through to existing Script
}

// New Function logic only runs for tagged users
// At the top of your run function
let is_tester = input
    .cart()
    .buyer_identity()
    .and_then(|bi| bi.customer())
    .map(|c| c.has_any_tag())
    .unwrap_or(&false);

if !*is_tester {
    return Ok(default_result);  // Fall through to existing Script
}

// New Function logic only runs for tagged users

단계 3: 입력 쿼리에 hasAnyTag 추가


cart {
  buyerIdentity {
    customer {
      hasAnyTag(tags: ["FN-TESTER"])
    }
  }
}
cart {
  buyerIdentity {
    customer {
      hasAnyTag(tags: ["FN-TESTER"])
    }
  }
}

단계 4: 체크아웃에서 확인

태그된 테스트 유저로 로그인하여 체크아웃을 진행하고 Function이 성공적으로 호출되는지 확인하십시오. 태그가 없는 일반 사용자로 로그인하여 기존 Script가 정상적으로 수행되는지도 점검합니다. 며칠 간 문제없이 양쪽이 균형을 이루는 것을 확인하면 태그 체크 조건을 해제하고 전체 사용자를 대상으로 Function을 가동합니다.

단계 5: 기존 Script 게시 중단

Apps → Script Editor → [사용 중인 Script] → Unpublish 순서로 이동하여 비활성화합니다. 정상 비게시 상태가 되면 새로 도입한 Function이 단일 기준이 됩니다.

지속 확장이 가능한 실제 배포 워크플로

모든 배포를 매번 개발자 개인의 로컬 PC에서 직접 처리하는 우는 범하지 않아야 합니다. 한두 개의 Scripts 마이그레이션을 마쳤다면 전용 CI 파이프라인을 구축할 때입니다.

가장 기본적이고 효율적인 검증 워크플로 구성


# .github/workflows/deploy-functions.yml
name: Deploy Shopify Functions
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: wasm32-wasip1 }
      - run: npm install -g @shopify/cli@latest
      - run: shopify app deploy --force
        env:
          SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}
# .github/workflows/deploy-functions.yml
name: Deploy Shopify Functions
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: wasm32-wasip1 }
      - run: npm install -g @shopify/cli@latest
      - run: shopify app deploy --force
        env:
          SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}

Partner 대시보드의 Settings → Tokens 메뉴에서 Partner 토큰을 생성하십시오. 이제 main 브랜치로 머지될 때마다 설계한 Functions가 즉시 자동 배포됩니다. "그거 배포했나요?" 하며 슬랙 메시지를 쓸 필요가 없습니다.

버전 제어 및 롤백

shopify app deploy 명령을 내리면 스냅샷 버전이 기록됩니다. 필요 시 아래와 같이 손쉽게 이전 코드로 롤백이 가능합니다.


shopify app versions list
shopify app release --version <previous-version-id

shopify app versions list
shopify app release --version <previous-version-id

기존 Scripts에서 롤백을 위해 "기존 코드를 일일이 기억해 복사 붙여넣기 하던 방식"과 비교해 보면 하늘과 땅 차이 수준으로 쾌적해졌습니다.



Shopify Functions deployment pipeline across local staging and production environments

대표적으로 실수하는 5가지 유형

지난 1년 동안 다수의 Plus 머천트들이 수행한 이번 마이그레이션 과업을 지원하면서 매번 공통적으로 목격해 온 가장 흔한 대표적 실수들을 공유합니다.

1. 1:1로만 가볍게 주입하려는 태도: 기존 Script가 세 개였다 하더라도 영리하게 리팩토링한 분기문을 활용해 탄탄한 1개의 Function 내부에서 모두 소화가 가능한 경우가 허다합니다. 이식을 위해 키보드를 잡기 전 전체 Scripts 설계를 종합 감사하는 접근이 필수적입니다.

2. Read scopes의 누락: 대개 구현하려는 대다수 Function은 기본값으로 read_customers, read_orders, 혹은 write_discounts 중 하나 이상의 스코프를 반드시 선언해 주어야 합니다. 잊지 말고 shopify.app.tomlscopes 영역에 입력한 뒤 승인해 주십시오. 그렇지 않으면 graphql 데이터가 null로 반환될 것입니다.

3. 비교 검증 없이 곧바로 일반 고객 대상 가동: 작성한 비즈니스 로직이 한눈에 보기에 완벽해 노출 문제가 없을지라도, 끈적하게 매달리는 경계 조건 예외 케이스(빈 장바구니 조건, 기프트카드 복합 결제 이슈, 적립금 등)는 기어코 버그를 발생시킵니다. 내부 테스트 태그 주차를 통한 며칠간의 소소한 지체는 P1 장애가 터져 심야에 복구 업무를 겪는 공포에 대처하는 현명한 보험비용입니다.

4. Customizations Report 미작동: 현재 라이브 환경에서 실동 중인 요소의 인벤토리를 가장 쉽고 투명하게 식별하여 일목요연하게 전달해 주는 레포트입니다. 기억에 의존하는 대차 대신 기계가 반환한 레포트 숫자를 바탕으로 엄밀하게 준비하십시오.

5. 콜렉션 ID 혹은 특정 태그값의 하드코딩: 판매자의 가변 관리가 개입해야 하는 상황이라면 메타필드를 활용해 설정값 데이터를 받아 동적으로 처리하십시오. CLI 툴링을 이용하면 기본 뼈대를 수월하게 구성해 낼 수 있습니다. 관련 문서의 Function 설정을 정독하십시오.

앞으로 남은 75일에 걸친 최종 마이그레이션 로드맵

June 30일까지 가장 평온하고 안전하게 도달하는 단계별 실전 일정표를 설계해 드립니다.


기간

수행할 주요 과업

1주 차 (이번 주)

상용 Customizations Report 확인. 구동 중인 누적 연동 상세 식별. Function 처리용 리스트업 확정.

2~3주 차

로컬에 필요한 기술 세팅 진행 및 구성. 기초적인 수준의 간소한 Function 생성(결제 감추기 로직 대상 추천).

4~6주 차

할인 관련 가이드 마이그레이션. Discounts API 내부에서 유연하고 촘촘한 정리를 요구하므로 공수가 가장 크게 듦. 충분한 테스트 기간 확보 필요.

7~8주 차

배송/요금 관련 Script 포팅 연동. 완성된 Delivery Customizations를 어드민 메뉴에서 연계하여 활성화.

9~10주 차

깃헙 액션 등을 연동해 CI/CD 파이프라인 정비. 더 이상 개발자 각자 노트북 단위 수동 배포가 없도록 선회.

11주 차 (6월 중순)

동일 성능 구현 완결성 테스트 수행. 구 Scripts 비게시 비활성 처리. 2주간 온전히 신규 Functions만으로 서비스 운영 모니터링.

June 30

최종 Sunset 시행일 조우. 미리 선조치해 둔 덕에 아무 장애 현상도 경험하지 않는 쾌가 보장됨.

지금 첫걸음을 뗀다면 일정에 여유가 있지만, 6월에 시작하는 경우 버퍼가 남아있지 않을 것입니다.



Week-by-week migration roadmap from Shopify Scripts to Functions

자주 묻는 질문 (FAQ)

Functions를 사용하려면 무조건 Shopify Plus 플랜이어야 하나요?

독립형 커스텀 함수(Custom Functions) 제작의 경우 오직 Shopify Plus 플랜에서만 작성이 허용되지만, 공개 앱 생태계에 게시된 Functions 기반 솔루션 플러그인은 전 플랜 누구나 즉시 설치하여 적용하는 길이 확보되어 있습니다. 작성 주도가 불가능한 하위 요금 상태라면 대체재가 될 만한 검증된 공개 앱을 Shopify App Store에서 발굴해 장착하는 방향 혹은, 온전히 독립 자산을 소지하기 위하여 Plus로 업그레이드할 것을 권해 드립니다.

TypeScript 언어도 공식 대응하나요?

예, 무결하게 커버하며 생성 툴링 역시 온전하게 지원합니다. shopify app generate extension 단계 안내시 "JavaScript" 선택 시 내부적으로 개발 연계를 위한 타입 선언 바인딩 리소스들이 import("../generated/api")를 통하여 수반될 것입니다. tsconfig.json 파일을 기입하고 확장자를 .ts로 승격시켜 주면 별다른 이격 없이 사용 가능합니다. 어차피 산출 결과인 웹어셈블리 단에서는 원본 언어에 따른 차이점이 발생할 여지가 없습니다.

성능 면에서 Scripts 대비 얼마나 민첩하게 도나요?

대개 5ms 이하 극단으로 정밀하게 마침표를 찍으며 기존 Ruby Scripts 작동 시간보다 압도적으로 빨라집니다. 단일 WASM 타깃 런타임 특성에 따라 Shopify 코어 측에서 요청 당 5ms 처리 예산 리밋을 원칙적으로 강제 준수하게 합니다. 만일 이 리밋의 상단을 뚫으면 해당 호출 작업에 즉시 블락 처리가 내려집니다. 통상 적절히 튜닝된 헬시 레벨 로직은 평균 1~2ms 안팎으로 통과되어 기존 대비 쾌적성이 증명됩니다.

함수 본체 구동 중 서드파티 외부 API도 직접 붙여 땡겨올 수 있나요?

불가능합니다. Functions 내부에서 네트워크 소켓 등의 아웃바운드 발신 처리는 절대 불가능하게 격리 설계되어 있습니다. 전적으로 오로지 유입 장바구니 데이터를 받아서 고속 논리 가공 처리를 얹은 뒤 결괏값을 내보내는 구조적 가동에 전념하게 해야 합니다. 실시간 연계 제휴 CRM 쿼리 같은 처리가 필요하다면 메타필드에 데이터를 선행 캐싱해 적재해 두거나, 대안이 될 플랫폼 구조(App Proxy 혹은 백엔드를 거친 Cart Transform) 설계를 별도 마련하시는 방향으로 사전에 우회하셔야 합니다.

Cart Transform API와 일반 Discounts 구성과의 근본적 체계상 구분이 모호합니다.

쉽게 말하자면 가격을 내릴 때는 Discounts를 기용하고 장바구니 구조의 수량을 조작 또는 엮을 때는 Cart Transform을 선택합니다. %율 적용, 세일 처리는 전자 범주이며 번들 세트 묶기 등 복합 구성 조립이 수반될 때 후자를 호출하는 것으로 두시면 깔끔합니다. 예전 구 Scripts 코드는 한 단락의 파일 안에 두 영역을 뒤섞는 경향이 많았습니다. 마이그레이션 시 깔끔히 두 개 함수 영역으로 개별 분할해 설계해 내는 것을 타깃으로 잡으십시오.

로컬 상황에서 개발 스토어 연결 통과 세팅 없이 동작 상황을 미리 볼 방법은 전혀 존재하지 않나요?

단위 처리는 cargo test 혹은 npm test 템플릿의 실행을 통해 어느 정도 검토가 충분히 설계되나, 최종 종단 체크아웃 구동 연동은 어쩔 수 없이 개발 스토어의 존재가 확보되어 있어야 진행을 담보합니다. 셸 내부에서 쓸 수 있는 shopify app function run을 실행하여 유입 Mock JSON 파일을 대상으로 결괏값을 보는 작업만 로컬 개발 도중 중간 테스트용으로 유용하게 차용해 보시기 바랍니다.

동일 형식의 여러 Function 종류를 동시에 적재해 연속 작동되도록 체이닝 구성이 허용되나요?

예, 가능합니다. 지정한 결정 순서 우선 체계에 정직하게 입각하여 차례로 병렬 수반되어 내리게 돌아갑니다. 할인은 스태킹 정책 설정값의 연계를 따르고 배송 및 결제 사용자 지정은 결과 처리가 줄을 이어 다음 작동 인풋으로 순차 적용되는 방식으로 수행하므로 관념상 한 단일 유형 내에는 직관적 가시성을 고려해 한 개 함수의 결합으로 응집력 있게 풀어내 전개하시는 것을 장려합니다.

새 Function 이식을 성공적으로 끝내서 올려두면 이전의 구 Scripts는 즉시 지워지나요?

사용자가 직접 임의로 Script Editor 상에서 비게시 설정을 가할 때까지 우선 두 영역의 자산이 공동으로 물려 실서버에 돌게 보장됩니다. 설계 이격을 대비해 병행 실행해서 추이를 대조 판단할 기회를 기획적으로 지원하게 만드는 조치입니다. 온전하게 동작 검증을 거친 직후 구형을 마감시키면 됩니다. 어차피 2026년 June 30일이 지난다면 어드민에 켜진 채 남아있는 스위치가 있더라도 강제 효력 상실을 거치기에 무조건 꺼져 내리게 되어 있습니다.

이번 마이그레이션 변경 조치가 혹시 운영 중인 자체 사이트 SEO 영역에 악영향을 주나요?

영향을 끼치지 않습니다. Functions 연산 처리는 전적으로 체크아웃 단계 서버사이드 내부에서 기밀하게 수반 및 소거되므로 테마 HTML 렌더 파일에 간섭하거나 구글 크롤 스크립트 검색에 아무 영항을 끼치지 않습니다. 테마 스킨과 상품 노출 및 검색 노출의 전면은 기존대로 평화롭게 유지 보장받으니 걱정 덜어내십시오.

기존 Input.line_items 상의 커스텀 추가 메타 정보 값을 받던 옛 룬은 어떻게 매핑하나요?

GraphQL 인풋 쿼리의 attribute 항목을 선언해 줌으로써 기존에 쓰던 기능의 구성을 백퍼센트 복원하여 소화할 수 있습니다. lines 셀렉터 내부 스코프에 attribute(key: "attribute_name") { value }를 태워 뽑아 활용하십시오. 예전 루비 문법 체득으로 파고들어 보던 변숫값 호출이 GraphQL 타깃으로 바뀌었을 뿐, 완전히 같은 내용을 보장합니다.

주문 관련 태깅 작업도 구동 본체 내에서 태그 필드 가입 처리를 진행해 반환해 주나요?

불가능합니다. Functions는 장바구니 인스턴스의 트랜스폼 결과 데이터만을 담아서 반환하므로 내부에서 데이터베이스 태그 필드를 수정하는 등의 영속성 처리를 일으킬 수 없습니다. 대신 우수 사후 조치를 설계하고 싶다면 완료 이벤트와 연계되어 돌아가기에 간편한 노코드 워크플로 툴인 Shopify Flow를 도입해 연동 장악하는 길을 선택해 유연하게 엮어 쓰셔야 맞습니다. (예시: 특정 할인 코드 매칭 감지 후 Flow를 타고 해당 주문 문서에 "VOLUME-DISCOUNT-APPLIED" 라벨 태그 입력 처리)

시간이 없을 때 우선 임시 해결이 어렵다면, App Store에 게시되어 기성 판매 중인 Functions 솔루션을 그냥 매는 방향은 부적합한가요?

오히려 탁월한 훌륭한 해답입니다. 현시점 마켓플레이스에는 Functions를 기반 삼아 작동하는 강력한 플러그인 유틸들이 널리 상용 출시되어 성업 중입니다. "할인 관련 기능", "결제 및 배송 수단 가드 관련 필터 설정" 키워드로 마켓 내 상위에 기재된 가벼운 플러그인 도구를 선별 연계해 이식하는 방식으로 수일 혹은 수개월에 걸친 개발 인프라 인건비와 스케줄 소요를 완전히 지상 최고 효율로 대체 가성비 좋게 정리하여 안전망을 신속 확보해 넘기는 것도 고도 전략의 판단 중 하나입니다. 진짜 비즈니스상 필수불결한 독자 도메인 단독 로직에만 골몰해 리소스를 아껴 활용하십시오.

최악의 수로 만약 June 30 적용 마지노선 타임라인을 놓칠 때는 어떻게 처리되나요?

즉각적인 동작 중지로 직결됩니다. 긴급 연장 신청 꼼수, 유예 조치 및 롤백 기회 따위는 절대 허용되지 않습니다. 기존 Scripts가 떠받치던 개별 감면 조건이나 감춰진 요금 방어 및 수단 제어로 보완되던 구조들이 한순간에 원복 풀려 일제히 노출 가동되는 불상사를 직면하게 되므로 최소한 정식 작동 데드라인 3~4주 전에 충분한 안정화 세팅을 끝내어 안착시키는 일정을 전개하십시오.

시간이 흐른 뒤 마이그레이션이 다 정리되면 기존 Script Editor 자체도 완전히 영구 이별 삭제해야 하나요?

지우는 것이 정상 수순으로 맞습니다. 6월 말일 기점 Shopify 공식단에서도 무난히 자동 잔재 정리 삭제를 진행할 계획이며 이미 Functions 시대로 교체를 조기 매김 한 머천트의 상황이라면 망설임 없이 현시점에 당장 제거하셔도 전용 함수 작동에 일체 결함이 생기지 않습니다.

이번 주 내에 집중 진행할 긴급 액션 가이드

이 가이드를 읽고 브라우저 탭을 조용히 다시 닫아버리는 것으로 끝나서는 안 됩니다. 지금 당장 7일 안으로 딱 이 네 가지 일들을 직접 실행에 옮깁십시오.

1. Customizations Report 즉시 다운로드: Settings → Checkout → Customizations Report 링크를 클릭하여 파일 사본을 입수하십시오. 이것이 당신의 우선순위 백로그 작업 명세서가 됩니다.

2. 개발 환경 설정 완료: 로컬 PC에 Node 18+ 탑재, CLI 유틸 탑재, 추가로 Rust가 필요한 상황이라면 해당 자산 컴파일 도구까지 설치해 shopify version 출력이 제대로 나오는지 찍어보는 시간을 30분만 투자해 매조지하십시오.

3. 초소형 단위의 테스트용 Function을 개발 스토어에 업로드해 보기: 가장 구조가 만만한 결제 숨김 등의 초미니 예제 파일 한 단락을 실제 샌드박스로 밀어 넣어 구동 단계를 체험해 보십시오. 단 한 판만으로 작업 체인의 사이클 프로세스를 온몸으로 완벽히 인지하여 체득할 수 있습니다.

4. 수 주 분량의 정식 전용 캘린더 작업 스프린트 기간 강제 예약: 이런 중대 이식 연동 과정은 일상 스케줄 틈 사이에 어설프게 끼워 처리하려다 기한을 맞추지 못하고 미끄러지기 마련입니다. 매주 이틀 정도 전담 전용 시간을 온전하게 확보해 스케줄을 잠가두고 사명감 있게 접근하십시오.

지근거리에서 확인한 전형적인 마이그레이션 성공 패턴은 대개 이러했습니다: 고민하며 눈치 보던 전야 지체 시기 2주, 실제 본체 코드 가동을 위한 빌드에 몰입해 달성한 3주, 자잘한 변수 검증 및 정리 기간 1주. 통합 총합 최소 6주 구간이 소비됩니다. 여러분에게 현재 남은 시간은 대략 10주 전후입니다. 넉넉하고 쫀쫀한 완衝 시야는 오직 코딩 및 안전 품질 보장 확인 영역에만 온전히 소모하셔야 안전합니다.

그리고 체크아웃 단에 들어선 필수 로직 구성이 원활하게 수습되어 나아간다면 대개 다음 단계 타깃은 고객의 사후 주문 수정 고충 영역의 교정으로 넘어가게 흐름을 가집니다. 맞춤 주소 처리나 옵션 수정 및 추가 할인 입수 요구가 지속 발생하기 때문입니다. 이러한 고민도 결국 장기 개선 목록에 대기 중이었다면(Plus 운영사나 Advanced 상점 모두에게 필수입니다) Shopify App Store에 상위 등록된 Revize 솔루션을 마음에 심고 여러분이 이번 마이그레이션 과정에서 빌드 완료한 Functions 구조들의 우수한 파트너로 기용해 함께 운행하시는 것을 보증해 드립니다.

추천 학습 자료 모음


관련 주제 아티클 추천


2026년 4월 16일입니다. 어제인 4월 15일은 Shopify가 Script Editor를 영구적으로 폐쇄한 날이었습니다. 이제 새로운 Script를 생성하거나 게시할 수 없습니다. 실행 중단은 75일 후인 2026년 June 30일에 적용됩니다.

지난 12개월 동안 이 마이그레이션을 "다음 스프린트"로 미뤄왔던 Shopify Plus 개발자나 Plus 스토어를 운영하는 대행사라면 지금 문제가 발생한 것입니다. "해결하면 좋은" 수준의 문제가 아닙니다. "7월 1일 자정에 체크아웃이 중단되는" 문제입니다. 대부분의 Plus 스토어는 수년에 걸쳐 5개에서 20개의 Script를 누적해 왔으며, 각 Script는 아무도 기억하지 못하는 할인 규칙, 배송 비활성화 또는 결제 게이트를 조용히 구동하고 있습니다.

이 가이드는 지난 1월에 있었으면 좋았을 기술 마이그레이션 매뉴얼입니다. 전략뿐만 아니라 실제 코드를 다룹니다. 이 글을 다 읽을 때쯤이면 Shopify CLI로 어떻게 Function을 스캐폴딩하는지, 할인, 배송 커스텀, 결제 커스텀을 위한 Rust 또는 JavaScript 로직을 어떻게 작성하는지, 태그된 고객 하위 집합을 대상으로 안전하게 테스트하는 방법과 기존 체크아웃을 중단 없이 프로덕션에 배포하는 방법을 알게 될 것입니다.

스토어에서 Scripts를 제거하고 Functions를 도입해 보겠습니다.



Developer migrating Shopify Scripts to Shopify Functions modules

요약: 60초 만에 끝내는 Scripts → Functions

한 줄 요약: Shopify Scripts(Script Editor의 Ruby 코드, Plus 전용)가 Shopify Functions(Rust 또는 JavaScript로 작성된 WebAssembly 모듈, 모든 플랜에서 사용 가능)로 대체됩니다. shopify app generate extension으로 Function을 스캐폴딩하고, 필요한 카트 데이터를 가져오는 run.graphql 쿼리를 작성한 다음, 작업(할인, 숨겨진 배송 방법 등)을 반환하는 run.rs 또는 run.js 파일을 작성합니다. 그리고 shopify app deploy로 배포한 뒤 Admin 또는 GraphQL 뮤테이션을 통해 활성화합니다. Functions는 컴파일된 WASM으로 5ms 미만의 대기 시간으로 실행되며, 모든 플랜에서 작동하고, 향후 Shopify가 지원하는 유일한 커스텀 방식입니다.

June 30일에 실제로 변경되는 사항

코드를 다루기 전에 날짜를 명확히 확인하십시오. 두 날짜 모두 중요합니다.


날짜

변경 사항

필요한 조치

2026년 April 15일 (지난 날짜)

Script Editor가 읽기 전용으로 전환되었습니다. 신규 Script 생성 및 기존 Script 편집이 불가합니다.

기존 Scripts는 여전히 실행됩니다. 지금 마이그레이션하거나 로직을 동결하십시오.

2026년 June 30일

모든 Shopify Scripts의 실행이 중단됩니다. 예외는 없습니다.

이 날짜 이전에 대체할 Function이 라이브 상태여야 합니다.

마이그레이션은 이분법적입니다. June 30일까지 Function을 배포하여 체크아웃을 계속 작동시키거나, 배포하지 못해 영향을 받는 모든 장바구니가 표준 가격, 표준 배송 요금 및 모든 활성화된 결제 수단으로 자동 기본 설정되거나 둘 중 하나입니다. 부분 점수는 없습니다. Script가 실행되거나 실행되지 않거나 둘 중 하나이며, June 30일 이후에는 실행되지 않습니다.

팁: Shopify admin에서 Settings → Checkout → Customizations Report를 여십시오. 스토어의 모든 활성 Script, 기능 및 권장되는 대체 Function 유형이 나열되어 있습니다. 거기서부터 시작하십시오.

Functions 대 Scripts: 실제 차이점


구분

Shopify Scripts (지원 종료)

Shopify Functions (대체)

언어

Ruby DSL (Shopify 전용)

Rust, JavaScript, TypeScript

런타임

Shopify 인프라의 샌드박스형 Ruby

WebAssembly (WASM) — 5ms 미만 실행

사용 가능 플랜

Plus 전용

모든 플랜 (커스텀 앱은 Plus 필요, 공개 앱은 제한 없음)

에디터

어드민 내 Script Editor

로컬 IDE + Shopify CLI

버전 관리

없음 — 라이브 편집

Git 연동 지원 — 전체 버전 제어 가능

테스트

체크아웃에서 수동 테스트

shopify app dev를 통한 로컬 개발, 미리보기 링크

배포

어드민에서 "Save" 클릭

터미널에서 shopify app deploy 실행

대상 (Targets)

Line items, shipping, payments

Discounts, Cart Transform, Validation, Delivery Customization, Payment Customization, Order Routing, Fulfillment Constraints 등

아키텍처의 전환이 중요합니다. Scripts는 "텍스트 영역에서 Ruby를 수정하는 것"이었습니다. Functions는 "실제 앱을 작성하고, 버전을 관리하고, 로컬에서 테스트하고, 실제 CI 파이프라인을 통해 배포하는 것"입니다. 이는 진입 장벽이 더 높습니다. 또한 당분간 체크아웃 로직을 위해 수행할 마지막 마이그레이션이 될 것입니다. Functions는 Scripts처럼 임시방편이 아닌 Shopify의 장기적인 약속입니다.

Scripts에 맞는 올바른 Function 유형 매핑

현재 사용 중인 모든 Script는 정확히 하나의 Function API에 매핑됩니다. 모니터에 고정해 두어야 할 대조표는 다음과 같습니다.


기존 Script 유형

기능

신규 Function API

Function Target

Line Item Script

특정 상품 / 고객 / 카트 조건에 할인 적용

Cart & Checkout Discounts API

cart.lines.discounts.generate.run

Shipping Script (할인)

카트 규칙에 기반한 무료 / 할인 배송 적용

Cart & Checkout Discounts API

cart.delivery-options.discounts.generate.run

Shipping Script (숨기기 / 이름 변경 / 순서 변경)

특정 금액 초과 시 배송비 숨기기, "Standard"를 "$50 이상 무료"로 변경 등

Delivery Customization API

cart.delivery-options.transform.run

Payment Script

B2B 고객 대상 PayPal 숨기기, $500 초과 시 COD 숨기기, 결제 수단 정렬 등

Payment Customization API

cart.payment-methods.transform.run

카트 변경 Script (드묾)

번들 상품 구성, 품목 교체

Cart Transform API

cart.transform.run

체크아웃 차단 Script

유효하지 않은 SKU 조합 시 카트 거부

Cart & Checkout Validation API

cart.validations.generate.run

Scripts가 10개인 경우, 대개 3~5개의 Functions를 빌드하게 됩니다. 여러 Scripts가 더 깔끔한 분기 로직을 통해 하나의 Function으로 통합되는 경우가 많기 때문입니다.



Shopify Functions unifying discounts, delivery, and payment customizations

사전 요구 사항: 로컬 개발 환경 설정

Function을 스캐폴딩하기 전에 로컬에 세 가지가 설치되어 있어야 합니다. 터미널에서 다음 확인 작업을 수행하십시오.

1. Node.js 18+


node --version
# Must be >= 18.0.0

구버전인 경우 nvm을 통해 설치하거나 nodejs.org에서 다운로드하십시오.

2. Shopify CLI 3+


npm install -g @shopify/cli@latest
shopify version
# Should output 3.x or higher

3. Rust 툴체인 (Rust로 Functions를 작성할 경우에만 해당)


curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-wasip1
cargo --version

JavaScript Functions의 경우 Rust가 필요하지 않습니다. 팀 내에서 하나의 언어를 선택하여 유지하십시오. 혼용 시 유지관리 리소스가 늘어납니다.

4. 개발 스토어

Partner 대시보드에 로그인하여 새 개발 스토어를 만들거나 기존 스토어를 사용하십시오. 프로덕션에 적용하기 전에 이 개발 스토어에 Functions를 배포하여 검증하게 됩니다.

첫 번째 Function 스캐폴딩

CLI가 상용구 코드의 대부분을 처리합니다. 아무 디렉터리 내에서 다음과 같이 실행하십시오.


# Create a new Shopify app (skip if you already have one)
shopify app init my-checkout-functions
cd my-checkout-functions

# Generate a Function extension
shopify app generate extension

CLI가 안내 프롬프트를 표시합니다. 할인 Function의 경우 다음과 같이 선택하십시오.

  • Type: Function

  • Template: discount (또는 cart_checkout_validation, delivery_customization, payment_customization 등)

  • Language: Rust 또는 JavaScript

  • Name: volume-discount-fn과 같은 이름

그러면 다음 구성으로 extensions/volume-discount-fn/이 생성됩니다.


extensions/volume-discount-fn/
├── shopify.extension.toml      # Function config targets, build, version
├── src/
├── cart_lines_discounts_generate_run.graphql   # Input query
└── cart_lines_discounts_generate_run.rs        # Function logic
├── Cargo.toml                  # Rust dependencies (Rust only)
└── README.md

자주 편집하게 될 세 파일은 .toml(설정), .graphql(입력), 그리고 .rs / .js(로직)입니다. 이것이 전부입니다.

튜토리얼 1: Line Item Script 대체 (수량 할인)

기존 Script에서 카트에 특정 컬렉션 상품이 5개 이상 담겨 있을 때마다 주문 소계의 10%를 할인해 주었다고 가정해 보겠습니다. 다음은 이를 대체하는 Function 버전입니다.

단계 1.1: 구성 (shopify.extension.toml)


api_version = "2026-01"

[[extensions]]
name = "volume-discount-fn"
handle = "volume-discount-fn"
type = "function"

[[extensions.targeting]]
target = "cart.lines.discounts.generate.run"
input_query = "src/cart_lines_discounts_generate_run.graphql"
export = "cart_lines_discounts_generate_run"

[extensions.build]
command = "cargo build --target=wasm32-wasip1 --release"
path = "target/wasm32-wasip1/release/volume-discount-fn.wasm"
watch = ["src/**/*.rs"]

단계 1.2: 입력 쿼리 (src/cart_lines_discounts_generate_run.graphql)


query Input {
  cart {
    lines {
      id
      quantity
      cost {
        subtotalAmount {
          amount
        }
      }
      merchandise {
        ... on ProductVariant {
          product {
            inAnyCollection(ids: ["gid://shopify/Collection/123456789"])
          }
        }
      }
    }
  }
  discount {
    discountClasses
  }
}

팁: Functions는 쿼리한 데이터만 볼 수 있습니다. GraphQL을 최소한으로 유지하십시오. 생략하는 필드가 많을수록 실행 속도가 빨라지고 비용이 절감됩니다.

단계 1.3: 로직 (src/cart_lines_discounts_generate_run.rs)


use super::schema;
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function]
fn cart_lines_discounts_generate_run(
    input: schema::cart_lines_discounts_generate_run::Input,
) -> Result<schema::CartLinesDiscountsGenerateRunResult> {
    // Bail if discount class doesn't match
    let has_order_discount = input
        .discount()
        .discount_classes()
        .contains(&schema::DiscountClass::Order);

    if !has_order_discount {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // Sum quantities of items in the target collection
    let qualifying_qty: i64 = input
        .cart()
        .lines()
        .iter()
        .filter(|line| {
            if let schema::Merchandise::ProductVariant(v) = line.merchandise() {
                *v.product().in_any_collection()
            } else {
                false
            }
        })
        .map(|line| *line.quantity())
        .sum();

    if qualifying_qty < 5 {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // Apply 10% off the order subtotal
    Ok(schema::CartLinesDiscountsGenerateRunResult {
        operations: vec![schema::CartOperation::OrderDiscountsAdd(
            schema::OrderDiscountsAddOperation {
                selection_strategy: schema::OrderDiscountSelectionStrategy::First,
                candidates: vec![schema::OrderDiscountCandidate {
                    targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal(
                        schema::OrderSubtotalTarget {
                            excluded_cart_line_ids: vec![],
                        },
                    )],
                    message: Some("Volume discount: 10% off".to_string()),
                    value: schema::OrderDiscountCandidateValue::Percentage(
                        schema::Percentage { value: Decimal(10.0) }
                    ),
                    conditions: None,
                    associated_discount_code: None,
                }],
            },
        )],
    })
}

단계 1.4: 테스트, 배포, 활성화


# Local development with hot reload
shopify app dev

# When ready, deploy
shopify app deploy

# In the GraphiQL panel that opens (press `g` in the dev terminal),
# create the automatic discount that uses your Function:
mutation {
  discountAutomaticAppCreate(
    automaticAppDiscount: {
      title: "Volume Discount (5+ collection items)"
      functionHandle: "volume-discount-fn"
      discountClasses: [ORDER]
      startsAt: "2026-04-16T00:00:00Z"
    }
  ) {
    automaticAppDiscount { discountId }
    userErrors { field message }
  }
}

완료되었습니다. Function이 활성화되어 버전 관리되며 기존 Script를 완벽하게 대체합니다.

튜토리얼 2: Shipping Script 대체 (카트 임계값 초과 시 배송비 숨기기)

자주 사용되는 Script: "대량 구매 시 비용이 많이 드는 특송 배송을 방지하기 위해 장바구니 가격이 $500를 초과하면 Express Shipping을 숨깁니다." 다음은 Delivery Customization Function 버전입니다.

단계 2.1: 스캐폴딩


shopify app generate extension --template delivery_customization --name hide-express-fn

단계 2.2: 입력 쿼리 (src/run.graphql)


query Input {
  cart {
    cost {
      subtotalAmount {
        amount
      }
    }
    deliveryGroups {
      deliveryOptions {
        handle
        title
      }
    }
  }
}

단계 2.3: 로직 (src/run.js — JavaScript 방식)


// @ts-check
/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 */

const NO_CHANGES = { operations: [] };
const THRESHOLD = 500.0;
const HIDE_TITLES = ["Express", "Overnight"];

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount);
  if (subtotal < THRESHOLD) return NO_CHANGES;

  const operations = input.cart.deliveryGroups.flatMap((group) =>
    group.deliveryOptions
      .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t)))
      .map((opt) => ({
        hide: { deliveryOptionHandle: opt.handle },
      }))
  );

  return { operations };
}

단계 2.4: Admin을 통한 활성화 (GraphQL 불필요)

Delivery Customizations에는 고유한 Admin UI가 있습니다. shopify app deploy를 수행한 후 다음 단계를 진행하십시오.

  1. Settings → Shipping and delivery로 이동합니다.

  2. 하단의 Customizations 섹션으로 스크롤합니다.

  3. Add customization을 클릭한 다음 작성한 Function을 선택합니다.

  4. 저장합니다.

숨김 규칙이 프로덕션에 적용되었습니다. Mutation 처리를 거치지 않아도 됩니다.



Shopify delivery customization Function hiding shipping option at checkout

튜토리얼 3: Payment Script 대체 (B2B 고객 대상 COD 숨기기)

기존 Script: "'B2B' 태그가 지정된 고객의 착불 결제(Cash on Delivery)를 숨깁니다." 다음은 Payment Customization 버전입니다.

단계 3.1: 스캐폴딩


shopify app generate extension --template payment_customization --name hide-cod-b2b-fn

단계 3.2: 입력 쿼리


query Input {
  cart {
    buyerIdentity {
      customer {
        hasTags(tags: [{ tag: "B2B" }]) {
          tag
          hasTag
        }
      }
    }
  }
  paymentMethods {
    id
    name
  }
}

단계 3.3: 로직 (src/run.js)


const NO_CHANGES = { operations: [] };

export function run(input) {
  const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0];
  const isB2B = tagCheck?.hasTag === true;
  if (!isB2B) return NO_CHANGES;

  const codMethod = input.paymentMethods.find((pm) =>
    pm.name.toLowerCase().includes("cash on delivery")
  );
  if (!codMethod) return NO_CHANGES;

  return {
    operations: [{ hide: { paymentMethodId: codMethod.id } }],
  };
}

단계 3.4: 활성화

Payment Customizations 역시 아래 경로에 Admin UI가 존재합니다: Settings → Payments → Customizations. 배송 설정과 동일한 흐름으로 진행하십시오. Function을 선택하고 저장하면 완료됩니다.

함께 읽을거리 — 사후 구매(Post-Purchase)에 관하여

이곳은 Revize 블로그이므로 간략히 안내해 드립니다. Revize는 Functions가 건드릴 수 없는 부분들을 처리합니다. 주문이 완료된 후 고객이 상품을 추가하거나, 옵션을 변경하거나, 배송 주소를 수정하거나, 누락된 할인을 수동 적용하고 싶어 할 때가 있습니다. Functions는 체크아웃 시점에 작동하지만, Revize는 체크아웃 이후에 작동합니다. Functions가 장바구니에 허용될 항목을 결정한다면, Revize는 환불 후 다시 처음부터 주문을 빌드하는 불필요한 과정 없이 고객과 지원 팀이 주문 완료 후에도 상품을 간단하게 편집할 수 있도록 돕습니다. 이는 두 종류의 상점에 꼭 필요합니다. 수동 편집이 어려울 만큼 주문량이 막대한 스토어(Plus 운영사나 대규모 Advanced 스토어)와, 고객 경험이 브랜드의 핵심 가치여서 "변경 처리가 어렵습니다" 유형의 이메일이 재구매율 하락으로 연결되는 스토어입니다.

만약 마이그레이션 계획이 Scripts → Functions에만 맞추어져 있고 사후 주문 편집 영역을 고려하지 않았다면, 약 3주 뒤에 "대체 왜 이렇게 복잡하지?"라는 한계에 부딪힐 것입니다. 최근 완결된 주문 관리 가이드에 체크아웃 이후 관리를 위한 완벽한 해결책을 제시해 두었습니다.

다시 마이그레이션 안내로 돌아가겠습니다.

테스트 전략: 태그 기반 고객(Tagged Customer) 패턴

Functions는 Admin에서 켜고 깔 수 있는 별도의 "초안 모드(Draft Mode)"를 제공하지 않습니다. 이 때문에 가장 프로페셔널한 방식은 새로 개발한 Function을 특정 고객 태그 기준으로 제어하여, 기존 Script와 신규 Function을 병렬로 구동하면서 테스트 가입자들에게 정확하게 동일한 결과가 도출되는지 확인한 뒤 실 서비스에 완전히 반영하는 것입니다.

단계 1: 테스트 대상 사용자 태그 설정

Customers 메뉴에서 내부 개발 테스트 계정 2~3개에 FN-TESTER 태그를 추가합니다.

단계 2: 태그 존재 여부에 따라 Function 분기 처리


// At the top of your run function
let is_tester = input
    .cart()
    .buyer_identity()
    .and_then(|bi| bi.customer())
    .map(|c| c.has_any_tag())
    .unwrap_or(&false);

if !*is_tester {
    return Ok(default_result);  // Fall through to existing Script
}

// New Function logic only runs for tagged users

단계 3: 입력 쿼리에 hasAnyTag 추가


cart {
  buyerIdentity {
    customer {
      hasAnyTag(tags: ["FN-TESTER"])
    }
  }
}

단계 4: 체크아웃에서 확인

태그된 테스트 유저로 로그인하여 체크아웃을 진행하고 Function이 성공적으로 호출되는지 확인하십시오. 태그가 없는 일반 사용자로 로그인하여 기존 Script가 정상적으로 수행되는지도 점검합니다. 며칠 간 문제없이 양쪽이 균형을 이루는 것을 확인하면 태그 체크 조건을 해제하고 전체 사용자를 대상으로 Function을 가동합니다.

단계 5: 기존 Script 게시 중단

Apps → Script Editor → [사용 중인 Script] → Unpublish 순서로 이동하여 비활성화합니다. 정상 비게시 상태가 되면 새로 도입한 Function이 단일 기준이 됩니다.

지속 확장이 가능한 실제 배포 워크플로

모든 배포를 매번 개발자 개인의 로컬 PC에서 직접 처리하는 우는 범하지 않아야 합니다. 한두 개의 Scripts 마이그레이션을 마쳤다면 전용 CI 파이프라인을 구축할 때입니다.

가장 기본적이고 효율적인 검증 워크플로 구성


# .github/workflows/deploy-functions.yml
name: Deploy Shopify Functions
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: wasm32-wasip1 }
      - run: npm install -g @shopify/cli@latest
      - run: shopify app deploy --force
        env:
          SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}

Partner 대시보드의 Settings → Tokens 메뉴에서 Partner 토큰을 생성하십시오. 이제 main 브랜치로 머지될 때마다 설계한 Functions가 즉시 자동 배포됩니다. "그거 배포했나요?" 하며 슬랙 메시지를 쓸 필요가 없습니다.

버전 제어 및 롤백

shopify app deploy 명령을 내리면 스냅샷 버전이 기록됩니다. 필요 시 아래와 같이 손쉽게 이전 코드로 롤백이 가능합니다.


shopify app versions list
shopify app release --version <previous-version-id

기존 Scripts에서 롤백을 위해 "기존 코드를 일일이 기억해 복사 붙여넣기 하던 방식"과 비교해 보면 하늘과 땅 차이 수준으로 쾌적해졌습니다.



Shopify Functions deployment pipeline across local staging and production environments

대표적으로 실수하는 5가지 유형

지난 1년 동안 다수의 Plus 머천트들이 수행한 이번 마이그레이션 과업을 지원하면서 매번 공통적으로 목격해 온 가장 흔한 대표적 실수들을 공유합니다.

1. 1:1로만 가볍게 주입하려는 태도: 기존 Script가 세 개였다 하더라도 영리하게 리팩토링한 분기문을 활용해 탄탄한 1개의 Function 내부에서 모두 소화가 가능한 경우가 허다합니다. 이식을 위해 키보드를 잡기 전 전체 Scripts 설계를 종합 감사하는 접근이 필수적입니다.

2. Read scopes의 누락: 대개 구현하려는 대다수 Function은 기본값으로 read_customers, read_orders, 혹은 write_discounts 중 하나 이상의 스코프를 반드시 선언해 주어야 합니다. 잊지 말고 shopify.app.tomlscopes 영역에 입력한 뒤 승인해 주십시오. 그렇지 않으면 graphql 데이터가 null로 반환될 것입니다.

3. 비교 검증 없이 곧바로 일반 고객 대상 가동: 작성한 비즈니스 로직이 한눈에 보기에 완벽해 노출 문제가 없을지라도, 끈적하게 매달리는 경계 조건 예외 케이스(빈 장바구니 조건, 기프트카드 복합 결제 이슈, 적립금 등)는 기어코 버그를 발생시킵니다. 내부 테스트 태그 주차를 통한 며칠간의 소소한 지체는 P1 장애가 터져 심야에 복구 업무를 겪는 공포에 대처하는 현명한 보험비용입니다.

4. Customizations Report 미작동: 현재 라이브 환경에서 실동 중인 요소의 인벤토리를 가장 쉽고 투명하게 식별하여 일목요연하게 전달해 주는 레포트입니다. 기억에 의존하는 대차 대신 기계가 반환한 레포트 숫자를 바탕으로 엄밀하게 준비하십시오.

5. 콜렉션 ID 혹은 특정 태그값의 하드코딩: 판매자의 가변 관리가 개입해야 하는 상황이라면 메타필드를 활용해 설정값 데이터를 받아 동적으로 처리하십시오. CLI 툴링을 이용하면 기본 뼈대를 수월하게 구성해 낼 수 있습니다. 관련 문서의 Function 설정을 정독하십시오.

앞으로 남은 75일에 걸친 최종 마이그레이션 로드맵

June 30일까지 가장 평온하고 안전하게 도달하는 단계별 실전 일정표를 설계해 드립니다.


기간

수행할 주요 과업

1주 차 (이번 주)

상용 Customizations Report 확인. 구동 중인 누적 연동 상세 식별. Function 처리용 리스트업 확정.

2~3주 차

로컬에 필요한 기술 세팅 진행 및 구성. 기초적인 수준의 간소한 Function 생성(결제 감추기 로직 대상 추천).

4~6주 차

할인 관련 가이드 마이그레이션. Discounts API 내부에서 유연하고 촘촘한 정리를 요구하므로 공수가 가장 크게 듦. 충분한 테스트 기간 확보 필요.

7~8주 차

배송/요금 관련 Script 포팅 연동. 완성된 Delivery Customizations를 어드민 메뉴에서 연계하여 활성화.

9~10주 차

깃헙 액션 등을 연동해 CI/CD 파이프라인 정비. 더 이상 개발자 각자 노트북 단위 수동 배포가 없도록 선회.

11주 차 (6월 중순)

동일 성능 구현 완결성 테스트 수행. 구 Scripts 비게시 비활성 처리. 2주간 온전히 신규 Functions만으로 서비스 운영 모니터링.

June 30

최종 Sunset 시행일 조우. 미리 선조치해 둔 덕에 아무 장애 현상도 경험하지 않는 쾌가 보장됨.

지금 첫걸음을 뗀다면 일정에 여유가 있지만, 6월에 시작하는 경우 버퍼가 남아있지 않을 것입니다.



Week-by-week migration roadmap from Shopify Scripts to Functions

자주 묻는 질문 (FAQ)

Functions를 사용하려면 무조건 Shopify Plus 플랜이어야 하나요?

독립형 커스텀 함수(Custom Functions) 제작의 경우 오직 Shopify Plus 플랜에서만 작성이 허용되지만, 공개 앱 생태계에 게시된 Functions 기반 솔루션 플러그인은 전 플랜 누구나 즉시 설치하여 적용하는 길이 확보되어 있습니다. 작성 주도가 불가능한 하위 요금 상태라면 대체재가 될 만한 검증된 공개 앱을 Shopify App Store에서 발굴해 장착하는 방향 혹은, 온전히 독립 자산을 소지하기 위하여 Plus로 업그레이드할 것을 권해 드립니다.

TypeScript 언어도 공식 대응하나요?

예, 무결하게 커버하며 생성 툴링 역시 온전하게 지원합니다. shopify app generate extension 단계 안내시 "JavaScript" 선택 시 내부적으로 개발 연계를 위한 타입 선언 바인딩 리소스들이 import("../generated/api")를 통하여 수반될 것입니다. tsconfig.json 파일을 기입하고 확장자를 .ts로 승격시켜 주면 별다른 이격 없이 사용 가능합니다. 어차피 산출 결과인 웹어셈블리 단에서는 원본 언어에 따른 차이점이 발생할 여지가 없습니다.

성능 면에서 Scripts 대비 얼마나 민첩하게 도나요?

대개 5ms 이하 극단으로 정밀하게 마침표를 찍으며 기존 Ruby Scripts 작동 시간보다 압도적으로 빨라집니다. 단일 WASM 타깃 런타임 특성에 따라 Shopify 코어 측에서 요청 당 5ms 처리 예산 리밋을 원칙적으로 강제 준수하게 합니다. 만일 이 리밋의 상단을 뚫으면 해당 호출 작업에 즉시 블락 처리가 내려집니다. 통상 적절히 튜닝된 헬시 레벨 로직은 평균 1~2ms 안팎으로 통과되어 기존 대비 쾌적성이 증명됩니다.

함수 본체 구동 중 서드파티 외부 API도 직접 붙여 땡겨올 수 있나요?

불가능합니다. Functions 내부에서 네트워크 소켓 등의 아웃바운드 발신 처리는 절대 불가능하게 격리 설계되어 있습니다. 전적으로 오로지 유입 장바구니 데이터를 받아서 고속 논리 가공 처리를 얹은 뒤 결괏값을 내보내는 구조적 가동에 전념하게 해야 합니다. 실시간 연계 제휴 CRM 쿼리 같은 처리가 필요하다면 메타필드에 데이터를 선행 캐싱해 적재해 두거나, 대안이 될 플랫폼 구조(App Proxy 혹은 백엔드를 거친 Cart Transform) 설계를 별도 마련하시는 방향으로 사전에 우회하셔야 합니다.

Cart Transform API와 일반 Discounts 구성과의 근본적 체계상 구분이 모호합니다.

쉽게 말하자면 가격을 내릴 때는 Discounts를 기용하고 장바구니 구조의 수량을 조작 또는 엮을 때는 Cart Transform을 선택합니다. %율 적용, 세일 처리는 전자 범주이며 번들 세트 묶기 등 복합 구성 조립이 수반될 때 후자를 호출하는 것으로 두시면 깔끔합니다. 예전 구 Scripts 코드는 한 단락의 파일 안에 두 영역을 뒤섞는 경향이 많았습니다. 마이그레이션 시 깔끔히 두 개 함수 영역으로 개별 분할해 설계해 내는 것을 타깃으로 잡으십시오.

로컬 상황에서 개발 스토어 연결 통과 세팅 없이 동작 상황을 미리 볼 방법은 전혀 존재하지 않나요?

단위 처리는 cargo test 혹은 npm test 템플릿의 실행을 통해 어느 정도 검토가 충분히 설계되나, 최종 종단 체크아웃 구동 연동은 어쩔 수 없이 개발 스토어의 존재가 확보되어 있어야 진행을 담보합니다. 셸 내부에서 쓸 수 있는 shopify app function run을 실행하여 유입 Mock JSON 파일을 대상으로 결괏값을 보는 작업만 로컬 개발 도중 중간 테스트용으로 유용하게 차용해 보시기 바랍니다.

동일 형식의 여러 Function 종류를 동시에 적재해 연속 작동되도록 체이닝 구성이 허용되나요?

예, 가능합니다. 지정한 결정 순서 우선 체계에 정직하게 입각하여 차례로 병렬 수반되어 내리게 돌아갑니다. 할인은 스태킹 정책 설정값의 연계를 따르고 배송 및 결제 사용자 지정은 결과 처리가 줄을 이어 다음 작동 인풋으로 순차 적용되는 방식으로 수행하므로 관념상 한 단일 유형 내에는 직관적 가시성을 고려해 한 개 함수의 결합으로 응집력 있게 풀어내 전개하시는 것을 장려합니다.

새 Function 이식을 성공적으로 끝내서 올려두면 이전의 구 Scripts는 즉시 지워지나요?

사용자가 직접 임의로 Script Editor 상에서 비게시 설정을 가할 때까지 우선 두 영역의 자산이 공동으로 물려 실서버에 돌게 보장됩니다. 설계 이격을 대비해 병행 실행해서 추이를 대조 판단할 기회를 기획적으로 지원하게 만드는 조치입니다. 온전하게 동작 검증을 거친 직후 구형을 마감시키면 됩니다. 어차피 2026년 June 30일이 지난다면 어드민에 켜진 채 남아있는 스위치가 있더라도 강제 효력 상실을 거치기에 무조건 꺼져 내리게 되어 있습니다.

이번 마이그레이션 변경 조치가 혹시 운영 중인 자체 사이트 SEO 영역에 악영향을 주나요?

영향을 끼치지 않습니다. Functions 연산 처리는 전적으로 체크아웃 단계 서버사이드 내부에서 기밀하게 수반 및 소거되므로 테마 HTML 렌더 파일에 간섭하거나 구글 크롤 스크립트 검색에 아무 영항을 끼치지 않습니다. 테마 스킨과 상품 노출 및 검색 노출의 전면은 기존대로 평화롭게 유지 보장받으니 걱정 덜어내십시오.

기존 Input.line_items 상의 커스텀 추가 메타 정보 값을 받던 옛 룬은 어떻게 매핑하나요?

GraphQL 인풋 쿼리의 attribute 항목을 선언해 줌으로써 기존에 쓰던 기능의 구성을 백퍼센트 복원하여 소화할 수 있습니다. lines 셀렉터 내부 스코프에 attribute(key: "attribute_name") { value }를 태워 뽑아 활용하십시오. 예전 루비 문법 체득으로 파고들어 보던 변숫값 호출이 GraphQL 타깃으로 바뀌었을 뿐, 완전히 같은 내용을 보장합니다.

주문 관련 태깅 작업도 구동 본체 내에서 태그 필드 가입 처리를 진행해 반환해 주나요?

불가능합니다. Functions는 장바구니 인스턴스의 트랜스폼 결과 데이터만을 담아서 반환하므로 내부에서 데이터베이스 태그 필드를 수정하는 등의 영속성 처리를 일으킬 수 없습니다. 대신 우수 사후 조치를 설계하고 싶다면 완료 이벤트와 연계되어 돌아가기에 간편한 노코드 워크플로 툴인 Shopify Flow를 도입해 연동 장악하는 길을 선택해 유연하게 엮어 쓰셔야 맞습니다. (예시: 특정 할인 코드 매칭 감지 후 Flow를 타고 해당 주문 문서에 "VOLUME-DISCOUNT-APPLIED" 라벨 태그 입력 처리)

시간이 없을 때 우선 임시 해결이 어렵다면, App Store에 게시되어 기성 판매 중인 Functions 솔루션을 그냥 매는 방향은 부적합한가요?

오히려 탁월한 훌륭한 해답입니다. 현시점 마켓플레이스에는 Functions를 기반 삼아 작동하는 강력한 플러그인 유틸들이 널리 상용 출시되어 성업 중입니다. "할인 관련 기능", "결제 및 배송 수단 가드 관련 필터 설정" 키워드로 마켓 내 상위에 기재된 가벼운 플러그인 도구를 선별 연계해 이식하는 방식으로 수일 혹은 수개월에 걸친 개발 인프라 인건비와 스케줄 소요를 완전히 지상 최고 효율로 대체 가성비 좋게 정리하여 안전망을 신속 확보해 넘기는 것도 고도 전략의 판단 중 하나입니다. 진짜 비즈니스상 필수불결한 독자 도메인 단독 로직에만 골몰해 리소스를 아껴 활용하십시오.

최악의 수로 만약 June 30 적용 마지노선 타임라인을 놓칠 때는 어떻게 처리되나요?

즉각적인 동작 중지로 직결됩니다. 긴급 연장 신청 꼼수, 유예 조치 및 롤백 기회 따위는 절대 허용되지 않습니다. 기존 Scripts가 떠받치던 개별 감면 조건이나 감춰진 요금 방어 및 수단 제어로 보완되던 구조들이 한순간에 원복 풀려 일제히 노출 가동되는 불상사를 직면하게 되므로 최소한 정식 작동 데드라인 3~4주 전에 충분한 안정화 세팅을 끝내어 안착시키는 일정을 전개하십시오.

시간이 흐른 뒤 마이그레이션이 다 정리되면 기존 Script Editor 자체도 완전히 영구 이별 삭제해야 하나요?

지우는 것이 정상 수순으로 맞습니다. 6월 말일 기점 Shopify 공식단에서도 무난히 자동 잔재 정리 삭제를 진행할 계획이며 이미 Functions 시대로 교체를 조기 매김 한 머천트의 상황이라면 망설임 없이 현시점에 당장 제거하셔도 전용 함수 작동에 일체 결함이 생기지 않습니다.

이번 주 내에 집중 진행할 긴급 액션 가이드

이 가이드를 읽고 브라우저 탭을 조용히 다시 닫아버리는 것으로 끝나서는 안 됩니다. 지금 당장 7일 안으로 딱 이 네 가지 일들을 직접 실행에 옮깁십시오.

1. Customizations Report 즉시 다운로드: Settings → Checkout → Customizations Report 링크를 클릭하여 파일 사본을 입수하십시오. 이것이 당신의 우선순위 백로그 작업 명세서가 됩니다.

2. 개발 환경 설정 완료: 로컬 PC에 Node 18+ 탑재, CLI 유틸 탑재, 추가로 Rust가 필요한 상황이라면 해당 자산 컴파일 도구까지 설치해 shopify version 출력이 제대로 나오는지 찍어보는 시간을 30분만 투자해 매조지하십시오.

3. 초소형 단위의 테스트용 Function을 개발 스토어에 업로드해 보기: 가장 구조가 만만한 결제 숨김 등의 초미니 예제 파일 한 단락을 실제 샌드박스로 밀어 넣어 구동 단계를 체험해 보십시오. 단 한 판만으로 작업 체인의 사이클 프로세스를 온몸으로 완벽히 인지하여 체득할 수 있습니다.

4. 수 주 분량의 정식 전용 캘린더 작업 스프린트 기간 강제 예약: 이런 중대 이식 연동 과정은 일상 스케줄 틈 사이에 어설프게 끼워 처리하려다 기한을 맞추지 못하고 미끄러지기 마련입니다. 매주 이틀 정도 전담 전용 시간을 온전하게 확보해 스케줄을 잠가두고 사명감 있게 접근하십시오.

지근거리에서 확인한 전형적인 마이그레이션 성공 패턴은 대개 이러했습니다: 고민하며 눈치 보던 전야 지체 시기 2주, 실제 본체 코드 가동을 위한 빌드에 몰입해 달성한 3주, 자잘한 변수 검증 및 정리 기간 1주. 통합 총합 최소 6주 구간이 소비됩니다. 여러분에게 현재 남은 시간은 대략 10주 전후입니다. 넉넉하고 쫀쫀한 완衝 시야는 오직 코딩 및 안전 품질 보장 확인 영역에만 온전히 소모하셔야 안전합니다.

그리고 체크아웃 단에 들어선 필수 로직 구성이 원활하게 수습되어 나아간다면 대개 다음 단계 타깃은 고객의 사후 주문 수정 고충 영역의 교정으로 넘어가게 흐름을 가집니다. 맞춤 주소 처리나 옵션 수정 및 추가 할인 입수 요구가 지속 발생하기 때문입니다. 이러한 고민도 결국 장기 개선 목록에 대기 중이었다면(Plus 운영사나 Advanced 상점 모두에게 필수입니다) Shopify App Store에 상위 등록된 Revize 솔루션을 마음에 심고 여러분이 이번 마이그레이션 과정에서 빌드 완료한 Functions 구조들의 우수한 파트너로 기용해 함께 운행하시는 것을 보증해 드립니다.

추천 학습 자료 모음


관련 주제 아티클 추천


2026년 4월 16일입니다. 어제인 4월 15일은 Shopify가 Script Editor를 영구적으로 폐쇄한 날이었습니다. 이제 새로운 Script를 생성하거나 게시할 수 없습니다. 실행 중단은 75일 후인 2026년 June 30일에 적용됩니다.

지난 12개월 동안 이 마이그레이션을 "다음 스프린트"로 미뤄왔던 Shopify Plus 개발자나 Plus 스토어를 운영하는 대행사라면 지금 문제가 발생한 것입니다. "해결하면 좋은" 수준의 문제가 아닙니다. "7월 1일 자정에 체크아웃이 중단되는" 문제입니다. 대부분의 Plus 스토어는 수년에 걸쳐 5개에서 20개의 Script를 누적해 왔으며, 각 Script는 아무도 기억하지 못하는 할인 규칙, 배송 비활성화 또는 결제 게이트를 조용히 구동하고 있습니다.

이 가이드는 지난 1월에 있었으면 좋았을 기술 마이그레이션 매뉴얼입니다. 전략뿐만 아니라 실제 코드를 다룹니다. 이 글을 다 읽을 때쯤이면 Shopify CLI로 어떻게 Function을 스캐폴딩하는지, 할인, 배송 커스텀, 결제 커스텀을 위한 Rust 또는 JavaScript 로직을 어떻게 작성하는지, 태그된 고객 하위 집합을 대상으로 안전하게 테스트하는 방법과 기존 체크아웃을 중단 없이 프로덕션에 배포하는 방법을 알게 될 것입니다.

스토어에서 Scripts를 제거하고 Functions를 도입해 보겠습니다.



Developer migrating Shopify Scripts to Shopify Functions modules

요약: 60초 만에 끝내는 Scripts → Functions

한 줄 요약: Shopify Scripts(Script Editor의 Ruby 코드, Plus 전용)가 Shopify Functions(Rust 또는 JavaScript로 작성된 WebAssembly 모듈, 모든 플랜에서 사용 가능)로 대체됩니다. shopify app generate extension으로 Function을 스캐폴딩하고, 필요한 카트 데이터를 가져오는 run.graphql 쿼리를 작성한 다음, 작업(할인, 숨겨진 배송 방법 등)을 반환하는 run.rs 또는 run.js 파일을 작성합니다. 그리고 shopify app deploy로 배포한 뒤 Admin 또는 GraphQL 뮤테이션을 통해 활성화합니다. Functions는 컴파일된 WASM으로 5ms 미만의 대기 시간으로 실행되며, 모든 플랜에서 작동하고, 향후 Shopify가 지원하는 유일한 커스텀 방식입니다.

June 30일에 실제로 변경되는 사항

코드를 다루기 전에 날짜를 명확히 확인하십시오. 두 날짜 모두 중요합니다.


날짜

변경 사항

필요한 조치

2026년 April 15일 (지난 날짜)

Script Editor가 읽기 전용으로 전환되었습니다. 신규 Script 생성 및 기존 Script 편집이 불가합니다.

기존 Scripts는 여전히 실행됩니다. 지금 마이그레이션하거나 로직을 동결하십시오.

2026년 June 30일

모든 Shopify Scripts의 실행이 중단됩니다. 예외는 없습니다.

이 날짜 이전에 대체할 Function이 라이브 상태여야 합니다.

마이그레이션은 이분법적입니다. June 30일까지 Function을 배포하여 체크아웃을 계속 작동시키거나, 배포하지 못해 영향을 받는 모든 장바구니가 표준 가격, 표준 배송 요금 및 모든 활성화된 결제 수단으로 자동 기본 설정되거나 둘 중 하나입니다. 부분 점수는 없습니다. Script가 실행되거나 실행되지 않거나 둘 중 하나이며, June 30일 이후에는 실행되지 않습니다.

팁: Shopify admin에서 Settings → Checkout → Customizations Report를 여십시오. 스토어의 모든 활성 Script, 기능 및 권장되는 대체 Function 유형이 나열되어 있습니다. 거기서부터 시작하십시오.

Functions 대 Scripts: 실제 차이점


구분

Shopify Scripts (지원 종료)

Shopify Functions (대체)

언어

Ruby DSL (Shopify 전용)

Rust, JavaScript, TypeScript

런타임

Shopify 인프라의 샌드박스형 Ruby

WebAssembly (WASM) — 5ms 미만 실행

사용 가능 플랜

Plus 전용

모든 플랜 (커스텀 앱은 Plus 필요, 공개 앱은 제한 없음)

에디터

어드민 내 Script Editor

로컬 IDE + Shopify CLI

버전 관리

없음 — 라이브 편집

Git 연동 지원 — 전체 버전 제어 가능

테스트

체크아웃에서 수동 테스트

shopify app dev를 통한 로컬 개발, 미리보기 링크

배포

어드민에서 "Save" 클릭

터미널에서 shopify app deploy 실행

대상 (Targets)

Line items, shipping, payments

Discounts, Cart Transform, Validation, Delivery Customization, Payment Customization, Order Routing, Fulfillment Constraints 등

아키텍처의 전환이 중요합니다. Scripts는 "텍스트 영역에서 Ruby를 수정하는 것"이었습니다. Functions는 "실제 앱을 작성하고, 버전을 관리하고, 로컬에서 테스트하고, 실제 CI 파이프라인을 통해 배포하는 것"입니다. 이는 진입 장벽이 더 높습니다. 또한 당분간 체크아웃 로직을 위해 수행할 마지막 마이그레이션이 될 것입니다. Functions는 Scripts처럼 임시방편이 아닌 Shopify의 장기적인 약속입니다.

Scripts에 맞는 올바른 Function 유형 매핑

현재 사용 중인 모든 Script는 정확히 하나의 Function API에 매핑됩니다. 모니터에 고정해 두어야 할 대조표는 다음과 같습니다.


기존 Script 유형

기능

신규 Function API

Function Target

Line Item Script

특정 상품 / 고객 / 카트 조건에 할인 적용

Cart & Checkout Discounts API

cart.lines.discounts.generate.run

Shipping Script (할인)

카트 규칙에 기반한 무료 / 할인 배송 적용

Cart & Checkout Discounts API

cart.delivery-options.discounts.generate.run

Shipping Script (숨기기 / 이름 변경 / 순서 변경)

특정 금액 초과 시 배송비 숨기기, "Standard"를 "$50 이상 무료"로 변경 등

Delivery Customization API

cart.delivery-options.transform.run

Payment Script

B2B 고객 대상 PayPal 숨기기, $500 초과 시 COD 숨기기, 결제 수단 정렬 등

Payment Customization API

cart.payment-methods.transform.run

카트 변경 Script (드묾)

번들 상품 구성, 품목 교체

Cart Transform API

cart.transform.run

체크아웃 차단 Script

유효하지 않은 SKU 조합 시 카트 거부

Cart & Checkout Validation API

cart.validations.generate.run

Scripts가 10개인 경우, 대개 3~5개의 Functions를 빌드하게 됩니다. 여러 Scripts가 더 깔끔한 분기 로직을 통해 하나의 Function으로 통합되는 경우가 많기 때문입니다.



Shopify Functions unifying discounts, delivery, and payment customizations

사전 요구 사항: 로컬 개발 환경 설정

Function을 스캐폴딩하기 전에 로컬에 세 가지가 설치되어 있어야 합니다. 터미널에서 다음 확인 작업을 수행하십시오.

1. Node.js 18+


node --version
# Must be >= 18.0.0

구버전인 경우 nvm을 통해 설치하거나 nodejs.org에서 다운로드하십시오.

2. Shopify CLI 3+


npm install -g @shopify/cli@latest
shopify version
# Should output 3.x or higher

3. Rust 툴체인 (Rust로 Functions를 작성할 경우에만 해당)


curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-wasip1
cargo --version

JavaScript Functions의 경우 Rust가 필요하지 않습니다. 팀 내에서 하나의 언어를 선택하여 유지하십시오. 혼용 시 유지관리 리소스가 늘어납니다.

4. 개발 스토어

Partner 대시보드에 로그인하여 새 개발 스토어를 만들거나 기존 스토어를 사용하십시오. 프로덕션에 적용하기 전에 이 개발 스토어에 Functions를 배포하여 검증하게 됩니다.

첫 번째 Function 스캐폴딩

CLI가 상용구 코드의 대부분을 처리합니다. 아무 디렉터리 내에서 다음과 같이 실행하십시오.


# Create a new Shopify app (skip if you already have one)
shopify app init my-checkout-functions
cd my-checkout-functions

# Generate a Function extension
shopify app generate extension

CLI가 안내 프롬프트를 표시합니다. 할인 Function의 경우 다음과 같이 선택하십시오.

  • Type: Function

  • Template: discount (또는 cart_checkout_validation, delivery_customization, payment_customization 등)

  • Language: Rust 또는 JavaScript

  • Name: volume-discount-fn과 같은 이름

그러면 다음 구성으로 extensions/volume-discount-fn/이 생성됩니다.


extensions/volume-discount-fn/
├── shopify.extension.toml      # Function config targets, build, version
├── src/
├── cart_lines_discounts_generate_run.graphql   # Input query
└── cart_lines_discounts_generate_run.rs        # Function logic
├── Cargo.toml                  # Rust dependencies (Rust only)
└── README.md

자주 편집하게 될 세 파일은 .toml(설정), .graphql(입력), 그리고 .rs / .js(로직)입니다. 이것이 전부입니다.

튜토리얼 1: Line Item Script 대체 (수량 할인)

기존 Script에서 카트에 특정 컬렉션 상품이 5개 이상 담겨 있을 때마다 주문 소계의 10%를 할인해 주었다고 가정해 보겠습니다. 다음은 이를 대체하는 Function 버전입니다.

단계 1.1: 구성 (shopify.extension.toml)


api_version = "2026-01"

[[extensions]]
name = "volume-discount-fn"
handle = "volume-discount-fn"
type = "function"

[[extensions.targeting]]
target = "cart.lines.discounts.generate.run"
input_query = "src/cart_lines_discounts_generate_run.graphql"
export = "cart_lines_discounts_generate_run"

[extensions.build]
command = "cargo build --target=wasm32-wasip1 --release"
path = "target/wasm32-wasip1/release/volume-discount-fn.wasm"
watch = ["src/**/*.rs"]

단계 1.2: 입력 쿼리 (src/cart_lines_discounts_generate_run.graphql)


query Input {
  cart {
    lines {
      id
      quantity
      cost {
        subtotalAmount {
          amount
        }
      }
      merchandise {
        ... on ProductVariant {
          product {
            inAnyCollection(ids: ["gid://shopify/Collection/123456789"])
          }
        }
      }
    }
  }
  discount {
    discountClasses
  }
}

팁: Functions는 쿼리한 데이터만 볼 수 있습니다. GraphQL을 최소한으로 유지하십시오. 생략하는 필드가 많을수록 실행 속도가 빨라지고 비용이 절감됩니다.

단계 1.3: 로직 (src/cart_lines_discounts_generate_run.rs)


use super::schema;
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function]
fn cart_lines_discounts_generate_run(
    input: schema::cart_lines_discounts_generate_run::Input,
) -> Result<schema::CartLinesDiscountsGenerateRunResult> {
    // Bail if discount class doesn't match
    let has_order_discount = input
        .discount()
        .discount_classes()
        .contains(&schema::DiscountClass::Order);

    if !has_order_discount {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // Sum quantities of items in the target collection
    let qualifying_qty: i64 = input
        .cart()
        .lines()
        .iter()
        .filter(|line| {
            if let schema::Merchandise::ProductVariant(v) = line.merchandise() {
                *v.product().in_any_collection()
            } else {
                false
            }
        })
        .map(|line| *line.quantity())
        .sum();

    if qualifying_qty < 5 {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // Apply 10% off the order subtotal
    Ok(schema::CartLinesDiscountsGenerateRunResult {
        operations: vec![schema::CartOperation::OrderDiscountsAdd(
            schema::OrderDiscountsAddOperation {
                selection_strategy: schema::OrderDiscountSelectionStrategy::First,
                candidates: vec![schema::OrderDiscountCandidate {
                    targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal(
                        schema::OrderSubtotalTarget {
                            excluded_cart_line_ids: vec![],
                        },
                    )],
                    message: Some("Volume discount: 10% off".to_string()),
                    value: schema::OrderDiscountCandidateValue::Percentage(
                        schema::Percentage { value: Decimal(10.0) }
                    ),
                    conditions: None,
                    associated_discount_code: None,
                }],
            },
        )],
    })
}

단계 1.4: 테스트, 배포, 활성화


# Local development with hot reload
shopify app dev

# When ready, deploy
shopify app deploy

# In the GraphiQL panel that opens (press `g` in the dev terminal),
# create the automatic discount that uses your Function:
mutation {
  discountAutomaticAppCreate(
    automaticAppDiscount: {
      title: "Volume Discount (5+ collection items)"
      functionHandle: "volume-discount-fn"
      discountClasses: [ORDER]
      startsAt: "2026-04-16T00:00:00Z"
    }
  ) {
    automaticAppDiscount { discountId }
    userErrors { field message }
  }
}

완료되었습니다. Function이 활성화되어 버전 관리되며 기존 Script를 완벽하게 대체합니다.

튜토리얼 2: Shipping Script 대체 (카트 임계값 초과 시 배송비 숨기기)

자주 사용되는 Script: "대량 구매 시 비용이 많이 드는 특송 배송을 방지하기 위해 장바구니 가격이 $500를 초과하면 Express Shipping을 숨깁니다." 다음은 Delivery Customization Function 버전입니다.

단계 2.1: 스캐폴딩


shopify app generate extension --template delivery_customization --name hide-express-fn

단계 2.2: 입력 쿼리 (src/run.graphql)


query Input {
  cart {
    cost {
      subtotalAmount {
        amount
      }
    }
    deliveryGroups {
      deliveryOptions {
        handle
        title
      }
    }
  }
}

단계 2.3: 로직 (src/run.js — JavaScript 방식)


// @ts-check
/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 */

const NO_CHANGES = { operations: [] };
const THRESHOLD = 500.0;
const HIDE_TITLES = ["Express", "Overnight"];

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount);
  if (subtotal < THRESHOLD) return NO_CHANGES;

  const operations = input.cart.deliveryGroups.flatMap((group) =>
    group.deliveryOptions
      .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t)))
      .map((opt) => ({
        hide: { deliveryOptionHandle: opt.handle },
      }))
  );

  return { operations };
}

단계 2.4: Admin을 통한 활성화 (GraphQL 불필요)

Delivery Customizations에는 고유한 Admin UI가 있습니다. shopify app deploy를 수행한 후 다음 단계를 진행하십시오.

  1. Settings → Shipping and delivery로 이동합니다.

  2. 하단의 Customizations 섹션으로 스크롤합니다.

  3. Add customization을 클릭한 다음 작성한 Function을 선택합니다.

  4. 저장합니다.

숨김 규칙이 프로덕션에 적용되었습니다. Mutation 처리를 거치지 않아도 됩니다.



Shopify delivery customization Function hiding shipping option at checkout

튜토리얼 3: Payment Script 대체 (B2B 고객 대상 COD 숨기기)

기존 Script: "'B2B' 태그가 지정된 고객의 착불 결제(Cash on Delivery)를 숨깁니다." 다음은 Payment Customization 버전입니다.

단계 3.1: 스캐폴딩


shopify app generate extension --template payment_customization --name hide-cod-b2b-fn

단계 3.2: 입력 쿼리


query Input {
  cart {
    buyerIdentity {
      customer {
        hasTags(tags: [{ tag: "B2B" }]) {
          tag
          hasTag
        }
      }
    }
  }
  paymentMethods {
    id
    name
  }
}

단계 3.3: 로직 (src/run.js)


const NO_CHANGES = { operations: [] };

export function run(input) {
  const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0];
  const isB2B = tagCheck?.hasTag === true;
  if (!isB2B) return NO_CHANGES;

  const codMethod = input.paymentMethods.find((pm) =>
    pm.name.toLowerCase().includes("cash on delivery")
  );
  if (!codMethod) return NO_CHANGES;

  return {
    operations: [{ hide: { paymentMethodId: codMethod.id } }],
  };
}

단계 3.4: 활성화

Payment Customizations 역시 아래 경로에 Admin UI가 존재합니다: Settings → Payments → Customizations. 배송 설정과 동일한 흐름으로 진행하십시오. Function을 선택하고 저장하면 완료됩니다.

함께 읽을거리 — 사후 구매(Post-Purchase)에 관하여

이곳은 Revize 블로그이므로 간략히 안내해 드립니다. Revize는 Functions가 건드릴 수 없는 부분들을 처리합니다. 주문이 완료된 후 고객이 상품을 추가하거나, 옵션을 변경하거나, 배송 주소를 수정하거나, 누락된 할인을 수동 적용하고 싶어 할 때가 있습니다. Functions는 체크아웃 시점에 작동하지만, Revize는 체크아웃 이후에 작동합니다. Functions가 장바구니에 허용될 항목을 결정한다면, Revize는 환불 후 다시 처음부터 주문을 빌드하는 불필요한 과정 없이 고객과 지원 팀이 주문 완료 후에도 상품을 간단하게 편집할 수 있도록 돕습니다. 이는 두 종류의 상점에 꼭 필요합니다. 수동 편집이 어려울 만큼 주문량이 막대한 스토어(Plus 운영사나 대규모 Advanced 스토어)와, 고객 경험이 브랜드의 핵심 가치여서 "변경 처리가 어렵습니다" 유형의 이메일이 재구매율 하락으로 연결되는 스토어입니다.

만약 마이그레이션 계획이 Scripts → Functions에만 맞추어져 있고 사후 주문 편집 영역을 고려하지 않았다면, 약 3주 뒤에 "대체 왜 이렇게 복잡하지?"라는 한계에 부딪힐 것입니다. 최근 완결된 주문 관리 가이드에 체크아웃 이후 관리를 위한 완벽한 해결책을 제시해 두었습니다.

다시 마이그레이션 안내로 돌아가겠습니다.

테스트 전략: 태그 기반 고객(Tagged Customer) 패턴

Functions는 Admin에서 켜고 깔 수 있는 별도의 "초안 모드(Draft Mode)"를 제공하지 않습니다. 이 때문에 가장 프로페셔널한 방식은 새로 개발한 Function을 특정 고객 태그 기준으로 제어하여, 기존 Script와 신규 Function을 병렬로 구동하면서 테스트 가입자들에게 정확하게 동일한 결과가 도출되는지 확인한 뒤 실 서비스에 완전히 반영하는 것입니다.

단계 1: 테스트 대상 사용자 태그 설정

Customers 메뉴에서 내부 개발 테스트 계정 2~3개에 FN-TESTER 태그를 추가합니다.

단계 2: 태그 존재 여부에 따라 Function 분기 처리


// At the top of your run function
let is_tester = input
    .cart()
    .buyer_identity()
    .and_then(|bi| bi.customer())
    .map(|c| c.has_any_tag())
    .unwrap_or(&false);

if !*is_tester {
    return Ok(default_result);  // Fall through to existing Script
}

// New Function logic only runs for tagged users

단계 3: 입력 쿼리에 hasAnyTag 추가


cart {
  buyerIdentity {
    customer {
      hasAnyTag(tags: ["FN-TESTER"])
    }
  }
}

단계 4: 체크아웃에서 확인

태그된 테스트 유저로 로그인하여 체크아웃을 진행하고 Function이 성공적으로 호출되는지 확인하십시오. 태그가 없는 일반 사용자로 로그인하여 기존 Script가 정상적으로 수행되는지도 점검합니다. 며칠 간 문제없이 양쪽이 균형을 이루는 것을 확인하면 태그 체크 조건을 해제하고 전체 사용자를 대상으로 Function을 가동합니다.

단계 5: 기존 Script 게시 중단

Apps → Script Editor → [사용 중인 Script] → Unpublish 순서로 이동하여 비활성화합니다. 정상 비게시 상태가 되면 새로 도입한 Function이 단일 기준이 됩니다.

지속 확장이 가능한 실제 배포 워크플로

모든 배포를 매번 개발자 개인의 로컬 PC에서 직접 처리하는 우는 범하지 않아야 합니다. 한두 개의 Scripts 마이그레이션을 마쳤다면 전용 CI 파이프라인을 구축할 때입니다.

가장 기본적이고 효율적인 검증 워크플로 구성


# .github/workflows/deploy-functions.yml
name: Deploy Shopify Functions
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: wasm32-wasip1 }
      - run: npm install -g @shopify/cli@latest
      - run: shopify app deploy --force
        env:
          SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}

Partner 대시보드의 Settings → Tokens 메뉴에서 Partner 토큰을 생성하십시오. 이제 main 브랜치로 머지될 때마다 설계한 Functions가 즉시 자동 배포됩니다. "그거 배포했나요?" 하며 슬랙 메시지를 쓸 필요가 없습니다.

버전 제어 및 롤백

shopify app deploy 명령을 내리면 스냅샷 버전이 기록됩니다. 필요 시 아래와 같이 손쉽게 이전 코드로 롤백이 가능합니다.


shopify app versions list
shopify app release --version <previous-version-id

기존 Scripts에서 롤백을 위해 "기존 코드를 일일이 기억해 복사 붙여넣기 하던 방식"과 비교해 보면 하늘과 땅 차이 수준으로 쾌적해졌습니다.



Shopify Functions deployment pipeline across local staging and production environments

대표적으로 실수하는 5가지 유형

지난 1년 동안 다수의 Plus 머천트들이 수행한 이번 마이그레이션 과업을 지원하면서 매번 공통적으로 목격해 온 가장 흔한 대표적 실수들을 공유합니다.

1. 1:1로만 가볍게 주입하려는 태도: 기존 Script가 세 개였다 하더라도 영리하게 리팩토링한 분기문을 활용해 탄탄한 1개의 Function 내부에서 모두 소화가 가능한 경우가 허다합니다. 이식을 위해 키보드를 잡기 전 전체 Scripts 설계를 종합 감사하는 접근이 필수적입니다.

2. Read scopes의 누락: 대개 구현하려는 대다수 Function은 기본값으로 read_customers, read_orders, 혹은 write_discounts 중 하나 이상의 스코프를 반드시 선언해 주어야 합니다. 잊지 말고 shopify.app.tomlscopes 영역에 입력한 뒤 승인해 주십시오. 그렇지 않으면 graphql 데이터가 null로 반환될 것입니다.

3. 비교 검증 없이 곧바로 일반 고객 대상 가동: 작성한 비즈니스 로직이 한눈에 보기에 완벽해 노출 문제가 없을지라도, 끈적하게 매달리는 경계 조건 예외 케이스(빈 장바구니 조건, 기프트카드 복합 결제 이슈, 적립금 등)는 기어코 버그를 발생시킵니다. 내부 테스트 태그 주차를 통한 며칠간의 소소한 지체는 P1 장애가 터져 심야에 복구 업무를 겪는 공포에 대처하는 현명한 보험비용입니다.

4. Customizations Report 미작동: 현재 라이브 환경에서 실동 중인 요소의 인벤토리를 가장 쉽고 투명하게 식별하여 일목요연하게 전달해 주는 레포트입니다. 기억에 의존하는 대차 대신 기계가 반환한 레포트 숫자를 바탕으로 엄밀하게 준비하십시오.

5. 콜렉션 ID 혹은 특정 태그값의 하드코딩: 판매자의 가변 관리가 개입해야 하는 상황이라면 메타필드를 활용해 설정값 데이터를 받아 동적으로 처리하십시오. CLI 툴링을 이용하면 기본 뼈대를 수월하게 구성해 낼 수 있습니다. 관련 문서의 Function 설정을 정독하십시오.

앞으로 남은 75일에 걸친 최종 마이그레이션 로드맵

June 30일까지 가장 평온하고 안전하게 도달하는 단계별 실전 일정표를 설계해 드립니다.


기간

수행할 주요 과업

1주 차 (이번 주)

상용 Customizations Report 확인. 구동 중인 누적 연동 상세 식별. Function 처리용 리스트업 확정.

2~3주 차

로컬에 필요한 기술 세팅 진행 및 구성. 기초적인 수준의 간소한 Function 생성(결제 감추기 로직 대상 추천).

4~6주 차

할인 관련 가이드 마이그레이션. Discounts API 내부에서 유연하고 촘촘한 정리를 요구하므로 공수가 가장 크게 듦. 충분한 테스트 기간 확보 필요.

7~8주 차

배송/요금 관련 Script 포팅 연동. 완성된 Delivery Customizations를 어드민 메뉴에서 연계하여 활성화.

9~10주 차

깃헙 액션 등을 연동해 CI/CD 파이프라인 정비. 더 이상 개발자 각자 노트북 단위 수동 배포가 없도록 선회.

11주 차 (6월 중순)

동일 성능 구현 완결성 테스트 수행. 구 Scripts 비게시 비활성 처리. 2주간 온전히 신규 Functions만으로 서비스 운영 모니터링.

June 30

최종 Sunset 시행일 조우. 미리 선조치해 둔 덕에 아무 장애 현상도 경험하지 않는 쾌가 보장됨.

지금 첫걸음을 뗀다면 일정에 여유가 있지만, 6월에 시작하는 경우 버퍼가 남아있지 않을 것입니다.



Week-by-week migration roadmap from Shopify Scripts to Functions

자주 묻는 질문 (FAQ)

Functions를 사용하려면 무조건 Shopify Plus 플랜이어야 하나요?

독립형 커스텀 함수(Custom Functions) 제작의 경우 오직 Shopify Plus 플랜에서만 작성이 허용되지만, 공개 앱 생태계에 게시된 Functions 기반 솔루션 플러그인은 전 플랜 누구나 즉시 설치하여 적용하는 길이 확보되어 있습니다. 작성 주도가 불가능한 하위 요금 상태라면 대체재가 될 만한 검증된 공개 앱을 Shopify App Store에서 발굴해 장착하는 방향 혹은, 온전히 독립 자산을 소지하기 위하여 Plus로 업그레이드할 것을 권해 드립니다.

TypeScript 언어도 공식 대응하나요?

예, 무결하게 커버하며 생성 툴링 역시 온전하게 지원합니다. shopify app generate extension 단계 안내시 "JavaScript" 선택 시 내부적으로 개발 연계를 위한 타입 선언 바인딩 리소스들이 import("../generated/api")를 통하여 수반될 것입니다. tsconfig.json 파일을 기입하고 확장자를 .ts로 승격시켜 주면 별다른 이격 없이 사용 가능합니다. 어차피 산출 결과인 웹어셈블리 단에서는 원본 언어에 따른 차이점이 발생할 여지가 없습니다.

성능 면에서 Scripts 대비 얼마나 민첩하게 도나요?

대개 5ms 이하 극단으로 정밀하게 마침표를 찍으며 기존 Ruby Scripts 작동 시간보다 압도적으로 빨라집니다. 단일 WASM 타깃 런타임 특성에 따라 Shopify 코어 측에서 요청 당 5ms 처리 예산 리밋을 원칙적으로 강제 준수하게 합니다. 만일 이 리밋의 상단을 뚫으면 해당 호출 작업에 즉시 블락 처리가 내려집니다. 통상 적절히 튜닝된 헬시 레벨 로직은 평균 1~2ms 안팎으로 통과되어 기존 대비 쾌적성이 증명됩니다.

함수 본체 구동 중 서드파티 외부 API도 직접 붙여 땡겨올 수 있나요?

불가능합니다. Functions 내부에서 네트워크 소켓 등의 아웃바운드 발신 처리는 절대 불가능하게 격리 설계되어 있습니다. 전적으로 오로지 유입 장바구니 데이터를 받아서 고속 논리 가공 처리를 얹은 뒤 결괏값을 내보내는 구조적 가동에 전념하게 해야 합니다. 실시간 연계 제휴 CRM 쿼리 같은 처리가 필요하다면 메타필드에 데이터를 선행 캐싱해 적재해 두거나, 대안이 될 플랫폼 구조(App Proxy 혹은 백엔드를 거친 Cart Transform) 설계를 별도 마련하시는 방향으로 사전에 우회하셔야 합니다.

Cart Transform API와 일반 Discounts 구성과의 근본적 체계상 구분이 모호합니다.

쉽게 말하자면 가격을 내릴 때는 Discounts를 기용하고 장바구니 구조의 수량을 조작 또는 엮을 때는 Cart Transform을 선택합니다. %율 적용, 세일 처리는 전자 범주이며 번들 세트 묶기 등 복합 구성 조립이 수반될 때 후자를 호출하는 것으로 두시면 깔끔합니다. 예전 구 Scripts 코드는 한 단락의 파일 안에 두 영역을 뒤섞는 경향이 많았습니다. 마이그레이션 시 깔끔히 두 개 함수 영역으로 개별 분할해 설계해 내는 것을 타깃으로 잡으십시오.

로컬 상황에서 개발 스토어 연결 통과 세팅 없이 동작 상황을 미리 볼 방법은 전혀 존재하지 않나요?

단위 처리는 cargo test 혹은 npm test 템플릿의 실행을 통해 어느 정도 검토가 충분히 설계되나, 최종 종단 체크아웃 구동 연동은 어쩔 수 없이 개발 스토어의 존재가 확보되어 있어야 진행을 담보합니다. 셸 내부에서 쓸 수 있는 shopify app function run을 실행하여 유입 Mock JSON 파일을 대상으로 결괏값을 보는 작업만 로컬 개발 도중 중간 테스트용으로 유용하게 차용해 보시기 바랍니다.

동일 형식의 여러 Function 종류를 동시에 적재해 연속 작동되도록 체이닝 구성이 허용되나요?

예, 가능합니다. 지정한 결정 순서 우선 체계에 정직하게 입각하여 차례로 병렬 수반되어 내리게 돌아갑니다. 할인은 스태킹 정책 설정값의 연계를 따르고 배송 및 결제 사용자 지정은 결과 처리가 줄을 이어 다음 작동 인풋으로 순차 적용되는 방식으로 수행하므로 관념상 한 단일 유형 내에는 직관적 가시성을 고려해 한 개 함수의 결합으로 응집력 있게 풀어내 전개하시는 것을 장려합니다.

새 Function 이식을 성공적으로 끝내서 올려두면 이전의 구 Scripts는 즉시 지워지나요?

사용자가 직접 임의로 Script Editor 상에서 비게시 설정을 가할 때까지 우선 두 영역의 자산이 공동으로 물려 실서버에 돌게 보장됩니다. 설계 이격을 대비해 병행 실행해서 추이를 대조 판단할 기회를 기획적으로 지원하게 만드는 조치입니다. 온전하게 동작 검증을 거친 직후 구형을 마감시키면 됩니다. 어차피 2026년 June 30일이 지난다면 어드민에 켜진 채 남아있는 스위치가 있더라도 강제 효력 상실을 거치기에 무조건 꺼져 내리게 되어 있습니다.

이번 마이그레이션 변경 조치가 혹시 운영 중인 자체 사이트 SEO 영역에 악영향을 주나요?

영향을 끼치지 않습니다. Functions 연산 처리는 전적으로 체크아웃 단계 서버사이드 내부에서 기밀하게 수반 및 소거되므로 테마 HTML 렌더 파일에 간섭하거나 구글 크롤 스크립트 검색에 아무 영항을 끼치지 않습니다. 테마 스킨과 상품 노출 및 검색 노출의 전면은 기존대로 평화롭게 유지 보장받으니 걱정 덜어내십시오.

기존 Input.line_items 상의 커스텀 추가 메타 정보 값을 받던 옛 룬은 어떻게 매핑하나요?

GraphQL 인풋 쿼리의 attribute 항목을 선언해 줌으로써 기존에 쓰던 기능의 구성을 백퍼센트 복원하여 소화할 수 있습니다. lines 셀렉터 내부 스코프에 attribute(key: "attribute_name") { value }를 태워 뽑아 활용하십시오. 예전 루비 문법 체득으로 파고들어 보던 변숫값 호출이 GraphQL 타깃으로 바뀌었을 뿐, 완전히 같은 내용을 보장합니다.

주문 관련 태깅 작업도 구동 본체 내에서 태그 필드 가입 처리를 진행해 반환해 주나요?

불가능합니다. Functions는 장바구니 인스턴스의 트랜스폼 결과 데이터만을 담아서 반환하므로 내부에서 데이터베이스 태그 필드를 수정하는 등의 영속성 처리를 일으킬 수 없습니다. 대신 우수 사후 조치를 설계하고 싶다면 완료 이벤트와 연계되어 돌아가기에 간편한 노코드 워크플로 툴인 Shopify Flow를 도입해 연동 장악하는 길을 선택해 유연하게 엮어 쓰셔야 맞습니다. (예시: 특정 할인 코드 매칭 감지 후 Flow를 타고 해당 주문 문서에 "VOLUME-DISCOUNT-APPLIED" 라벨 태그 입력 처리)

시간이 없을 때 우선 임시 해결이 어렵다면, App Store에 게시되어 기성 판매 중인 Functions 솔루션을 그냥 매는 방향은 부적합한가요?

오히려 탁월한 훌륭한 해답입니다. 현시점 마켓플레이스에는 Functions를 기반 삼아 작동하는 강력한 플러그인 유틸들이 널리 상용 출시되어 성업 중입니다. "할인 관련 기능", "결제 및 배송 수단 가드 관련 필터 설정" 키워드로 마켓 내 상위에 기재된 가벼운 플러그인 도구를 선별 연계해 이식하는 방식으로 수일 혹은 수개월에 걸친 개발 인프라 인건비와 스케줄 소요를 완전히 지상 최고 효율로 대체 가성비 좋게 정리하여 안전망을 신속 확보해 넘기는 것도 고도 전략의 판단 중 하나입니다. 진짜 비즈니스상 필수불결한 독자 도메인 단독 로직에만 골몰해 리소스를 아껴 활용하십시오.

최악의 수로 만약 June 30 적용 마지노선 타임라인을 놓칠 때는 어떻게 처리되나요?

즉각적인 동작 중지로 직결됩니다. 긴급 연장 신청 꼼수, 유예 조치 및 롤백 기회 따위는 절대 허용되지 않습니다. 기존 Scripts가 떠받치던 개별 감면 조건이나 감춰진 요금 방어 및 수단 제어로 보완되던 구조들이 한순간에 원복 풀려 일제히 노출 가동되는 불상사를 직면하게 되므로 최소한 정식 작동 데드라인 3~4주 전에 충분한 안정화 세팅을 끝내어 안착시키는 일정을 전개하십시오.

시간이 흐른 뒤 마이그레이션이 다 정리되면 기존 Script Editor 자체도 완전히 영구 이별 삭제해야 하나요?

지우는 것이 정상 수순으로 맞습니다. 6월 말일 기점 Shopify 공식단에서도 무난히 자동 잔재 정리 삭제를 진행할 계획이며 이미 Functions 시대로 교체를 조기 매김 한 머천트의 상황이라면 망설임 없이 현시점에 당장 제거하셔도 전용 함수 작동에 일체 결함이 생기지 않습니다.

이번 주 내에 집중 진행할 긴급 액션 가이드

이 가이드를 읽고 브라우저 탭을 조용히 다시 닫아버리는 것으로 끝나서는 안 됩니다. 지금 당장 7일 안으로 딱 이 네 가지 일들을 직접 실행에 옮깁십시오.

1. Customizations Report 즉시 다운로드: Settings → Checkout → Customizations Report 링크를 클릭하여 파일 사본을 입수하십시오. 이것이 당신의 우선순위 백로그 작업 명세서가 됩니다.

2. 개발 환경 설정 완료: 로컬 PC에 Node 18+ 탑재, CLI 유틸 탑재, 추가로 Rust가 필요한 상황이라면 해당 자산 컴파일 도구까지 설치해 shopify version 출력이 제대로 나오는지 찍어보는 시간을 30분만 투자해 매조지하십시오.

3. 초소형 단위의 테스트용 Function을 개발 스토어에 업로드해 보기: 가장 구조가 만만한 결제 숨김 등의 초미니 예제 파일 한 단락을 실제 샌드박스로 밀어 넣어 구동 단계를 체험해 보십시오. 단 한 판만으로 작업 체인의 사이클 프로세스를 온몸으로 완벽히 인지하여 체득할 수 있습니다.

4. 수 주 분량의 정식 전용 캘린더 작업 스프린트 기간 강제 예약: 이런 중대 이식 연동 과정은 일상 스케줄 틈 사이에 어설프게 끼워 처리하려다 기한을 맞추지 못하고 미끄러지기 마련입니다. 매주 이틀 정도 전담 전용 시간을 온전하게 확보해 스케줄을 잠가두고 사명감 있게 접근하십시오.

지근거리에서 확인한 전형적인 마이그레이션 성공 패턴은 대개 이러했습니다: 고민하며 눈치 보던 전야 지체 시기 2주, 실제 본체 코드 가동을 위한 빌드에 몰입해 달성한 3주, 자잘한 변수 검증 및 정리 기간 1주. 통합 총합 최소 6주 구간이 소비됩니다. 여러분에게 현재 남은 시간은 대략 10주 전후입니다. 넉넉하고 쫀쫀한 완衝 시야는 오직 코딩 및 안전 품질 보장 확인 영역에만 온전히 소모하셔야 안전합니다.

그리고 체크아웃 단에 들어선 필수 로직 구성이 원활하게 수습되어 나아간다면 대개 다음 단계 타깃은 고객의 사후 주문 수정 고충 영역의 교정으로 넘어가게 흐름을 가집니다. 맞춤 주소 처리나 옵션 수정 및 추가 할인 입수 요구가 지속 발생하기 때문입니다. 이러한 고민도 결국 장기 개선 목록에 대기 중이었다면(Plus 운영사나 Advanced 상점 모두에게 필수입니다) Shopify App Store에 상위 등록된 Revize 솔루션을 마음에 심고 여러분이 이번 마이그레이션 과정에서 빌드 완료한 Functions 구조들의 우수한 파트너로 기용해 함께 운행하시는 것을 보증해 드립니다.

추천 학습 자료 모음


관련 주제 아티클 추천


Shopify 스토어를 개선하십시오. 고객 경험이 최우선입니다.

© Copyright 2024, All Rights Reserved

Shopify 스토어를 개선하십시오. 고객 경험이 최우선입니다.

© Copyright 2024, All Rights Reserved

Shopify 스토어를 개선하십시오. 고객 경험이 최우선입니다.

© Copyright 2024, All Rights Reserved

Shopify 스토어를 개선하십시오. 고객 경험이 최우선입니다.

© Copyright 2024, All Rights Reserved