마인드케어 제작당시에는 swr 로 데이터패치를 했지만,
아쉬운 점이 get 요청으로만 제작되었다는 점이다.
물론, axios 에서 post 요청이나 delete put 요청등을
주고 난후에, mutate 의 활용으로
충분히 유용하게 사용할 수 있기에, swr 도 매우
좋은 툴이라 생각하지만,
npm trends 에서 React Query 의 사용도가 압도적이라는
점과 get 이외의 요청에도 mutation 에서 기능을
제공한다는 점에서 흥미를 느껴 이번 프로젝트에
도입해보기로 하였다. ( + 더 많은 라이브러리들을 다뤄볼 생각으로)
한국어로 된 자료가 부족한 점이 아쉽지만,
기록해둔다. (크롬의 번역기능만으로도 충분히 익힐 수 있다.)
공식홈페이지
https://react-query.tanstack.com
_app.js
import React from "react";
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { Hydrate, QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
export default function MyApp({ Component, pageProps }: AppProps) {
const [queryClient] = React.useState(() => new QueryClient());
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
<ReactQueryDevtools />
</QueryClientProvider>
);
}
_app.js 는 공식홈페이지에서 copy + paste 했다.
context provider 처럼 provider 로 감싸고,
hybrate 로 감싸고 있다.
지난 2회차에서 다루었던 것을 리액트 쿼리로 재구성해보았다.
hooks/api
usePosts.tsx
import axios from "axios";
import { useQuery } from "react-query";
const fetchPosts = async (limit = 10) => {
const { data } = await axios("https://jsonplaceholder.typicode.com/posts");
const result = data.filter((x: any) => x.id <= limit);
return result;
};
const usePosts = (limit: number) => {
return useQuery(["posts", limit], () => fetchPosts(limit));
};
export { usePosts, fetchPosts };
pages/index
import Link from "next/link";
import React from "react";
import { dehydrate, QueryClient, useQuery } from "react-query";
import { fetchPosts } from "../hooks/apis/usePosts";
const Home = () => {
const { isLoading, error, data } = useQuery<any[], Error>("posts", () =>
fetchPosts(10)
);
if (isLoading) return <div>Loading</div>;
if (error) return "An error has occurred: " + error?.message;
return (
<>
<div>index</div>
<Link href="/test">
<a>test페이지로</a>
</Link>
<ul>
{data?.map((post: any) => (
<li key={post.id}>
<div>
<span>{post.id}. </span>
<span key={post.id}>
<Link href={`/ssr2/${post.id}`}>{post.title}</Link>
</span>
<a href="#">{post.title}</a>
</div>
</li>
))}
</ul>
</>
);
};
export async function getStaticProps() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery("posts", () => fetchPosts(10));
return {
props: {
dehydratedState: dehydrate(queryClient),
},
};
}
export default Home;
ssr2/[id].tsx
import React from "react";
import { useRouter } from "next/router";
import { dehydrate, QueryClient, useQuery } from "react-query";
import { fetchPosts } from "../../hooks/apis/usePosts";
import Link from "next/link";
interface Post {
title: string;
body: string;
id: number;
}
function Ssr2() {
const { isLoading, error, data } = useQuery<Post[], Error>("posts", () =>
fetchPosts(10)
);
const router = useRouter();
const { id } = router.query;
const idx = Number(id) - 1;
if (isLoading) return <div>Loading</div>;
if (error) return "An error has occurred: " + error?.message;
return (
<div className="Detail">
<Link href="/">
<a>index로</a>
</Link>
{data && (
<>
<h1>{data[idx].title}</h1>
<p>{data[idx].body}</p>
<p>{data[idx].id}번째 게시글</p>
</>
)}
</div>
);
}
export async function getServerSideProps() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery("posts", () => fetchPosts(10));
return {
props: {
dehydratedState: dehydrate(queryClient),
},
};
}
export default Ssr2;
데이터 캐시 및 패치,
getStaticProps (ssg) 와
getServerSideProps (ssr) 이 잘 동작되는 것을 확인할 수 있었다.
(테스트 중이라, type 을 any 라고 적었으나 실제 작업 중에는 제대로
된 type 을 적어줘야 타입스크립트를 쓰는 이유가 있으니, 실제 본 작업에
들어가서는 any 를 타입으로 바꿔주자)
다음편에서는 mutation 에 대해서 알아보자