Zustand 상태관리 + AsyncStorage Persist
개요
Zustand는 React/React Native에서 사용하는 경량 상태관리 라이브러리입니다. Redux보다 보일러플레이트가 적고, Context API보다 성능이 좋습니다.
설치
npm install zustand
npm install @react-native-async-storage/async-storage
기본 사용법
1. 스토어 생성
// src/stores/counterStore.ts
import { create } from "zustand";
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
export const useCounterStore = create<CounterState>((set) => ({
// 상태
count: 0,
// 액션
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
2. 컴포넌트에서 사용
import { useCounterStore } from "../stores/counterStore";
const CounterComponent = () => {
// 개별 셀렉터 사용 (권장)
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
return (
<View>
<Text>{count}</Text>
<Button onPress={increment} title="증가" />
</View>
);
};
AsyncStorage Persist (앱 종료 후에도 상태 유지)
웹에서는 localStorage를 사용하지만, React Native에서는 AsyncStorage를 사용합니다.
// src/stores/authStore.ts
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
import AsyncStorage from "@react-native-async-storage/async-storage";
interface AuthState {
accessToken: string | null;
isLoggedIn: boolean;
login: (token: string) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
accessToken: null,
isLoggedIn: false,
login: (token) => set({
accessToken: token,
isLoggedIn: true
}),
logout: () => set({
accessToken: null,
isLoggedIn: false
}),
}),
{
name: "auth-storage", // AsyncStorage 키 이름
storage: createJSONStorage(() => AsyncStorage),
}
)
);
컴포넌트 외부에서 상태 접근
Axios 인터셉터 등 컴포넌트 외부에서 상태에 접근해야 할 때 사용합니다.
// src/stores/authStore.ts
export const useAuthStore = create<AuthState>()(/* ... */);
// 컴포넌트 외부용 헬퍼 함수
export const getAuthState = () => useAuthStore.getState();
// src/api/axios.ts (인터셉터에서 사용)
import { getAuthState } from "../stores/authStore";
api.interceptors.request.use((config) => {
const { accessToken } = getAuthState();
if (accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
}
return config;
});
셀렉터 최적화
// ❌ 안티패턴: 전체 스토어 구독
const { count, increment } = useCounterStore();
// → 스토어의 어떤 값이 변경되어도 리렌더링
// ✅ 권장: 개별 셀렉터
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
// → 해당 값이 변경될 때만 리렌더링
웹(localStorage) vs React Native(AsyncStorage) 비교
| 항목 | 웹 | React Native |
|---|---|---|
| 저장소 | localStorage | AsyncStorage |
| 동기/비동기 | 동기 | 비동기 |
| 설정 | 기본 제공 | createJSONStorage 필요 |