개발/React
NEXTJS14 에서 SSR 시점에 메타데이터 동적으로 할당할 때 API 2번 호출 되는 문제
큐토
2024. 7. 13. 04:28
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: { id: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const id = params.id
// fetch data
const product = await fetch(`https://.../${id}`).then((res) => res.json())
// optionally access and extend (rather than replace) parent metadata
const previousImages = (await parent).openGraph?.images || []
return {
title: product.title,
openGraph: {
images: ['/some-specific-page-image.jpg', ...previousImages],
},
}
}
export default function Page({ params, searchParams }: Props) {}
서버사이드에서 메타데이터를 동적으로 관리하려면 위 처럼 generateMetadata 함수를 사용하라고 한다.
문제는 동적 메타데이터의 정보가 페이지 렌더링에 필요한 데이터셋과 같은 경우
// src\app\[locale]\variables\[keyword]\page.tsx
import {
HydrationBoundary,
QueryClient,
dehydrate,
} from "@tanstack/react-query";
import React from 'react';
import { getVariables } from '@/api/getVariables';
import VariablesPage from "../components/VariablesPage";
import { Variable } from '@/types/variable';
import type { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
interface VariablesWithQueryPageProps {
params: {
locale: string;
keyword: string;
};
}
const fetchDataAndMetadata = async (locale: string, keyword: string) => {
const t = await getTranslations('variables');
const queryClient = new QueryClient();
const variables = await queryClient.fetchQuery<Variable[]>({
queryKey: ['variables', locale, keyword],
queryFn: () => getVariables(locale, keyword),
});
const dehydratedState = dehydrate(queryClient);
let description = variables.map((variable, i) => (i === 0 ? '' : ', ') + variable.variableName).join('');
const metadata: Metadata = {
title: decodeURIComponent(keyword),
description: description,
applicationName: t('title')
};
return { variables, dehydratedState, metadata };
};
export async function generateMetadata({ params: { locale, keyword } }: VariablesWithQueryPageProps): Promise<Metadata> {
const { metadata } = await fetchDataAndMetadata(locale, keyword);
return metadata;
}
const VariablesWithQueryPage = async ({ params: { locale, keyword } }: VariablesWithQueryPageProps) => {
const { dehydratedState } = await fetchDataAndMetadata(locale, keyword);
return (
<HydrationBoundary state={dehydratedState}>
<VariablesPage locale={locale} keyword={keyword}/>
</HydrationBoundary>
);
};
export default VariablesWithQueryPage;
이런 식으로 응용을 하는데 문제는 SSR 시점에 API가 2번 호출 된다. 당연한 것 같기도 하다.
// src\app\[locale]\variables\[keyword]\page.tsx
import {
HydrationBoundary,
QueryClient,
dehydrate,
} from "@tanstack/react-query";
import React from 'react';
import { getVariables } from '@/api/getVariables';
import VariablesPage from "../components/VariablesPage";
import { Variable } from '@/types/variable';
import type { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
import { cache } from "react";
interface VariablesWithQueryPageProps {
params: {
locale: string;
keyword: string;
};
}
const fetchDataAndMetadata = cache(async (locale: string, keyword: string) => {
const t = await getTranslations('variables');
const queryClient = new QueryClient();
const variables = await queryClient.fetchQuery<Variable[]>({
queryKey: ['variables', locale, keyword],
queryFn: () => getVariables(locale, keyword),
});
const dehydratedState = dehydrate(queryClient);
let description = variables.map((variable, i) => (i === 0 ? '' : ', ') + variable.variableName).join('');
const metadata: Metadata = {
title: decodeURIComponent(keyword),
description: description,
applicationName: t('title')
};
return { variables, dehydratedState, metadata };
});
export async function generateMetadata({ params: { locale, keyword } }: VariablesWithQueryPageProps): Promise<Metadata> {
const { metadata } = await fetchDataAndMetadata(locale, keyword);
return metadata;
}
const VariablesWithQueryPage = async ({ params: { locale, keyword } }: VariablesWithQueryPageProps) => {
const { dehydratedState } = await fetchDataAndMetadata(locale, keyword);
return (
<HydrationBoundary state={dehydratedState}>
<VariablesPage locale={locale} keyword={keyword}/>
</HydrationBoundary>
);
};
export default VariablesWithQueryPage;
솔루션은 react cache
아 nextjs14 appRouter 너무 피곤하다