@shoal/next는 App Router용 헬퍼로 코어 SDK를 감쌉니다. 플래그를 서버에서 평가하므로 첫 바이트부터 올바른 변형이 렌더링되고 화면이 깜빡이지 않습니다.
Terminal
npm install @shoal/nextTerminal
pnpm add @shoal/nextTerminal
yarn add @shoal/nextTerminal
bun add @shoal/next클라이언트 만들기
프로세스마다 클라이언트를 하나 만들어 재사용하세요. 헬퍼가 요청 사이에 룰셋을 캐시합니다.
lib/shoal.ts
import { createNextClient } from "@shoal/next";
export const shoal = createNextClient({
key: process.env.SHOAL_KEY!,
context: async ({ cookies }) => ({
user: { id: cookies.get("uid")?.value ?? "anonymous" },
}),
});서버 컴포넌트
app/checkout/page.tsx
import { shoal } from "@/lib/shoal";
export default async function CheckoutPage() {
const variant = await shoal.variation("checkout-theme", "classic");
return variant === "compact" ? <CompactCheckout /> : <ClassicCheckout />;
}미들웨어
엣지에서 다른 페이지로 rewrite합니다. 페이지 전체를 바꾸는 실험에 유용합니다.
middleware.ts
import { NextResponse, type NextRequest } from "next/server";
import { shoal } from "@/lib/shoal";
export async function middleware(req: NextRequest) {
if (await shoal.isOn("new-pricing", { request: req })) {
return NextResponse.rewrite(new URL("/pricing-v2", req.url));
}
}
export const config = { matcher: "/pricing" };클라이언트 컴포넌트
브라우저에서 다시 평가하지 말고 서버에서 평가한 값을 내려보내세요.
app/layout.tsx
const flags = await shoal.bootstrap(["summer-banner", "chat-widget"]);
return <FlagsProvider value={flags}>{children}</FlagsProvider>;components/banner.tsx
"use client";
import { useFlag } from "@shoal/react";
export function Banner() {
return useFlag("summer-banner") ? <SummerBanner /> : null;
}