[React-query] Next.js에서 React Query 사용하기

SoShy·2024년 5월 15일

React-Query

목록 보기
13/13
post-thumbnail

Next.js에서 React Query 사용하기


🏷️ 초기 설정


import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

export default function App({ Component, pageProps }) {
  const [queryClient] = React.useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            // 보통 SSR에서는 staleTime을 0 이상으로 해줌으로써
            // 클라이언트 사이드에서 바로 다시 데이터를 refetch 하는 것을 피한다.
            staleTime: 60 * 1000,
          },
        },
      })
  );

  return (
    <QueryClientProvider client={queryClient}>
      <Component {...pageProps} />
    </QueryClientProvider>
  );
}

우선 App 컴포넌트에서 초기 설정을 해주어야 한다.

React 프로젝트와는 다르게, App 컴포넌트 안에 새로운 QueryClientuseState()를 사용하여 state로 선언해주어야 한다.

App 컴포넌트 바깥에 선언하게 된다면, SSR 시 쿼리 캐시가 다른 사용자들과 request 간에 공유될 수 있기 때문에, 반드시 App 컴포넌트 내부에 선언을 해주어야 하며,

Next.js에서는 페이지를 이동하면 App 컴포넌트로부터 새롭게 렌더링되기 때문에, 쿼리 클라이언트가 매번 새롭게 생성되는 것을 막기 위해 state로 저장해주어야 한다.

🏷️ Prefetch 구현하기


React Query에서는 두 가지 방법으로 prefetching을 지원한다.

1. initialData를 사용한 prefetching

export async function getServerSideProps() {
  const posts = await getPosts()
  return { props: { posts } }
}

function Posts(props) {
  const { data } = useQuery({
    queryKey: ['posts'],
    queryFn: getPosts,
    initialData: props.posts,
  })

  // ...
}

이 방법은, Next.js에서 정적 생성, 서버 사이드 렌더링을 하면서 prefetch한 데이터를 useQuery()initialData로 설정해주는 방법이다.

사용 방법이 매우 간단하며, prefetching 단계에서는 React Query를 전혀 사용하지 않아도 된다는 장점이 있다.

다만, 몇 가지 단점도 존재하긴 한다.

  • getStaticProps(), getServerSideProps()pages 폴더 안에서만 동작하기 때문에, useQuery()를 사용하려는 컴포넌트까지 prefetch한 데이터를 props drilling으로 내려주어야 한다.

  • 같은 쿼리의 useQuery()를 여러 곳에서 사용한다면, 모든 useQuery()에 똑같은 initialData를 설정해주어야 한다.

  • 쿼리가 서버로부터 언제 fetch 되었는 지 정확하게 알 수 없기 때문에, dataUpdatedAt의 시간이나 쿼리 refetching이 필요한 지에 대한 여부는 페이지가 로드된 시점으로부터 계산된다.

  • 특정 쿼리 키로 캐싱된 데이터가 이미 있다면, initialData는 해당 데이터를 절대 덮어쓰지 않는다. 때문에, 이미 캐싱된 데이터가 더 오래 된 것이더라도, getServerSideProps() 함수로 받아 온 데이터는 initialData로 설정이 되기 때문에, 새로운 데이터로 업데이트 할 수 없다.

2. Hydration API 사용하기

import { dehydrate, HydrationBoundary, QueryClient, useQuery } from '@tanstack/react-query'

export async function getStaticProps() {
  const queryClient = new QueryClient()

  await queryClient.prefetchQuery({
    queryKey: ['posts'],
    queryFn: getPosts,
  })

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  }
}

function Posts() {
  const { data } = useQuery({ queryKey: ['posts'], queryFn: getPosts })

  // 이 쿼리는 서버에서 prefetch하지 않는 데이터. 
    // prefetch하는 데이터와 아닌 데이터를 자유롭게 섞어서 활용할 수 있다.
  const { data: commentsData } = useQuery({
    queryKey: ['posts-comments'],
    queryFn: getComments,
  })

  // ...
}

export default PostsRoute({ dehydratedState }) {
  return (
    <HydrationBoundary state={dehydratedState}>
      <Posts />
    </HydrationBoundary>
  )
}

Hydration은 이미 렌더링된 정적인 HTML을 React 코드와 연결해서 동적인 상태로 바꿔주는 것을, Dehydrate는 동적인 것을 다시 정적인 상태로 만드는 작업을 말하는데,

위 코드와 같이, prefetch 한 결과값이 담긴 queryClient를 dehydrate해서 클라이언트로 보내줄 수도 있다.

이렇게 하면, 초기 설정 코드가 늘어나긴 하지만, initialData를 이용하면서 발생하는 여러 단점을 모두 해결할 수 있다.

profile
프론트엔드 개발자가 되기 위해 노력 중인 새싹🌱 입니다.

0개의 댓글