Stripe + Next.js 궁극 가이드 (2026년판)
I’m happy to help translate the article, but I don’t see the text you’d like translated—only the source link you provided. Could you please paste the content you want translated into Korean? Once I have the text, I’ll keep the source line exactly as‑is and translate the rest while preserving all formatting and technical terms.
1. 2026 라이프사이클: 임베디드 vs. 호스티드
Stripe는 이제 Embedded Checkout을 강력히 권장합니다. 기존 리디렉션과 달리, 이는 iframe 또는 웹 컴포넌트를 사용하여 Next.js 페이지 내부에 삽입되며, 사용자를 도메인에 머무르게 하면서 PCI 준수 책임을 Stripe에 위임합니다.
2. 최신 프로젝트 설정
우리는 App Router와 최신 Stripe SDK를 사용하여 Next.js 프로젝트를 초기화합니다.
npx create-next-app@latest my-store-2026 --typescript --tailwind --app
cd my-store-2026
npm install stripe @stripe/stripe-js @stripe/react-stripe-js
3. 보안 환경 변수
절대로 STRIPE_SECRET_KEY를 노출하지 마세요. 2026년 현재, Next.js 환경 변수는 엄격히 적용됩니다.
# .env.local
STRIPE_SECRET_KEY="sk_test_51..."
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_51..."
STRIPE_WEBHOOK_SECRET="whsec_..."
4. 서버 액션 패턴
2026 문서에서는 Server Actions가 Checkout Session을 생성하는 표준 방법으로 정의되어 있습니다. 이를 통해 /api/checkout 폴더가 필요 없게 됩니다.
src/app/actions/stripe.ts 파일을 생성합니다:
"use server";
import { stripe } from "@/lib/stripe";
import { headers } from "next/headers";
export async function createCheckoutSession(priceId: string) {
const origin = (await headers()).get("origin");
const session = await stripe.checkout.sessions.create({
ui_mode: "embedded", // Enables the 2026 Embedded UI
line_items: [{ price: priceId, quantity: 1 }],
mode: "subscription",
return_url: `${origin}/return?session_id={CHECKOUT_SESSION_ID}`,
});
return { clientSecret: session.client_secret };
}
5. 임베디드 체크아웃 구현
@stripe/react-stripe-js의 EmbeddedCheckout 컴포넌트는 원활한 “리다이렉션 없는” 경험을 제공합니다.
Create src/components/CheckoutForm.tsx:
"use client";
import { loadStripe } from "@stripe/stripe-js";
import {
EmbeddedCheckoutProvider,
EmbeddedCheckout,
} from "@stripe/react-stripe-js";
import { createCheckoutSession } from "@/app/actions/stripe";
const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
);
export default function CheckoutForm({
priceId,
}: {
priceId: string;
}) {
// Fetch the clientSecret via the Server Action
const fetchClientSecret = async () => {
const { clientSecret } = await createCheckoutSession(priceId);
return clientSecret as string;
};
return (
);
}
6. 핵심: 2026 웹훅 핸들러
Server Actions를 사용하더라도 Route Handlers는 여전히 웹훅에 필요합니다. Stripe가 결제가 성공했을 때 “ping”할 정적 URL이 필요하기 때문입니다.
src/app/api/webhook/route.ts 파일을 생성합니다:
import { stripe } from "@/lib/stripe";
import { headers } from "next/headers";
export async function POST(req: Request) {
const body = await req.text();
const signature = (await headers()).get("stripe-signature")!;
let event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err: any) {
return new Response(`Webhook Error: ${err.message}`, { status: 400 });
}
// Handle the event
if (event.type === "checkout.session.completed") {
const session = event.data.object;
// 2026 Practice: Trigger a background sync or email service
console.log(`💰 Payment confirmed for ${session.id}`);
}
return new Response(null, { status: 200 });
}
2026 Stripe Docs의 새로운 기능
- Link Authentication Element: 사용자가 “Link”(Stripe의 1‑클릭 결제) 계정을 가지고 있는지 자동으로 감지하고 세부 정보를 미리 채워, 전환율을 최대 10%까지 높입니다.
- Adaptive Pricing: 이제 Checkout Sessions에서 기본적으로 지원됩니다—Stripe가 사용자의 IP를 기반으로 현지 통화로 가격을 자동 표시합니다.
- Enhanced Tax ID Collection: 세션 생성 시
tax_id_collection: { enabled: true }를 바로 토글하여 추가 로직 없이 전 세계 B2B 세금 준수를 처리할 수 있습니다. - Pro‑Tip: 로컬 테스트를 위해 Stripe CLI 를 사용하세요.
stripe listen --forward-to localhost:3000/api/webhook를 실행하면 로컬 머신에서 성공적인 결제를 시뮬레이션할 수 있습니다.