4.6 KiB
4.6 KiB
05. 상태 관리
ST 앱의 상태 관리 전략과 Zustand 기반 전역 상태 설계를 설명합니다.
1. 상태 관리 전략 개요
ST 앱은 상태의 **성격(클라이언트 vs 서버)**에 따라 다른 도구를 사용합니다.
┌─────────────────────────────────────────────────────────┐
│ 상태 관리 분리 │
├────────────────────────┬────────────────────────────────┤
│ 클라이언트 상태 │ 서버 상태 │
│ (UI, 세션 데이터) │ (API 응답 데이터) │
│ │ │
│ ✅ Zustand │ ✅ SWR (조회) │
│ │ ✅ TanStack Query (변경) │
└────────────────────────┴────────────────────────────────┘
2. Zustand - 전역 클라이언트 상태
Zustand는 보일러플레이트가 없는 경량 전역 상태 관리 라이브러리입니다.
스토어 목록
| 스토어 파일 | 관리 상태 | 주요 위치 |
|---|---|---|
user-store.ts |
로그인 사용자 정보 (이름, ID 등) | shared/store/ |
menu-store.ts |
현재 메뉴 정보, BBS 설정, 페이지 권한 | shared/store/ |
alert-store.ts |
전역 알림(Alert) 메시지 | shared/store/ |
attachment-store.ts |
첨부파일 상태 | shared/store/ |
tab-store.ts |
탭 목록 및 현재 활성 탭 | store/ |
admin-store.ts |
관리자 관련 상태 | store/ |
2-1. 스토어 생성 패턴
Vanilla 스토어 패턴 (shared/store/ 내 스토어들):
// shared/store/menu-store.ts
import { createStore } from "zustand/vanilla";
export const createMenuStore = (initState = defaultInitState) => {
return createStore<MenuStore>()((set) => ({
...initState,
setCurrentMenuInfo: (menu) => set({ currentMenuInfo: menu }),
// ...
}));
};
zustand/vanilla를 사용하여 React에 종속되지 않는 순수 스토어를 생성store-provider.tsx에서 React Context와 연결하여 컴포넌트에서 사용
React 스토어 패턴 (store/tab-store.ts):
// store/tab-store.ts
import { create } from "zustand";
const useTabStoreHook = create<TabStore>((set, get) => ({
tabs: [],
activeTabId: null,
addTab: (menuItem) => { /* ... */ },
removeTab: (tabId) => { /* ... */ },
}));
export const useTabStore = useTabStoreHook;
create를 사용하여 직접 React Hook 형태로 사용
2-2. 스토어 사용 패턴
// 컴포넌트에서 사용
const { setMessage: alert } = useAlertStore((state) => state);
const activeTabId = useTabStore((state) => state.activeTabId);
const userInfo = useUserStore((state) => state.userInfo);
(state) => state.xxx셀렉터 패턴을 사용하여 필요한 상태만 구독하고, 불필요한 리렌더링을 방지합니다.
3. tab-store 설계 - 탭 UI 관리
탭 기반 내비게이션의 핵심 스토어입니다.
주요 상태
| 상태 | 타입 | 설명 |
|---|---|---|
tabs |
Tab[] |
현재 열린 탭 목록 |
activeTabId |
string | null |
현재 활성화된 탭 ID |
menuList |
MenuItem[] | null |
전체 메뉴 목록 |
주요 액션
| 액션 | 설명 |
|---|---|
addTab(menuItem) |
탭 추가 (이미 있으면 활성화, 최대 10개 제한) |
removeTab(tabId) |
탭 닫기 (이전/다음 탭으로 자동 전환) |
setActiveTab(tabId) |
탭 활성화 전환 |
closeAllTabs() |
모든 탭 닫기 |
closeOtherTabs(tabId) |
현재 탭 외 모두 닫기 |
4. 화면 탭 포커스 감지 패턴
각 화면 컴포넌트에서 탭 전환을 감지하여 데이터를 자동 갱신하는 패턴입니다.
const activeTabId = useTabStore((state) => state.activeTabId);
useEffect(() => {
// 이 컴포넌트의 탭이 활성화되면 데이터 새로고침
if (activeTabId === 'StatsInputStatusInp' && swrKey) {
mutate(swrKey);
}
}, [activeTabId]);
동작 원리:
activeTabId가 변경될 때마다useEffect가 실행됨- 현재 컴포넌트의 탭 ID와 일치하는지 확인
- 일치하면 SWR의
mutate()로 최신 데이터를 서버에서 다시 가져옴