Next.js 세 번째 복습으로 react-query를 선택했다. react-query는 React에서 사용해본 경험은 있지만 Next.js에서 서버 컴포넌트와 클라이언트 컴포넌트에서 사용하는 방식이 달라서 이를 기록하고자 했다.
그 전에 React-Query를 사용 하는 이유에 대해 생각해보자.
1) 서버의 데이터를 가져오는 것 / 리덕스는 컴포넌트끼리 데이터 공유 할 때 사용
2) 캐싱이 가능해서 트래픽 줄여줌
3) query.getClient로 데이터 공유도 가능
4) React-Query로 캐싱 후 zustand로 데이터 공유 자주 사용
5) 로딩 , 에러 , 펜딩 , 성공 같은 인터페이스 표준화 지원
다음으로 Next.js에서 React-Query를 사용하는 방법은 크게 두 가지가 있다고 한다.
1. initalData
2. 서버에서 캐시를 Dehydrate 후 클라이언트에서 Hydrate 하는 방식
이 강의에서는 2번째 방법을 사용했다.
Hydrate : 서버에서 불러온 데이터를 클라이언트에서 형식 그대로 받아오는 것
순서는 아래와 같다.
1. RQProvider.tsx로 리액트 쿼리 설정 만들어줌(리액트와 동일) 다만 차이는 React에서는 App.tsx나 index.tsx를 감싸줬다면 얘는 클라이언트 컴포넌트이기 때문에 따로 서버 컴포넌트와 구분을 위해 컴포넌트화 해줬다.
"use client";
import React, { useState } from "react";
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 [client] = useState(
new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: false,
},
},
})
);
return (
<QueryClientProvider client={client}>
{children}
<ReactQueryDevtools
initialIsOpen={process.env.NEXT_PUBLIC_NODE === "local"}
/>
</QueryClientProvider>
);
}
2. 서버에서 prefetching 해서 Dehydrate 후 클라이언트에서 hydrate 해주는 과정
: 아래 코드는 무한스크롤 구현된 것인데 queryClient.prefetching으로 바꿔주면 클라이언트 컴포넌트에서 쓰이는 useQuery와 같은 역할을 한다.
import TabDecider from "@/app/(afterLogin)/home/_component/TabDecider";
import {
dehydrate,
HydrationBoundary,
QueryClient,
} from "@tanstack/react-query";
import { getPostRecommends } from "@/app/(afterLogin)/home/_lib/getPostRecommends";
export default async function TabDeciderSuspense() {
const queryClient = new QueryClient();
await queryClient.prefetchInfiniteQuery({
queryKey: ["posts", "recommends"],
queryFn: getPostRecommends,
initialPageParam: 0,
});
const dehydratedState = dehydrate(queryClient);
return (
<HydrationBoundary state={dehydratedState}>
<TabDecider />
</HydrationBoundary>
);
}
3. 클라이언트 컴포넌트에서의 React-Query
const { data, fetchNextPage, hasNextPage, isFetching, isPending, isError } =
useInfiniteQuery<
IPost[],
Object,
InfiniteData<IPost[]>,
[_1: string, _2: string],
number
>({
queryKey: ["posts", "recommends"],
queryFn: getPostRecommends,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.at(-1)?.postId,
staleTime: 60 * 1000, // 1분
gcTime: 300 * 1000,
});
const { ref, inView } = useInView({
threshold: 0, // div가 보이고 몇 px 움직여야 이벤트가 실행 될 것인가?
delay: 0.5, // 몇 초후에 이벤트를 실행 시킬 것인가?
});
useEffect(() => {
if (inView) {
!isFetching && hasNextPage && fetchNextPage();
}
}, [inView, isFetching, hasNextPage, fetchNextPage]);
4. 구성 요소 설명
1) queryKey : 이 키를 갖고 있을 때는 항상 queryFn을 실행해라
2) queryClient.getQueryData로 데이터를 불러올 수도 있다.
3) initalPageParam: 처음 페이지
4) getNextPageParam: 다음 페이지를 보여주고 싶을 때 사용
5) inview => useInView 라는 react-intersection-observer 라이브러리로 스크롤 위치 파악 후
<div ref={ref} /> 을 통해 다음 페이지 위치 계산 가능
5. 기타 알아두면 좋을 것들
1) Props 있으면 <>가 아닌 <Fragment> , 없으면 <> 사용 가능
2) Suspense : 로딩이 끝나기 전까지 보여주는 컴포넌트 계층
'CS 대비 > FrontEnd Part' 카테고리의 다른 글
[React] React Hooks 정리 (0) | 2023.12.23 |
---|---|
[Next.js] Next.js 복습 (2) - parallel Route & intercepting Route , routing handler (1) | 2023.12.22 |
[Next.js] Next.js 복습 (1) - Next.js 소개 ~ style (1) | 2023.12.22 |
[React] 전역 상태 관리 - Recoil (0) | 2023.11.03 |
[React] 전역 상태 관리 - Redux (1) | 2023.11.03 |