
開発用(かいはつよう)のバックエンドサーバーを構築(こうちく)します。
JSON Serverを使(つか)って簡単(かんたん)にREST APIを作成(さくせい)できます。
개발용 백엔드 서버를 구축합니다.
JSON Server를 사용해서 간단하게 REST API를 만들 수 있습니다.
npm i json-server -D
server/db.json
{
"todos": [
{
"id": 1,
"content": "Todo 1",
"isDone": true
},
{
"id": 2,
"content": "Todo 2",
"isDone": true
},
{
"id": 3,
"content": "Todo 3",
"isDone": false
}
]
}
serverフォルダー内(ない)のファイル変更(へんこう)を監視(かんし)しないようにします。
サーバーファイルが変(か)わってもリレンダリングが発生(はっせい)しません。
server 폴더 내의 파일 변경을 감시하지 않도록 합니다.
서버 파일이 변해도 리렌더링이 발생하지 않습니다.
vite.config.ts
export default defineConfig({
// ...
server: {
watch: {
ignored: ["**/server/**"],
},
},
});
ターミナルを2つ開(ひら)いて使用(しよう)します。
1つ目(め)はフロントエンド、2つ目(め)はバックエンドサーバーです。
터미널을 2개 열어서 사용합니다.
첫 번째는 프론트엔드, 두 번째는 백엔드 서버입니다.
# 터미널 1: 프론트엔드
npm run dev
# 터미널 2: 백엔드
npx json-server src/server/db.json
アクセス:
http://localhost:3000/todos → 전체 todos
http://localhost:3000/todos/1 → id가 1인 todo
JSON Server: RESTful API를 자동으로 생성해주는 개발 도구
npm i @tanstack/react-query
QueryClientを一種(いっしゅ)の貯蔵庫(ちょぞうこ)として考(かんが)えます。
キャッシング値(あたい)などを管理(かんり)します。
QueryClient를 일종의 저장소로 생각합니다.
캐싱 값 등을 관리합니다.
main.tsx
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import { BrowserRouter } from "react-router";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
const queryClient = new QueryClient();
createRoot(document.getElementById("root")!).render(
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</BrowserRouter>,
);
| 要素(ようそ) | 役割(やくわり) |
|---|---|
QueryClient | 캐시 저장소(ちょぞうしょ) |
QueryClientProvider | React 앱에 QueryClient 제공(ていきょう) |
API URLを毎回(まいかい)設定(せってい)しないようにします。
定数(ていすう)ファイルで一元管理(いちげんかんり)します。
API URL을 매번 설정하지 않도록 합니다.
상수 파일로 일원 관리합니다.
lib/constants.ts
export const API_URL = "http://localhost:3000";
src/api/fetch-todos.ts
import { API_URL } from "@/lib/constants";
import type { Todo } from "@/types";
export async function fetchTodos() {
const response = await fetch(`${API_URL}/todos`);
if (!response.ok) throw new Error("Fetch Failed");
const data: Todo[] = await response.json();
return data;
}
TanStack Queryを使(つか)ってデータを照会(しょうかい)するフックを作(つく)ります。
queryFnとqueryKeyを設定(せってい)します。
TanStack Query를 사용해서 데이터를 조회하는 훅을 만듭니다.
queryFn과 queryKey를 설정합니다.
src/hooks/queries/use-todos-data.ts
import { fetchTodos } from "@/api/fetch-todos";
import { useQuery } from "@tanstack/react-query";
export function useTodosData() {
return useQuery({
queryFn: fetchTodos,
queryKey: ["todos"],
});
}
| プロパティ | 説明(せつめい) |
|---|---|
queryFn | 데이터를 가져오는 비동기(ひどうき) 함수(かんすう) |
queryKey | 캐시 식별(しきべつ)을 위한 고유(こゆう) 키(き) |
pages/todo-list-page.tsx
import TodoEditor from "@/components/todo-list/todo-editor";
import TodoItem from "@/components/todo-list/todo-item";
import { useTodosData } from "@/hooks/queries/use-todos-data";
export default function TodoListPage() {
const { data: todos, isLoading, error } = useTodosData();
if (error) return <div>오류가 발생했습니다.</div>;
if (isLoading) return <div>로딩 중 입니다 ...</div>;
return (
<div className="flex flex-col gap-5 p-5">
<h1 className="text-2xl font-bold">TodoList</h1>
<TodoEditor />
{todos?.map((todo) => <TodoItem key={todo.id} {...todo} />)}
</div>
);
}
| 返却値(へんきゃくち) | 説明(せつめい) |
|---|---|
data | 가져온 데이터 |
isLoading | 로딩 상태(じょうたい) |
error | 에러 정보(じょうほう) |
要請(ようせい)を通(とお)して読(よ)み込(こ)んだデータをqueryKeyを利用(りよう)してキャッシングします。
特定時間(とくていじかん)の間(あいだ)、不要(ふよう)な要請(ようせい)を防止(ぼうし)します。
요청을 통해 불러온 데이터를 queryKey를 이용해서 캐싱합니다.
특정 시간 동안 불필요한 요청을 방지합니다.
キャッシング フロー:
1. 요청(ようせい) → 데이터 가져오기
2. queryKey로 캐시에 저장(ちょぞう)
3. 동일(どういつ)한 queryKey 요청(ようせい) → 캐시 데이터 사용(しよう)
4. 적절(てきせつ)한 타이밍에 자동(じどう) 갱신(こうしん)
データは5つの状態(じょうたい)を持(も)ちます。
fetching、fresh、staleが主要(しゅよう)な状態(じょうたい)です。
데이터는 5개의 상태를 가집니다.
fetching, fresh, stale이 주요 상태입니다.
状態 遷移(せんい):
fetching (データ読込中(よみこみちゅう))
↓
fresh (新鮮(しんせん)な状態(じょうたい))
↓ (staleTime 経過(けいか))
stale (古(ふる)い状態(じょうたい))
↓ (리페칭 트리거)
fetching (再読込(さいよみこみ))
| 状態(じょうたい) | 説明(せつめい) |
|---|---|
fetching | 데이터를 불러오는 중(ちゅう) |
fresh | 데이터가 신선(しんせん)한 상태(じょうたい) |
stale | 데이터가 오래된 상태(じょうたい) |
inactive | 사용(しよう)되지 않는 캐시 |
deleted | 삭제(さくじょ)된 캐시 |
新鮮(しんせん)な状態(じょうたい)を維持(いじ)する時間(じかん)です。
賞味期限(しょうみきげん)のようなものです。
신선한 상태를 유지하는 시간입니다.
유통 기한 같은 것입니다.
useQuery({
queryFn: fetchTodos,
queryKey: ["todos"],
staleTime: 5000, // 5초 동안 fresh 상태 유지
});
4つのタイミングでデータを再読込(さいよみこみ)します。
stale状態(じょうたい)からfetching状態(じょうたい)に変(か)わります。
4가지 타이밍에 데이터를 다시 불러옵니다.
stale 상태에서 fetching 상태로 변합니다.
| トリガー | 説明(せつめい) |
|---|---|
mount | 컴포넌트가 마운트될 때 |
windowFocus | 사용자(しようしゃ)가 탭으로 돌아올 때 |
reconnect | 인터넷이 다시 연결(れんけつ)될 때 |
interval | 특정(とくてい) 시간(じかん) 주기(しゅうき)로 |
useQuery({
queryFn: fetchTodos,
queryKey: ["todos"],
// 리페칭 설정
refetchOnMount: true, // 마운트 시
refetchOnWindowFocus: true, // 윈도우 포커스 시
refetchOnReconnect: true, // 재연결 시
refetchInterval: 10000, // 10초마다
});
視覚的(しかくてき)にキャッシュ状態(じょうたい)を確認(かくにん)できます。
開発中(かいはつちゅう)にデータの流(なが)れを把握(はあく)するのに便利(べんり)です。
시각적으로 캐시 상태를 확인할 수 있습니다.
개발 중에 데이터 흐름을 파악하는 데 편리합니다.
npm i @tanstack/react-query-devtools
main.tsx
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import { BrowserRouter } from "react-router";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const queryClient = new QueryClient();
createRoot(document.getElementById("root")!).render(
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools />
<App />
</QueryClientProvider>
</BrowserRouter>,
);
Devtools: 화면 하단에 아이콘 표시 → 클릭하면 캐시 상태 확인 가능
App.tsx
import { Outlet, Route, Routes } from "react-router";
import "./App.css";
import IndexPage from "@/pages/index-page";
import CounterPage from "./pages/counter-page";
import TodoListPage from "@/pages/todo-list-page";
import TodoDetailPage from "@/pages/todo-detail-page";
function App() {
return (
<Routes>
<Route path="/" element={<IndexPage />} />
<Route path="/counter" element={<CounterPage />} />
<Route path="/todolist" element={<TodoListPage />} />
<Route path="/todolist/:id" element={<TodoDetailPage />} />
{/* ... */}
</Routes>
);
}
export default App;
src/api/fetch-todo-by-id.ts
import { API_URL } from "@/lib/constants";
import type { Todo } from "@/types";
export async function fetchTodoById(id: number) {
const response = await fetch(`${API_URL}/todos/${id}`);
if (!response.ok) throw new Error("Fetch Failed");
const data: Todo = await response.json();
return data;
}
src/hooks/queries/use-todo-data-by-id.ts
import { fetchTodoById } from "@/api/fetch-todo-by-id";
import { useQuery } from "@tanstack/react-query";
export function useTodoDataById(id: number) {
return useQuery({
queryFn: () => fetchTodoById(id),
queryKey: ["todos", id], // 개별 캐시 키
staleTime: 5000,
// 선택적 리페칭 설정
// refetchOnMount: false,
// refetchOnWindowFocus: false,
// refetchOnReconnect: false,
// refetchInterval: false,
});
}
queryKey:
["todos", id]로 개별 todo에 대한 캐시 생성
pages/todo-detail-page.tsx
import { useTodoDataById } from "@/hooks/queries/use-todo-data-by-id";
import { useParams } from "react-router";
export default function TodoDetailPage() {
const params = useParams();
const id = params.id;
const { data, isLoading, error } = useTodoDataById(Number(id));
if (isLoading) return <div>로딩 중 입니다 ...</div>;
if (error || !data) return <div>오류가 발생했습니다</div>;
return <div>{data.content}</div>;
}
components/todo-list/todo-item.tsx
import { Button } from "@/components/ui/button";
import { useDeleteTodo } from "@/store/todos";
import { Link } from "react-router";
export default function TodoItem({
id,
content,
}: {
id: number;
content: string;
}) {
const deleteTodo = useDeleteTodo();
const handleDeleteClick = () => {
deleteTodo(id);
};
return (
<div className="flex items-center justify-between border p-2">
<Link to={`/todolist/${id}`}>{content}</Link>
<Button onClick={handleDeleteClick} variant={"destructive"}>
삭제
</Button>
</div>
);
}
TanStack Queryで強力(きょうりょく)なキャッシング機能(きのう)を活用(かつよう)できます。
ローディング画面(がめん)を最小限(さいしょうげん)にし、自動(じどう)最適化(さいてきか)でアプリ性能(せいのう)が向上(こうじょう)します。
TanStack Query로 강력한 캐싱 기능을 활용할 수 있습니다.
로딩 화면을 최소화하고 자동 최적화로 앱 성능이 향상됩니다.
핵심: JSON Server (백엔드) + TanStack Query (캐싱) + Devtools (디버깅) = 최적화된 서버 상태 관리