Next.js에서 getServerSideProps를 활용하여 서버에서 API호출을 한 결과값을 HTML 파일에 포함시켜서 받아올 수 있다.
type Todo = {
title: string;
completed: boolean;
};
type Props = {
todos: Todo[];
};
export default function Home({ todos }: Props) {
return (
<div>
{todos.map(({ title, completed }) => (
<p key={title}>
<strong>{title}</strong>
{completed && <span>완료</span>}
</p>
))}
</div>
);
}
export const getServerSideProps: GetServerSideProps = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos');
const todos = await res.json();
return {
props: {
todos,
},
};
};
특정 페이지(pages 폴더 내) 컴포넌트에서 값을 props로 넘겨주도록 getServerSideProps 메서드를 작성하면, 해당 페이지 컴포넌트의 props로 getServerSideProps에서 전달한 값을 받아서 쓸 수 있다. 이를 이용해서 처음부터 todos라는 값이 있는 상태로 클라이언트에서 렌더링이 일어난다.
크롬 개발자도구의 네트워크 탭을 보면 최초 전달된 HTML 파일에 이미 값이 들어간 상태임을 확인할 수 있다.
그러나 이러한 방식은 페이지 컴포넌트에 값을 전달하기 때문에, 해당 페이지의 하위 컴포넌트에서 값에 접근하려면 페이지 컴포넌트로부터 하위 컴포넌트로 값을 전달해야 하는 단점이 있다. React Query의 Hydration을 사용하면 기존의 React Query의 useQuery와 동일한 방식으로 서버에서 전달된 값에 접근할 수 있다.
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query';
const queryFn = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos');
const data = await res.json();
return data as Todo[];
};
export const getServerSideProps: GetServerSideProps = async () => {
const queryClient = new QueryClient();
await queryClient.prefetchQuery(['todos'], queryFn);
return {
props: {
dehydratedProps: dehydrate(queryClient),
},
};
};
먼저 getServerSideProps에서 새로운 QueryClient를 생성하고 해당 QueryClient에 값을 prefetch한다. 그리고 이 queryClient를 dehydrate시켜서 props로 전달한다.
// pages/_app.tsx
import { QueryClient, QueryClientProvider, Hydrate } from '@tanstack/react-query';
export default function App({ Component, pageProps }: AppProps) {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedProps}>
<Component {...pageProps} />
<ReactQueryDevtools />
</Hydrate>
</QueryClientProvider>
);
}
그 다음, _app.tsx에서 pageProps에 전달된 dehydrate된 props를 가져와서 hydrate시킨다. 즉, getServerSideProps에서는 데이터를 가진 상태의 queryClient를 말려서(dehydrate) 전달하고, 다시 클라이언트에서 물을 붓는(hydrate) 것이다. 이렇게 하면 React Query의 useQuery에서 동일한 queryKey(['todos'])로 값에 접근할 수 있다.
type Todo = {
title: string;
completed: boolean;
};
const queryFn = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos');
const data = await res.json();
return data as Todo[];
};
export default function Home() {
const { data } = useQuery(['todos'], queryFn, { staleTime: 10 * 1000 });
return (
<div>
{data?.map(({ title, completed }) => (
<p key={title}>
<strong>{title}</strong>
{completed && <span>완료</span>}
</p>
))}
</div>
);
}
export const getServerSideProps: GetServerSideProps = async () => {
const queryClient = new QueryClient();
await queryClient.prefetchQuery(['todos'], queryFn);
return {
props: {
dehydratedProps: dehydrate(queryClient),
hi: 'hello',
},
};
};
이렇게 하면 처음 1회는 SSR로 값을 받아와서 react query에 의해 캐시가 되고, stale 상태가 된 이후에는 클라이언트에서 동일하게 API호출을 하여 값을 갱신하게 된다.
'IT > Next.js' 카테고리의 다른 글
[Next.js] Next.js에서 mongoose 연동해서 API 만들기 (0) | 2022.04.03 |
---|---|
[Next.js] Next.js에서 Prop `className` did not match 경고가 뜨는 이유 (1) | 2022.03.05 |