// 프로젝트 생성
npx create-next-app@latest react-query-practice --ts
// 비동기 통신 라이브러리 axios
// 서버 관련 라이브러리 json-server
yarn add axios json-server
// react-query 설치
yarn add @tanstack/react-query
yarn add @tanstack/react-query-devtools
// _app.tsx
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { useState } from "react";
import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
// React18 에 따른 타입이슈 해결하기 위한 코드
declare module "react-query/types/react/QueryClientProvider" {
interface QueryClientProviderProps {
children?: React.ReactNode;
}
}
function MyApp({ Component, pageProps }: AppProps) {
// 먼저 QueryClient 를 이용하여 queryClient 인스턴스를 만들어주고,
// QueryClientProvider 의 props 로 전달
const [ queryClient ] = useState(() => new QueryClient());
return (
<>
<QueryClientProvider client={new QueryClient}>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} position="bottom-right" />
</QueryClientProvider>
</>
)
}
// ReactQueryDevtools 는 개발도구로 개발환경이 development 일때만 나타남
// declare module ~ {interface {} } 는 React18 에 따른 타입이슈 해결을 위한 코드
// 리액트를 사용할 땐 App.js or App.tsx 를 <QueryClientProvider> 로 감싸주면 됨
export default MyApp
// 프로젝트의 루트 디렉토리에 db.json 생성 후 다음과 같이 작성
// 서버와 DB 를 대신하는 dummy data 라고 생각하면 됨
{
"posts": [
{
"id": 1,
"title": "게시물1",
"author": "Kkiri",
"description": "벌레는 흙으로 별에도 헤는 그리고 둘 위에 딴은 위에도 버리었습니다. 내린 계절이 밤을 아무 나는 하나에 경, 별 봅니다. 잠, 묻힌 그리고 패, 강아지, 잔디가 가을로 나의 다 듯합니다. 이름과, 잔디가 까닭이요, 위에 밤이 사람들의 계십니다. 잠, 이름과, 차 이 까닭입니다. 같이 딴은 가을로 경, 별빛이 비둘기, 동경과 하나에 둘 계십니다. 멀리 없이 시인의 오면 불러 봅니다. 자랑처럼 이름자 많은 불러 딴은 별 별을 버리었습니다. 하나에 언덕 릴케 있습니다."
},
{
"id": 2,
"title": "게시물2",
"author": "끼리",
"description": "노새, 나의 라이너 너무나 있습니다. 별 계절이 멀리 아무 파란 가득 무덤 가을 자랑처럼 듯합니다. 이런 별이 별 위에도 까닭입니다. 계집애들의 않은 까닭이요, 까닭입니다. 멀리 것은 차 계절이 헤일 이 딴은 위에 어머님, 까닭입니다. 그리워 지나가는 쓸쓸함과 헤는 거외다. 잔디가 멀리 별 이네들은 이름을 까닭입니다. 불러 봄이 헤는 않은 오면 하나에 버리었습니다. 내일 하늘에는 무덤 나는 계십니다. 불러 벌써 별 까닭입니다. 나의 별 밤을 듯합니다."
},
{
"id": 3,
"title": "게시물3",
"author": "끼리끼리",
"description": "애기 소녀들의 이름과 다하지 별이 듯합니다. 이웃 쓸쓸함과 멀리 하나에 둘 별에도 별 소학교 계십니다. 이국 비둘기, 불러 거외다. 이네들은 청춘이 위에 자랑처럼 내 헤일 계십니다. 나는 멀리 아스라히 무덤 별이 때 라이너 헤는 까닭입니다. 언덕 하늘에는 사랑과 다 둘 듯합니다. 별 멀듯이, 위에도 말 이런 봅니다. 나는 봄이 이네들은 나는 옥 까닭입니다. 써 노새, 남은 아스라히 있습니다. 아이들의 새워 덮어 까닭입니다."
}
]
}
// useQuery 그리고 queryKey & queryFn 을 알아보기 :)
// index.tsx
import type { NextPage } from "next";
import { useQuery } from "react-query";
import axios from "axios";
import { Fragment } from "react";
interface Post {
id: number;
title: string;
author: string;
description: string;
}
const getPosts = async () => {
const { data } = await axios.get<Post[]>("http://localhost:5000/posts");
return data;
};
// 아래와 같이 작성할 수도 있음
// const getPost = async () => axios.get<Post[]>("http://localhost:5000/posts")
// 하지만 이후에 SSR 직렬화 과정 중 에러가 발생하는 이슈가 있고, data 구조가 data.data 이기 때문에 구조분해 하여 반환함
const Home: NextPage = () => {
const {
data: posts,
isLoading,
isError,
error,
} = useQuery<Post[], Error>(["posts"], getPosts);
// useQuery 의 첫번째 인자로는 queryKey 를 넣어줌. queryKey 란 이후에 사용되는 캐싱이나 데이터 재요청, 상태 변경 등 여러 곳에서 사용됨
// 즉, useQuery 를 여러개 사용할 때, 각각의 useQuery 를 식별하기 위한 식별자. React Query v4 부터는 배열로 통일!
// useQuery 의 두번째 인자로는 queryFn 을 넣어줌. queryFn 에는 비동기 통신하여 Promise 를 반환하는 함수를 넣어줌
// useQuery 의 세번째 인자로는 옵션을 설정해줄 수 있음. (이번 예제에는 옵션 설정 따로 안함)
// useQuery 가 반환하는 값들 중 data 에는 비동기 통신이 성공(resolve)했을 경우의 데이터가 들어가게 됨
// 실패했을 경우에는 isError 가 true 가 되고, error 에 에러 내용이 들어가게 됨
if (isError) {
return <div>{error.message}</div>;
}
return (
<>
<div>
{isLoading ? (
<div>Loading...</div>
) : (
posts?.map((post) => (
<Fragment key={post.id}>
<div>id: {post.id}</div>
<div>제목: {post.title}</div>
<div>작성자: {post.author}</div>
<div>내용: {post.description.slice(0, 100)}...</div>
<hr />
</Fragment>
))
)}
</div>
</>
);
};
export default Home;
// package.json 의 script 에 아래와 같이 "server" 스크립트 추가
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"server": "json-server --watch db.json --port 5000"
},
// next 실행
yarn next dev
// 서버 실행(아래 사진 참고)
yarn run server
참고
https://devkkiri.com/post/f14703ea-a105-46e2-89e8-26282de36a3a