- auth-store.ts: 사용자 인증 상태 관리 (localStorage 지속성) - ui-store.ts: UI 상태 관리 (테마, 사이드바, 모달, 토스트)
112 lines
2.8 KiB
TypeScript
112 lines
2.8 KiB
TypeScript
import { create } from "zustand";
|
|
import { persist } from "zustand/middleware";
|
|
|
|
/**
|
|
* [UI 상태 인터페이스]
|
|
*/
|
|
interface UIState {
|
|
// ========== 테마 ==========
|
|
theme: "light" | "dark" | "system";
|
|
setTheme: (theme: "light" | "dark" | "system") => void;
|
|
|
|
// ========== 사이드바 ==========
|
|
isSidebarOpen: boolean;
|
|
toggleSidebar: () => void;
|
|
setSidebarOpen: (isOpen: boolean) => void;
|
|
|
|
// ========== 모달 ==========
|
|
isModalOpen: boolean;
|
|
modalContent: React.ReactNode | null;
|
|
openModal: (content: React.ReactNode) => void;
|
|
closeModal: () => void;
|
|
|
|
// ========== 토스트/알림 ==========
|
|
toasts: Toast[];
|
|
addToast: (toast: Omit<Toast, "id">) => void;
|
|
removeToast: (id: string) => void;
|
|
}
|
|
|
|
/**
|
|
* [토스트 메시지 타입]
|
|
*/
|
|
export interface Toast {
|
|
id: string;
|
|
type: "success" | "error" | "warning" | "info";
|
|
message: string;
|
|
duration?: number;
|
|
}
|
|
|
|
/**
|
|
* [UI 스토어]
|
|
*
|
|
* 전역 UI 상태를 관리합니다.
|
|
* - 테마 설정 (다크/라이트 모드)
|
|
* - 사이드바 열림/닫힘
|
|
* - 모달 상태
|
|
* - 토스트 알림
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* import { useUIStore } from '@/stores/ui-store';
|
|
*
|
|
* function Header() {
|
|
* const { theme, setTheme, toggleSidebar } = useUIStore();
|
|
*
|
|
* return (
|
|
* <header>
|
|
* <button onClick={toggleSidebar}>Menu</button>
|
|
* <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
|
|
* Toggle Theme
|
|
* </button>
|
|
* </header>
|
|
* );
|
|
* }
|
|
* ```
|
|
*/
|
|
export const useUIStore = create<UIState>()(
|
|
persist(
|
|
(set) => ({
|
|
// ========== 테마 ==========
|
|
theme: "system",
|
|
setTheme: (theme) => set({ theme }),
|
|
|
|
// ========== 사이드바 ==========
|
|
isSidebarOpen: true,
|
|
toggleSidebar: () =>
|
|
set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
|
|
setSidebarOpen: (isOpen) => set({ isSidebarOpen: isOpen }),
|
|
|
|
// ========== 모달 ==========
|
|
isModalOpen: false,
|
|
modalContent: null,
|
|
openModal: (content) => set({ isModalOpen: true, modalContent: content }),
|
|
closeModal: () => set({ isModalOpen: false, modalContent: null }),
|
|
|
|
// ========== 토스트 ==========
|
|
toasts: [],
|
|
addToast: (toast) =>
|
|
set((state) => ({
|
|
toasts: [
|
|
...state.toasts,
|
|
{
|
|
...toast,
|
|
id: `toast-${Date.now()}-${Math.random()}`,
|
|
},
|
|
],
|
|
})),
|
|
removeToast: (id) =>
|
|
set((state) => ({
|
|
toasts: state.toasts.filter((toast) => toast.id !== id),
|
|
})),
|
|
}),
|
|
{
|
|
name: "ui-storage", // localStorage 키 이름
|
|
// 일부 상태는 지속하지 않음 (모달, 토스트)
|
|
partialize: (state) => ({
|
|
theme: state.theme,
|
|
isSidebarOpen: state.isSidebarOpen,
|
|
}),
|
|
},
|
|
),
|
|
);
|