'use client'; import { useState, useEffect, useCallback, useRef } from 'react'; import { useItemMaster } from '@/contexts/ItemMasterContext'; import { useItemMasterStore } from '@/stores/item-master/useItemMasterStore'; import { itemMasterApi } from '@/lib/api/item-master'; import { getErrorMessage, ApiError } from '@/lib/api/error-handler'; import { transformPagesResponse, transformSectionsResponse, transformSectionTemplatesResponse, transformFieldsResponse, transformCustomTabsResponse, transformUnitOptionsResponse, transformSectionTemplateFromSection, } from '@/lib/api/transformers'; import { toast } from 'sonner'; import type { CustomTab } from './useTabManagement'; import type { MasterOption } from '../types'; // 타입 alias type UnitOption = MasterOption; export interface UseInitialDataLoadingReturn { isInitialLoading: boolean; error: string | null; reload: () => Promise; } interface UseInitialDataLoadingProps { setCustomTabs: React.Dispatch>; setUnitOptions: React.Dispatch>; } export function useInitialDataLoading({ setCustomTabs, setUnitOptions, }: UseInitialDataLoadingProps): UseInitialDataLoadingReturn { const { loadItemPages, loadSectionTemplates, loadItemMasterFields, loadIndependentSections, loadIndependentFields, } = useItemMaster(); // ✅ 2025-12-24: Zustand store 연동 const initFromApi = useItemMasterStore((state) => state.initFromApi); const [isInitialLoading, setIsInitialLoading] = useState(true); const [error, setError] = useState(null); // 초기 로딩이 이미 실행되었는지 추적하는 ref const hasInitialLoadRun = useRef(false); const loadInitialData = useCallback(async () => { try { setIsInitialLoading(true); setError(null); // ✅ Zustand store 초기화 (정규화된 상태로 저장) // Context와 병행 운영 - 점진적 마이그레이션 try { await initFromApi(); console.log('✅ [Zustand] Store initialized'); } catch (zustandError) { // Zustand 초기화 실패해도 Context로 fallback console.warn('⚠️ [Zustand] Init failed, falling back to Context:', zustandError); } const data = await itemMasterApi.init(); // 1. 페이지 데이터 로드 (섹션이 이미 포함되어 있음) const transformedPages = transformPagesResponse(data.pages); loadItemPages(transformedPages); // 2. 독립 섹션 로드 (모든 재사용 가능 섹션) if (data.sections && data.sections.length > 0) { const transformedSections = transformSectionsResponse(data.sections); loadIndependentSections(transformedSections); console.log('✅ 독립 섹션 로드:', transformedSections.length); } // 3. 섹션 템플릿 로드 if (data.sectionTemplates && data.sectionTemplates.length > 0) { const transformedTemplates = transformSectionTemplatesResponse(data.sectionTemplates); loadSectionTemplates(transformedTemplates); } else if (data.sections && data.sections.length > 0) { const templates = data.sections .filter((s: { is_template?: boolean }) => s.is_template) .map(transformSectionTemplateFromSection); if (templates.length > 0) { loadSectionTemplates(templates); } } // 4. 필드 로드 if (data.fields && data.fields.length > 0) { const transformedFields = transformFieldsResponse(data.fields); const independentOnlyFields = transformedFields.filter( f => f.section_id === null || f.section_id === undefined ); // eslint-disable-next-line @typescript-eslint/no-explicit-any loadItemMasterFields(transformedFields as any); loadIndependentFields(independentOnlyFields); console.log('✅ 필드 로드:', { total: transformedFields.length, independent: independentOnlyFields.length, }); } // 5. 커스텀 탭 로드 if (data.customTabs && data.customTabs.length > 0) { const transformedTabs = transformCustomTabsResponse(data.customTabs); setCustomTabs(transformedTabs); } // 6. 단위 옵션 로드 if (data.unitOptions && data.unitOptions.length > 0) { const transformedUnits = transformUnitOptionsResponse(data.unitOptions); setUnitOptions(transformedUnits); } console.log('✅ Initial data loaded:', { pages: data.pages?.length || 0, sections: data.sections?.length || 0, fields: data.fields?.length || 0, customTabs: data.customTabs?.length || 0, unitOptions: data.unitOptions?.length || 0, }); } catch (err) { if (err instanceof ApiError && err.errors) { const errorMessages = Object.entries(err.errors) .map(([field, messages]) => `${field}: ${messages.join(', ')}`) .join('\n'); toast.error(errorMessages); setError('입력값을 확인해주세요.'); } else { const errorMessage = getErrorMessage(err); setError(errorMessage); toast.error(errorMessage); } console.error('❌ Failed to load initial data:', err); } finally { setIsInitialLoading(false); } }, [ loadItemPages, loadSectionTemplates, loadItemMasterFields, loadIndependentSections, loadIndependentFields, setCustomTabs, setUnitOptions, ]); // 초기 로딩은 한 번만 실행 (의존성 배열의 함수들이 불안정해도 무한 루프 방지) useEffect(() => { if (hasInitialLoadRun.current) { return; } hasInitialLoadRun.current = true; loadInitialData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { isInitialLoading, error, reload: loadInitialData, }; }