리액트 라우터 v7 + TanStack Query 조합에서
외부 API 데이터를 가져오는 구조는 “라우팅 + 쿼리 훅 분리”가 핵심이다
src/
├── api/
│ └── fetchPosts.ts
├── hooks/
│ └── usePosts.ts
├── pages/
│ └── PostsPage.tsx
├── router.tsx
└── main.tsx
👉 여기서 외부 API 호출만 담당
// api/fetchPosts.ts
export async function fetchPosts() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
if (!res.ok) {
throw new Error("데이터 불러오기 실패");
}
return res.json();
}
👉 React 컴포넌트는 이 훅만 사용하게 만든다
// hooks/usePosts.ts
import { useQuery } from "@tanstack/react-query";
import { fetchPosts } from "../api/fetchPosts";
export function usePosts() {
return useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
});
}
// pages/PostsPage.tsx
import { usePosts } from "../hooks/usePosts";
export default function PostsPage() {
const { data, isLoading, isError } = usePosts();
if (isLoading) return <div>로딩중...</div>;
if (isError) return <div>에러 발생</div>;
return (
<div>
<h1>게시글 리스트</h1>
<ul>
{data.map((post: any) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
// router.tsx
import { createBrowserRouter } from "react-router";
import PostsPage from "./pages/PostsPage";
export const router = createBrowserRouter([
{
path: "/",
element: <PostsPage />,
},
]);
// main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { router } from "./router";
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById("root")!).render(
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
);
게시글 리스트
- 제목1
- 제목2
- 제목3
...
👉 데이터는 TanStack Query가 담당
너 지금 하는 구조 기준으로는 이렇게 확장한다:
queryKey: ["posts", { page, keyword }]
👉 검색, 페이지네이션까지 바로 확장 가능
👉 라우터 + 프리패칭 같이 쓰기
// router.tsx
import { queryClient } from "./queryClient";
import { fetchPosts } from "./api/fetchPosts";
export const router = createBrowserRouter([
{
path: "/",
loader: () =>
queryClient.ensureQueryData({
queryKey: ["posts"],
queryFn: fetchPosts,
}),
element: <PostsPage />,
},
]);
👉 페이지 들어가기 전에 데이터 미리 가져옴 (UX 좋아짐)