1. 오늘 학습 주제
Zustand 기초 자체보다, 실제 데이터를 다루는 흐름에 더 가까운 내용을 학습했다.
특히 json-server로 가짜 API 서버를 만들고, React Query로 서버 상태를 관리하며, Zustand의 persist 미들웨어로 로컬 스토리지에 상태를 저장하는 흐름을 익혔다.
- db.json으로 가짜 API 데이터 만들기
- json-server를 통해 CRUD 테스트용 서버 구성하기
- PostApi로 API 요청 로직 분리하기
- React Query의 QueryClientProvider와 useQuery 사용법
- Zustand persist 미들웨어로 상태를 새로고침 후에도 유지하기
- CRUD 테스트 페이지에서 게시글 조회와 등록 흐름 이해하기
2. json-server와 db.json
실제 백엔드 없이도 API 요청을 연습하기 위해 json-server를 사용하는 구조를 학습했다.
db.json 파일은 가짜 데이터베이스 역할을 하며, JSON 구조를 기반으로 REST API처럼 동작한다.
{
"posts": [
{
"id": "1",
"title": "리액트 공부하기",
"author": "파안다"
},
{
"id": "2",
"title": "json-server 연결하기",
"author": "판다"
}
]
}
- db.json은 가짜 데이터 저장소 역할을 한다.
- posts 같은 키가 하나의 리소스가 된다.
- 프론트엔드에서 실제 API처럼 요청을 보내며 CRUD를 연습할 수 있다.
3. API 요청 로직 분리
API 통신 코드는 컴포넌트 안에 직접 길게 쓰기보다 별도 모듈로 분리하는 것이 좋다.
PostApi.js에서 게시글 관련 요청을 모아 관리했다.
const BASE_URL = "<http://localhost:3001/posts>";
export const postApi = {
getPosts: async () => {
const res = await fetch(BASE_URL);
return res.json();
},
createPost: async (newPost) => {
const res = await fetch(BASE_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newPost),
});
return res.json();
}
}
- API 요청 로직을 별도 파일로 분리하면 재사용성이 좋아진다.
- 컴포넌트는 화면과 이벤트 처리에 더 집중할 수 있다.
- getPosts, createPost처럼 기능별로 함수를 나누면 관리가 편하다.
4. 게시글 조회와 등록
PostApi에서는 두 가지 핵심 작업을 다뤘다.
4.1 조회
const res = await fetch(BASE_URL);
return res.json();
- GET 요청으로 전체 게시글 목록을 가져온다.
- 응답 데이터를 JSON으로 변환해 사용한다.
4.2 등록
const res = await fetch(BASE_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newPost),
});
- POST 메서드로 새 글 데이터를 보낸다.
- Content-Type을 application/json으로 지정해야 한다.
- body에는 문자열 형태의 JSON 데이터를 담아야 한다.
정리
- 조회는 보통 GET
- 등록은 보통 POST
- 요청 본문이 있을 때는 headers와 body 설정이 중요하다.
5. CRUD 테스트 페이지
crud-test/page.js에서는 가장 기본적인 CRUD 흐름 중 Read와 Create를 직접 테스트했다.
const [posts, setPosts] = useState([]);
먼저 목록을 불러오는 함수를 만들고,
const loadPosts = async () => {
const data = await postApi.getPosts();
setPosts(data);
};
마운트 시 한 번 실행했다.
useEffect(() => { loadPosts(); }, []);
그리고 새 글 등록 후 다시 목록을 불러와 화면을 갱신했다.
const handleAdd = async () => {
await postApi.createPost({ title: "새로운 글" + Date.now(), author: "익명" });
loadPosts();
};
- 목록 조회 후 state에 저장해 화면에 출력할 수 있다.
- 새 글 등록 후 다시 목록을 조회하면 최신 상태를 반영할 수 있다.
- 가장 기본적인 CRUD 흐름은 조회와 등록만으로도 충분히 연습 가능하다.
6. React Query란
React Query는 서버에서 가져온 데이터를 더 효율적으로 관리하기 위한 라이브러리이다.
기존에는 useEffect와 useState를 직접 조합해 데이터를 가져왔지만, React Query를 사용하면 로딩, 에러, 캐싱 같은 기능을 더 체계적으로 다룰 수 있다.
- React Query는 서버 상태 관리에 특화된 도구이다.
- 비동기 데이터 요청을 더 선언적으로 처리할 수 있다.
- 로딩, 에러, 캐시 관리를 쉽게 할 수 있다.
7. QueryClientProvider와 Provider
React Query를 사용하려면 먼저 앱 최상단에서 QueryClientProvider로 감싸야 한다.
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const [queryClient] = useState(() => new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
},
}));
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
- React Query는 QueryClientProvider가 필요하다.
- QueryClient는 캐시와 요청 설정을 관리한다.
- staleTime을 설정하면 일정 시간 동안 다시 요청하지 않고 캐시를 재사용할 수 있다.
8. staleTime 개념
예제에서는 staleTime: 60 * 1000을 설정했다.
staleTime: 60 * 1000
이 설정은 1분 동안 데이터를 신선한 상태로 간주하겠다는 뜻이다.
- staleTime 동안은 데이터를 다시 가져오지 않고 캐시를 우선 사용한다.
- 너무 자주 같은 데이터를 요청하는 것을 줄일 수 있다.
- 서버 데이터의 변경 빈도에 따라 적절히 조절할 수 있다.
9. useQuery 사용법
query/page.js에서는 useQuery를 사용해 유저 목록을 불러왔다.
const { data, isLoading, isError, error } = useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
});
여기서 중요한 두 가지는 다음과 같다.
- queryKey: 캐시를 구분하는 이름
- queryFn: 실제 데이터를 가져오는 비동기 함수
정리
- useQuery는 조회용 데이터 요청에 자주 사용한다.
- queryKey는 캐싱의 기준이 된다.
- 같은 키를 사용하면 React Query가 캐시를 활용할 수 있다.
10. React Query의 상태 처리
useQuery는 로딩과 에러 상태도 함께 제공한다.
if (isLoading) return <h1>데이터를 불러오는 중입니다...</h1>;
if (isError) return <p>{error.message}</p>;
성공했을 때만 실제 데이터를 출력한다.
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
- React Query는 로딩 상태를 별도로 제공한다.
- 에러 처리도 구조적으로 가능하다.
- 데이터 요청 흐름이 더 명확해진다.
11. Zustand persist 미들웨어
Zustand에서 상태를 브라우저 저장소에 유지하는 방법도 학습했다.
persist 미들웨어를 사용하면 새로고침 후에도 상태가 남아 있게 만들 수 있다.
import { persist } from "zustand/middleware";
export const usePersistStroe = create(
persist(
(set) => ({
theme: "light",
toggleTheme: () => set((state) => ({
theme: state.theme === "light" ? "dark" : "light"
}))
}),
{
name: "theme-storage"
}
)
);
- persist는 Zustand 상태를 로컬 스토리지 등에 저장하는 기능이다.
- 새로고침해도 이전 상태를 복원할 수 있다.
- 테마, 장바구니, 로그인 유지 같은 기능에 유용하다.
12. persist의 동작 방식
예제에서는 theme 상태를 저장하고 토글했다.
theme: "light"
toggleTheme: () => set((state) => ({
theme: state.theme === "light" ? "dark" : "light"
}))
그리고 name: "theme-storage"를 통해 저장 키 이름을 지정했다.
- 저장소에 어떤 이름으로 저장할지 직접 정할 수 있다.
- 상태를 바꾸면 브라우저 저장소에도 함께 반영된다.
- 다음에 앱을 열어도 이전 값이 유지된다.
13. 오늘 학습 흐름 정리
학습은 크게 두 축으로 나눌 수 있었다.
13.1 서버 상태 관리
- db.json으로 데이터 준비
- json-server로 가짜 API 구성
- PostApi로 요청 함수 분리
- crud-test/page.js로 조회/등록 테스트
- useQuery와 Provider로 React Query 적용
13.2 클라이언트 상태 유지
- Zustand persist 미들웨어 사용
- 브라우저 저장소에 상태 저장
- 새로고침 이후에도 상태 유지
14. 오늘 배운 핵심 정리
- db.json은 json-server에서 가짜 데이터베이스 역할을 한다.
- PostApi처럼 API 요청 로직을 별도 파일로 분리하면 재사용성과 가독성이 좋아진다.
- 게시글 조회는 GET, 등록은 POST 방식으로 처리할 수 있다.
- CRUD 테스트 페이지에서는 목록 조회 후 state에 저장하고, 등록 후 다시 조회하는 흐름을 연습했다.
- React Query를 사용하려면 QueryClientProvider로 앱을 감싸야 한다.
- QueryClient는 캐시와 기본 요청 설정을 담당한다.
- useQuery는 조회용 서버 데이터를 다룰 때 매우 유용하다.
- queryKey는 캐시 구분용 키이고, queryFn은 실제 요청 함수이다.
- staleTime을 설정하면 일정 시간 동안 캐시 데이터를 재사용할 수 있다.
- Zustand의 persist 미들웨어를 사용하면 상태를 브라우저 저장소에 유지할 수 있다.
- 테마 같은 설정값은 persist와 매우 잘 어울리는 대표적인 상태이다.
'TIL > [TIL]' 카테고리의 다른 글
| [TIL]Java Literal과 자료형 연산 이해 (0) | 2026.04.28 |
|---|---|
| [TIL]Mini Cart 프로젝트, json-server, API Layer 분리, Zustand persist (0) | 2026.04.28 |
| [TIL]Zustand를 활용한 전역 상태 관리와 Immer 개요 (0) | 2026.04.24 |
| [TIL]React Custom Hook과 Next.js App Router (0) | 2026.04.22 |
| [TIL]React API 통신과 데이터 컴포넌트 (0) | 2026.04.21 |