Next.js app router에서 React Query SSR 설정하기

nyoung·2024년 5월 1일
1

Next.js app라우터와 React-Query를 같이 사용하는 사람들이 보면 좋을 글

Next.js 프로젝트에서 react-query를 세팅하면서 최신 문서로 작성된 SSR처리를 공부하고, 그 내용을 기록 & 공유한 글입니다.

Server Rendering & React Query

서버 렌더링이 무엇일까요?
서버 렌더링은 서버에서 초기의 HTML을 생성하는 행위이고, 그래서 유저가 페이지가 로드 되자마자 어떠한 컨텐츠를 볼 수 있는 것을 의미합니다.

서버사이드 렌더링은 페이지가 SSR을 요구할 때 또는, 빌드 타임에도 일어날 수 있습니다.(SSG)

  • Request Waterfall (CSR방식)
1. |-> Markup (without content)
2.   |-> JS
3.     |-> Query

위와 같이 CSR방식이면, 페이지의 컨텐츠가 전부 보여지기 전 최소 3번 이상의 서버 통신이 일어납니다.

반면, SSR 방식은 아래와 같이 일어납니다.

1. |-> Markup (with content AND initial data)
2.   |-> JS

첫째로, 서버에서 렌더링된 컨텐츠가 보여져서 유저는 컨텐츠를 볼 수 있습니다.
둘째로, 페이지가 인터렉티브하게 변합니다. 첫 번째로 우리가 필요한 초기 데이터를 포함하고 있기 때문에, 데이터를 revalidate 할 필요가 없는 이상 3번 과정은 없어도 됩니다.

서버 사이드 렌더링을 수행하기 위해 서버 쪽에서는,
데이터가 포함된 페이지를 만들기 위해 렌더링하기 전에 데이터를 prefetch해야 합니다.
(QueryClient.prefetch)
그리고 prefetch한 데이터를 dehydrate해서 직렬화할 수 있는 포맷으로 만들어야 합니다.

리액트 쿼리로 Next.js에서 SSR 하는 방법

리액트 쿼리를 Next.js에 적용할 수 있는 방법은 크게 두 가지가 안정적인 버전으로 제공됩니다.
(저는 그 중에서 프리패치 방식을 선호합니다 데이터를 별도 Props로 안넘겨줘도 되기 때문에)

  • initialData
  • prefetch

Quick note on terminology

서버 ≠ 서버 컴포넌트
클라이언트 ≠ 클라이언트 컴포넌트

서버 컴포넌트는 서버에서만 돌아감,
클라이언트 컴포넌트는 둘 다에서 돌아갑니다.(인터렉션, 훅 제외)

Initial setup

// In Next.js, this file would be called: app/providers.jsx
'use client'

// We can not useState or useRef in a server component, which is why we are
// extracting this part out into it's own file with 'use client' on top
import { useState } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

// ServerSide
function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        // With SSR, we usually want to set some default staleTime
        // above 0 to avoid refetching immediately on the client
        staleTime: 60 * 1000,
      },
    },
  })
}

let browserQueryClient: QueryClient | undefined = undefined

function getQueryClient() {
  if (typeof window === 'undefined') {
    // Server: always make a new query client
    return makeQueryClient()
  } else {
    // Browser: make a new query client if we don't already have one
    // This is very important so we don't re-make a new client if React
    // suspends during the initial render. This may not be needed if we
    // have a suspense boundary BELOW the creation of the query client
    if (!browserQueryClient) browserQueryClient = makeQueryClient()
    return browserQueryClient
  }
}

export default function Providers({ children }) {
  // NOTE: Avoid useState when initializing the query client if you don't
  //       have a suspense boundary between this and the code that may
  //       suspend because React will throw away the client on the initial
  //       render if it suspends and there is no boundary
  const queryClient = getQueryClient()

  return (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  )
}

캐싱 여부

최신 버전의 공식 문서 예제에서는 데이터를 가져오는 각 서버 컴포넌트마다 새로운 queryClient를 생성합니다.
이렇게 React의 cache API를 사용해 싱글톤 인스턴스로 만들 수는 있지만, 장단점이 존재합니다.

// app/getQueryClient.jsx
import { QueryClient } from '@tanstack/react-query'
import { cache } from 'react'

const getQueryClient = cache(() => new QueryClient())
export default getQueryClient

위의 방식의 장점은, 서버 컴포넌트에서 호출되는 어디에서나 getQueryClient()를 호출하여 이 클라이언트를 사용할 수 있다는 것입니다.
단점은 dehydrate(getQueryClient())를 호출할 때마다 queryClient 전체를 직렬화하는데, 이는 이미 직렬화된 쿼리를 포함하고 현재 서버 컴포넌트와 관련이 없는 불필요한 오버헤드가 될 수 있습니다.

Next.js는 이미 fetch()를 사용하는 요청을 중복 제거하지만, queryFn에서 다른 것을 사용하거나 자동으로 이러한 요청을 중복 제거하지 않는 프레임워크를 사용하는 경우 위에서 설명한 대로 단일 queryClient를 사용하는 것이 의미가 있을 수 있습니다.

이 내용에 대해서는 처음엔 잘 이해가 가지 않았지만, QueryClient를 매번 생성한다 -> 매번 새로운 요청을 보낸다 -> Next.js는 fetch 요청을 중복 제거한다 -> 매번 새로운 QueryClient를 생성해도 무리가 없다. 는 내용으로 받아들였습니다.

별개로, react-query docs별로 내용이 다른게 많기 때문에, 꼭 URL을 확인해서 최신 버전 혹은 자기가 사용하고 있는 버전의 docs가 맞는지 확인해야 할 것 같습니다😅

참고한 글 & 더 알아보면 좋을 글
next.js와 react-query : https://github.com/wpcodevo/nextjs13-react-query
아직 리액트 쿼리를 사용하는 이유 : https://www.youtube.com/watch?v=9kjc6SWxBIA

react-query ssr 설정법 :
https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#server-components--nextjs-app-router

https://tanstack.com/query/latest/docs/framework/react/guides/ssr#using-the-hydration-apis

profile
코드는 죄가 없다,,

0개의 댓글

관련 채용 정보