1) 설치
npm install @tanstack/react-query2) queryClient 생성 ( _app.js )
export default function MyApp({ Component, pageProps }) {
  // 순수 react 의 경우에는 이렇게 선언
  // const queryClient = new QueryClient();
  
  const [queryClient] = useState(()=> new QueryClient({
    // 옵션 추가 가능
    defaultOptions:{
      queries:{
        retry:1,
        retryDelay: 0,
        staleTime: 60 * 1000
      }
    }
  }));
  return (
    <QueryClientProvider client={queryClient}>
  		<Hydrate state={pageProps.dehydratedState}>
	      <Component {...pageProps} />
		</Hydrate>
    </QueryClientProvider>
  )
}[ queryClient를 useState로 선언하는 이유 ]
Next.js의 경우 페이지를 이동할 때 _app.js부터 새롭게 렌더링 시키기 때문에 useState를 이용해 한번만 선언되게 만들어준다. 이렇게 하지 않으면 새로운 queryClient가 생성되면서 기존 데이터가 유실될 수 있다.
QueryClientProvider는 ContextProvider로 동작하며 하위 컴포넌트에서 QueryClient를 사용할 수 있게 해준다. ( 캐시 데이터 사용 가능)
[ Prefetching data with React Query ]
서버사이드에서 useQuery를 사용해 데이터를 prefetching 하는 방법에는 두 가지가 있다.
export async function getStaticProps() {
  const posts = await getPosts()
  return { props: { posts } }
}
function Posts(props) {
  const { data } = useQuery(['posts'], getPosts, { initialData: props.posts })
  // ...
}서버사이드에서 prefetch한 쿼리를 queryClient에 dehydrate 하는 방식이다. (SSG, SSR 동일)
export async function getStaticProps() {
  const queryClient = new QueryClient()
  await queryClient.prefetchQuery(['posts'], getPosts)
  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  }
}
function Posts() {
  // This useQuery could just as well happen in some deeper child to
  // the "Posts"-page, data will be available immediately either way
  const { data } = useQuery(['posts'], getPosts)
  // This query was not prefetched on the server and will not start
  // fetching until on the client, both patterns are fine to mix
  const { data: otherData } = useQuery(['posts-2'], getPosts)
  // ...
}// memberApi.js
export const getMemberList = (param) => {
  return useQuery(["memberLists", param], ()=>getMemberListFetch(param),{
    ...queryOption,
    onSuccess: data => {
      // 성공 시 호출
    },
    onError: error => {
      // 실패 시 호출   
    }
  })
}
export const getMemberListFetch = async (param) => {
  const data = await request.get(PATH_ADMIN_API.MEMBER.LIST(param)).catch((error) => {
    throw error
  })
  return data
}// member.js
export default function MemberInfoTable() {
	const {data:memberInfoLists} = getMemberList(param)
	return ...
}const { data: todoList, error, isFetching } = useQuery("todos", fetchTodoList);
const { data: nextTodo, error, isFetching } = useQuery(
  "nextTodos",
  fetchNextTodoList,
  {
    enabled: !!todoList // true가 되면 fetchNextTodoList를 실행한다
  }
);const info = {
  memberId: "2"
};
const result = useQueries([
  {
    queryKey: ["memberInfo", info.memberId],
    queryFn: params => {
      console.log(params); // {queryKey: ['memberInfo', '2'], pageParam: undefined, meta: undefined}
      return api.getRunInfo(riot.version);
    }
  },
  {
    queryKey: ["getSpell", riot.version],
    queryFn: () => api.getSpellInfo(riot.version)
  }
]);const queryCache = queryClient.getQueryCache();
const query = queryCache.find({queryKey})const query = queryCache.findAll({queryKey})const queryCache = queryClient.getQueryCache();
const callback = event =>{
  // 에러처리 함수
  console.log('error: ', event?.query?.state?.error)
}
const unsubscribe = queryCache.subscribe(callback));callback 함수는 쿼리 캐시가 업데이트 될 때마다 호출된다. 이 기능을 이용해 에러를 캐치해 처리하는 errorProvider를 만들어 전역으로 관리했다.
//memberApi.js
export const putMemberAuthChangeMutate = (userKey) => {
  return useMutation((param) => putMemberAuthChangeFetch(param, userKey))
}
export const putMemberAuthChangeFetch = async (param, userKey) => {
  return await request.put(PATH_ADMIN_API.MEMBER.AUTH_CHANGE(userKey),param).catch((error) => {
    throw error
  })
}//member.js
const {mutate: changeAuth} = putMemberAuthChangeMutate(userKey);
const handleChangeAuth = () =>{
    changeAuth(param,{
      onSuccess: () => {
       //성공 시 해당 queryKey의 api를 재실행
        queryClient.invalidateQueries('memberLists')
        queryClient.invalidateQueries('memberDetail')
      }
    })
  }참고