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;