개발 환경
next: v14.2.15
@tanstack/react-query: v5.60.6
@tanstack/react-query-devtools: v5.60.6
오늘은 개인 프로젝트에 Tanstack Query(React Query)를 적용하는 과정 중에 여러 코드에서 볼 수 있던 QueryClient에 useState를 적용하는 이유에 대해서 작성해보고자 한다.
📙 설치
npm i @tanstack/react-query
yarn add @tanstack/react-query
📙 적용
🟡 Provider 설정
NextJS에서는 use client 로 Provider를 클라이언트 컴포넌트로 변경해주어야 에러가 발생하지 않는다.
// RQProvider.tsx
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
type Props = {
children: React.ReactNode;
};
export default function RQProvider({ children }: Props) {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: false,
},
},
});
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools />
</QueryClientProvider>
);
}
첫 단계로는, 애플리케이션에 QueryClient을 연결하고 제공하기 위해 Provider를 설정해주어야 한다.
Query Client
Query Client는 캐시와 상호 작용하는 데 사용한다.
defaultOptions
를 사용하여 모든 쿼리와 뮤테이션에 대한 기본 값을 정의할 수 있다.
refetchOnWindowFocus
- default: true
- 화면에 초점을 다시 맞출 때 쿼리가 데이터 패치를 재시도할 것인지에 대한 여부
refetchOnReconnect
- default: true
- 네트워크 연결이 다시 설정될 때 쿼리가 데이터 패치를 재시도할 것인지에 대한 여부
retry
- 요청 실패 시 쿼리나 뮤테이션을 몇 번 재시도해야 하는지 지정
- 쿼리는 기본적으로 3번, 뮤테이션은 자동으로 재시도되지 않는다.
- 재시도를 비활성하려면
false
를, 무한 재시도는true
로 설정한다.
🟡 QueryClient 생성 시 useState 는 왜 사용할까?
function App() {
const [queryClient] = useState(() => new QueryClient())
return (
<QueryClientProvider client={queryClient}>
<Home />
</QueryClientProvider>
)
}
여러 프로젝트 코드를 보면 useState를 사용하여 Query Client를 생성한 코드를 볼 수 있었다.
기존 코드와 어떤 차이점이 있을까?
기존 코드는 Provider 컴포넌트 안에서 Query Client를 생성하고 있었다.
// 기존 코드
export default function RQProvider({ children }: Props) {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: false,
},
},
});
이는 Quey Client가 일반 변수로 선언되어 렌더링이 발생할 때마다 queryClient
가 새로 생성된다.
따라서 공식 문서에서도 참고할 수 있듯이 위와 같은 패턴은 안티 패턴으로 정의하고 있다.
이를 해결하기 위해서는 3가지 방법이 존재한다.
1. useState 사용
function App() {
const [queryClient] = useState(() => new QueryClient())
return (
<QueryClientProvider client={queryClient}>
<Home />
</QueryClientProvider>
)
}
2. 컴포넌트 외부에 정의
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Home />
</QueryClientProvider>
)
}
3. prefetchQuery() 사용
async function App() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(options)
}
3가지 모두 Query Client 인스턴스가 한번만 생성된다.
prefetchQuery
의 경우 캐시에 해당 데이터가 있다면, 캐시에 있는 데이터를 반환하여 추가로 생성하지 않게 된다.
나는 이 중 useState
를 사용하는 방식을 채택했다.
export default function RQProvider({ children }: Props) {
const [queryClient] = useState(
new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: false,
},
},
}),
);
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools />
</QueryClientProvider>
);
}
useState
를 선택한 이유는 제일 리액트스럽다 생각했기 때문이다.
useState
를 통해 Query Client 인스턴스를 생성하면 React 컴포넌트의 상태로 관리하게 된다.
React의 상태 초기화는 첫 렌더링 시 한 번만 수행되어 컴포넌트의 렌더링 간에 같은 인스턴스를 유지하도록 보장하기 때문에 인스턴스가 한번만 생성되는 것이다.
🟡 적당한 위치에 Provider 적용시키기
import { ReactNode } from "react";
import RQProvider from "./_component/RQProvider";
import Header from "@/_component/common/Header";
export default function HeaderLayout({ children }: { children: ReactNode }) {
return (
<>
<Header />
<RQProvider>{children}</RQProvider>
</>
);
}
쿼리 패칭이 필요한 부분에 Provider를 적용시킨다.
📙 Devtools
npm i @tanstack/react-query-devtools
yarn add @tanstack/react-query-devtools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
Devtools는 기본적으로 process.env.NODE_ENV === 'development'
인 경우에만 번들에 포함되어 개발 환경에서만 동작한다.
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
function App() {
return (
<QueryClientProvider client={queryClient}>
{/* The rest of your application */}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
initialIsOpen
- default: false
- Devtools가 기본적으로 열려 있게 하려면
true
로 설정한다.
그 외에도 Devtools를 열고 닫을 수 있는 로고 버튼의 위치, 패널의 위치도 변경할 수 있고, QueryClient를 여러개 사용할 경우 Devtools를 사용할 QueryClient를 설정하는 등 여러가지 옵션을 지정할 수 있다.
Devtools를 적용하면 기본적으로는 화면 우측 하단에 Devtools Pannel을 열고 닫을 수 있는 버튼이 생성되며, 버튼을 누르면 패널이 열려서 쿼리 패칭 동작을 시각적으로 볼 수 있다.
그동안 프로젝트에서 Tanstack Query를 여러번 적용해봤었지만 useState를 통해 적용해본 것은 처음이라 왜 많은 코더들이 이렇게 적용하는 것인지 궁금증이 생겨 한번 알아보게 되었다.
코딩을 할 때 여러 방면에서 최적화된 코드에 대해 고민하면서 코드를 작성해야 함을 깨닫게 되는 과정이었다 ! :)
'Retrospective > Project' 카테고리의 다른 글
[TUI Editor] NextJS 14에서 Toast UI Editor 적용하기 (0) | 2025.01.11 |
---|---|
[TailwindCSS] Tailwind CSS 동적 스타일링 적용법 (+ 주의사항) (0) | 2024.11.02 |
[AWS] AWS Lambda + S3 연동해서 파일 가져오기 (0) | 2023.11.15 |
[Vuepress] Vuepress 설치부터 배포 자동화 적용하기 (0) | 2023.09.19 |
[React] 상품/장바구니 수량 조절 기능 구현하기 (0) | 2023.06.09 |