/** * 화면 컨텍스트 * 같은 화면 내의 컴포넌트들이 서로 통신할 수 있도록 합니다. */ "use client"; import React, { createContext, useContext, useCallback, useRef, useState } from "react"; import type { DataProvidable, DataReceivable } from "@/types/data-transfer"; import { logger } from "@/lib/utils/logger"; import type { SplitPanelPosition } from "@/contexts/SplitPanelContext"; interface ScreenContextValue { screenId?: number; tableName?: string; menuObjid?: number; // 메뉴 OBJID (카테고리 값 조회 시 필요) splitPanelPosition?: SplitPanelPosition; // 🆕 분할 패널 위치 (left/right) // 🆕 폼 데이터 (RepeaterFieldGroup 등 컴포넌트 데이터 저장) formData: Record; updateFormData: (fieldName: string, value: any) => void; // 컴포넌트 등록 registerDataProvider: (componentId: string, provider: DataProvidable) => void; unregisterDataProvider: (componentId: string) => void; registerDataReceiver: (componentId: string, receiver: DataReceivable) => void; unregisterDataReceiver: (componentId: string) => void; // 컴포넌트 조회 getDataProvider: (componentId: string) => DataProvidable | undefined; getDataReceiver: (componentId: string) => DataReceivable | undefined; // 모든 컴포넌트 조회 getAllDataProviders: () => Map; getAllDataReceivers: () => Map; } const ScreenContext = createContext(null); interface ScreenContextProviderProps { screenId?: number; tableName?: string; menuObjid?: number; // 메뉴 OBJID splitPanelPosition?: SplitPanelPosition; // 🆕 분할 패널 위치 children: React.ReactNode; } /** * 화면 컨텍스트 프로바이더 */ export function ScreenContextProvider({ screenId, tableName, menuObjid, splitPanelPosition, children, }: ScreenContextProviderProps) { const dataProvidersRef = useRef>(new Map()); const dataReceiversRef = useRef>(new Map()); // 🆕 폼 데이터 상태 (RepeaterFieldGroup 등 컴포넌트 데이터 저장) const [formData, setFormData] = useState>({}); // 🆕 폼 데이터 업데이트 함수 const updateFormData = useCallback((fieldName: string, value: any) => { setFormData((prev) => { const updated = { ...prev, [fieldName]: value }; logger.debug("ScreenContext formData 업데이트", { fieldName, valueType: typeof value, isArray: Array.isArray(value), }); return updated; }); }, []); const registerDataProvider = useCallback((componentId: string, provider: DataProvidable) => { dataProvidersRef.current.set(componentId, provider); logger.debug("데이터 제공자 등록", { componentId, componentType: provider.componentType }); }, []); const unregisterDataProvider = useCallback((componentId: string) => { dataProvidersRef.current.delete(componentId); logger.debug("데이터 제공자 해제", { componentId }); }, []); const registerDataReceiver = useCallback((componentId: string, receiver: DataReceivable) => { dataReceiversRef.current.set(componentId, receiver); logger.debug("데이터 수신자 등록", { componentId, componentType: receiver.componentType }); }, []); const unregisterDataReceiver = useCallback((componentId: string) => { dataReceiversRef.current.delete(componentId); logger.debug("데이터 수신자 해제", { componentId }); }, []); const getDataProvider = useCallback((componentId: string) => { return dataProvidersRef.current.get(componentId); }, []); const getDataReceiver = useCallback((componentId: string) => { return dataReceiversRef.current.get(componentId); }, []); const getAllDataProviders = useCallback(() => { return new Map(dataProvidersRef.current); }, []); const getAllDataReceivers = useCallback(() => { return new Map(dataReceiversRef.current); }, []); // 🆕 useMemo로 value 객체 메모이제이션 (무한 루프 방지) const value = React.useMemo( () => ({ screenId, tableName, menuObjid, splitPanelPosition, formData, updateFormData, registerDataProvider, unregisterDataProvider, registerDataReceiver, unregisterDataReceiver, getDataProvider, getDataReceiver, getAllDataProviders, getAllDataReceivers, }), [ screenId, tableName, menuObjid, splitPanelPosition, formData, updateFormData, registerDataProvider, unregisterDataProvider, registerDataReceiver, unregisterDataReceiver, getDataProvider, getDataReceiver, getAllDataProviders, getAllDataReceivers, ], ); return {children}; } /** * 화면 컨텍스트 훅 */ export function useScreenContext() { const context = useContext(ScreenContext); if (!context) { throw new Error("useScreenContext는 ScreenContextProvider 내부에서만 사용할 수 있습니다."); } return context; } /** * 화면 컨텍스트 훅 (선택적) * 컨텍스트가 없어도 에러를 발생시키지 않습니다. */ export function useScreenContextOptional() { return useContext(ScreenContext); }