SSR
React Query는 서버로부터 데이터를 미리 받아오고, QueryClient에 제공하는 두가지 방법을 지원한다.
- 첫째, 클라이언트 측에서 데이터를 미리 가져와 initialData로서 전달하는 것
-> 이 방법은 간단한 경우에 빠르게 설정할 수 있지만 몇 가지 주의 사항이 있다. - 둘째, 서버에서 쿼리를 미리 가져오고 캐시를 비활성화하여 클라이언트에서 재사용하는 것
-> 이 방법은 초기 설정에 약간 더 많은 설정이 필요하지만 더 많은 성능 향상을 제공할 수 있다.
Using 'initialData'
Next.js의 getStaticProps 혹은 getServerSideProps와 함께 사용하면 두 메서드중 어느 하나에서 fetch한 데이터를
useQuery의 initialData옵션에 전달할 수 있다. 아래 예시를 보자.
export async function getStaticProps() {
const posts = await getPosts()
return { props: { posts } }
}
function Posts(props) {
const { data } = useQuery({
queryKey: ['posts'],
queryFn: getPosts,
initialData: props.posts,
})
// ...
}
위의 설정은 Nest.js와 React Query를 동시에 이용하는 최소한의 설정이다. 일부 경우에는 빠른 해결책이 될 수 있지만, 전체 접근 방식과 비교했을 때 고려해야 할 몇 가지 트레이드오프가 있다.
- 트리의 더 깊은 컴포넌트에서 useQuery를 호출하는 경우, 초기 데이터를 해당 지점까지 내려줘야 한다.
- 동일한 쿼리를 사용하여 여러 위치에서 useQuery를 호출하는 경우, 모든 위치에 초기 데이터를 전달해야 한다.
- 서버에서 쿼리가 언제 검색되었는지를 알 수 있는 방법이 없으므로, dataUpdatedAt 및 쿼리가 다시 가져와야 하는지 여부를 판단하는 것은 페이지가 로드된 시간에 따라 결정된다.
Using Hydration
React Query는 Next.js에서 서버에서 여러 개의 쿼리를 미리 가져온 다음 이러한 쿼리를 queryClient로 dehydrate할 수 있도록 지원해준다. 이는 서버에서 즉시 사용 가능한 마크업을 미리 렌더링할 수 있게 해주고, JS가 사용 가능해지면 React Query가 이러한 쿼리를 라이브러리의 모든 기능과 함께 업그레이드하거나 hydrate할 수 있게 한다. 이는 렌더링된 시점 이후에 해당 쿼리가 오래되어 더 이상 유효하지 않은 경우 클라이언트에서 이러한 쿼리를 다시 가져올 수 있도록 해준다.
React Query에서 서버 캐싱을 지원하고 hydrate를 설정하려면 다음과 같은 작업을 수행해야 한다.
- 앱 내에서 새로운 QueryClient 인스턴스를 생성하고 인스턴스 ref(또는 React state)에 저장한다. 이렇게 함으로써 데이터가 다른 사용자 및 요청 간에 공유되지 않으면서, 컴포넌트 라이프 사이클마다 QueryClient를 한 번만 생성할 수 있다.
- <QueryClientProvider>로 앱 컴포넌트를 감싸고 client 인스턴스를 전달한다.
- <Hydrate>로 앱 컴포넌트를 감싸고 pageProps에서 dehydratedState props를 전달한다.
// _app.jsx
import {
Hydrate,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
export default function MyApp({ Component, pageProps }) {
const [queryClient] = React.useState(() => new QueryClient())
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
)
}
이제 각각의 페이지에서 getStaticProps(정적 생성을 위한 것) 또는 getServerSideProps(SSR을 위한 것)를 사용하여 데이터를 prefetch할 셋업되었다. 아래는 getStaticProps를 사용하는 예시이다.
- 페이지 요청마다 새로운 QueryClient 인스턴스를 생성한다. 이는 데이터가 다른 사용자 및 요청 간에 공유되지 않도록 하기위함이다.
- 클라이언트의 prefetchQuery 메소드를 사용하여 데이터를 prefetch하고 완료될 때까지 기다린다.
- dehydrate를 사용하여 쿼리 캐시를 dehydrate하고 dehydratedState props를 통해 페이지에 전달한다. 이 props는 _app.js에서 캐시가 수집될 때 사용하는 것과 동일하다.
// pages/posts.jsx
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query'
export async function getStaticProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(['posts'], getPosts)
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
function Posts() {
// This useQuery could just as well happen in some deeper child to
// the "Posts"-page, data will be available immediately either way
const { data } = useQuery({ queryKey: ['posts'], queryFn: getPosts })
// This query was not prefetched on the server and will not start
// fetching until on the client, both patterns are fine to mix
const { data: otherData } = useQuery({
queryKey: ['posts-2'],
queryFn: getPosts,
})
// ...
}
위에서 보여준 것처럼, 일부 쿼리를 미리 가져오고 나머지는 queryClient에서 가져오는 것도 괜찮다. 이렇게하면 특정 쿼리에 대한 서버 렌더링 여부를 추가하거나 제거함으로써 콘텐츠를 제어할 수 있다.
Next.js rewites 기능에 대한 주의사항
Next.js의 재작성 기능을 Automatic Static Optimization 또는 getStaticProps와 함께 사용하는 경우 문제가 발생할 수 있다. 이것은 Next.js가 클라이언트에서 재작성을 구문 분석하고 수집하여 라우터 쿼리에서 제공할 수 있도록하기 위해서이다.
그 결과, 모든 하이드레이션 데이터에 대한 참조적 동등성이 결여된다. 이는 예를 들어 데이터가 구성 요소의 props로 사용되거나 useEffects/useMemos의 종속성 배열에 사용되는 경우에도 발생한다.
'React-Query' 카테고리의 다른 글
Guides&Concepts_(Infinite Queries) (0) | 2023.04.23 |
---|---|
Guides&Concepts_(Query Retries & Paginated Queries) (0) | 2023.04.22 |
Guides&Concepts_(Paginated / Lagged Queries) (0) | 2023.03.27 |
Guides&Concepts_(Window Focus Refetching & Disabling/Pausing Queries) (0) | 2023.03.21 |
Guides&Concepts_(Dependent Qeuries & Background Fetching indicators ) (0) | 2023.03.21 |