import { useState, useCallback } from 'react'; const PREFIX = 'kcg.'; /** * localStorage 연동 useState — JSON 직렬화/역직렬화 자동 처리. * 새 키가 추가된 Record 타입은 defaults와 자동 머지. */ export function useLocalStorage(key: string, defaults: T): [T, (v: T | ((prev: T) => T)) => void] { const storageKey = PREFIX + key; const [value, setValueRaw] = useState(() => { try { const raw = localStorage.getItem(storageKey); if (raw === null) return defaults; const parsed = JSON.parse(raw) as T; // Record 타입이면 defaults에 있는 키가 저장값에 없을 때 머지 if (defaults !== null && typeof defaults === 'object' && !Array.isArray(defaults)) { return { ...defaults, ...parsed }; } return parsed; } catch { return defaults; } }); const setValue = useCallback((updater: T | ((prev: T) => T)) => { setValueRaw(prev => { const next = typeof updater === 'function' ? (updater as (prev: T) => T)(prev) : updater; try { localStorage.setItem(storageKey, JSON.stringify(next)); } catch { /* quota exceeded — 무시 */ } return next; }); }, [storageKey]); return [value, setValue]; } /** * Set용 localStorage 연동 — 내부적으로 Array로 직렬화. */ export function useLocalStorageSet(key: string, defaults: Set): [Set, (v: Set | ((prev: Set) => Set)) => void] { const storageKey = PREFIX + key; const [value, setValueRaw] = useState>(() => { try { const raw = localStorage.getItem(storageKey); if (raw === null) return defaults; const arr = JSON.parse(raw); return Array.isArray(arr) ? new Set(arr) : defaults; } catch { return defaults; } }); const setValue = useCallback((updater: Set | ((prev: Set) => Set)) => { setValueRaw(prev => { const next = typeof updater === 'function' ? updater(prev) : updater; try { localStorage.setItem(storageKey, JSON.stringify(Array.from(next))); } catch { /* quota exceeded */ } return next; }); }, [storageKey]); return [value, setValue]; }