Usage with Next.js

App Router

서버 컴포넌트

Next.js의 App Router에서는 모든 컴포넌트가 기본적으로 React Server Component(RSC) 입니다. 따라서 RSC에서는 SWR의 SWRConfig와 키 직렬화(Key Serialization) API를 가져올 수 있습니다.

import { unstable_serialize } from 'swr' // ✅ 서버 컴포넌트에서 사용 가능합니다.
import { unstable_serialize as infinite_unstable_serialize } from 'swr/infinite' // ✅ 서버 컴포넌트에서 사용 가능합니다.

import { SWRConfig } from 'swr' // ✅ 서버 컴포넌트에서 사용 가능합니다.

hook API는 RSC에서 사용할 수 없으므로 SWR에서 가져올 수 없습니다.

import useSWR from 'swr' // ❌ 서버 컴포넌트에서 사용할 수 없습니다.
import useSWRInfinite from 'swr/infinite' // ❌ 서버 컴포넌트에서 사용할 수 없습니다.
import useSWRMutation from 'swr/mutation' // ❌ 서버 컴포넌트에서 사용할 수 없습니다.

클라이언트 컴포넌트

컴포넌트에 'use client' 지시어를 추가하거나 클라이언트 컴포넌트에서 SWR을 가져오면, SWR의 클라이언트 데이터 페칭 훅을 사용할 수 있습니다.

'use client'

import useSWR from 'swr'

export default function Page() {
  const { data } = useSWR('/api/user', fetcher)
  return <h1>{data.name}</h1>
}

서버 컴포넌트에서 데이터 프리페치하기

기본 데이터를 이용한 사전 렌더링 패턴과 유사하게, React Server Components (RSC)를 사용하면 더 나아갈 수 있습니다.

서버 사이드에서 데이터 프리페칭을__시작__하고, <SWRConfig> provider의 fallback 옵션을 통해 클라이언트 컴포넌트 트리에 __promise__를 전달할 수 있습니다:

import { SWRConfig } from 'swr'

export default async function Layout({ children }: { children: React.ReactNode }) {
  // 서버 사이드에서 데이터 페칭을 시작합니다.
  const userPromise = fetchUserFromAPI()
  const postsPromise = fetchPostsFromAPI()

  return (
    <SWRConfig
      value={{
        fallback: {
          // promise를 클라이언트 컴포넌트에 전달합니다.
          '/api/user': userPromise,
          '/api/posts': postsPromise,
        },
      }}
    >
      {children}
    </SWRConfig>
  )
}

두 데이터 페칭 함수 호출 fetchUserFromAPI()fetchPostsFromAPI() 는 즉시 await 하지 않기 때문에 서버 사이드에서 병렬로 실행됩니다.

React Server Components에서는 "use client" 경계를 넘어 promise를 전달할 수 있으며, SWR은 서버 사이드 렌더링 중에 이를 자동으로 해석합니다:

'use client'

import useSWR from 'swr'

export default function Page() {
  // SWR은 서버 컴포넌트에서 전달된 promise를 해석합니다.
  // SSR과 클라이언트 hydration 중에 `user`와 `posts` 모두 준비됩니다.
  const { data: user } = useSWR('/api/user', fetcher)
  const { data: posts } = useSWR('/api/posts', fetcher)

  return (
    <div>
      <h1>{user.name}'s Posts</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  )
}

그런 다음 클라이언트 측에서 SWR이 제어권을 가져와 평소처럼 동작을 유지합니다.

서버 컴포넌트에서 클라이언트 컴포넌트로 promise를 전달함으로써, 데이터 페칭을 가능한 한 빨리 서버 측에서 시작할 수 있습니다. 그리고 실제로 데이터를 사용하는 UI 경계(가장 가까운 Suspense 경계 또는 Next.js layout)만 스트리밍 SSR 중에 차단됩니다.

애플리케이션에서 이 프리페치 패턴을 점진적으로 채택하려면 strictServerPrefetchWarning 옵션을 활성화할 수 있습니다. 이는 키에 미리 채워진 데이터가 제공되지 않은 경우 콘솔에 경고 메시지를 표시하여 어떤 데이터 페칭 호출이 서버 측 프리페칭의 이점을 얻을 수 있는지 식별하는 데 도움을 줍니다.

클라이언트 사이드 데이터 페칭

페이지에 자주 변경되는 데이터가 포함되어 있고, 이를 사전 렌더링할 필요가 없다면, SWR이 최적의 선택입니다. 별도의 설정 없이 useSWR을 가져와서 데이터가 필요한 컴포넌트에서 훅을 사용하면 됩니다.

동작 방식:

  • 먼저, 데이터를 제외한 페이지를 즉시 렌더링합니다. 이때, 로딩 상태를 표시할 수 있습니다.
  • 클라이언트에서 데이터를 가져온 후, 준비되면 화면에 표시합니다.

이 방식은 대시보드와 같은 사용자 전용 페이지에 적합합니다. 대시보드는 개인화된 페이지이므로 SEO가 중요하지 않으며, 사전 렌더링이 필요하지 않습니다. 또한, 데이터가 자주 변경되므로 요청 시점에 데이터를 가져오는 방식이 요구됩니다.

기본 데이터를 이용한 사전 렌더링

페이지를 사전 렌더링해야 하는 경우, Next.js는 두 가지 방식을 지원합니다: **정적 생성(SSG)**과 서버 사이드 렌더링(SSR)

SWR과 함께 사용하면, SEO를 위한 사전 렌더링과 더불어 클라이언트 측 캐싱, 재검증, 포커스 감지, 주기적인 데이터 갱신 같은 기능도 활용할 수 있습니다.

사전 패칭된 데이터를 SWR 훅의 초기 값으로 설정하려면, SWRConfigfallback 옵션을 사용할 수 있습니다.

getStaticProps에 대한 예시입니다:

 export async function getStaticProps () {
  // `getStaticProps`는 서버 측에서 실행됩니다.
  const article = await getArticleFromAPI()
  return {
    props: {
      fallback: {
        '/api/article': article
      }
    }
  }
}

function Article() {
  // `data`는 `fallback`에 있으므로 항상 사용할 수 있습니다.
  const { data } = useSWR('/api/article', fetcher)
  return <h1>{data.title}</h1>
}

export default function Page({ fallback }) {
  // `SWRConfig` 범위 안에 있는 SWR 훅은 해당 값을 사용하게 됩니다.
  return (
    <SWRConfig value={{ fallback }}>
      <Article />
    </SWRConfig>
  )
}

The page is still pre-rendered. It's SEO friendly, fast to response, but also fully powered by SWR on the client side. The data can be dynamic and self-updated over time. 페이지는 여전히 사전 렌더링됩니다. SEO 친화적이고 응답 속도가 빠를 뿐만 아니라 클라이언트 측에서 SWR에 의해 완전히 구동됩니다. 데이터는 시간이 지남에 따라 동적으로 자체 업데이트될 수 있습니다.

Article 컴포넌트는 미리 생성된 데이터를 사전 렌더링하고 페이지가 업데이트된 후 최신 데이터를 다시 가져와 최신 상태로 유지합니다.

복합 키

useSWRarray 또는 function 형태의 키와 함께 사용할 수 있습니다. 이러한 키를 사용할 때 사전 패칭된 데이터를 적용하려면, unstable_serialize을 사용하여 fallback 키를 직렬화해야 합니다.

import useSWR, { unstable_serialize } from 'swr'

export async function getStaticProps () {
  const article = await getArticleFromAPI(1)
  return {
    props: {
      fallback: {
        // unstable_serialize() 배열 형태의 키
        [unstable_serialize(['api', 'article', 1])]: article,
      }
    }
  }
}

function Article() {
  // 배열 형태의 키를 사용하세요.
  const { data } = useSWR(['api', 'article', 1], fetcher)
  return <h1>{data.title}</h1>
}

export default function Page({ fallback }) {
  return (
    <SWRConfig value={{ fallback }}>
      <Article />
    </SWRConfig>
  )
}