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

Next js(App router)에서 React Query를 사용하는 방법에 대해 알아봅니다.


들어가기에 앞서, 이 글은 Next.js 14 App router 및 React Query v5를 사용하는 경우에 대해 설명합니다.

목차

  1. Next js App router에서 React Query 설치 및 세팅
  2. React Query의 단점

1. Next js App router에서 React Query 설치 및 세팅

npm i @tanstack/react-query
 
# Devtools가 필요하다면
npm i @tanstack/react-query-devtools

React Query를 Next js App router에서 사용하기 위해서는 따로 Provider를 추가해주어야 합니다.

폴더 구조는 다음과 같이 구성하였습니다.

root & src/
  ├── app/
  ├── providers/
  │   ├── TanStackProvider.tsx
  ...

providers 폴더를 만들고 TanStackProvider.tsx 파일을 생성합니다.

providers/TanStackProvider.tsx
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
 
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { ReactNode } from 'react';
 
export default function TanStackProvider({
  children,
}: {
  children: ReactNode;
}) {
  const queryClient = new QueryClient();
 
  return (
    <QueryClientProvider client={queryClient}>
      {children}
      <ReactQueryDevtools /> {/* Devtools가 필요하다면 */}
    </QueryClientProvider>
  );
}

그리고 layout.tsx 파일에서 Provider를 추가합니다.

app/layout.tsx
// ... 생략
<html lang="en">
  <body>
    <TanStackProvider>{children}</TanStackProvider>
  </body>
</html>

이제 페이제 어디에서든지 React Query를 사용할 수 있습니다.

간단한 예시를 만들어 보겠습니다.

post API를 호출하는 함수를 만들어보겠습니다.

next js에서 fetch API는 기본적으로 캐싱이 되기 때문에 cache: no-store를 사용하여 캐싱을 막아 줍니다.
lib/posts.ts
export const fetchPost = async (): Promise<Post[]> => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
    cache: 'no-store',
  });
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

그리고 해당 함수를 사용하여 데이터를 가져오는 컴포넌트를 만들어보겠습니다.

App router에서 React query를 사용하기 위해서는 반드시 파일 최상단 'use client'가 필요합니다.
page.tsx
'use client'; // 반드시 최상단에 위치해야 합니다.
import { useQuery } from '@tanstack/react-query';
import { fetchPost } from 'lib/posts';
 
export default function Home() {
  const { data, isError, isLoading, error } = useQuery({
    queryKey: ['posts'],
    queryFn: fetchPost,
  });
 
  if (isLoading) return <div>Loading...</div>;
  if (isError) return <div>{error.toString()}</div>;
 
  return (
    <div>
      <h1>QueryPosts</h1>
      {data?.map((post) => (
        <div key={post.id}>
          <div>{post.userId}</div>
          <div>{post.title}</div>
          <div>{post.body}</div>
        </div>
      ))}
    </div>
  );
}

page.tsx에서 'use client'를 사용하면 Next js App router의 서버 사이드 랜더링 효과를 얻지 못합니다.

page.tsx는 layout.tsx에서 사용하는 페이지입니다. page.tsx에서 'use client'를 사용하면 하위 컴포넌트 모두에 영향을 미칩니다.

이를 해결하기 위해서 데이터 가져오는 컴포넌트를 생성하고 해당 컴포넌트를 사용하여 데이터를 가져오는 방법을 사용합니다.

components/QueryPosts.tsx
'use client'; // 반드시 최상단에 위치해야 합니다.
import { useQuery } from '@tanstack/react-query';
import { fetchPost } from 'lib/posts';
 
export default function QueryPosts() {
  const { data, isError, isLoading, error } = useQuery({
    queryKey: ['posts'],
    queryFn: fetchPost,
  });
 
  if (isLoading) return <div>Loading...</div>;
  if (isError) return <div>{error.toString()}</div>;
 
  return (
    // ... 이하 동일
  )
}

이제 page.tsx에서 QueryPosts 컴포넌트를 사용하여 데이터를 가져옵니다.

page.tsx
// 기존 있던 'use client'를 제거합니다.
import QueryPosts from 'components/QueryPosts';
 
export default function Home() {
  return (
    <div>
      <h1>QueryPosts</h1>
      <QueryPosts />
    </div>
  );
}

useMutation도 동일한 방식으로 사용할 수 있습니다.

2. React Query의 단점

React Query는 클라이언트 사이드에서 데이터를 캐싱하고 관리하는데 사용됩니다.

간단한 예시로 jsonplaceholder API를 사용하여 데이터를 가져와봤습니다.

가져온 데이터 목록

가져온 데이터 목록

브라우저에서 우클릭 -> 페이지 소스보기를 클릭하여 가져온 데이터를 확인해보면, 데이터가 노출되지 않습니다.

데이터가 노출되지 않음

데이터가 노출되지 않음

Loading... 이라는 글자만 노출되고 가져온 데이터는 노출되지 않습니다.

구글에서는 CSR 방식으로 데이터를 가져오는 경우, SEO를 지원한다곤 하였으나, 네이버, 다음, 빙 등 다른 검색엔진의 SEO를 고려해야 하기때문에 CSR 방식으로 데이터를 가져오는 경우, SEO에 미치는 영향을 고려해야 합니다.

고로 SEO를 고려해야 하는 경우, Next fetch API를 사용하여 데이터를 가져오는 방법을 사용하는 것이 좋습니다.

React Query를 사용하기 좋은 경우는 개인화된 데이터를 가져오는 경우, Admin 등 사용자가 로그인을 하여 가져오는 데이터는 SEO가 필요하지 않기 때문에 React Query를 사용해도 무방합니다.

단, 사용자가 로그인하여 개인화된 블로그 글 같은 SEO가 필요한 경우, SEO에 미치는 영향을 고려해야 합니다. 이때는 Next fetch API를 사용하여 데이터를 가져오는 것이 좋습니다.

만약, Next.js에서 React Query를 사용하여 SSR - 서버사이드 렌더링을 하고 싶다면, 다음 글을 참고해주세요.

`공식문서 - Advanced Server Rendering`

마지막으로

개인화가 되어있는 사용자 정보가 있는, 노출되어선 안되는 페이지는 next fetch나 react-query를 어느것을 사용하더라도 반드시 크롤러가 접근하지 못하도록 robots.txt 파일 설정(Disallow)을 하고, html meta tag에 noindex, nofollow를 추가해주세요.


관련 태그