react query
react-query를 사용하는 이유
- React Application에서 서버상태를 불러오고, 캐싱하며, 지속적으로 동기화하고 업데이트하는 작업을 도와주는 라이브러리
- 복잡하고 장황한 코드가 필요한 다른 데이터 불러오기 방식과 달리 React Component 내부에서 간단하고 직관적으로 API를 사용할 수 있다.
- React Query에서 제공하는 캐싱, Window focus Refetching 등 다양한 기능을 활용하여 API 요청과 관련된 번잡한 작업없이 핵심로직에 집중할 수있다.
react-query의 기본옵션
refetchOnWindowFocus, //default: true - 브라우저에 포커스가 들어온 경우
refetchOnMount, //default: true - 새로운 컴포넌트 마운트가 발생한경우
refetchOnReconnect, //default: true - 네트워크 재연결이 발생한 경우
staleTime, //default: 0
cacheTime, //default: 5분 (60 * 5 * 1000)
staleTime
- staleTime은 fresh -> stale 상태로 변경되는데 걸리는 시간이다.
- fresh 상태인경우 refetch 트리거 (위 3가지 경우) 가 발생해도 refetch가 일어나지 않는다.
- 기본값이 0이므로 따로 설정하지 않으면 refetch트리거가 발생했을 때 무조건 refetch가 발생
cacheTime
- 특정 컴포넌트가 unmount되면 사용된 데이터는 inactive상태로 바뀌고, cacheTime 만큼 유지된다.
- cacheTime 이후 데이터는 가비지 콜렉터로 수집되어 메모리에서 해제된다.
- cacheTime이 지나지 않았는데 해당 데이터를 사용하는 컴포넌트가 mount 되면, 새로운 데이터를 fetch해오는 동안 캐싱된 데이터를 보여준다.
대표적인 기능
기본적으로 GET 에는 useQuery
PUT, UPDATE, DELETE에는 useMutation이 사용된다.
useQuery에 isLoading, isPending, error 등 다양한 옵션을 제공한다.
조건에 맞는경우만 fetch를 실행하는 경우 enabled를 사용해서 구현할 수있다.
const {data} = useQuery({
queryKey: ['trends'],
queryFn: getTrends,
staleTime: 60 * 1000,
gcTime: 300* 1000,
enabled: !!session.user // session.user 값이 있는경우만 fetch 해준다.
})
설치하기
npm i @tanstack/react-query@5
Dev tool도 설치하기
debugging 하기위해서
npm i @tanstack/react-query-devtools@5 -D
react-query debugger 기능
refetch - 새로데이터를 가져오는 것, 바로 데이터를 호출해서 가져옴
invalidate - 기존 데이터를 사용하지 않는다는것, 새로데이터 가져옴, 바로 데이터를 호출해서 가져오는게 아니라 페이지에서 사용하는 부분이 있으면 호출해서 가져온다.
reset - initialData가 있는경우 초기데이터로 되돌리고 싶은경우 reset을 사용한다.
trigger loading - 로딩상태 보고싶은경우
restore Error - 에러상태 보고싶은경우
react query provider 만들기
"use client";
import React, {useState} from "react";
import {QueryClientProvider, QueryClient} from "@tanstack/react-query";
import {ReactQueryDevtools} from "@tanstack/react-query-devtools";
type Props = {
children: React.ReactNode;
};
function RQProvider({children}: Props) {
const [client] = useState(
new QueryClient({
defaultOptions: { // react-query 전역 설정
queries: {
refetchOnWindowFocus: false,
retryOnMount: true,
refetchOnReconnect: false,
retry: false,
},
},
})
);
return (
<QueryClientProvider client={client}>
{children}
<ReactQueryDevtools initialIsOpen={process.env.NEXT_PUBLIC_MODE === 'local' }/>
</QueryClientProvider>
);
}
export default RQProvider;
서버에서 react-query 사용하기
hydrate와 dehydrate는 서버에서 미리 가져온 데이터를 클라이언트 사이드에서 재사용 할 수 있게 해줍니다.
import {dehydrate, QueryClient, HydrationBoundary } from '@tanstack/react-query';
export default async function Home() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery({queryKey: ['posts', 'recommends'], queryFn: getPostRecommends })
}
const dehydratedState = dehydrate(qeuryClient);
// 데이터 가져올 떄
queryClient.getQueryData(['posts', 'recommends'])
return (
<HydrationBoundary state={dehydratedState}>
<Home/>
</HydrationBoundary>
)
}
react query에 query key를 api에서 받을수있다.
export default function SearchResult({ searchParams }: props) {
const {data} = useQuery({
queryKey: ["posts", "search", searchParams],
queryFn: getSearchResult,
staleTime: 60 * 1000,
gcTime: 300 * 1000
})
}
// api
export async function getSearchResult({ queryKey }) {
const [_1, _2, searchParams] = queryKey;
const res = await fetch(`http://localhost:9090/api/search/${searchParams.q}?{searchParams.toString()}`,
next: {
tags: ["posts", "search", searchParams.q]
},
cache: 'no-store'
)
}
숫자 콤마를 표기하기
정규식을 이용할 수 도있지만 toLocaleString()을 이용하면 표기하기 수월하다.
trend.count.toLocaleString()
타입스크립트에서 Object 는 모든값을 의미한다
오브젝트 타입을 사용하고 싶으면 소문자 object
{} 타입: undefined, null을 제외한 모든 값이 할당 가능하다.
Object 타입: {} 타입과 동일하다.
object 타입: 원시값을 제외한 모든 값이 할당 가능하다.
react-query 인피니트 스크롤링
인피니트 스크롤링을 위한 값(fetchNextPage, hasNextPage)을 제공한다.
서버쪽에서 스크롤링 데이터를 가져올때
export default async function Home () {
const queryClient = new QueryClient();
await queryClient.prefetchInfiniteQuery({
queryKey: ['posts', 'recommends'],
queryFn: getPostRecommends,
initialPageParam: 0,
})
}
const {data: infiniteData, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ['posts', 'recommends'],
queryFn: getPostRecommends,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.at(-1)?.postId,
staleTime: 60 * 1000, // refresh -> stale //5분이 유효시간
gcTime: 300 * 1000
})
마지막 요소가 나오면 데이터 fetch를 위해 react intersection observer 플러그인을 설치한다.
const {ref, inView} = useInView({
threshold: 0,
delay: 0
});
useEffect(() => {
if(inview) { //화면에 보이면 inview true
!isFetching && hasNextPage && fetchNextPage() //데이터를 가져오고있지않거나, 다음페이지가 있는경우 fetch 한다.
}
}, [inView, isFetching, hasNextPage, fetchNextPage])
참고
https://velog.io/@kandy1002/React-Query-%ED%91%B9-%EC%B0%8D%EC%96%B4%EB%A8%B9%EA%B8%B0
Suspense Loading , Error
Suspense 17버전에서 추가된 기능
데이터 fetch 완료전 fallback prop로 전달했던 로딩 컴포넌트를 보여준다.
fetch전까지 빈화면이 나오지 않아서 사용성이 좋아진다.
서버렌더링이 완료되면 새 콘텐츠가 클라이언트 컴포넌트에서 갈이된다.
참고
https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
errorBoudary - error 가 나면 fallback로 내려준 error 컴포넌트를 띄워준다.
https://nextjs.org/docs/app/building-your-application/routing/error-handling
suspense
https://react-ko.dev/reference/react/Suspense
Suspense 컴포넌트를 사용할 수 있는 경우
- next에서 Suspense를 사용한 데이터페칭
- lazy를 이용한 지연 로딩 컴포넌트 코드
- react use를 사용한 경우
suspense는 서버컴포넌트에서 로딩 처리가 필요한경우 사용한다.
클라이언트 컴포넌트에서 react-query에 ispending을 이용해서 구현하면 된다.
클라이언트컴포넌트에서도 서버 suspense loading.tsx를 동일하게 사용하고 싶은경우
useSuspenseQuery를 사용하면된다.
서버 컴포넌트 suspense를 사용할수있게 된다.
<Suspense fallback=<Loading/>>
<FollowingPosts/>
</Suspense>
// FollowingPosts
export default function FollowingPosts() {
const { data } = useSuspenseQuery({
queryKey: ['posts', 'followings'],
queryFn: getFollowingPosts,
staleTime: 60 * 1000,
gcTime: 300 * 1000
})
return data?.map((post) => (
<Post key={post.postId} post={post}/>
))
}
'NOTE' 카테고리의 다른 글
[Next + React Query로 SNS 서비스 만들기] 섹션 5 정리 (5) | 2024.09.15 |
---|---|
[Next + React Query로 SNS 서비스 만들기] 섹션 4 정리 (1) | 2024.09.12 |
[Next + React Query로 SNS 서비스 만들기] 섹션 3-1 정리 (msw, server-action, next-auth) (0) | 2024.08.24 |
Next serverActions (0) | 2024.08.10 |
[Next + React Query로 SNS 서비스 만들기] 섹션 2 정리 (0) | 2024.08.05 |