React.js + Next.js/Setting

Next.js 환경에서 React-query 사용하기

진호우 2024. 4. 12. 11:26

Next.js에서 React-query를 사용하려면 React 프로젝트와는 조금 다르게 환경을 세팅해야한다.

13버전 이후부터는 RSC가 도입되어 가장 상위 모듈인 layout에서 QueryProvider로 QueryClient를 props로 내려줄 수 없기 때문.

 

Setting

app router 환경에서 사용.

 

Next.js 애플리케이션의 엔트리 포인트 지점을 ReactQueryStreamedHydration 과 QueryClientProvider 컴포넌트로 감싸주어야 한다.


app에 속하는 하위 컴포넌트들은 기본적으로 서버 컴포넌트로 동작하기 때문에, 에러가 발생할 수 있다. QueryClientProvider는 클라이언트에서 초기화되어야 하기 때문.

따라서 client 사이드에서 동작하는 provider 컴포넌트를 별도로 만들어 초기화해주고, 해당 provider 컴포넌트가 하위 컴포넌트를 감싸도록 해 하위 컴포넌트에 원하는 props를 전달해주도록 한다.

 

app/Providers.tsx

'use client';

import React, { useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental';

export function Providers(props: { children: React.ReactNode }) {
  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            staleTime: 5 * 1000,
          },
        },
      }),
  );

  return (
    <QueryClientProvider client={queryClient}>
      <ReactQueryStreamedHydration>
        {props.children}
      </ReactQueryStreamedHydration>
    </QueryClientProvider>
  );
}

 

app/layout.tsx

import { Providers } from './Providers';

export const metadata = {
  title: 'Next.js',
  description: 'Generated by Next.js',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

 

 

Usage

basic

'use client';
import axios from 'axios';

import { useQuery } from '@tanstack/react-query';

export default function BasicPageTest() {
  const { isPending, error, data, isFetching } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      axios
        .get('https://api.github.com/repos/tannerlinsley/react-query')
        .then((res) => res.data),
  });

  return (
    <>
      <div>
        <h1>Basic page</h1>
        {!isFetching && (
          <>
            <h1>name: {data.name}</h1>
            <p>description: {data.description}</p>
            <strong>subscribers_count 👀: {data.subscribers_count}</strong>
            {' | '}
            <strong>stargazers_count ✨: {data.stargazers_count}</strong>
            {' | '}
            <strong>forks_count 🍴: {data.forks_count}</strong>
          </>
        )}

        <div>{isFetching ? 'Updating...' : ''}</div>
      </div>
    </>
  );
}

 


추가

Axios 설정하기

utils/axios.ts

import axios, { AxiosInstance } from 'axios';
import { getRefreshToken, setSession } from './jwt';
import { API_GATEWAY_URL } from '../config';

// ----------------------------------------------------------------------

// axios 옵션 추가 가능
const axiosInstance: AxiosInstance = axios.create();

axiosInstance.interceptors.response.use(
  (response) => response,
  async (error) => {
    let errData = error?.response?.data;
    let errStatus = errData || false;
    if (
      errStatus &&
      String(error.response.status) === '401' &&
      (errStatus.error === 'JE01' || errStatus.error === 'JE02')
    ) {
      // refresh token 있는지 확인
      if (refreshToken) {
        try {
          // access token 갖고오기
        } catch (error) {
          setSession(null, null);
          // alert('Session expired.\nPlease login again.');
          // 기본 페이지로 이동 로직
        }
      }
    } else {
      return Promise.reject(error);
    }
  }
);

export default axiosInstance;