Next.js에서 React Query (v5) 사용하기

JaeHong Jeong·2023년 9월 24일
3

NextJS

목록 보기
4/4
post-thumbnail

React Query는 서버에서 데이터를 프리페칭 하고 이를 queryClient에 전달하는 두 가지 방법을 지원한다.

  • 데이터를 프리페치하여 initialData 로 전달한다.
    • 간단한 경우 빠르게 설정 가능
    • 몇 가지 주의 사항이 있음
  • 서버에서 쿼리를 프리페치하고, 캐시를 dehydrate하고 클라이언트에서 다시 rehydrate 한다.
    • 약간 더 많은 설정이 필요하다.

Using Next.js

이러한 메커니즘의 정확한 구현은 플랫폼마다 다를 수 있지만 두 가지 형태의 사전 렌더링을 지원하는 Next.js로 시작하는 것이 좋다.

  • Static Generation (SSG)
  • Server-side Rendering (SSR)

React Query는 사용 중인 플랫폼에 관계없이 이러한 사전 렌더링 형식을 모두 지원한다.

참고: Next.js의 새로운 /app-folder와 통합하는 방법에 대한 참고 사항은 이 가이드의 아래쪽을 참조

Using initialData

Next.js의 getStaticProps또는getServerSideProps 와 함께 두 메서드 중 하나에서 가져온 데이터를 useQueryinitialData 옵션에 전달할 수 있다. React Query의 관점에서 이들은 동일한 방식으로 통합된다. getStaticProps 는 아래와 같다.

export async function getStaticProps() {
  const posts = await getPosts()
  return { props: { posts } }
}
function Posts(props) {
  const { data } = useQuery({
    queryKey: ['posts'],
    queryFn: getPosts,
    initialData: props.posts,
  })
// ...
}

설정은 최소화되어 겨우에 따라 빠른 해결 방법이 될 수 있지만 전체 접근 방식과 비교할 때 고려해야 할 몇 가지 절충점이 있다.

  • 트리에서 더 깊은 하위 컴포넌트에서 useQuery 를 호출하는 경우 initialData 를 해당 지점으로 전달해야한다.
  • 여러 위치에서 동일한 쿼리로 useQuery 를 호출하는 경우 모든 위치에 initialData 를 전달해야한다.
  • 서버에서 쿼리를 가져온 시간을 알 수 있는 방법이 없으므로 dataUpdatedAt 및 쿼리를 다시 가져와야 하는지 여부는 대신 페이지가 로드된 시간에 따라 결정된다.

Using hydration

React Query는 Next.js의 서버에서 여러 쿼리를 미리 가져온 다음 해당 쿼리를 queryClient로 dehydration하는 것을 지원한다. 즉, 서버는 페이지 로드 시 즉시 사용할 수 있는 마크업을 사전 렌더링할 수 있으며, JS를 사용할 수 있게 되자마자 React Query는 라이브러리의 전체 기능으로 해당 쿼리를 업그레이드하거나 hydrate할 수 있다. 여기에는 해당 쿼리가 서버에서 렌더링된 이후 오래된 경우 클라이언트에서 해당 쿼리를 다시 가져오는 것이 포함된다.

서버에서 캐싱 쿼리를 지원하고 hydration을 설정하려면 다음을 수행해라.

  • 앱 내부와 인스턴스 참조(또는 리액트 상태에서)에서 새 QueryClient 인스턴스를 만든다. 이렇게 하면 여러 사용자와 요청간에 데이터가 공유되지 않고 컴포넌트 생명주기 한번만 QueryClient를 생성할 수 있다.
  • <QueryClientProvider> 로 앱 컴포넌트를 래핑하고 클라이언트 인스턴스에 전달한다.
  • 앱 컴포넌트를 <HydrationBoundary> 로 래핑하고 pagePropsdehydratedState prop에 전달한다.
// _app.jsx
import { HydrationBoundary, QueryClient, QueryClientProvider } from '@tanstack/react-query'

export default function MyApp({ Component, pageProps }) {
  const [queryClient] = React.useState(() => new QueryClient())

  return (
    <QueryClientProvider client={queryClient}>
      <HydrationBoundary state={pageProps.dehydratedState}>
        <Component {...pageProps} />
      </HydrationBoundary>
    </QueryClientProvider>
  )
}

이제 getStaticProps (SSG용) 또는 getServerSideProps (SSR용)를 사용하여 페이지의 일부 데이터를 미리 가져올 준비가 되었다. React Query의 관점에서 이들은 동일한 방식으로 통합된다. getStaticProps 는 아래와 같다.

  • 각 페이지 요청에 대해 새 QueryClient 인스턴스를 만든다. 이렇게 하면 사용자와 요청 간에 데이터가 공유되지 않는다.
  • 클라이언트 prefetchQuery 메서드를 사용하여 데이터를 미리 가져오고 완료될 때까지 기다린다.
  • 쿼리 캐시를 dehydrate하고 dehydratedState prop을 통해 페이지에 전달하려면 dehydrate 을 사용해라. 이는 _app.js 에서 캐시를 선택하는 것과 동일한 prop이다.
// 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에서 가져오도록 하는 것이 좋다. 즉, 특정 쿼리에 대해 prefetchQuery 를 추가하거나 제거하여 컨텐츠 서버가 렌더링하는 내용을 제어할 수 있다.

Caveat for Next.js rewrites ( Next.js rewrites에 대한 주의사항 )

Automatic Static Optimization이나 getStaticProps 와 함께 Next.js의 rewrite를 사용하는 경우 문제가 있다. 이는 React Query의 두번 째 hydration을 유발한다. 그 이유는 Next.js가 클라이언트에서 rewrite 구문 분석하고 hydration 후 모든 매개변수를 수집하여 router.query 에 제공될 수 있도록 해야 하기 때문이다.

결과적으로 모든 hydration 데이터에 대한 참조 동등성이 누락된다. 예를 들어 데이터가 컴포넌트의 prop로 사용되거나 useEffect / useMemo 의 종속성 배열에서 사용될 때마다 트리거된다.

profile
반갑습니다.

0개의 댓글