본문으로 건너뛰기
버전: 11.x

정적 사이트 생성

정적 사이트 생성을 수행하려면 각 페이지에서 getStaticProps 내부에서 tRPC 쿼리를 실행해야 합니다.

이것은 서버 측 헬퍼를 사용하여 쿼리를 사전에 가져오고, 탈수화(dehydrate)한 뒤 페이지로 전달하는 방식으로 수행할 수 있습니다. 그러면 쿼리가 trpcState를 자동으로 가져와 초기 값으로 사용합니다.

getStaticProps에서 데이터 가져오기

pages/posts/[id].tsx
tsx
import { createServerSideHelpers } from '@trpc/react-query/server';
import { prisma } from './server/context';
import { appRouter } from './server/routers/_app';
import { trpc } from './utils/trpc';
import {
GetStaticPaths,
GetStaticPropsContext,
InferGetStaticPropsType,
} from 'next';
import superjson from 'superjson';
 
export async function getStaticProps(
context: GetStaticPropsContext<{ id: string }>,
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx: {},
transformer: superjson, // optional - adds superjson serialization
});
const id = context.params?.id as string;
 
// prefetch `post.byId`
await helpers.post.byId.prefetch({ id });
 
return {
props: {
trpcState: helpers.dehydrate(),
id,
},
revalidate: 1,
};
}
 
export const getStaticPaths: GetStaticPaths = async () => {
const posts = await prisma.post.findMany({
select: {
id: true,
},
});
 
return {
paths: posts.map((post) => ({
params: {
id: post.id,
},
})),
// https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-blocking
fallback: 'blocking',
};
};
 
export default function PostViewPage(
props: InferGetStaticPropsType<typeof getStaticProps>,
) {
const { id } = props;
const postQuery = trpc.post.byId.useQuery({ id });
 
if (postQuery.status !== 'success') {
// won't happen since we're using `fallback: "blocking"`
return <>Loading...</>;
}
const { data } = postQuery;
return (
<>
<h1>{data.title}</h1>
<em>Created {data.createdAt.toLocaleDateString('en-us')}</em>
 
<p>{data.text}</p>
 
<h2>Raw data:</h2>
<pre>{JSON.stringify(data, null, 4)}</pre>
</>
);
}
pages/posts/[id].tsx
tsx
import { createServerSideHelpers } from '@trpc/react-query/server';
import { prisma } from './server/context';
import { appRouter } from './server/routers/_app';
import { trpc } from './utils/trpc';
import {
GetStaticPaths,
GetStaticPropsContext,
InferGetStaticPropsType,
} from 'next';
import superjson from 'superjson';
 
export async function getStaticProps(
context: GetStaticPropsContext<{ id: string }>,
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx: {},
transformer: superjson, // optional - adds superjson serialization
});
const id = context.params?.id as string;
 
// prefetch `post.byId`
await helpers.post.byId.prefetch({ id });
 
return {
props: {
trpcState: helpers.dehydrate(),
id,
},
revalidate: 1,
};
}
 
export const getStaticPaths: GetStaticPaths = async () => {
const posts = await prisma.post.findMany({
select: {
id: true,
},
});
 
return {
paths: posts.map((post) => ({
params: {
id: post.id,
},
})),
// https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-blocking
fallback: 'blocking',
};
};
 
export default function PostViewPage(
props: InferGetStaticPropsType<typeof getStaticProps>,
) {
const { id } = props;
const postQuery = trpc.post.byId.useQuery({ id });
 
if (postQuery.status !== 'success') {
// won't happen since we're using `fallback: "blocking"`
return <>Loading...</>;
}
const { data } = postQuery;
return (
<>
<h1>{data.title}</h1>
<em>Created {data.createdAt.toLocaleDateString('en-us')}</em>
 
<p>{data.text}</p>
 
<h2>Raw data:</h2>
<pre>{JSON.stringify(data, null, 4)}</pre>
</>
);
}

react-query는 기본적으로 마운트될 때 클라이언트에서 데이터를 다시 가져옵니다. getStaticProps에서 가져온 데이터만 사용하려면 쿼리 옵션의 refetchOnMountrefetchOnWindowFocusfalse로 설정해야 합니다.

예를 들어 호출 횟수가 제한된 서드파티 API를 사용해 API 요청 수를 최소화해야 할 때 이 방식이 더 적합할 수 있습니다.

쿼리별로 다음과 같이 수행할 수 있습니다:

tsx
import { trpc } from './utils/trpc';
 
const data = trpc.example.useQuery(
// if your query takes no input, make sure that you don't
// accidentally pass the query options as the first argument
undefined,
{ refetchOnMount: false, refetchOnWindowFocus: false },
);
tsx
import { trpc } from './utils/trpc';
 
const data = trpc.example.useQuery(
// if your query takes no input, make sure that you don't
// accidentally pass the query options as the first argument
undefined,
{ refetchOnMount: false, refetchOnWindowFocus: false },
);

또는 앱 전체의 모든 쿼리가 동일한 방식으로 동작해야 하는 경우 전역으로 설정할 수 있습니다:

utils/trpc.ts
tsx
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import superjson from 'superjson';
import type { AppRouter } from './api/trpc/[trpc]';
export const trpc = createTRPCNext<AppRouter>({
config(config) {
return {
links: [
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
}),
],
// Change options globally
queryClientConfig: {
defaultOptions: {
queries: {
refetchOnMount: false,
refetchOnWindowFocus: false,
},
},
},
};
},
});
utils/trpc.ts
tsx
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import superjson from 'superjson';
import type { AppRouter } from './api/trpc/[trpc]';
export const trpc = createTRPCNext<AppRouter>({
config(config) {
return {
links: [
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
}),
],
// Change options globally
queryClientConfig: {
defaultOptions: {
queries: {
refetchOnMount: false,
refetchOnWindowFocus: false,
},
},
},
};
},
});

앱에 정적 쿼리와 동적 쿼리가 혼합되어 있는 경우 이 접근 방식에 주의해야 합니다.