Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ vi.mock("@/lib/components/home/HomeShell", () => ({
HomeShell: () => null,
}));

// `setRequestLocale` (static-render enabler) resolves to next-intl's
// client build under vitest and throws "not supported in Client Components".
// It's orthogonal to locale propagation — no-op it here.
vi.mock("next-intl/server", () => ({
setRequestLocale: vi.fn(),
}));

// Spy on the server fetch util; route by path so each helper gets a usable shape.
const { serverApiGet } = vi.hoisted(() => ({
serverApiGet: vi.fn(async (path: string, _options?: { locale?: string }) => {
Expand All @@ -27,7 +34,6 @@ describe("Home RSC prefetch locale propagation (#838)", () => {

await Home({
params: Promise.resolve({ locale: "ko" }),
searchParams: Promise.resolve({}),
});

expect(serverApiGet.mock.calls.length).toBeGreaterThan(0);
Expand All @@ -41,7 +47,6 @@ describe("Home RSC prefetch locale propagation (#838)", () => {

await Home({
params: Promise.resolve({ locale: "en" }),
searchParams: Promise.resolve({}),
});

for (const [, options] of serverApiGet.mock.calls) {
Expand Down
10 changes: 9 additions & 1 deletion packages/web/app/[locale]/(shell)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { setRequestLocale } from "next-intl/server";
import { AppShell } from "@/lib/components/shell/AppShell";

export default function ShellLayout({
export default async function ShellLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
// 정적 렌더에는 [locale] 트리의 모든 layout·page 가 setRequestLocale 을
// 호출해야 한다 — 하나라도 빠지면 세그먼트가 dynamic 으로 떨어진다(#942).
const { locale } = await params;
setRequestLocale(locale);

return <AppShell>{children}</AppShell>;
}
7 changes: 4 additions & 3 deletions packages/web/app/[locale]/(shell)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
CurationListResponse,
FeedItem,
} from "@/lib/api/generated/models";
import { setRequestLocale } from "next-intl/server";
import { serverApiGet } from "@/lib/api/server-instance";
import { isLocale, type Locale } from "@/lib/i18n/locale";
import { getPostMatch } from "@/lib/api/adapters/postMatch";
Expand Down Expand Up @@ -188,18 +189,18 @@ async function fetchHeroSlides(

export default async function Home({
params,
searchParams,
}: {
params: Promise<{ locale: string }>;
searchParams: Promise<{ variant?: string }>;
}) {
const { locale: rawLocale } = await params;
// Enable static rendering — every layout/page in the [locale] tree must call
// this or the segment falls back to on-demand dynamic (#942).
setRequestLocale(rawLocale);
// params.locale is a raw string; narrow to Locale so serverApiGet forwards
// the request locale (invalid values fall back to the util's RENDERED_LOCALE).
const locale: Locale | undefined = isLocale(rawLocale)
? rawLocale
: undefined;
await searchParams;

const [popularPosts, recentPosts, moodPool, artistProfileMap, curations] =
await Promise.all([
Expand Down
20 changes: 19 additions & 1 deletion packages/web/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ import { notFound } from "next/navigation";
import { routing } from "@/i18n/routing";
import { LazyTasteOnboardingPrompt } from "@/lib/components/onboarding/LazyTasteOnboardingPrompt";

// 빌드타임에 locale 을 확정해 [locale] 트리를 정적/ISR 렌더 가능하게 한다
// (없으면 Next 가 세그먼트 전체를 on-demand dynamic 으로 처리 — #942).
export function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }));
}

// 앱 전체 canonical 타임존. next-intl 포매터를 서버/클라 어디서도 쓰지 않으므로
// (날짜 "time ago" 는 컴포넌트에서 수동 계산) 이 값은 렌더 결과에 영향이 없고,
// 오직 NextIntlClientProvider 의 정적 렌더 조건(locale·now·timeZone 명시)을
// 충족시키기 위한 것이다 — ko-first 제품에 맞춰 Asia/Seoul 로 고정.
const APP_TIME_ZONE = "Asia/Seoul";

export default async function LocaleLayout({
children,
modal,
Expand All @@ -18,7 +30,13 @@ export default async function LocaleLayout({
setRequestLocale(locale);

return (
<NextIntlClientProvider>
// locale·now·timeZone 을 명시해야 provider 가 정적 렌더된다 — 명시하지
// 않으면 next-intl 이 이 트리를 dynamic 으로 opt-in 시킨다(#942).
<NextIntlClientProvider
locale={locale}
now={new Date()}
timeZone={APP_TIME_ZONE}
>
{children}
{modal}
{/* 로그인 유저 taste 넛지 — next-intl 컨텍스트가 필요해 [locale] 레이아웃
Expand Down
Loading