axios는 HTTP 요청을 보내고 응답을 받는 데에 중점을 두는 반면,
React Query는 데이터의 상태 관리와 최적화에 초점이 있다.
1.caching, fetching, 동기화, 서버 업데이트, 에러 핸들링 등 서버 상태를 업데이트 하는데 좀 더 쉽게 만들어준다.
3.caching을 통해 반복적인 비동기 데이터 호출을 방지하여, 애플리케이션 속도를 향상 시킨다.
4.오래된 데이터의 상태를 파악하여 updating을 지원한다.
5.프로젝트가 기존보다 단순해져 새로운 기능을 만들기 쉬워졌고, 유지보수 하기에도 간편해졌다.
6.복잡하고 장황한 코드가 필요한 다른 데이터 불러오기 방식과 달리 React Component 내부에서 간단하고 직관적으로 API를 사용할 수 있다.
그 외에도 여러가지 장점이 있다.
따라서 axios와 React Query를 함께 사용하면 HTTP 요청과 데이터와 상태 관리를 효과적으로 조합하여 구현할 수 있다!
그 외에 react query를 사용하는 이유
https://velog.io/@carrotdy/React-Native-React-Query
v4부터 React Query에서 @tanstack/react-query로 패키지가 변경되었다.
v3
yarn add react-query
v4
yarn add @tanstack/react-query
yarn add @tanstack/react-query-devtools
v3
return (
<QueryClientProvider client={queryClient}>
<AppMain />
</QueryClientProvider>;
)
v4
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const App = () => {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
<AppMain />
</QueryClientProvider>
);
}
v3에서는 queryKey를 문자열 또는 배열로 지정할 수 있었다.
사실 React Query는 내부적으로 항상 Array Keys로만 작동했기 때문에
v4에서는 배열로 통일 시켰다.
v3
- useQuery("todos", fetchTodos);
v4
+ useQuery(["todos"], fetchTodos);
쿼리의 상태를 더 세분화하여 관리하고 이를 기반으로 애플리케이션의 UI나 로직을 더 정확하게 제어할 수 있도록 한다.
따라서 쿼리의 로딩 상태를 더 정확하게 파악하고 처리할 수 있게 된다.
v3
- status: "idle";
v4
+ status: "loading";
+ fetchStatus: "idle"; // fetchStatus: 쿼리의 데이터 페치 상태
<참고>
https://github.com/ssi02014/react-query-tutorial/blob/main/document/v4.md
// An individual todo
useQuery({ queryKey: [ "todo", 5 ], ... })
// An individual todo in a "preview" format
useQuery({ queryKey: [ "todo", 5, { preview: true } ], ...})
import { useQuery } from "@tanstack/react-query";
const QuerySelect = () => {
const { data } = useQuery({
queryKey: ["project"],
queryFn: fetchProject, //api 함수
onError: async () => { // 성공했을 때 실행
navigate('BottomTab', {
screen: 'Home'
});
},
onSuccess: async () => { // 성공했을 때 실행
navigate('BottomTab', {
screen: 'Home'
});
},
select: (value: any) => value.filter((x: any) => x % 2 === 0)
//위의 코드는 쿼리 함수는 1부터 6까지 숫자의 배열을 반환하지만, select 옵션을 사용하여 짝수 배열 값만 얻을 수 있다.
enabled: isEnable
//isEnable이 true 일 때만 쿼리 활성화
retry: 3, // 최대 3번 재시도
retryDelay: 1000, // 재시도 간격: 1초
});
return <div>{data?.join(",")}</div>;
};
export default QuerySelect;
여러개의 데이터 쿼리를 가져올 때 유용하다.
각 쿼리는 별도의 요청으로 처리되므로 서버 응답 시간에 따라 시간이 오래 걸릴 수 있다.
하지만 useQueries는 캐싱 기능도 제공하기 때문에 동일한 쿼리가 여러 번 호출될 경우 이전에 캐싱된 결과를 반환하여 네트워크 트래픽을 절약할 수 있고, queryResults 배열을 활용하여 전체 쿼리에 대한 상태를 한 번에 처리할 수도 개별적으로 처리할 수도 있다.
ex) 웰잇고, Home에서 useQueries 훅을 사용하여 여러 개의 쿼리(디바이스 정보, 건강 등급 정보, 비즈 사이트 가져오기 등...)를 동시에 호출하고 있다.
useQuery
const usersQuery = useQuery("users", fetchUsers);
const teamsQuery = useQuery("teams", fetchTeams);
const projectsQuery = useQuery("projects", fetchProjects);
useQueries
배열을 활용하여 전체 쿼리에 대한 상태를 한 번에 처리할 수도 개별적으로 처리할 수도 있다.
const { data, isLoading } = useQueries({
queries: [
{ queryKey: ['deviceInfo'], queryFn: fetchDeviceInfo},
{ queryKey: ['healthGrade'], queryFn: fetchHealthGrade },
{ queryKey: ['bizSite'], queryFn: fetchBizSite },
],
})
const [
{ isLoading: isLoadingDeviceInfo, data: dataDeviceInfo },
{ isLoading: isLoadingHealthGrade, data: dataHealthGrade },
{ isLoading: isLoadingBizSite, data: dataBizSite }
] = useQueries([
{ queryKey: 'deviceInfo', queryFn: fetchDeviceInfo },
{ queryKey: 'healthGrade', queryFn: fetchHealthGrade },
{ queryKey: 'bizSite', queryFn: fetchBizSite },
]);
React에서 cacheTime과 staleTime은 React Query라는 라이브러리에서 사용되는 옵션이다.
이 옵션들은 데이터의 캐싱 및 유효성 검사에 관련된 기능을 제어하는 데 사용된다.
👉🏻 staleTime
👉🏻 cacheTime
글로 봐서는 정확하게 이해할 수가 없었다ㅠㅠ
stale과 cache는 비슷한 역할을 하는 것 같은데.. 왜 stale과 cache를 나눠서 관리할까?

위 그래프에서 페이지 이동 직전에 캐시가 만료되었다고 가정해보겠다!
두개의 차이점은 C이다.
현재처럼 stale과 cache로 관리할 경우,
C에서 stale 상태의 캐싱된 데이터를 이용하여 UI를 보여준 후
refetching을 완료하면 새로운 데이터를 이용한 UI로 교체해줄 수 있다.
반면 cache만으로 관리하게 된다면,
캐시가 만료되어 삭제되었을테니 C에서 refetching이 완료되기 전까지 데이터가 없으므로 Loading 페이지를 보여줘야 한다.
그리고 refetching이 완료되서야 d 데이터를 보여줄 수 있다.
이렇게 되면 사용자 페이지는 이동 후 느리게 서비스를 이용하게 된다.
import { useInfiniteQuery } from '@tanstack/react-query'
const { data, fetchNextPage, isFetchingNextPage, hasNextPage } =
useInfiniteQuery(
[QueryKey.RECORD_LIST],
({ pageParam = 1 }) =>
RecordService.Record.list({ page: pageParam, size: 5 }),
{
getNextPageParam: (lastPage, allPages) => {
const totalCount = lastPage.data.data.totalCount;
const currentPageDataCount = lastPage.data.data.diaries.length;
if (currentPageDataCount < totalCount) {
return allPages.length + 1;
} else {
return undefined;
}
},
}
);
위 코드는 페이지네이션을 처리하고,
현재 페이지에서 반환된 데이터 수가 전체 데이터 수보다 작으면 (더 가져올 데이터가 남아 있으면) 다음 페이지를 가져오는 로직으로 구현하였다.
import { useMutation, useQueryClient } from "@tanstack/react-query";
const queryClient = useQueryClient();
const { mutate } = useMutation(
(diaryId: number) => DiaryService.diary.delete(diaryId),
{
onSuccess: async data => {
if (data && data.status === 200) {
Toast.show({
type: 'success',
text1: '삭제되었습니다.',
});
setIsVisibleMore(false);
await queryClient.invalidateQueries([QueryKey.DIARY_LIST]);
//쿼리를 무효화함
}
},
onError: (error) => {
console.error('Delete error:', error);
}
}
)
...
<Pressable
onPress={() => {
if (deleteItemId !== null) {
mutate(deleteItemId);
}
}}
style={styles.ButtonContainer}>
<Title
text={"삭제"}
fontSize={16}
color={Colors.White}
style={{ textAlign: "center" }}
/>
</Pressable>
<참고>
https://tanstack.com/query/v4/docs/framework/react/reference/useQuery
<나중에 할 일>
1.v4로 변경
2.queryKey 관리
key를 통해서 데이터를 캐싱하고 관리하기 때문에 중요하다!
별도로 query-key를 한 곳에서 관리하는게 좋다