현대 React 애플리케이션에서 서버 상태와 클라이언트 상태를 효율적으로 관리하는 것은 중요한 과제입니다. 이를 해결하기 위해 React Query와 Zustand와 같은 라이브러리를 사용할 수 있습니다. 이 글에서는 React Query와 Zustand를 사용하여 상태 관리를 간단하고 직관적으로 처리하는 방법을 단계별로 알아보겠습니다.
1. React Query 소개 및 사용법
React Query는 서버 상태를 쉽게 관리할 수 있도록 도와주는 라이브러리입니다. 이를 통해 데이터 페칭, 캐싱, 동기화, 서버 상태 업데이트 등의 작업을 간단하게 처리할 수 있습니다.
설치
npm install @tanstack/react-query
npm install @tanstack/react-query-devtools
설정
먼저 QueryClient를 설정하고, 애플리케이션을 QueryClientProvider로 감싸야 합니다.
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
{/* Your app components go here */}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
export default App;
데이터 페칭
`useQuery` 훅을 사용하여 데이터를 페칭할 수 있습니다.
import React from 'react';
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
const fetchUsers = async () => {
const { data } = await axios.get('https://jsonplaceholder.typicode.com/users');
return data;
};
function Users() {
const { data, error, isLoading } = useQuery(['users'], fetchUsers);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{data.map(user => (
<div key={user.id}>
{user.name} - {user.email}
</div>
))}
</div>
);
}
export default Users;
Mutation 사용
데이터를 수정하거나 삭제하는 작업은 `useMutation` 훅을 사용하여 처리할 수 있습니다.
import React from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
const addUser = async (newUser) => {
const { data } = await axios.post('https://jsonplaceholder.typicode.com/users', newUser);
return data;
};
function AddUser() {
const queryClient = useQueryClient();
const mutation = useMutation(addUser, {
onSuccess: () => {
queryClient.invalidateQueries(['users']);
},
});
const handleAddUser = () => {
mutation.mutate({ name: 'New User', email: 'newuser@example.com' });
};
return (
<div>
<button onClick={handleAddUser}>
{mutation.isLoading ? 'Adding...' : 'Add User'}
</button>
{mutation.isError && <div>Error: {mutation.error.message}</div>}
{mutation.isSuccess && <div>User added!</div>}
</div>
);
}
export default AddUser;
2. Zustand 소개 및 사용법
Zustand는 React 애플리케이션에서 상태 관리를 간단하고 직관적으로 할 수 있게 도와주는 상태 관리 라이브러리입니다.
설치
npm install zustand
상태 스토어 생성
import create from 'zustand';
const useStore = create((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
}));
export default useStore;
상태 사용
import React from 'react';
import useStore from './store';
function Counter() {
const { count, increase, decrease } = useStore();
return (
<div>
<h1>{count}</h1>
<button onClick={increase}>Increase</button>
<button onClick={decrease}>Decrease</button>
</div>
);
}
export default Counter;
미들웨어 사용
상태 변경 시 로컬 스토리지에 저장하는 예제입니다.
import create from 'zustand';
import { persist } from 'zustand/middleware';
const useStore = create(persist(
(set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'counter-storage',
}
));
export default useStore;
DevTools 사용
Redux DevTools와의 통합을 지원합니다.
import create from 'zustand';
import { devtools } from 'zustand/middleware';
const useStore = create(devtools((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
})));
export default useStore;
3. 실제 예제 코드
React Query와 Zustand를 함께 사용하여 상태를 관리하는 전체 예제입니다.
import React from 'react';
import create from 'zustand';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import axios from 'axios';
const queryClient = new QueryClient();
const useStore = create((set) => ({
count: 0,
name: 'Zustand',
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
setName: (newName) => set(() => ({ name: newName })),
}));
const fetchUsers = async () => {
const { data } = await axios.get('https://jsonplaceholder.typicode.com/users');
return data;
};
function Users() {
const { data, error, isLoading } = useQuery(['users'], fetchUsers);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{data.map(user => (
<div key={user.id}>
{user.name} - {user.email}
</div>
))}
</div>
);
}
function Counter() {
const { count, name, increase, decrease, setName } = useStore();
return (
<div>
<h1>{count}</h1>
<h2>{name}</h2>
<button onClick={increase}>Increase</button>
<button onClick={decrease}>Decrease</button>
<button onClick={() => setName('New Zustand Name')}>Change Name</button>
</div>
);
}
function App() {
return (
<QueryClientProvider client={queryClient}>
<Counter />
<Users />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
export default App;
4. 상태 관리 모범 사례 및 결론
- 단순화된 상태 모델: 상태 모델을 단순화하고, 가능한 한 적은 상태로 애플리케이션을 관리합니다.
- 분리된 관심사: 상태 관리 로직을 컴포넌트 로직과 분리하여 유지보수성을 높입니다.
- 효율적인 데이터 페칭: 필요한 경우에만 데이터를 페칭하고, 데이터 캐싱을 활용하여 성능을 최적화합니다.
- 비동기 작업 관리: 비동기 작업을 적절히 처리하여 UI의 일관성을 유지합니다.
이렇게 React Query와 Zustand를 함께 사용하면, 서버 상태와 클라이언트 상태를 효율적으로 관리하여 React 애플리케이션의 성능과 유지보수성을 높일 수 있습니다. 이제 이 가이드를 참고하여 여러분의 프로젝트에 적용해보세요!