- Published on
ReactQuery in Nextjs
- Authors
- Name
- Inhwan Cho
React Query 설치하기
# 리액트 쿼리
$ npm i @tanstack/react-query
# 디버깅을 위한 dev-tool
$ npm i @tanstack/react-query-devtools
# eslint 필요 시 아래도 설치
$ npm i -D @tanstack/eslint-plugin-query
# nextjs에서 스트림 사용시 아래도 설치(실험 버전 - 코드 간략화 됨)
$ npm i @tanstack/react-query-next-experimental
React Query vs SWR
React Query
와 SWR
은 모두 데이터 페칭, 캐싱, 동기화를 쉽게 관리할 수 있게 도와주는 React 라이브러리입니다.
기능 | Tanstack Query | SWR |
---|---|---|
데이터 페칭 및 캐싱 | 자동 캐싱과 데이터 재사용, 백그라운드 업데이트 지원 | 자동 캐싱 및 재검증으로 최신 상태 유지 |
서버 상태 동기화 | 다양한 캐싱 전략과 상세 설정 가능 | 데이터 변화 시 자동 재검증으로 동기화 |
백그라운드 업데이트 | 지원, 구성에 따라 세밀하게 조절 가능 | 지원, 데이터 포커스 재검증 등을 통해 최신 상태 유지 |
페이지네이션 및 무한 스크롤 | 구성이 용이한 훅 제공 | 무한 로딩 훅 (useSWRInfinite) 제공 |
쿼리 취소 및 실패 관리 | 요청 취소와 재시도 관리 가능 | 요청 재시도는 지원하지만 취소는 제한적 |
사용 용이성 | 광범위한 기능 제공, 세밀한 설정 가능 | 단순하고 직관적인 API, 쉬운 시작 |
데이터 패칭에 세밀한 제어가 필요하지 않다면, SWR
의 간단하고 빠른 접근 방식이 더 적합할 수 있습니다.
반면, React Query
에는 강력한 디버깅 툴(dev-tools)과 복잡한 데이터 관리와 캐싱 전략이 필요한 경우 좋은 선택이 될 수 있습니다.
React Query는 TanStack 데이터 관리 라이브러리에 포함된 일부로 TanStack Query로 브랜드가 변경되었습니다. 즉, react-query == tanstack-query라고 생각하시면 됩니다.
Nextjs에서 Tanstack Query 사용하기
prefetching 방법 정하기
prefetching 방법에는 두 가지가 있습니다
initialData
: 서버에서 데이터를 미리 가져오고 이를 클라이언트 컴포넌트로 prop으로 전달합니다. 설정이 간단하며 빠르게 적용할 수 있지만, prop drilling이 필요할 수 있습니다.<Hydrate>
: 서버에서 데이터를 미리 가져오고, 이 데이터를dehydrate
하여 클라이언트에서rehydrate
합니다. 더 복잡한 설정을 요구하지만, 서버에서 prefetch된 시점을 기준으로 데이터를 관리합니다.
v5에서는 <HydrationBoundary>
으로 용어가 변경되었습니다
용어 설명
Prop Drilling
: React에서 데이터를 부모 컴포넌트에서 자식 컴포넌트로 계속해서 전달하는 것을 말합니다.hydrate
: Nextjs에서는 초기 HTML 파일(더미 HTML)을 먼저 전달하고 이후, HTML 요소들을 React 컴포넌트로 변환 및 이벤트리스너 등을 부착하여 React DOM에서 관리하게 하는데 이 과정을Hydration
이라고 합니다.
즉, 기본 HTML을 먼저 받고, 추후 js를 이용하여 interactive하게 변경하는걸 Hydration이라 합니다.
use client
가 적힌 컴포넌트를 CSR이라 하는데 이 CSR만 Hydration 작업을 수행하게됩니다. 즉 use client
가 적힌 컴포넌트는 interactive 하다는 의미이지, client에서 rendering 된다는 의미가 아닙니다. backend에서 rendering되고 frontend에서 Hydrate됩니다.
- QueryClientProvider 설정 및 컴포넌트 감싸기
'use client'
import { useState } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
// 디버깅 필요 시
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
// reretching time setting
staleTime: 60 * 1000,
},
},
})
}
let browserQueryClient: QueryClient | undefined = undefined
// Server: 항상 새로운 쿼리 생성
// Browser: 클라이언트가 없으면 새 쿼리 생성
function getQueryClient() {
if (typeof window === 'undefined') {
return makeQueryClient()
} else {
if (!browserQueryClient) browserQueryClient = makeQueryClient()
return browserQueryClient
}
}
export default function Providers({ children }) {
// 공식문서에서는 suspense를 사용하지 않는다면 useState를 사용하지 말라고 권고 하고 있습니다.
const queryClient = getQueryClient()
return <QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
}
import Providers from '@/providers'
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}
'use client'
import { useQuery } from "@tanstack/react-query";
export default function Page() {
//
const { data, isLoading } = useQuery({
queryKey: ["stream-hydrate-users"],
queryFn: () => fetch('https://jsonplaceholder.typicode.com/todos').then((res) => res.json())
});
const { data as SuspenseData } = useSuspenseQuery({'동일 값'})
return // ...
{ isLoading ? '...' : data.title }
<Suspense
fallback={
<p>loading...</p>
}>
<DataComponent/> //SuspenseData
</Suspense>
}
- 단,
Suspense
를 사용할땐, v4 이하에선useQuries
, v5부터는useSuspenseQuery
를 사용하면 병렬로 호출 할 수 있습니다.
Client Stream Hydration 사용하기
- Nextjs에서 리액트 쿼리를 사용하는 이유 중 하나가 바로 캐싱 기능이었는데, Next.js 13이후 에서 cache 옵션을 사용해 렌더링 방식을 적용하도록 하기때문에 비교적 간단한
SWR
을 많이 사용합니다.
Client Stream Hydration
이라는 기능이 나왔습니다.
설정 방법은 기존과 유사하나 컴포넌트를 QueryClientProvider로
감싸기 전에 ReactQueryStreamedHydration도
감싸야 합니다.
// 나머지는 기존과 동일
import { ReactQueryStreamedHydration } from "@tanstack/react-query-next-experimental";
return (
<QueryClientProvider client={queryClient}>
<ReactQueryStreamedHydration>
{children}
</ReactQueryStreamedHydration>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
그리고 data를 client에서 불러올때 suspense : true
를 해줘야 합니다.
'use client'
import { useQuery } from "@tanstack/react-query";
export default function Page() {
const { data } = useQuery<Todo[]>({
queryKey: ["stream-hydrate-users"],
queryFn: () => fetch('https://jsonplaceholder.typicode.com/todos').then((res) => res.json()),
suspense: true, // true 설정
staleTime: 5 * 1000,
});
return // ...
<Suspense
fallback={
<p>loading...</p>
}>
<DataComponent>
</Suspense>
}
단, 이 기능을 사용하면 SEO에서 불리합니다.
그래서 Infinity scroll 같은 복잡한 기능이 필요없다면, Nextjs app-router의 경우, 굳이 react-query만 사용할 필요는 없고,
데이터를 가져올때는 react-query
를 사용하여 서버 컴포넌트로 가져오고, 데이터 전달은 server action
을 사용해도 됩니다.