"use client"; /** * PivotState 훅 * 피벗 그리드 상태 저장/복원 관리 */ import { useState, useEffect, useCallback } from "react"; import { PivotFieldConfig, PivotGridState } from "../types"; // ==================== 타입 ==================== export interface PivotStateConfig { enabled: boolean; storageKey?: string; storageType?: "localStorage" | "sessionStorage"; } export interface SavedPivotState { version: string; timestamp: number; fields: PivotFieldConfig[]; expandedRowPaths: string[][]; expandedColumnPaths: string[][]; filterConfig: Record; sortConfig: { field: string; direction: "asc" | "desc"; } | null; } export interface UsePivotStateResult { // 상태 fields: PivotFieldConfig[]; pivotState: PivotGridState; // 상태 변경 setFields: (fields: PivotFieldConfig[]) => void; setPivotState: (state: PivotGridState | ((prev: PivotGridState) => PivotGridState)) => void; // 저장/복원 saveState: () => void; loadState: () => boolean; clearState: () => void; hasStoredState: () => boolean; // 상태 정보 lastSaved: Date | null; isDirty: boolean; } // ==================== 상수 ==================== const STATE_VERSION = "1.0.0"; const DEFAULT_STORAGE_KEY = "pivot-grid-state"; // ==================== 훅 ==================== export function usePivotState( initialFields: PivotFieldConfig[], config: PivotStateConfig ): UsePivotStateResult { const { enabled, storageKey = DEFAULT_STORAGE_KEY, storageType = "localStorage", } = config; // 상태 const [fields, setFieldsInternal] = useState(initialFields); const [pivotState, setPivotStateInternal] = useState({ expandedRowPaths: [], expandedColumnPaths: [], sortConfig: null, filterConfig: {}, }); const [lastSaved, setLastSaved] = useState(null); const [isDirty, setIsDirty] = useState(false); const [initialStateLoaded, setInitialStateLoaded] = useState(false); // 스토리지 가져오기 const getStorage = useCallback(() => { if (typeof window === "undefined") return null; return storageType === "localStorage" ? localStorage : sessionStorage; }, [storageType]); // 저장된 상태 확인 const hasStoredState = useCallback((): boolean => { const storage = getStorage(); if (!storage) return false; return storage.getItem(storageKey) !== null; }, [getStorage, storageKey]); // 상태 저장 const saveState = useCallback(() => { if (!enabled) return; const storage = getStorage(); if (!storage) return; const stateToSave: SavedPivotState = { version: STATE_VERSION, timestamp: Date.now(), fields, expandedRowPaths: pivotState.expandedRowPaths, expandedColumnPaths: pivotState.expandedColumnPaths, filterConfig: pivotState.filterConfig, sortConfig: pivotState.sortConfig, }; try { storage.setItem(storageKey, JSON.stringify(stateToSave)); setLastSaved(new Date()); setIsDirty(false); console.log("✅ 피벗 상태 저장됨:", storageKey); } catch (error) { console.error("❌ 피벗 상태 저장 실패:", error); } }, [enabled, getStorage, storageKey, fields, pivotState]); // 상태 불러오기 const loadState = useCallback((): boolean => { if (!enabled) return false; const storage = getStorage(); if (!storage) return false; try { const saved = storage.getItem(storageKey); if (!saved) return false; const parsedState: SavedPivotState = JSON.parse(saved); // 버전 체크 if (parsedState.version !== STATE_VERSION) { console.warn("⚠️ 저장된 상태 버전이 다름, 무시됨"); return false; } // 상태 복원 setFieldsInternal(parsedState.fields); setPivotStateInternal({ expandedRowPaths: parsedState.expandedRowPaths, expandedColumnPaths: parsedState.expandedColumnPaths, sortConfig: parsedState.sortConfig, filterConfig: parsedState.filterConfig, }); setLastSaved(new Date(parsedState.timestamp)); setIsDirty(false); console.log("✅ 피벗 상태 복원됨:", storageKey); return true; } catch (error) { console.error("❌ 피벗 상태 복원 실패:", error); return false; } }, [enabled, getStorage, storageKey]); // 상태 초기화 const clearState = useCallback(() => { const storage = getStorage(); if (!storage) return; try { storage.removeItem(storageKey); setLastSaved(null); console.log("🗑️ 피벗 상태 삭제됨:", storageKey); } catch (error) { console.error("❌ 피벗 상태 삭제 실패:", error); } }, [getStorage, storageKey]); // 필드 변경 (dirty 플래그 설정) const setFields = useCallback((newFields: PivotFieldConfig[]) => { setFieldsInternal(newFields); setIsDirty(true); }, []); // 피벗 상태 변경 (dirty 플래그 설정) const setPivotState = useCallback( (newState: PivotGridState | ((prev: PivotGridState) => PivotGridState)) => { setPivotStateInternal(newState); setIsDirty(true); }, [] ); // 초기 로드 useEffect(() => { if (!initialStateLoaded && enabled && hasStoredState()) { loadState(); setInitialStateLoaded(true); } }, [enabled, hasStoredState, loadState, initialStateLoaded]); // 초기 필드 동기화 (저장된 상태가 없을 때) useEffect(() => { if (initialStateLoaded) return; if (!hasStoredState() && initialFields.length > 0) { setFieldsInternal(initialFields); setInitialStateLoaded(true); } }, [initialFields, hasStoredState, initialStateLoaded]); // 자동 저장 (변경 시) useEffect(() => { if (!enabled || !isDirty) return; const timeout = setTimeout(() => { saveState(); }, 1000); // 1초 디바운스 return () => clearTimeout(timeout); }, [enabled, isDirty, saveState]); return { fields, pivotState, setFields, setPivotState, saveState, loadState, clearState, hasStoredState, lastSaved, isDirty, }; } export default usePivotState;