/** * 차트 워크스페이스 설정을 DB(백엔드)에 자동 저장·복원하는 훅. * * 사용법: * const { isLoaded, saveNow } = useWorkspacePersist({ layoutId, syncOptions, slots, setLayoutId, setSlots }); * * - 마운트 시 DB에서 설정을 로드하여 상태를 복원한다. * - 설정이 변경될 때마다 debounce(2초) 후 자동 저장한다. * - saveNow() 를 호출하면 즉시 저장한다. */ import { useCallback, useEffect, useRef, useState } from 'react'; import { loadWorkspace, saveWorkspace, loadWatchlist, loadHoldings, WatchlistItem, HoldingsItem, } from '../utils/backendApi'; export interface WorkspaceState { layoutId: string; syncOptions: unknown; slots: unknown[]; } interface UseWorkspacePersistOptions { /** 로그인/로그아웃 시 증가 → DB에서 워크스페이스 재로드 */ sessionKey?: number; /** 현재 워크스페이스 상태 */ state: WorkspaceState; /** DB 로드 후 상태를 반영하는 콜백 */ onLoad: (data: WorkspaceState) => void; /** 관심종목 로드 콜백 */ onWatchlistLoad?: (items: WatchlistItem[]) => void; /** 보유종목 로드 콜백 */ onHoldingsLoad?: (items: HoldingsItem[]) => void; /** 자동 저장 debounce ms (기본 2000) */ debounceMs?: number; /** 자동 저장 비활성화 */ noAutoSave?: boolean; } export function useWorkspacePersist({ sessionKey = 0, state, onLoad, onWatchlistLoad, onHoldingsLoad, debounceMs = 2000, noAutoSave = false, }: UseWorkspacePersistOptions) { const [isLoaded, setIsLoaded] = useState(false); const saveTimerRef = useRef | null>(null); const isFirstRender = useRef(true); // ── 초기 로드 (세션 변경 시 재로드) ───────────────────────────────────────── useEffect(() => { let cancelled = false; setIsLoaded(false); isFirstRender.current = true; (async () => { const [ws, watchlist, holdings] = await Promise.all([ loadWorkspace(), onWatchlistLoad ? loadWatchlist() : Promise.resolve(null), onHoldingsLoad ? loadHoldings() : Promise.resolve(null), ]); if (cancelled) return; if (ws) { onLoad({ layoutId: ws.layoutId, syncOptions: ws.syncOptions, slots: ws.slots, }); } if (watchlist && onWatchlistLoad) onWatchlistLoad(watchlist); if (holdings && onHoldingsLoad) onHoldingsLoad(holdings); setIsLoaded(true); })(); return () => { cancelled = true; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [sessionKey]); // ── 자동 저장 (debounce) ────────────────────────────────────────────────── useEffect(() => { if (!isLoaded || noAutoSave) return; if (isFirstRender.current) { isFirstRender.current = false; return; } if (saveTimerRef.current) clearTimeout(saveTimerRef.current); saveTimerRef.current = setTimeout(() => { saveWorkspace(state as Parameters[0]); }, debounceMs); return () => { if (saveTimerRef.current) clearTimeout(saveTimerRef.current); }; }, [state, isLoaded, noAutoSave, debounceMs]); // ── 즉시 저장 ────────────────────────────────────────────────────────────── const saveNow = useCallback(() => { if (saveTimerRef.current) clearTimeout(saveTimerRef.current); return saveWorkspace(state as Parameters[0]); }, [state]); return { isLoaded, saveNow }; }