diff --git a/backend-node/src/controllers/entitySearchController.ts b/backend-node/src/controllers/entitySearchController.ts index 2b944e2b..5046d8bb 100644 --- a/backend-node/src/controllers/entitySearchController.ts +++ b/backend-node/src/controllers/entitySearchController.ts @@ -17,6 +17,15 @@ export async function searchEntity(req: Request, res: Response) { limit = "20", } = req.query; + // tableName 유효성 검증 + if (!tableName || tableName === "undefined" || tableName === "null") { + logger.warn("엔티티 검색 실패: 테이블명이 없음", { tableName }); + return res.status(400).json({ + success: false, + message: "테이블명이 지정되지 않았습니다. 컴포넌트 설정에서 sourceTable을 확인해주세요.", + }); + } + // 멀티테넌시 const companyCode = req.user!.companyCode; diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index aa87e83f..dcd80a62 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -70,9 +70,6 @@ import { toast } from "sonner"; import { MenuAssignmentModal } from "./MenuAssignmentModal"; import { FileAttachmentDetailModal } from "./FileAttachmentDetailModal"; import { initializeComponents } from "@/lib/registry/components"; -import { AutocompleteSearchInputRenderer } from "@/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputRenderer"; -import { EntitySearchInputRenderer } from "@/lib/registry/components/entity-search-input/EntitySearchInputRenderer"; -import { ModalRepeaterTableRenderer } from "@/lib/registry/components/modal-repeater-table/ModalRepeaterTableRenderer"; import { ScreenFileAPI } from "@/lib/api/screenFile"; import { safeMigrateLayout, needsMigration } from "@/lib/utils/widthToColumnSpan"; @@ -4967,12 +4964,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD )} - {/* 숨겨진 컴포넌트 렌더러들 (레지스트리 등록용) */} -
- - - -
); } diff --git a/frontend/components/table-category/CategoryValueAddDialog.tsx b/frontend/components/table-category/CategoryValueAddDialog.tsx index ae47cf44..c486cc1d 100644 --- a/frontend/components/table-category/CategoryValueAddDialog.tsx +++ b/frontend/components/table-category/CategoryValueAddDialog.tsx @@ -79,14 +79,14 @@ export const CategoryValueAddDialog: React.FC< const valueCode = generateCode(valueLabel); onAdd({ - tableName: "", - columnName: "", + tableName: "", // CategoryValueManager에서 오버라이드됨 + columnName: "", // CategoryValueManager에서 오버라이드됨 valueCode, valueLabel: valueLabel.trim(), - description: description.trim(), - color: color, + description: description.trim() || undefined, // 빈 문자열 대신 undefined + color: color === "none" ? undefined : color, // "none"은 undefined로 isDefault: false, - }); + } as TableCategoryValue); // 초기화 setValueLabel(""); diff --git a/frontend/lib/registry/DynamicComponentRenderer.tsx b/frontend/lib/registry/DynamicComponentRenderer.tsx index 144b1f1a..865a361f 100644 --- a/frontend/lib/registry/DynamicComponentRenderer.tsx +++ b/frontend/lib/registry/DynamicComponentRenderer.tsx @@ -308,6 +308,8 @@ export const DynamicComponentRenderer: React.FC = style: finalStyle, // size를 포함한 최종 style config: component.componentConfig, componentConfig: component.componentConfig, + // componentConfig의 모든 속성을 props로 spread (tableName, displayField 등) + ...(component.componentConfig || {}), value: currentValue, // formData에서 추출한 현재 값 전달 // 새로운 기능들 전달 autoGeneration: component.autoGeneration || component.componentConfig?.autoGeneration, diff --git a/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputRenderer.tsx b/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputRenderer.tsx index 82691247..4829ae77 100644 --- a/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputRenderer.tsx +++ b/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputRenderer.tsx @@ -1,19 +1,9 @@ "use client"; -import React, { useEffect } from "react"; import { ComponentRegistry } from "../../ComponentRegistry"; import { AutocompleteSearchInputDefinition } from "./index"; -export function AutocompleteSearchInputRenderer() { - useEffect(() => { - ComponentRegistry.registerComponent(AutocompleteSearchInputDefinition); - console.log("✅ AutocompleteSearchInput 컴포넌트 등록 완료"); - - return () => { - // 컴포넌트 언마운트 시 해제하지 않음 (싱글톤 패턴) - }; - }, []); - - return null; -} +// 파일 로드 시 즉시 등록 +ComponentRegistry.registerComponent(AutocompleteSearchInputDefinition); +console.log("✅ AutocompleteSearchInput 컴포넌트 등록 완료"); diff --git a/frontend/lib/registry/components/entity-search-input/EntitySearchInputRenderer.tsx b/frontend/lib/registry/components/entity-search-input/EntitySearchInputRenderer.tsx index fca4afd2..1b9d4f88 100644 --- a/frontend/lib/registry/components/entity-search-input/EntitySearchInputRenderer.tsx +++ b/frontend/lib/registry/components/entity-search-input/EntitySearchInputRenderer.tsx @@ -1,19 +1,9 @@ "use client"; -import React, { useEffect } from "react"; import { ComponentRegistry } from "../../ComponentRegistry"; import { EntitySearchInputDefinition } from "./index"; -export function EntitySearchInputRenderer() { - useEffect(() => { - ComponentRegistry.registerComponent(EntitySearchInputDefinition); - console.log("✅ EntitySearchInput 컴포넌트 등록 완료"); - - return () => { - // 컴포넌트 언마운트 시 해제하지 않음 (싱글톤 패턴) - }; - }, []); - - return null; -} +// 파일 로드 시 즉시 등록 +ComponentRegistry.registerComponent(EntitySearchInputDefinition); +console.log("✅ EntitySearchInput 컴포넌트 등록 완료"); diff --git a/frontend/lib/registry/components/entity-search-input/EntitySearchModal.tsx b/frontend/lib/registry/components/entity-search-input/EntitySearchModal.tsx index 03648f8f..a31c6cab 100644 --- a/frontend/lib/registry/components/entity-search-input/EntitySearchModal.tsx +++ b/frontend/lib/registry/components/entity-search-input/EntitySearchModal.tsx @@ -155,17 +155,19 @@ export function EntitySearchModal({ ) : ( - results.map((item, index) => ( - handleSelect(item)} - > - {displayColumns.map((col, colIndex) => ( - - {item[col] || "-"} - - ))} + results.map((item, index) => { + const uniqueKey = item[valueField] !== undefined ? `${item[valueField]}` : `row-${index}`; + return ( + handleSelect(item)} + > + {displayColumns.map((col) => ( + + {item[col] || "-"} + + ))}