저는 Stripe를 Next.js 프로젝트에 연결한 횟수가 셀 수 없을 정도로 많습니다. 매번 풍경이 조금씩 변합니다 — 새로운 API 버전, 새로운 결제 방법, 웹훅이 조용히 실패할 때 오전 2시에 물어뜯는 새로운 엣지 케이스들. 이 가이드는 내가 처음 헤드리스 커머스 스택을 구축했을 때 존재했기를 바라던 모든 것입니다. Checkout Sessions, Payment Intents, 웹훅, 구독, Apple Pay, Google Pay, Link, 그리고 원클릭 체크아웃을 다룹니다. 손흔들기 없음. 실제 코드, 실제 아키텍처 결정, 실제 문제점들입니다.

2026년 중반 기준으로, Stripe의 API는 버전 2025-12-18.acacia이고, Next.js 15.x는 App Router가 기본값으로 안정화되어 있으며, @stripe/stripe-js@stripe/react-stripe-js 패키지들이 상당히 성숙했습니다. 이전 버전을 기반으로 구축 중이라면, 대부분이 여전히 적용되지만 일부 서버 액션 패턴은 다를 것입니다.

목차

Stripe + Next.js Headless Commerce: The 2026 Integration Guide

헤드리스 커머스를 위해 Stripe + Next.js를 사용하는 이유

Stripe는 연간 1조 달러 이상의 결제 거래액을 처리합니다. Next.js는 이커머스 상점 수의 증가하는 부분을 지원하고 있습니다 — Vercel은 2025년 새로운 Next.js 프로젝트 40% 이상이 어떤 형태의 커머스 기능을 가지고 있다고 보고합니다. 이 조합은 몇 가지 구체적인 이유로 타당합니다:

  • Server Components와 Server Actions은 별도의 API 레이어를 구축하지 않고도 서버 측에서 Stripe SDK를 호출할 수 있게 해줍니다.
  • Edge 및 serverless 배포는 Vercel, Netlify 또는 AWS에서 결제 엔드포인트가 자동으로 확장됨을 의미합니다.
  • React Server Components는 Stripe 비밀 키를 추가적인 조작 없이 키가 속한 서버에 유지합니다.
  • App Router는 체크아웃 흐름에 잘 매핑되는 레이아웃, 로딩 상태, 및 오류 경계를 제공합니다.

헤드리스 커머스 아키텍처를 평가 중이라면, Social Animal에서 수십 개의 이런 것들을 구축했습니다 — 이러한 부분들이 어떻게 함께 맞춰지는지에 대한 더 많은 맥락을 위해 우리의 Next.js 개발 능력헤드리스 CMS 개발을 확인하세요.

아키텍처 개요

코드를 작성하기 전에, 아키텍처를 올바르게 이해해봅시다. 여기 전형적인 헤드리스 커머스 설정에서 부분들이 어떻게 연결되는지입니다:

┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐
│   Next.js App   │────▶│  Stripe API      │────▶│  Webhooks   │
│  (App Router)   │◀────│  (Server-side)   │     │  Endpoint   │
└─────────────────┘     └──────────────────┘     └──────┬──────┘
        │                                                │
        │                                                ▼
        ▼                                        ┌─────────────┐
┌─────────────────┐                              │  Database /  │
│  Headless CMS   │                              │  Order Mgmt  │
│  (Products)     │                              └─────────────┘
└─────────────────┘

중요한 결정은 Checkout Sessions(Stripe 호스팅 또는 임베드) 또는 Payment Intents(완전 커스텀 UI) 중 어느 것을 사용할지입니다. 각각을 사용할 시기는 다음과 같습니다:

기능 Checkout Sessions Payment Intents
개발 속도 빠름 — 며칠 느림 — 주
UI 커스터마이제이션 제한적 (Stripe 테마) 전체 제어
PCI 규정준수 범위 SAQ A (가장 단순) SAQ A-EP
결제 방법 지원 자동 (40+ 방법) 방법별 수동
구독 지원 내장 추가 코드 필요
Apple Pay / Google Pay 자동 Payment Request API를 통한 수동
전환율 최적화 Stripe 최적화 당신의 책임
가격 영향 동일한 Stripe 수수료 동일한 Stripe 수수료

제 솔직한 추천: 특별한 이유가 없다면 Checkout Sessions로 시작하세요. 나중에 언제든지 Payment Intents로 마이그레이션할 수 있으며, Stripe의 임베드 체크아웃은 2025-2026년에 현저히 개선되었습니다.

Next.js 15 프로젝트에서 Stripe 설정하기

기초를 설정해봅시다. App Router가 있는 Next.js 15 프로젝트가 있다고 가정하겠습니다.

npm install stripe @stripe/stripe-js @stripe/react-stripe-js

환경 변수를 생성하세요:

# .env.local
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

서버 측 Stripe 인스턴스를 설정하세요. 저는 항상 이것을 lib/stripe.ts 파일에 넣습니다:

// lib/stripe.ts
import Stripe from 'stripe';

export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2025-12-18.acacia',
  typescript: true,
});

그리고 클라이언트 측 로더:

// lib/stripe-client.ts
import { loadStripe } from '@stripe/stripe-js';

export const stripePromise = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
);

사람들을 헷갈리게 하는 한 가지: lib/stripe.ts를 클라이언트 컴포넌트에 절대 import하지 마세요. stripe npm 패키지는 비밀 키를 포함하고 서버 측에서만 실행되어야 합니다. Next.js 15는 실수로 'use client' 파일에서 import하면 빌드 오류를 발생시킵니다. 이것은 실제로 좋은 보호막입니다.

Stripe + Next.js Headless Commerce: The 2026 Integration Guide - architecture

Checkout Sessions: 빠른 경로

Checkout Sessions는 결제를 받아들이는 가장 빠른 방법입니다. Stripe가 결제 양식을 호스팅하고(또는 임베드할 수 있음), PCI 규정준수를 처리하고, Apple Pay, Google Pay, Link를 포함한 수십 가지 결제 방법을 자동으로 지원합니다.

Server Actions를 사용한 Checkout Session 생성하기

// app/actions/checkout.ts
'use server';

import { stripe } from '@/lib/stripe';
import { redirect } from 'next/navigation';

export async function createCheckoutSession(formData: FormData) {
  const priceId = formData.get('priceId') as string;
  const quantity = Number(formData.get('quantity')) || 1;

  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    line_items: [
      {
        price: priceId,
        quantity,
      },
    ],
    success_url: `${process.env.NEXT_PUBLIC_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/checkout/canceled`,
    automatic_tax: { enabled: true },
    // 모든 관련 결제 방법 활성화
    payment_method_types: undefined, // Stripe에 자동 감지하게 허용
  });

  redirect(session.url!);
}

임베드 체크아웃 (2026 추천 접근법)

Stripe의 임베드 체크아웃은 사용자를 도메인에 유지합니다. 이것은 더 나은 전환율을 가집니다 — Stripe의 2025년 자체 데이터는 리다이렉트 기반 체크아웃에 비해 반복 고객의 경우 10-15% 개선을 보여줍니다.

// app/checkout/embedded/page.tsx
'use client';

import { useCallback } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
  EmbeddedCheckoutProvider,
  EmbeddedCheckout,
} from '@stripe/react-stripe-js';

const stripePromise = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
);

export default function CheckoutPage() {
  const fetchClientSecret = useCallback(async () => {
    const res = await fetch('/api/checkout/embedded', {
      method: 'POST',
      body: JSON.stringify({ priceId: 'price_xxx', quantity: 1 }),
    });
    const { clientSecret } = await res.json();
    return clientSecret;
  }, []);

  return (
    <div className="max-w-lg mx-auto py-12">
      <EmbeddedCheckoutProvider
        stripe={stripePromise}
        options={{ fetchClientSecret }}
      >
        <EmbeddedCheckout />
      </EmbeddedCheckoutProvider>
    </div>
  );
}

그리고 API 라우트:

// app/api/checkout/embedded/route.ts
import { stripe } from '@/lib/stripe';
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const { priceId, quantity } = await req.json();

  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    line_items: [{ price: priceId, quantity }],
    ui_mode: 'embedded',
    return_url: `${process.env.NEXT_PUBLIC_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
  });

  return NextResponse.json({ clientSecret: session.client_secret });
}

Payment Intents: 전체 제어 모드

완전히 커스텀한 체크아웃 UI가 필요할 때 — 아마도 원페이지 체크아웃을 구축 중이거나, 당신의 디자인 팀이 특정 요구사항을 가지고 있을 때 — Payment Intents는 전체 제어를 제공합니다.

트레이드오프는 실제입니다: 더 많은 코드를 작성하고, 더 많은 엣지 케이스를 처리하고, 약간 더 높은 PCI 규정준수 부담을 떠맡게 됩니다. 하지만 일부 제품의 경우, 그럴 가치가 있습니다.

서버 측: Payment Intents 생성

// app/api/payment-intent/route.ts
import { stripe } from '@/lib/stripe';
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const { amount, currency = 'usd', metadata } = await req.json();

  const paymentIntent = await stripe.paymentIntents.create({
    amount, // 센트 단위
    currency,
    metadata,
    automatic_payment_methods: {
      enabled: true, // 이것은 Apple Pay, Google Pay, Link 등을 활성화합니다
    },
  });

  return NextResponse.json({
    clientSecret: paymentIntent.client_secret,
  });
}

클라이언트 측: 결제 양식

// components/PaymentForm.tsx
'use client';

import { useState } from 'react';
import {
  PaymentElement,
  useStripe,
  useElements,
} from '@stripe/react-stripe-js';

export function PaymentForm() {
  const stripe = useStripe();
  const elements = useElements();
  const [error, setError] = useState<string | null>(null);
  const [processing, setProcessing] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!stripe || !elements) return;

    setProcessing(true);
    setError(null);

    const { error: submitError } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: `${window.location.origin}/checkout/success`,
      },
    });

    if (submitError) {
      setError(submitError.message ?? 'Payment failed');
      setProcessing(false);
    }
    // 오류가 없으면, Stripe가 자동으로 리다이렉트됩니다
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement
        options={{
          layout: 'accordion',
          wallets: {
            applePay: 'auto',
            googlePay: 'auto',
          },
        }}
      />
      {error && <p className="text-red-500 mt-2">{error}</p>}
      <button
        type="submit"
        disabled={!stripe || processing}
        className="mt-4 w-full bg-black text-white py-3 rounded-lg disabled:opacity-50"
      >
        {processing ? 'Processing...' : 'Pay now'}
      </button>
    </form>
  );
}

서버 측의 automatic_payment_methods: { enabled: true }에 주목하세요. 이것이 2025-2026년의 결제 방법 지원 처리 방식입니다. Stripe는 고객의 기기, 위치, 통화에 따라 올바른 결제 방법을 자동으로 표시합니다. 더 이상 수동으로 payment_method_types를 나열할 필요가 없습니다.

실제로 작동하는 웹훅 처리

웹훅은 대부분의 Stripe 통합이 깨지는 곳입니다. 누군가가 웹훅 서명 확인을 잊었거나, 200을 다시 보내기 전에 핸들러가 오류를 발생시켰기 때문에 생산 시스템이 주문을 잃는 것을 봤습니다.

여기 제 검증된 웹훅 핸들러입니다:

// app/api/webhooks/stripe/route.ts
import { stripe } from '@/lib/stripe';
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';
import type Stripe from 'stripe';

export async function POST(req: Request) {
  const body = await req.text();
  const headersList = await headers();
  const signature = headersList.get('stripe-signature');

  if (!signature) {
    return NextResponse.json({ error: 'Missing signature' }, { status: 400 });
  }

  let event: Stripe.Event;

  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    console.error('Webhook signature verification failed:', err);
    return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
  }

  try {
    switch (event.type) {
      case 'checkout.session.completed': {
        const session = event.data.object as Stripe.Checkout.Session;
        await handleCheckoutComplete(session);
        break;
      }
      case 'payment_intent.succeeded': {
        const paymentIntent = event.data.object as Stripe.PaymentIntent;
        await handlePaymentSuccess(paymentIntent);
        break;
      }
      case 'payment_intent.payment_failed': {
        const paymentIntent = event.data.object as Stripe.PaymentIntent;
        await handlePaymentFailure(paymentIntent);
        break;
      }
      case 'customer.subscription.created':
      case 'customer.subscription.updated':
      case 'customer.subscription.deleted': {
        const subscription = event.data.object as Stripe.Subscription;
        await handleSubscriptionChange(subscription);
        break;
      }
      case 'invoice.payment_failed': {
        const invoice = event.data.object as Stripe.Invoice;
        await handleInvoiceFailure(invoice);
        break;
      }
      default:
        console.log(`Unhandled event type: ${event.type}`);
    }
  } catch (err) {
    console.error(`Error processing ${event.type}:`, err);
    // 여전히 200을 반환하여 Stripe가 재시도하는 것을 방지합니다
    // 수동 조사를 위해 오류를 기록합니다
  }

  return NextResponse.json({ received: true });
}

어려운 방식으로 배운 웹훅 함정

  1. 항상 200을 반환하세요. 당신의 처리가 실패하더라도. 그렇지 않으면 Stripe가 재시도하고, 같은 이벤트를 여러 번 처리할 수 있습니다. 오류를 기록하고 비동기적으로 처리하세요.

  2. 핸들러를 멱등성(idempotent)으로 만드세요. Stripe는 같은 이벤트를 여러 번 보낼 수 있습니다. 이벤트 ID 또는 객체의 메타데이터를 사용하여 이미 처리했는지 확인하세요.

  3. 서명 확인을 위해 req.text() 대신 req.json()을 사용하지 마세요. 서명은 원본 바디 문자열에 대해 계산됩니다. 먼저 파싱하면, 확인은 항상 실패합니다.

  4. 로컬 테스트를 위해 Stripe CLI를 설정하세요. 협상의 여지가 없습니다.

stripe listen --forward-to localhost:3000/api/webhooks/stripe
  1. Vercel에서, 웹훅 라우트는 특정 구성이 필요합니다. 당신의 라우트가 요청 바디를 수정하는 미들웨어 뒤에 있지 않은지 확인하세요. Next.js 15에서, App Router의 API 라우트는 기본적으로 이것을 올바르게 처리하지만, 커스텀 미들웨어가 있다면 다시 확인하세요.

구독 및 반복 청구

구독은 복잡성의 층을 추가합니다. 당신은 단지 일회성 결제를 처리하는 것이 아닙니다 — 당신은 생명주기를 관리합니다: 평가판, 업그레이드, 다운그레이드, 취소, 실패한 결제, 던닝.

Checkout을 통한 구독 생성

가장 쉬운 접근법:

// app/actions/subscribe.ts
'use server';

import { stripe } from '@/lib/stripe';
import { redirect } from 'next/navigation';

export async function createSubscriptionCheckout(
  customerId: string,
  priceId: string
) {
  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    customer: customerId,
    line_items: [{ price: priceId, quantity: 1 }],
    success_url: `${process.env.NEXT_PUBLIC_URL}/account/billing?success=true`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
    subscription_data: {
      trial_period_days: 14,
      metadata: {
        plan: 'pro', // 당신의 자체 메타데이터
      },
    },
    allow_promotion_codes: true,
    tax_id_collection: { enabled: true },
  });

  redirect(session.url!);
}

구독 관리

고객 포털의 경우 (업그레이드, 다운그레이드, 취소, 결제 방법 업데이트), Stripe의 Customer Portal은 2026년에 정말 훌륭합니다:

// app/actions/billing.ts
'use server';

import { stripe } from '@/lib/stripe';
import { redirect } from 'next/navigation';

export async function createBillingPortalSession(customerId: string) {
  const session = await stripe.billingPortal.sessions.create({
    customer: customerId,
    return_url: `${process.env.NEXT_PUBLIC_URL}/account/billing`,
  });

  redirect(session.url);
}

핵심 구독 웹훅 이벤트

이벤트 언제 발화되는가 할 일
customer.subscription.created 새 구독 액세스 제공
customer.subscription.updated 플랜 변경, 갱신 액세스 수준 업데이트
customer.subscription.deleted 취소 (기간 끝) 액세스 취소
invoice.payment_succeeded 성공한 갱신 청구 기록 업데이트
invoice.payment_failed 실패한 갱신 던닝 이메일 발송, 계정 플래그
customer.subscription.trial_will_end 평가판 종료 3일 전 알림 이메일 발송

API 호출에서만 구독 상태에 의존하지 마세요. 웹훅은 구독 상태 변경에 대한 진실의 출처입니다. 나는 웹훅 대신 Stripe API를 폴링하는 팀들을 봤고, 그것은 더 느리고 더 취약합니다.

Stripe의 2025-2026년 Payment Element의 아름다움은 지갑 결제가 대부분 그냥 작동한다는 것입니다. 하지만 사람들이 놓치는 일부 설정 요구사항이 있습니다.

Apple Pay 설정

  1. 도메인 확인이 필요합니다. 당신의 도메인 루트에서 .well-known/apple-developer-merchantid-domain-association 파일을 호스팅해야 합니다. Stripe는 Stripe Dashboard의 Settings → Payment Methods → Apple Pay에서 이 파일을 제공합니다.

  2. Next.js에서, 파일을 public/.well-known/apple-developer-merchantid-domain-association에 배치하세요.

  3. Stripe Dashboard에서 당신의 도메인을 등록하세요.

  4. Apple Pay는 Safari/iOS에서만 나타납니다. Chrome에서 테스트 중 나타나지 않는다고 놀라지 마세요.

Google Pay 설정

Google Pay는 더 적은 설정을 필요로 합니다 — Payment Element와 함께 자동으로 작동합니다. Stripe 계정이 올바르게 구성되어 있는 한 말입니다. Chrome과 Android 기기에서 나타납니다.

Link는 Shop Pay에 대한 Stripe의 답변입니다. 고객은 결제 정보를 한 번 저장하고 Link를 사용하는 모든 Stripe 판매자에서 원클릭으로 체크아웃할 수 있습니다.

2026년 현재, Link는 새로운 Stripe 계정에서 기본적으로 활성화됩니다. 전환율 상승은 실제입니다 — Stripe는 2025년의 자체 데이터에서 Link를 이용할 때 평균 7% 더 높은 체크아웃 완료를 보고합니다. Link 사용자 반복의 경우, 그것은 훨씬 더 높습니다.

Payment Element를 사용하면, Link는 자동으로 나타납니다. Checkout Sessions를 사용하면, 그것도 자동입니다. 당신은 특별히 할 일이 없습니다.

// Link는 Payment Element에서 자동이지만, 당신은 커스터마이즈할 수 있습니다:
<PaymentElement
  options={{
    wallets: {
      applePay: 'auto',
      googlePay: 'auto',
    },
    // Link는 이메일 필드에 자동으로 나타납니다
  }}
/>

Link는 고유한 섹션에 값합니다. 왜냐하면 심각한 전환 드라이버가 되었기 때문입니다. 여기 작동 방식입니다:

  1. 고객이 당신의 체크아웃 양식에서 이메일을 입력합니다.
  2. Link 계정이 있다면, 그들은 SMS를 통해 확인 코드를 받습니다.
  3. 확인 후, 저장된 주소와 결제 방법이 자동으로 채워집니다.
  4. "Pay" — 완료를 클릭합니다.

핵심 통찰: Link는 판매자 간에 작동합니다. 당신의 고객이 완전히 다른 사이트에서 Link를 사용했다면, 당신의 체크아웃에서도 원클릭 경험을 얻을 것입니다. Stripe의 네트워크 효과는 실제입니다 — 그들은 2026년 초 현재 1억 명 이상의 Link 사용자를 보고합니다.

Link 채택을 최대화하려면, 이메일 필드가 고객이 당신의 체크아웃 흐름에서 상호작용하는 첫 번째 것이 되도록 하세요. Payment Element는 accordion 레이아웃으로 이것을 잘 처리합니다.

더 나아가고 싶다면, Apple Pay, Google Pay, Link를 양식 위의 저명한 버튼으로 표시하도록 Express Checkout Element를 사용할 수 있습니다:

// components/ExpressCheckout.tsx
'use client';

import { ExpressCheckoutElement } from '@stripe/react-stripe-js';

export function ExpressCheckout() {
  return (
    <ExpressCheckoutElement
      onConfirm={async (event) => {
        // 익스프레스 결제 확인을 처리합니다
        console.log('Express checkout confirmed:', event);
      }}
      options={{
        buttonType: {
          applePay: 'buy',
          googlePay: 'buy',
        },
      }}
    />
  );
}

보안, 테스트, 및 실제 배포

보안 체크리스트

  • Stripe 비밀 키는 서버 측에서만 사용됨
  • 웹훅 서명은 모든 요청에 대해 확인됨
  • HTTPS는 생산에서 강제됨
  • 금액 계산은 서버 측에서 발생함 (클라이언트 발송 금액을 절대 신뢰하지 마세요)
  • API 라우트는 속도 제한이 있음
  • 고객 데이터는 당신의 개인정보 보호 정책에 따라 처리됨
  • CSP 헤더는 Stripe의 도메인을 허용합니다 (js.stripe.com, api.stripe.com)

테스트

Stripe의 테스트 모드는 훌륭합니다. 이러한 테스트 카드 번호를 사용하세요:

카드 번호 시나리오
4242 4242 4242 4242 성공한 결제
4000 0000 0000 3220 3D Secure 필요
4000 0000 0000 9995 거부됨
4000 0000 0000 0002 인증 필요
4000 0025 0000 3155 첨부 실패 (저장된 카드의 경우)

구독 테스트의 경우, Stripe의 테스트 클록을 사용하여 실제로 기다리지 않고 시간 경과를 시뮬레이션하세요.

실제 배포

  1. 당신의 키를 sk_test_에서 sk_live_로, pk_test_에서 pk_live_로 전환하세요.
  2. Stripe Dashboard에서 라이브 웹훅 엔드포인트를 설정하세요.
  3. 프로덕션에 대해 Apple Pay 도메인을 확인하세요.
  4. Stripe Dashboard에서 원하는 결제 방법을 활성화하세요.
  5. 당신의 Stripe 계정이 완전히 활성화되어 있는지 확인하세요 (신원 확인, 은행 계좌 등).

성능 고려사항

Stripe.js는 ~40KB 압축입니다. 무시할 수 없습니다. 몇 가지 팁:

  • Stripe.js를 지연 로드하세요. 모든 페이지에서 로드하지 마세요 — 체크아웃 관련 페이지에서만. loadStripe 함수는 이것을 잘 처리합니다; 당신이 그것을 호출할 때까지 스크립트를 가져오지 않습니다.

  • 정확히 스크립트가 언제 로드되는지 제어하고 싶다면 @stripe/stripe-js/pure를 사용하세요:

import { loadStripe } from '@stripe/stripe-js/pure';
// loadStripe()이 호출될 때까지 스크립트는 로드되지 않습니다
  • 제품 페이지에 Server Components를 사용하세요. Stripe 클라이언트 코드를 당신의 제품 목록 및 상세 페이지 밖으로 유지하세요. 사용자가 실제로 체크아웃을 시작할 때만 클라이언트 컴포넌트를 가져오세요.

  • API 라우트에 Edge runtime을 사용하세요. Stripe의 Node.js SDK는 2025년부터 Edge runtime에서 작동합니다. Stripe API 라우트에 export const runtime = 'edge'를 추가하여 더 낮은 지연을 얻을 수 있습니다.

고성능 헤드리스 상점을 구축하는 팀의 경우, Astro와 같은 프레임워크는 컨텐츠가 많은 페이지에 훌륭한 적합입니다. Next.js는 동적 체크아웃 흐름을 처리합니다. 우리는 여러 클라이언트에 대해 이러한 하이브리드 접근 방식을 수행했습니다 — 우리의 Astro 개발Next.js 개발 팀은 정기적으로 이러한 아키텍처에 협업합니다.

FAQ

2026년 Stripe의 거래 수수료는 얼마입니까?

Stripe의 표준 가격은 미국의 성공한 카드 충전당 2.9% + $0.30입니다. 유럽 카드의 경우, 1.5% + €0.25입니다. 연간 1백만 달러 이상을 처리하는 비즈니스의 경우 볼륨 할인을 사용할 수 있습니다. 표준 플랜에는 설정 수수료, 월간 수수료, 또는 숨겨진 비용이 없습니다. Stripe는 수동으로 입력된 카드에 대해 추가 0.5%, 국제 카드에 대해 1%를 청구합니다.

Checkout Sessions 또는 Payment Intents를 사용해야 합니까?

대부분의 경우 Checkout Sessions를 사용하세요. 구현이 더 빠르고, 자동으로 40+ 결제 방법을 지원하고, PCI 규정준수를 처리하고, Stripe는 지속적으로 전환율을 최적화합니다. 임베드 체크아웃으로 달성할 수 없는 완전히 커스텀한 체크아웃 UI가 필요하거나 결제 흐름에 대한 세밀한 제어가 필요한 경우 (분할 결제 또는 수동 캡처와 같은) Payment Intents를 사용하세요.

프로덕션에서 웹훅 실패를 어떻게 처리합니까?

웹훅 핸들러에서 항상 200 상태 코드를 반환하세요. 당신의 비즈니스 로직이 실패하더라도 말입니다. 오류를 기록하고 비동기적으로 처리하세요. 데이터베이스에 대해 이벤트 ID를 확인하여 핸들러를 멱등성으로 만드세요. Stripe는 최대 3일 동안 지수적 백오프로 웹훅을 재시도합니다. Stripe Dashboard에서 웹훅 실패 경고를 설정하고, 웹훅 페이로드를 비동기적으로 처리하기 위해 큐 (AWS SQS 또는 Inngest와 같은)를 사용하는 것을 고려하세요.

Stripe를 Sanity 또는 Contentful과 같은 헤드리스 CMS와 함께 사용할 수 있습니까?

물론입니다. 전형적인 패턴은 헤드리스 CMS에서 제품 정보 및 컨텐츠를 저장하고, Stripe에서 가격 및 결제 데이터를 저장하고, 공유 제품 ID 또는 SKU를 통해 연결합니다. Next.js 프론트엔드는 CMS에서 컨텐츠를 가져오고 고객이 구매할 준비가 되면 Stripe Checkout Sessions 또는 Payment Intents를 생성합니다. 우리는 헤드리스 CMS 개발 작업에서 이 패턴을 광범위하게 다룹니다.

로컬에서 Apple Pay를 테스트하려면 어떻게 합니까?

local host에서 Apple Pay를 쉽게 테스트할 수 없습니다. HTTPS와 도메인 확인이 필요하기 때문입니다. 최선의 접근 방식은 Stripe의 테스트 모드를 Payment Element에서 4242 테스트 카드로 사용하는 것입니다 — 결제 흐름을 시뮬레이션합니다. 실제 Apple Pay 테스트의 경우, HTTPS가 있는 스테이징 환경에 배포합니다. Stripe CLI는 Apple Pay 트랜잭션에 대한 웹훅 이벤트 전달도 지원합니다.

Stripe Link는 활성화하는 가치가 있습니까?

예. Link는 판매자에게 무료입니다 — Stripe는 추가 비용을 청구하지 않습니다. Payment Element 및 Checkout Sessions에서 자동으로 나타납니다. Stripe는 평균적으로 체크아웃 완료를 최대 7% 증가시키고, 반복 Link 사용자의 경우 더 높은 수를 보고합니다. 활성화하는 것에는 단점이 없으며, 2026년에 1억 명 이상의 Link 사용자와 함께, 네트워크 효과는 상당합니다.

Next.js에서 측정된 청구를 통해 구독을 어떻게 처리합니까?

Stripe에서 측정된 가격으로 구독을 생성합니다. 그런 다음, 당신의 백엔드에서 Usage Records API를 사용하여 사용량을 보고합니다. 각 청구 기간의 끝에서, Stripe는 자동으로 합계를 계산하고 고객에게 청구합니다. 당신의 웹훅 핸들러는 invoice.payment_succeededinvoice.payment_failed 이벤트를 청취하여 당신의 시스템을 동기화된 상태로 유지해야 합니다. Cron 작업 또는 이벤트 기반 아키텍처를 사용하여 서버 측에서 사용량을 보고합니다.

국제 고객에 대한 통화 및 가격을 처리하는 최선의 방법은 무엇입니까?

Stripe Adaptive Pricing (2025년 출시)은 자동으로 체크아웃 시 고객의 로컬 통화로 가격을 변환합니다. 당신의 기본 통화로 가격을 설정하면, Stripe는 변환, 표시, 정산을 처리합니다. 또는 Stripe에서 여러 통화의 제품별 가격을 생성하여 더 많은 제어를 위해 할 수 있습니다. 고객의 IP 또는 브라우저 로케일을 사용하여 제품 페이지에 표시할 통화를 결정하세요.

Stripe를 사용한 헤드리스 커머스 통합을 구축하는 데 비용이 얼마나 듭니까?

범위에 따라 다릅니다. 기본 Checkout Sessions 통합은 며칠 안에 완료할 수 있습니다. 구독, 고객 포털, 웹훅, 및 커스텀 UI를 갖춘 완전히 특징이 있는 설정은 일반적으로 2-6주의 개발 시간을 소요합니다. 당신의 구체적인 요구사항을 논의하고 싶다면, 가격 페이지를 확인하거나 연락 — 우리는 다양한 산업에서 이러한 통합을 구축했고 당신에게 현실적인 추정을 제공할 수 있습니다.