[Tanstack query] SSR + prefetch

개잼·2026년 1월 12일

1. 인사

안녕하세요. 오늘은 제가 즐겨 사용하는 Framework인 Nextjs에서 tanstack query를 맛있게 사용하는 방법에 대해 작성해보려고 합니다.


2. Tanstack query

옛날부터 Tanstack query의 존재는 알고 있었지만, 정작 사용하진 않고 있었습니다. 이번에 공부할겸 적절한 기회가 있어서 맛을 좀 봤는데, 왜 사람들이 tanstack query에 대해 자주 언급하는지 바로 이해가 되더군요.


3. 배경

이전에는 Nextjs를 사용했음에도 불구하고 Client Component에서만 useQuery를 이용하여 데이터를 fetch했었습니다. 그러나, Nextjs 특성상, Server Component를 통해 미리 fetch를 할 수 있는데 그럼 어떤식으로 코드를 작성해야하지?에 대해 의문이 생겼습니다.
이에 관해 tanstack query 사용 시, 어떤식으로 코드를 작성할 수 있는지에 대해 찾아보았습니다.


4. 공식문서

역시나 해답은 공식문서에 있었고 이에 관해 곧바로 적용할 수 있었습니다.

공식문서

// 앞서서 provider로 감싸는 작업은 알고 있다고 가정하겠습니다.
// app/posts/page.tsx
import {
  dehydrate,
  HydrationBoundary,
  QueryClient,
} from '@tanstack/react-query'
import Posts from './posts'

export default async function PostsPage() {
  const queryClient = new QueryClient()

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

  return (
    // Neat! Serialization is now as easy as passing props.
    // HydrationBoundary is a Client Component, so hydration will happen there.
    <HydrationBoundary state={dehydrate(queryClient)}>
      <Posts />
    </HydrationBoundary>
  )
}

보시는 바와 같이 먼저 Server Component에서 prefetchQuery를 이용하여 해당 값에 대한 fetch를 진행하고 그것을 Client component에 내려줍니다.

// app/posts/posts.tsx
'use client'

export default function Posts() {
  // This useQuery could just as well happen in some deeper
  // child to <Posts>, data will be available immediately either way
  const { data } = useQuery({
    queryKey: ['posts'],
    queryFn: () => getPosts(),
  })

}

그렇게 함으로써, 사실상 useQuery의 data는 이미 Server Component에서 값이 할당되어 있었고 우리는 client component 환경에서 가져다 쓰기만 하면 된다는 것입니다. 이에 따라 실질적으로 Data Fetch에 대한 로딩 시간이 클라이언트에서는 없게 됩니다.
(이미 Server Component에서 전부 진행했으므로)


5. TIPS

공식문서 상에는 아래의 코드처럼 나타나 있습니다.

// app/posts/posts.tsx
'use client'

export default function Posts() {
  // This useQuery could just as well happen in some deeper
  // child to <Posts>, data will be available immediately either way
  const { data } = useQuery({
    queryKey: ['posts'],
    queryFn: () => getPosts(),
  })

}

하지만 여기에는 staleTime을 지정해 주어야 합니다. 그 이유는 Server Component에서 prefetch를 진행했을 때, staleTime이 0(default value)이라면, 클라이언트는 prefetch를 통해 데이터를 받았을지라도 상했다고 판단하여 백그라운드에서 다시 요청(Refresh)을 합니다.

즉, staleTime을 지정하지 않으면 Client Component에서 이미 Server Component로부터 받아온 데이터가 있음에도 불구하고 바로 Refresh를 한다는 것이죠. 이러면 prefetch를 진행하는 의미가 퇴색됩니다.

따라서 서버에서 가져온 데이터를 바로 Refresh하려는 의도가 아니라면 staleTime을 지정해주는 것이 좋습니다.


profile
천천히 나아가는 중

0개의 댓글