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] || "-"}
+ |
+ ))}
|
- ))
+ );
+ })
)}
diff --git a/frontend/lib/registry/components/entity-search-input/useEntitySearch.ts b/frontend/lib/registry/components/entity-search-input/useEntitySearch.ts
index 04428a55..a71b2aa8 100644
--- a/frontend/lib/registry/components/entity-search-input/useEntitySearch.ts
+++ b/frontend/lib/registry/components/entity-search-input/useEntitySearch.ts
@@ -34,6 +34,13 @@ export function useEntitySearch({
const search = useCallback(
async (text: string, page: number = 1) => {
+ // tableName 유효성 검증
+ if (!tableName || tableName === "undefined" || tableName === "null") {
+ console.warn("엔티티 검색 건너뜀: tableName이 없음", { tableName });
+ setError("테이블명이 설정되지 않았습니다. 컴포넌트 설정을 확인해주세요.");
+ return;
+ }
+
try {
setLoading(true);
setError(null);
diff --git a/frontend/lib/registry/components/index.ts b/frontend/lib/registry/components/index.ts
index 8584932b..11f46ec1 100644
--- a/frontend/lib/registry/components/index.ts
+++ b/frontend/lib/registry/components/index.ts
@@ -46,9 +46,9 @@ import "./table-search-widget"; // 🆕 테이블 검색 필터 위젯
import "./customer-item-mapping/CustomerItemMappingRenderer"; // 🆕 거래처별 품목정보
// 🆕 수주 등록 관련 컴포넌트들
-import { AutocompleteSearchInputRenderer } from "./autocomplete-search-input/AutocompleteSearchInputRenderer";
-import { EntitySearchInputRenderer } from "./entity-search-input/EntitySearchInputRenderer";
-import { ModalRepeaterTableRenderer } from "./modal-repeater-table/ModalRepeaterTableRenderer";
+import "./autocomplete-search-input/AutocompleteSearchInputRenderer";
+import "./entity-search-input/EntitySearchInputRenderer";
+import "./modal-repeater-table/ModalRepeaterTableRenderer";
import "./order-registration-modal/OrderRegistrationModalRenderer";
// 🆕 조건부 컨테이너 컴포넌트
diff --git a/frontend/lib/registry/components/modal-repeater-table/ModalRepeaterTableRenderer.tsx b/frontend/lib/registry/components/modal-repeater-table/ModalRepeaterTableRenderer.tsx
index 6b7639f7..1e58f6d0 100644
--- a/frontend/lib/registry/components/modal-repeater-table/ModalRepeaterTableRenderer.tsx
+++ b/frontend/lib/registry/components/modal-repeater-table/ModalRepeaterTableRenderer.tsx
@@ -1,19 +1,9 @@
"use client";
-import React, { useEffect } from "react";
import { ComponentRegistry } from "../../ComponentRegistry";
import { ModalRepeaterTableDefinition } from "./index";
-export function ModalRepeaterTableRenderer() {
- useEffect(() => {
- ComponentRegistry.registerComponent(ModalRepeaterTableDefinition);
- console.log("✅ ModalRepeaterTable 컴포넌트 등록 완료");
-
- return () => {
- // 컴포넌트 언마운트 시 해제하지 않음 (싱글톤 패턴)
- };
- }, []);
-
- return null;
-}
+// 파일 로드 시 즉시 등록
+ComponentRegistry.registerComponent(ModalRepeaterTableDefinition);
+console.log("✅ ModalRepeaterTable 컴포넌트 등록 완료");