From cded99d64473e6414c3ded874932adaae6eec6bc Mon Sep 17 00:00:00 2001 From: kjs Date: Tue, 6 Jan 2026 13:08:33 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(main)/screens/[screenId]/page.tsx | 2 +- frontend/components/common/ScreenModal.tsx | 20 ++-- frontend/components/screen/EditModal.tsx | 58 ++++++------ .../button-primary/ButtonPrimaryComponent.tsx | 10 +- .../entity-search-input/useEntitySearch.ts | 10 +- .../RepeatScreenModalRenderer.tsx | 2 +- .../SplitPanelLayoutComponent.tsx | 62 ++++++------ .../TableSectionRenderer.tsx | 10 +- .../UniversalFormModalComponent.tsx | 94 +++++++++---------- frontend/lib/utils/buttonActions.ts | 10 +- frontend/stores/modalDataStore.ts | 2 +- 11 files changed, 140 insertions(+), 140 deletions(-) diff --git a/frontend/app/(main)/screens/[screenId]/page.tsx b/frontend/app/(main)/screens/[screenId]/page.tsx index dffbd75b..9e92bf2b 100644 --- a/frontend/app/(main)/screens/[screenId]/page.tsx +++ b/frontend/app/(main)/screens/[screenId]/page.tsx @@ -104,7 +104,7 @@ function ScreenViewPage() { // 편집 모달 이벤트 리스너 등록 useEffect(() => { const handleOpenEditModal = (event: CustomEvent) => { - console.log("🎭 편집 모달 열기 이벤트 수신:", event.detail); + // console.log("🎭 편집 모달 열기 이벤트 수신:", event.detail); setEditModalConfig({ screenId: event.detail.screenId, diff --git a/frontend/components/common/ScreenModal.tsx b/frontend/components/common/ScreenModal.tsx index cceadae9..f41c62af 100644 --- a/frontend/components/common/ScreenModal.tsx +++ b/frontend/components/common/ScreenModal.tsx @@ -261,7 +261,7 @@ export const ScreenModal: React.FC = ({ className }) => { // dataSourceId 파라미터 제거 currentUrl.searchParams.delete("dataSourceId"); window.history.pushState({}, "", currentUrl.toString()); - console.log("🧹 URL 파라미터 제거"); + // console.log("🧹 URL 파라미터 제거"); } setModalState({ @@ -277,7 +277,7 @@ export const ScreenModal: React.FC = ({ className }) => { setSelectedData([]); // 🆕 선택된 데이터 초기화 setContinuousMode(false); localStorage.setItem("screenModal_continuousMode", "false"); // localStorage에 저장 - console.log("🔄 연속 모드 초기화: false"); + // console.log("🔄 연속 모드 초기화: false"); }; // 저장 성공 이벤트 처리 (연속 등록 모드 지원) @@ -285,36 +285,36 @@ export const ScreenModal: React.FC = ({ className }) => { // 🆕 모달이 열린 후 500ms 이내의 저장 성공 이벤트는 무시 (이전 이벤트 방지) const timeSinceOpen = Date.now() - modalOpenedAtRef.current; if (timeSinceOpen < 500) { - console.log("⏭️ [ScreenModal] 모달 열린 직후 저장 성공 이벤트 무시:", { timeSinceOpen }); + // console.log("⏭️ [ScreenModal] 모달 열린 직후 저장 성공 이벤트 무시:", { timeSinceOpen }); return; } const isContinuousMode = continuousMode; - console.log("💾 저장 성공 이벤트 수신"); - console.log("📌 현재 연속 모드 상태:", isContinuousMode); - console.log("📌 localStorage:", localStorage.getItem("screenModal_continuousMode")); + // console.log("💾 저장 성공 이벤트 수신"); + // console.log("📌 현재 연속 모드 상태:", isContinuousMode); + // console.log("📌 localStorage:", localStorage.getItem("screenModal_continuousMode")); if (isContinuousMode) { // 연속 모드: 폼만 초기화하고 모달은 유지 - console.log("✅ 연속 모드 활성화 - 폼 초기화 및 화면 리셋"); + // console.log("✅ 연속 모드 활성화 - 폼 초기화 및 화면 리셋"); // 1. 폼 데이터 초기화 setFormData({}); // 2. 리셋 키 변경 (컴포넌트 강제 리마운트) setResetKey((prev) => prev + 1); - console.log("🔄 resetKey 증가 - 컴포넌트 리마운트"); + // console.log("🔄 resetKey 증가 - 컴포넌트 리마운트"); // 3. 화면 데이터 다시 로드 (채번 규칙 새로 생성) if (modalState.screenId) { - console.log("🔄 화면 데이터 다시 로드:", modalState.screenId); + // console.log("🔄 화면 데이터 다시 로드:", modalState.screenId); loadScreenData(modalState.screenId); } toast.success("저장되었습니다. 계속 입력하세요."); } else { // 일반 모드: 모달 닫기 - console.log("❌ 일반 모드 - 모달 닫기"); + // console.log("❌ 일반 모드 - 모달 닫기"); handleCloseModal(); } }; diff --git a/frontend/components/screen/EditModal.tsx b/frontend/components/screen/EditModal.tsx index 58149088..32451d18 100644 --- a/frontend/components/screen/EditModal.tsx +++ b/frontend/components/screen/EditModal.tsx @@ -190,14 +190,14 @@ export const EditModal: React.FC = ({ className }) => { const innerLayoutData = await screenApi.getLayout(section.screenId); saveButton = findSaveButtonInComponents(innerLayoutData?.components || []); if (saveButton) { - console.log("[EditModal] 조건부 컨테이너 내부에서 저장 버튼 발견:", { - sectionScreenId: section.screenId, - sectionLabel: section.label, - }); + // console.log("[EditModal] 조건부 컨테이너 내부에서 저장 버튼 발견:", { + // sectionScreenId: section.screenId, + // sectionLabel: section.label, + // }); break; } } catch (innerError) { - console.warn("[EditModal] 내부 화면 레이아웃 조회 실패:", section.screenId); + // console.warn("[EditModal] 내부 화면 레이아웃 조회 실패:", section.screenId); } } } @@ -207,7 +207,7 @@ export const EditModal: React.FC = ({ className }) => { } if (!saveButton) { - console.log("[EditModal] 저장 버튼을 찾을 수 없음:", targetScreenId); + // console.log("[EditModal] 저장 버튼을 찾을 수 없음:", targetScreenId); return null; } @@ -219,14 +219,14 @@ export const EditModal: React.FC = ({ className }) => { dataflowConfig: webTypeConfig.dataflowConfig, dataflowTiming: webTypeConfig.dataflowConfig?.flowConfig?.executionTiming || "after", }; - console.log("[EditModal] 저장 버튼 제어로직 설정 발견:", config); + // console.log("[EditModal] 저장 버튼 제어로직 설정 발견:", config); return config; } - console.log("[EditModal] 저장 버튼에 제어로직 설정 없음"); + // console.log("[EditModal] 저장 버튼에 제어로직 설정 없음"); return null; } catch (error) { - console.warn("[EditModal] 저장 버튼 설정 조회 실패:", error); + // console.warn("[EditModal] 저장 버튼 설정 조회 실패:", error); return null; } }; @@ -309,16 +309,16 @@ export const EditModal: React.FC = ({ className }) => { // 🆕 그룹 데이터 조회 함수 const loadGroupData = async () => { if (!modalState.tableName || !modalState.groupByColumns || modalState.groupByColumns.length === 0) { - console.warn("테이블명 또는 그룹핑 컬럼이 없습니다."); + // console.warn("테이블명 또는 그룹핑 컬럼이 없습니다."); return; } try { - console.log("🔍 그룹 데이터 조회 시작:", { - tableName: modalState.tableName, - groupByColumns: modalState.groupByColumns, - editData: modalState.editData, - }); + // console.log("🔍 그룹 데이터 조회 시작:", { + // tableName: modalState.tableName, + // groupByColumns: modalState.groupByColumns, + // editData: modalState.editData, + // }); // 그룹핑 컬럼 값 추출 (예: order_no = "ORD-20251124-001") const groupValues: Record = {}; @@ -329,14 +329,14 @@ export const EditModal: React.FC = ({ className }) => { }); if (Object.keys(groupValues).length === 0) { - console.warn("그룹핑 컬럼 값이 없습니다:", modalState.groupByColumns); + // console.warn("그룹핑 컬럼 값이 없습니다:", modalState.groupByColumns); return; } - console.log("🔍 그룹 조회 요청:", { - tableName: modalState.tableName, - groupValues, - }); + // console.log("🔍 그룹 조회 요청:", { + // tableName: modalState.tableName, + // groupValues, + // }); // 같은 그룹의 모든 레코드 조회 (entityJoinApi 사용) const { entityJoinApi } = await import("@/lib/api/entityJoin"); @@ -347,13 +347,13 @@ export const EditModal: React.FC = ({ className }) => { enableEntityJoin: true, }); - console.log("🔍 그룹 조회 응답:", response); + // console.log("🔍 그룹 조회 응답:", response); // entityJoinApi는 배열 또는 { data: [] } 형식으로 반환 const dataArray = Array.isArray(response) ? response : response?.data || []; if (dataArray.length > 0) { - console.log("✅ 그룹 데이터 조회 성공:", dataArray.length, "건"); + // console.log("✅ 그룹 데이터 조회 성공:", dataArray.length, "건"); setGroupData(dataArray); setOriginalGroupData(JSON.parse(JSON.stringify(dataArray))); // Deep copy toast.info(`${dataArray.length}개의 관련 품목을 불러왔습니다.`); @@ -374,7 +374,7 @@ export const EditModal: React.FC = ({ className }) => { try { setLoading(true); - console.log("화면 데이터 로딩 시작:", screenId); + // console.log("화면 데이터 로딩 시작:", screenId); // 화면 정보와 레이아웃 데이터 로딩 const [screenInfo, layoutData] = await Promise.all([ @@ -382,7 +382,7 @@ export const EditModal: React.FC = ({ className }) => { screenApi.getLayout(screenId), ]); - console.log("API 응답:", { screenInfo, layoutData }); + // console.log("API 응답:", { screenInfo, layoutData }); if (screenInfo && layoutData) { const components = layoutData.components || []; @@ -395,11 +395,11 @@ export const EditModal: React.FC = ({ className }) => { components, screenInfo: screenInfo, }); - console.log("화면 데이터 설정 완료:", { - componentsCount: components.length, - dimensions, - screenInfo, - }); + // console.log("화면 데이터 설정 완료:", { + // componentsCount: components.length, + // dimensions, + // screenInfo, + // }); } else { throw new Error("화면 데이터가 없습니다"); } diff --git a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx index a71f6e03..86155bd6 100644 --- a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx +++ b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx @@ -306,11 +306,11 @@ export const ButtonPrimaryComponent: React.FC = ({ useEffect(() => { const newData = splitPanelContext?.selectedLeftData ?? null; setTrackedSelectedLeftData(newData); - console.log("🔄 [ButtonPrimary] selectedLeftData 변경 감지:", { - label: component.label, - hasData: !!newData, - dataKeys: newData ? Object.keys(newData) : [], - }); + // console.log("🔄 [ButtonPrimary] selectedLeftData 변경 감지:", { + // label: component.label, + // hasData: !!newData, + // dataKeys: newData ? Object.keys(newData) : [], + // }); }, [splitPanelContext?.selectedLeftData, component.label]); // modalDataStore 상태 구독 (실시간 업데이트) diff --git a/frontend/lib/registry/components/entity-search-input/useEntitySearch.ts b/frontend/lib/registry/components/entity-search-input/useEntitySearch.ts index 2ae71595..a118cffe 100644 --- a/frontend/lib/registry/components/entity-search-input/useEntitySearch.ts +++ b/frontend/lib/registry/components/entity-search-input/useEntitySearch.ts @@ -53,11 +53,11 @@ export function useEntitySearch({ limit: pagination.limit.toString(), }); - console.log("[useEntitySearch] 검색 실행:", { - tableName, - filterCondition: filterConditionRef.current, - searchText: text, - }); + // console.log("[useEntitySearch] 검색 실행:", { + // tableName, + // filterCondition: filterConditionRef.current, + // searchText: text, + // }); const response = await apiClient.get( `/entity-search/${tableName}?${params.toString()}` diff --git a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalRenderer.tsx b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalRenderer.tsx index b43722d3..814e3545 100644 --- a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalRenderer.tsx +++ b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalRenderer.tsx @@ -6,7 +6,7 @@ import { RepeatScreenModalDefinition } from "./index"; // 컴포넌트 자동 등록 if (typeof window !== "undefined") { ComponentRegistry.registerComponent(RepeatScreenModalDefinition); - console.log("✅ RepeatScreenModal 컴포넌트 등록 완료"); + // console.log("✅ RepeatScreenModal 컴포넌트 등록 완료"); } export {}; diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index bfb26c90..ad7f5302 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -205,12 +205,12 @@ export const SplitPanelLayoutComponent: React.FC const splitPanelId = `split-panel-${component.id}`; // 디버깅: Context 연결 상태 확인 - console.log("🔗 [SplitPanelLayout] Context 연결 상태:", { - componentId: component.id, - splitPanelId, - hasRegisterFunc: typeof ctxRegisterSplitPanel === "function", - splitPanelsSize: splitPanelContext.splitPanels?.size ?? "없음", - }); + // console.log("🔗 [SplitPanelLayout] Context 연결 상태:", { + // componentId: component.id, + // splitPanelId, + // hasRegisterFunc: typeof ctxRegisterSplitPanel === "function", + // splitPanelsSize: splitPanelContext.splitPanels?.size ?? "없음", + // }); // Context에 분할 패널 등록 (좌표 정보 포함) - 마운트 시 1회만 실행 const ctxRegisterRef = useRef(ctxRegisterSplitPanel); @@ -235,15 +235,15 @@ export const SplitPanelLayoutComponent: React.FC isDragging: false, }; - console.log("📦 [SplitPanelLayout] Context에 분할 패널 등록:", { - splitPanelId, - panelInfo, - }); + // console.log("📦 [SplitPanelLayout] Context에 분할 패널 등록:", { + // splitPanelId, + // panelInfo, + // }); ctxRegisterRef.current(splitPanelId, panelInfo); return () => { - console.log("📦 [SplitPanelLayout] Context에서 분할 패널 해제:", splitPanelId); + // console.log("📦 [SplitPanelLayout] Context에서 분할 패널 해제:", splitPanelId); ctxUnregisterRef.current(splitPanelId); }; // 마운트/언마운트 시에만 실행, 위치/크기 변경은 별도 업데이트로 처리 @@ -311,11 +311,11 @@ export const SplitPanelLayoutComponent: React.FC // 🆕 그룹별 합산된 데이터 계산 const summedLeftData = useMemo(() => { - console.log("🔍 [그룹합산] leftGroupSumConfig:", leftGroupSumConfig); + // console.log("🔍 [그룹합산] leftGroupSumConfig:", leftGroupSumConfig); // 그룹핑이 비활성화되었거나 그룹 기준 컬럼이 없으면 원본 데이터 반환 if (!leftGroupSumConfig?.enabled || !leftGroupSumConfig?.groupByColumn) { - console.log("🔍 [그룹합산] 그룹핑 비활성화 - 원본 데이터 반환"); + // console.log("🔍 [그룹합산] 그룹핑 비활성화 - 원본 데이터 반환"); return leftData; } @@ -756,8 +756,8 @@ export const SplitPanelLayoutComponent: React.FC } }); - console.log("🔗 [분할패널] additionalJoinColumns:", additionalJoinColumns); - console.log("🔗 [분할패널] configuredColumns:", configuredColumns); + // console.log("🔗 [분할패널] additionalJoinColumns:", additionalJoinColumns); + // console.log("🔗 [분할패널] configuredColumns:", configuredColumns); const result = await entityJoinApi.getTableDataWithJoins(leftTableName, { page: 1, @@ -769,10 +769,10 @@ export const SplitPanelLayoutComponent: React.FC }); // 🔍 디버깅: API 응답 데이터의 키 확인 - if (result.data && result.data.length > 0) { - console.log("🔗 [분할패널] API 응답 첫 번째 데이터 키:", Object.keys(result.data[0])); - console.log("🔗 [분할패널] API 응답 첫 번째 데이터:", result.data[0]); - } + // if (result.data && result.data.length > 0) { + // console.log("🔗 [분할패널] API 응답 첫 번째 데이터 키:", Object.keys(result.data[0])); + // console.log("🔗 [분할패널] API 응답 첫 번째 데이터:", result.data[0]); + // } // 가나다순 정렬 (좌측 패널의 표시 컬럼 기준) const leftColumn = componentConfig.rightPanel?.relation?.leftColumn; @@ -1000,7 +1000,7 @@ export const SplitPanelLayoutComponent: React.FC if (leftTableName && !isDesignMode) { import("@/stores/modalDataStore").then(({ useModalDataStore }) => { useModalDataStore.getState().setData(leftTableName, [item]); - console.log(`✅ 분할 패널 좌측 선택: ${leftTableName}`, item); + // console.log(`✅ 분할 패널 좌측 선택: ${leftTableName}`, item); }); } }, @@ -1198,7 +1198,7 @@ export const SplitPanelLayoutComponent: React.FC } }); setLeftColumnLabels(labels); - console.log("✅ 좌측 컬럼 라벨 로드:", labels); + // console.log("✅ 좌측 컬럼 라벨 로드:", labels); } catch (error) { console.error("좌측 테이블 컬럼 라벨 로드 실패:", error); } @@ -1227,7 +1227,7 @@ export const SplitPanelLayoutComponent: React.FC } }); setRightColumnLabels(labels); - console.log("✅ 우측 컬럼 라벨 로드:", labels); + // console.log("✅ 우측 컬럼 라벨 로드:", labels); } catch (error) { console.error("우측 테이블 컬럼 정보 로드 실패:", error); } @@ -1269,7 +1269,7 @@ export const SplitPanelLayoutComponent: React.FC }; }); mappings[columnName] = valueMap; - console.log(`✅ 좌측 카테고리 매핑 로드 [${columnName}]:`, valueMap); + // console.log(`✅ 좌측 카테고리 매핑 로드 [${columnName}]:`, valueMap); } } catch (error) { console.error(`좌측 카테고리 값 조회 실패 [${columnName}]:`, error); @@ -1307,7 +1307,7 @@ export const SplitPanelLayoutComponent: React.FC } }); - console.log("🔍 우측 패널 카테고리 로드 대상 테이블:", Array.from(tablesToLoad)); + // console.log("🔍 우측 패널 카테고리 로드 대상 테이블:", Array.from(tablesToLoad)); // 각 테이블에 대해 카테고리 매핑 로드 for (const tableName of tablesToLoad) { @@ -1940,7 +1940,7 @@ export const SplitPanelLayoutComponent: React.FC useEffect(() => { const handleRefreshTable = () => { if (!isDesignMode) { - console.log("🔄 [SplitPanel] refreshTable 이벤트 수신 - 데이터 새로고침"); + // console.log("🔄 [SplitPanel] refreshTable 이벤트 수신 - 데이터 새로고침"); loadLeftData(); // 선택된 항목이 있으면 우측 패널도 새로고침 if (selectedLeftItem) { @@ -2104,12 +2104,12 @@ export const SplitPanelLayoutComponent: React.FC (() => { // 🆕 그룹별 합산된 데이터 사용 const dataSource = summedLeftData; - console.log( - "🔍 [테이블모드 렌더링] dataSource 개수:", - dataSource.length, - "leftGroupSumConfig:", - leftGroupSumConfig, - ); + // console.log( + // "🔍 [테이블모드 렌더링] dataSource 개수:", + // dataSource.length, + // "leftGroupSumConfig:", + // leftGroupSumConfig, + // ); // 🔧 로컬 검색 필터 적용 const filteredData = leftSearchQuery diff --git a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx index fb1b2ea3..b4c6d2d8 100644 --- a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx +++ b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx @@ -820,10 +820,10 @@ export function TableSectionRenderer({ const initialData = formData[tableSectionKey]; if (Array.isArray(initialData) && initialData.length > 0) { - console.log("[TableSectionRenderer] 초기 데이터 로드:", { - sectionId, - itemCount: initialData.length, - }); + // console.log("[TableSectionRenderer] 초기 데이터 로드:", { + // sectionId, + // itemCount: initialData.length, + // }); setTableData(initialData); initialDataLoadedRef.current = true; @@ -1327,7 +1327,7 @@ export function TableSectionRenderer({ } } } - console.log("[TableSectionRenderer] baseFilterCondition:", condition, "preFilters:", filters?.preFilters); + // console.log("[TableSectionRenderer] baseFilterCondition:", condition, "preFilters:", filters?.preFilters); return condition; }, [filters?.preFilters]); diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx index 5f087b71..3ba30347 100644 --- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx +++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx @@ -216,11 +216,11 @@ export function UniversalFormModalComponent({ // 초기화 - 최초 마운트 시 또는 initialData가 변경되었을 때 실행 useEffect(() => { - console.log("[UniversalFormModal] useEffect 시작", { - initialData, - hasInitialized: hasInitialized.current, - lastInitializedId: lastInitializedId.current, - }); + // console.log("[UniversalFormModal] useEffect 시작", { + // initialData, + // hasInitialized: hasInitialized.current, + // lastInitializedId: lastInitializedId.current, + // }); // initialData에서 ID 값 추출 (id, ID, objid 등) const currentId = initialData?.id || initialData?.ID || initialData?.objid; @@ -235,7 +235,7 @@ export function UniversalFormModalComponent({ if (hasInitialized.current && lastInitializedId.current === currentIdString) { // 생성 모드에서 데이터가 새로 전달된 경우는 재초기화 필요 if (!createModeDataHash || capturedInitialData.current) { - console.log("[UniversalFormModal] 초기화 스킵 - 이미 초기화됨"); + // console.log("[UniversalFormModal] 초기화 스킵 - 이미 초기화됨"); // 🆕 채번 플래그가 true인데 formData에 값이 없으면 재생성 필요 // (컴포넌트 remount로 인해 state가 초기화된 경우) return; @@ -245,18 +245,18 @@ export function UniversalFormModalComponent({ // 🆕 컴포넌트 remount 감지: hasInitialized가 true인데 formData가 비어있으면 재초기화 // (React의 Strict Mode나 EmbeddedScreen 리렌더링으로 인한 remount) if (hasInitialized.current && !currentIdString) { - console.log("[UniversalFormModal] 컴포넌트 remount 감지 - 채번 플래그 초기화"); + // console.log("[UniversalFormModal] 컴포넌트 remount 감지 - 채번 플래그 초기화"); numberingGeneratedRef.current = false; isGeneratingRef.current = false; } // 🆕 수정 모드: initialData에 데이터가 있으면서 ID가 변경된 경우 재초기화 if (hasInitialized.current && currentIdString && lastInitializedId.current !== currentIdString) { - console.log("[UniversalFormModal] ID 변경 감지 - 재초기화:", { - prevId: lastInitializedId.current, - newId: currentIdString, - initialData: initialData, - }); + // console.log("[UniversalFormModal] ID 변경 감지 - 재초기화:", { + // prevId: lastInitializedId.current, + // newId: currentIdString, + // initialData: initialData, + // }); // 채번 플래그 초기화 (새 항목이므로) numberingGeneratedRef.current = false; isGeneratingRef.current = false; @@ -266,10 +266,10 @@ export function UniversalFormModalComponent({ if (initialData && Object.keys(initialData).length > 0) { capturedInitialData.current = JSON.parse(JSON.stringify(initialData)); // 깊은 복사 lastInitializedId.current = currentIdString; - console.log("[UniversalFormModal] 초기 데이터 캡처:", capturedInitialData.current); + // console.log("[UniversalFormModal] 초기 데이터 캡처:", capturedInitialData.current); } - console.log("[UniversalFormModal] initializeForm 호출 예정"); + // console.log("[UniversalFormModal] initializeForm 호출 예정"); hasInitialized.current = true; initializeForm(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -279,7 +279,7 @@ export function UniversalFormModalComponent({ useEffect(() => { if (!hasInitialized.current) return; // 최초 초기화 전이면 스킵 - console.log("[useEffect config 변경] 재초기화 스킵 (채번 중복 방지)"); + // console.log("[useEffect config 변경] 재초기화 스킵 (채번 중복 방지)"); // initializeForm(); // 주석 처리 - config 변경 시 재초기화 안 함 (채번 중복 방지) // eslint-disable-next-line react-hooks/exhaustive-deps }, [config]); @@ -287,7 +287,7 @@ export function UniversalFormModalComponent({ // 컴포넌트 unmount 시 채번 플래그 초기화 useEffect(() => { return () => { - console.log("[채번] 컴포넌트 unmount - 플래그 초기화"); + // console.log("[채번] 컴포넌트 unmount - 플래그 초기화"); numberingGeneratedRef.current = false; isGeneratingRef.current = false; }; @@ -359,14 +359,14 @@ export function UniversalFormModalComponent({ // 테이블 타입 섹션 찾기 const tableSection = config.sections.find((s) => s.type === "table"); if (!tableSection) { - console.log("[UniversalFormModal] 테이블 섹션 없음 - _groupedData 무시"); + // console.log("[UniversalFormModal] 테이블 섹션 없음 - _groupedData 무시"); return; } - console.log("[UniversalFormModal] 수정 모드 - 테이블 섹션 초기화:", { - sectionId: tableSection.id, - itemCount: _groupedData.length, - }); + // console.log("[UniversalFormModal] 수정 모드 - 테이블 섹션 초기화:", { + // sectionId: tableSection.id, + // itemCount: _groupedData.length, + // }); // 원본 데이터 저장 (수정/삭제 추적용) setOriginalGroupedData(JSON.parse(JSON.stringify(_groupedData))); @@ -421,31 +421,31 @@ export function UniversalFormModalComponent({ } if (isGeneratingRef.current) { - console.log("[채번] 생성 진행 중 - 스킵"); + // console.log("[채번] 생성 진행 중 - 스킵"); return; } isGeneratingRef.current = true; // 진행 중 표시 - console.log("[채번] 생성 시작", { sectionsCount: config.sections.length }); + // console.log("[채번] 생성 시작", { sectionsCount: config.sections.length }); const updatedData = { ...currentFormData }; let hasChanges = false; for (const section of config.sections) { - console.log("[채번] 섹션 검사:", section.title, { type: section.type, repeatable: section.repeatable, fieldsCount: section.fields?.length }); + // console.log("[채번] 섹션 검사:", section.title, { type: section.type, repeatable: section.repeatable, fieldsCount: section.fields?.length }); if (section.repeatable || section.type === "table") continue; for (const field of (section.fields || [])) { // generateOnOpen은 기본값 true (undefined일 경우 true로 처리) const shouldGenerateOnOpen = field.numberingRule?.generateOnOpen !== false; - console.log("[채번] 필드 검사:", field.columnName, { - hasNumberingRule: !!field.numberingRule, - enabled: field.numberingRule?.enabled, - generateOnOpen: field.numberingRule?.generateOnOpen, - shouldGenerateOnOpen, - ruleId: field.numberingRule?.ruleId, - currentValue: updatedData[field.columnName], - }); + // console.log("[채번] 필드 검사:", field.columnName, { + // hasNumberingRule: !!field.numberingRule, + // enabled: field.numberingRule?.enabled, + // generateOnOpen: field.numberingRule?.generateOnOpen, + // shouldGenerateOnOpen, + // ruleId: field.numberingRule?.ruleId, + // currentValue: updatedData[field.columnName], + // }); if ( field.numberingRule?.enabled && shouldGenerateOnOpen && @@ -453,7 +453,7 @@ export function UniversalFormModalComponent({ !updatedData[field.columnName] ) { try { - console.log(`[채번 미리보기 API 호출] ${field.columnName}, ruleId: ${field.numberingRule.ruleId}`); + // console.log(`[채번 미리보기 API 호출] ${field.columnName}, ruleId: ${field.numberingRule.ruleId}`); // generateOnOpen: 미리보기만 표시 (DB 시퀀스 증가 안 함) const response = await previewNumberingCode(field.numberingRule.ruleId); if (response.success && response.data?.generatedCode) { @@ -465,10 +465,10 @@ export function UniversalFormModalComponent({ hasChanges = true; numberingGeneratedRef.current = true; // 생성 완료 표시 - console.log( - `[채번 미리보기 완료] ${field.columnName} = ${response.data.generatedCode} (저장 시 실제 할당)`, - ); - console.log(`[채번 규칙 ID 저장] ${ruleIdKey} = ${field.numberingRule.ruleId}`); + // console.log( + // `[채번 미리보기 완료] ${field.columnName} = ${response.data.generatedCode} (저장 시 실제 할당)`, + // ); + // console.log(`[채번 규칙 ID 저장] ${ruleIdKey} = ${field.numberingRule.ruleId}`); // 부모 컴포넌트에도 ruleId 전달 (ModalRepeaterTable → ScreenModal) if (onChange) { @@ -476,7 +476,7 @@ export function UniversalFormModalComponent({ ...updatedData, [ruleIdKey]: field.numberingRule.ruleId, }); - console.log(`[채번] 부모에게 ruleId 전달: ${ruleIdKey}`); + // console.log(`[채번] 부모에게 ruleId 전달: ${ruleIdKey}`); } } } catch (error) { @@ -497,17 +497,17 @@ export function UniversalFormModalComponent({ // 폼 초기화 const initializeForm = useCallback(async () => { - console.log("[initializeForm] 시작"); + // console.log("[initializeForm] 시작"); // 캡처된 initialData 사용 (props로 전달된 initialData가 아닌) const effectiveInitialData = capturedInitialData.current || initialData; - console.log("[initializeForm] 초기 데이터:", { - capturedInitialData: capturedInitialData.current, - initialData: initialData, - effectiveInitialData: effectiveInitialData, - hasData: effectiveInitialData && Object.keys(effectiveInitialData).length > 0, - }); + // console.log("[initializeForm] 초기 데이터:", { + // capturedInitialData: capturedInitialData.current, + // initialData: initialData, + // effectiveInitialData: effectiveInitialData, + // hasData: effectiveInitialData && Object.keys(effectiveInitialData).length > 0, + // }); const newFormData: FormDataState = {}; const newRepeatSections: { [sectionId: string]: RepeatSectionItem[] } = {}; @@ -671,9 +671,9 @@ export function UniversalFormModalComponent({ } // 채번규칙 자동 생성 - console.log("[initializeForm] generateNumberingValues 호출"); + // console.log("[initializeForm] generateNumberingValues 호출"); await generateNumberingValues(newFormData); - console.log("[initializeForm] 완료"); + // console.log("[initializeForm] 완료"); // eslint-disable-next-line react-hooks/exhaustive-deps }, [config]); // initialData는 의존성에서 제거 (capturedInitialData.current 사용) diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 681e9a3f..53373d41 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -3022,11 +3022,11 @@ export class ButtonActionExecutor { comp.componentType === "split-panel-layout", ); } - console.log("🔍 [openEditModal] 분할 패널 확인:", { - targetScreenId: config.targetScreenId, - hasSplitPanel, - componentTypes: layoutData?.components?.map((c: any) => c.type || c.componentType) || [], - }); + // console.log("🔍 [openEditModal] 분할 패널 확인:", { + // targetScreenId: config.targetScreenId, + // hasSplitPanel, + // componentTypes: layoutData?.components?.map((c: any) => c.type || c.componentType) || [], + // }); } catch (error) { console.warn("레이아웃 정보를 가져오지 못했습니다:", error); } diff --git a/frontend/stores/modalDataStore.ts b/frontend/stores/modalDataStore.ts index eb6a2d11..6b2b0f70 100644 --- a/frontend/stores/modalDataStore.ts +++ b/frontend/stores/modalDataStore.ts @@ -77,7 +77,7 @@ export const useModalDataStore = create()( dataRegistry: {}, setData: (sourceId, items) => { - console.log("📦 [ModalDataStore] 데이터 저장:", { sourceId, itemCount: items.length, items }); + // console.log("📦 [ModalDataStore] 데이터 저장:", { sourceId, itemCount: items.length, items }); set((state) => ({ dataRegistry: { ...state.dataRegistry, From eb61506acd4cc3304d54717113b35f5e90cb76d2 Mon Sep 17 00:00:00 2001 From: kjs Date: Tue, 6 Jan 2026 13:43:47 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EB=B6=84=ED=95=A0=ED=8C=A8=EB=84=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EB=8F=99=EC=9E=91=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/tableManagementController.ts | 10 +- .../TableSectionRenderer.tsx | 19 +- .../UniversalFormModalComponent.tsx | 219 ++++++++++++++++++ 3 files changed, 242 insertions(+), 6 deletions(-) diff --git a/backend-node/src/controllers/tableManagementController.ts b/backend-node/src/controllers/tableManagementController.ts index 7c84898b..83384be6 100644 --- a/backend-node/src/controllers/tableManagementController.ts +++ b/backend-node/src/controllers/tableManagementController.ts @@ -775,7 +775,8 @@ export async function getTableData( const userField = autoFilter?.userField || "companyCode"; const userValue = (req.user as any)[userField]; - if (userValue) { + // 🆕 최고 관리자(company_code = '*')는 모든 회사 데이터 조회 가능 + if (userValue && userValue !== "*") { enhancedSearch[filterColumn] = userValue; logger.info("🔍 현재 사용자 필터 적용:", { @@ -784,6 +785,10 @@ export async function getTableData( userValue, tableName, }); + } else if (userValue === "*") { + logger.info("🔓 최고 관리자 - 회사 필터 미적용 (모든 회사 데이터 조회)", { + tableName, + }); } else { logger.warn("⚠️ 사용자 정보 필드 값 없음:", { userField, @@ -792,6 +797,9 @@ export async function getTableData( } } + // 🆕 최종 검색 조건 로그 + logger.info(`🔍 최종 검색 조건 (enhancedSearch):`, JSON.stringify(enhancedSearch)); + // 데이터 조회 const result = await tableManagementService.getTableData(tableName, { page: parseInt(page), diff --git a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx index b4c6d2d8..9c238b47 100644 --- a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx +++ b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx @@ -816,14 +816,23 @@ export function TableSectionRenderer({ // 이미 초기화되었으면 스킵 if (initialDataLoadedRef.current) return; - const tableSectionKey = `_tableSection_${sectionId}`; + const tableSectionKey = `__tableSection_${sectionId}`; const initialData = formData[tableSectionKey]; + console.log("[TableSectionRenderer] 초기 데이터 확인:", { + sectionId, + tableSectionKey, + hasInitialData: !!initialData, + initialDataLength: Array.isArray(initialData) ? initialData.length : 0, + formDataKeys: Object.keys(formData).filter(k => k.startsWith("__tableSection_")), + }); + if (Array.isArray(initialData) && initialData.length > 0) { - // console.log("[TableSectionRenderer] 초기 데이터 로드:", { - // sectionId, - // itemCount: initialData.length, - // }); + console.log("[TableSectionRenderer] 초기 데이터 로드:", { + sectionId, + itemCount: initialData.length, + firstItem: initialData[0], + }); setTableData(initialData); initialDataLoadedRef.current = true; diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx index 3ba30347..a605c45e 100644 --- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx +++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx @@ -588,6 +588,225 @@ export function UniversalFormModalComponent({ } } + // 🆕 테이블 섹션(type: "table") 디테일 데이터 로드 (마스터-디테일 구조) + // 수정 모드일 때 디테일 테이블에서 데이터 가져오기 + if (effectiveInitialData) { + console.log("[initializeForm] 테이블 섹션 디테일 로드 시작", { + sectionsCount: config.sections.length, + effectiveInitialDataKeys: Object.keys(effectiveInitialData), + }); + + for (const section of config.sections) { + if (section.type !== "table" || !section.tableConfig) { + continue; + } + + const tableConfig = section.tableConfig; + const editConfig = tableConfig.editConfig; + const saveConfig = tableConfig.saveConfig; + + console.log(`[initializeForm] 테이블 섹션 ${section.id} 검사:`, { + hasEditConfig: !!editConfig, + loadOnEdit: editConfig?.loadOnEdit, + hasSaveConfig: !!saveConfig, + targetTable: saveConfig?.targetTable, + linkColumn: editConfig?.linkColumn, + }); + + // 수정 모드 로드 설정 확인 (기본값: true) + if (editConfig?.loadOnEdit === false) { + console.log(`[initializeForm] 테이블 섹션 ${section.id}: loadOnEdit=false, 스킵`); + continue; + } + + // 디테일 테이블과 연결 정보 확인 + const detailTable = saveConfig?.targetTable; + let linkColumn = editConfig?.linkColumn; + + if (!detailTable) { + console.log(`[initializeForm] 테이블 섹션 ${section.id}: saveConfig.targetTable 미설정, 스킵`); + continue; + } + + // linkColumn이 설정되지 않았으면, 디테일 테이블 컬럼 정보 조회하여 자동 감지 + if (!linkColumn?.masterField || !linkColumn?.detailField) { + try { + // 마스터 테이블명 확인 (saveConfig에서) + // 1. customApiSave.multiTable.mainTable.tableName (다중 테이블 저장) + // 2. saveConfig.tableName (단일 테이블 저장) + const masterTable = config.saveConfig?.customApiSave?.multiTable?.mainTable?.tableName + || config.saveConfig?.tableName; + + // 디테일 테이블의 컬럼 목록 조회 + const columnsResponse = await apiClient.get(`/table-management/tables/${detailTable}/columns`); + + if (columnsResponse.data?.success && columnsResponse.data?.data) { + // API 응답 구조: { success, data: { columns: [...], total, page, ... } } + const columnsArray = columnsResponse.data.data.columns || columnsResponse.data.data || []; + const detailColumnsData = Array.isArray(columnsArray) ? columnsArray : []; + const detailColumns = detailColumnsData.map((col: any) => col.column_name || col.columnName); + const masterKeys = Object.keys(effectiveInitialData); + + console.log(`[initializeForm] 테이블 섹션 ${section.id}: 연결 필드 자동 감지`, { + masterTable, + detailTable, + detailColumnsCount: detailColumnsData.length, + }); + + // 방법 1: 엔티티 관계 기반 감지 (정확) + // 디테일 테이블에서 마스터 테이블을 참조하는 엔티티 컬럼 찾기 + if (masterTable) { + for (const col of detailColumnsData) { + const colName = col.column_name || col.columnName; + const inputType = col.input_type || col.inputType; + + // 엔티티 타입 컬럼 확인 + if (inputType === "entity") { + // reference_table 또는 detail_settings에서 참조 테이블 확인 + let refTable = col.reference_table || col.referenceTable; + + // detail_settings에서 referenceTable 확인 + if (!refTable && col.detail_settings) { + try { + const settings = typeof col.detail_settings === "string" + ? JSON.parse(col.detail_settings) + : col.detail_settings; + refTable = settings.referenceTable; + } catch { + // JSON 파싱 실패 무시 + } + } + + // 마스터 테이블을 참조하는 컬럼 발견 + if (refTable === masterTable) { + // 참조 컬럼 확인 (마스터 테이블의 어떤 컬럼을 참조하는지) + let refColumn = col.reference_column || col.referenceColumn; + if (!refColumn && col.detail_settings) { + try { + const settings = typeof col.detail_settings === "string" + ? JSON.parse(col.detail_settings) + : col.detail_settings; + refColumn = settings.referenceColumn; + } catch { + // JSON 파싱 실패 무시 + } + } + + // 마스터 데이터에 해당 컬럼 값이 있는지 확인 + if (refColumn && effectiveInitialData[refColumn]) { + console.log(`[initializeForm] 테이블 섹션 ${section.id}: 엔티티 관계 감지 - ${colName} → ${masterTable}.${refColumn}`); + linkColumn = { masterField: refColumn, detailField: colName }; + break; + } + } + } + } + } + + // 방법 2: 공통 컬럼 패턴 기반 감지 (폴백) + // 엔티티 관계가 없으면 공통 컬럼명 패턴으로 찾기 + if (!linkColumn) { + const priorityPatterns = ["_no", "_number", "_code", "_id"]; + + for (const pattern of priorityPatterns) { + for (const masterKey of masterKeys) { + if (masterKey.endsWith(pattern) && + detailColumns.includes(masterKey) && + effectiveInitialData[masterKey] && + masterKey !== "id" && masterKey !== "company_code") { + console.log(`[initializeForm] 테이블 섹션 ${section.id}: 공통 컬럼 패턴 감지 - ${masterKey}`); + linkColumn = { masterField: masterKey, detailField: masterKey }; + break; + } + } + if (linkColumn) break; + } + } + + // 방법 3: 일반 공통 컬럼 (마지막 폴백) + if (!linkColumn) { + for (const masterKey of masterKeys) { + if (detailColumns.includes(masterKey) && + effectiveInitialData[masterKey] && + masterKey !== "id" && masterKey !== "company_code" && + !masterKey.startsWith("__")) { + console.log(`[initializeForm] 테이블 섹션 ${section.id}: 공통 컬럼 감지 - ${masterKey}`); + linkColumn = { masterField: masterKey, detailField: masterKey }; + break; + } + } + } + } + } catch (error) { + console.warn(`[initializeForm] 테이블 섹션 ${section.id}: 컬럼 정보 조회 실패`, error); + } + } + + if (!linkColumn?.masterField || !linkColumn?.detailField) { + console.log(`[initializeForm] 테이블 섹션 ${section.id}: linkColumn 미설정 및 자동 감지 실패, 스킵`); + continue; + } + + // 마스터 테이블의 연결 필드 값 가져오기 + const masterValue = effectiveInitialData[linkColumn.masterField]; + if (!masterValue) { + console.log(`[initializeForm] 테이블 섹션 ${section.id}: masterField(${linkColumn.masterField}) 값 없음, 스킵`); + continue; + } + + try { + console.log(`[initializeForm] 테이블 섹션 ${section.id}: 디테일 데이터 로드 시작`, { + detailTable, + linkColumn, + masterValue, + }); + + // 디테일 테이블에서 데이터 조회 + // operator: "equals"를 사용하여 정확히 일치하는 값만 검색 (엔티티 타입 컬럼에서 중요) + const searchCondition: Record = { + [linkColumn.detailField]: { value: masterValue, operator: "equals" }, + }; + + console.log(`[initializeForm] 테이블 섹션 ${section.id}: API 요청 - URL: /table-management/tables/${detailTable}/data`); + console.log(`[initializeForm] 테이블 섹션 ${section.id}: API 요청 - search:`, JSON.stringify(searchCondition)); + + const response = await apiClient.post(`/table-management/tables/${detailTable}/data`, { + search: searchCondition, // filters가 아닌 search로 전달 + page: 1, + size: 1000, // pageSize가 아닌 size로 전달 + autoFilter: { enabled: true }, // 멀티테넌시 필터 적용 + }); + + console.log(`[initializeForm] 테이블 섹션 ${section.id}: API 응답 - success: ${response.data?.success}, total: ${response.data?.data?.total}, dataLength: ${response.data?.data?.data?.length}`); + + if (response.data?.success) { + // 다양한 응답 구조 처리 + let items: any[] = []; + const data = response.data.data; + + if (Array.isArray(data)) { + items = data; + } else if (data?.items && Array.isArray(data.items)) { + items = data.items; + } else if (data?.rows && Array.isArray(data.rows)) { + items = data.rows; + } else if (data?.data && Array.isArray(data.data)) { + items = data.data; + } + + console.log(`[initializeForm] 테이블 섹션 ${section.id}: ${items.length}건 로드됨`, items); + + // 테이블 섹션 데이터를 formData에 저장 (TableSectionRenderer에서 사용) + const tableSectionKey = `__tableSection_${section.id}`; + newFormData[tableSectionKey] = items; + console.log(`[initializeForm] 테이블 섹션 ${section.id}: formData[${tableSectionKey}]에 저장됨`); + } + } catch (error) { + console.error(`[initializeForm] 테이블 섹션 ${section.id}: 디테일 데이터 로드 실패`, error); + } + } + } + setFormData(newFormData); setRepeatSections(newRepeatSections); setCollapsedSections(newCollapsed);