Next.js SSR
☁️환경☁️
- Next.js(page router)
- typeScript
- react-query
❓SSR
Server Side Rendring 의 약자로 ssr이라고 한다.
실행될 때 가장먼저 ssr컴포넌트가 실행되고,
이후에 실행되는게 _app.tsx 부분이다.
ssr 컴포넌트에서는 console.log("getServerSideProps")
_app.tsx 에서는 console.log("_app")
으로 확인해보면 아래와 같이 순서가 찍혀서 나온다.
위 순서를 기억하고 아래 내용을 읽으면 조금 더 이해가 쉽다.
1. query-key 만들어주기
import { getCards } from '@/remote/card'
import { useQuery } from 'react-query'
export default function useCard() {
return useQuery(['cards'], () => getCards(), {
suspense: true,
})
}
2. SSR에서의 React Query 데이터 사전 처리
import { getCards } from '@/remote/card'
import { dehydrate, QueryClient } from 'react-query'
export default function CardListPage() {
return <div>CardListPage</div>
}
export async function getServerSideProps() {
const client = new QueryClient()
await client.prefetchInfiniteQuery(['cards'], () => getCards())
return {
props: {
dehydratedState: JSON.parse(JSON.stringify(dehydrate(client))),
},
}
}
`CardListPage`
클라이언트가 보는 UI를 그리는 부분
` getServerSideProps `
CardListPage 가 서버에서 렌더링 될 때 실행될 부분
await client.prefetchInfiniteQuery(['cards'], () => getCards())
useCard에서 생성해준 query-key와 맞춰주어야한다. 그래야 cards에 값을 저장하고 사용할 수 있다.
return {
props: {
dehydratedState: JSON.parse(JSON.stringify(dehydrate(client))),
},
}
json 형태로 반환해 주어야한다.
` getServerSideProps `에서 받은 값은 _app.tsx 에서 받게된다.
서버에서 렌더링되어 값을 받았을 테니 다음으로 실행될 클라이언트 사이드 ` _app.tsx `로 전송된다.
3. _app.tsx 에서 dehydratedState 받기
서버사이드에서 수행한 값을
클라이언트 사이드에서 Hydrate 컴포넌트가 `dehydratedState` 를 받아 클라이언트 상태를 복원해주어야 사용가능하다.
서버사이드 렌더링이 끝난 후
다음 렌더링 순서인 ` _app.tsx `에서 ` rehydrate ` 후 React Query의 클라이언트 상태로 복원하면 된다.
import type { AppProps } from 'next/app'
import globalStyles from '@/styles/globalStyles'
import { Global } from '@emotion/react'
import Layout from '@/components/shared/Layout'
import { Hydrate, QueryClient, QueryClientProvider } from 'react-query'
const client = new QueryClient({})
export default function App({ Component, pageProps }: AppProps) {
return (
<Layout>
<Global styles={globalStyles} />
<QueryClientProvider client={client}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
</Layout>
)
}
✨ rehydrate 하는 방법
// 1. pageProps 객체에서 직접 dehydratedState를 참조하는 방법
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
// 2. 함수의 파라미터에서 pageProps를 구조 분해 후
// dehydratedState와 나머지 프로퍼티들을 명시적으로 분리하는 방법
export default function App({
Component,
pageProps: { dehydratedState, ...pageProps },
}: AppProps) {
return (
<Layout>
<Global styles={globalStyles} />
<QueryClientProvider client={client}>
<Hydrate state={dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
</Layout>
)
}
4. 데이터 사용하기
const { data: cardList } = useInfiniteQuery(
['cards'],
({ pageParam }) => getCards(pageParam),
{
getNextPageParam: (snapshot) => {
return snapshot.lastVisible
},
},
)
설정해 주었던 query-key를 이용해 데이터를 불러올 수 있다.