diff --git a/backend-node/src/routes/dataRoutes.ts b/backend-node/src/routes/dataRoutes.ts index 574f1cf8..a7757397 100644 --- a/backend-node/src/routes/dataRoutes.ts +++ b/backend-node/src/routes/dataRoutes.ts @@ -606,7 +606,7 @@ router.get( }); } - const { enableEntityJoin, groupByColumns } = req.query; + const { enableEntityJoin, groupByColumns, primaryKeyColumn } = req.query; const enableEntityJoinFlag = enableEntityJoin === "true" || (typeof enableEntityJoin === "boolean" && enableEntityJoin); @@ -626,17 +626,22 @@ router.get( } } + // ๐Ÿ†• primaryKeyColumn ํŒŒ์‹ฑ + const primaryKeyColumnStr = typeof primaryKeyColumn === "string" ? primaryKeyColumn : undefined; + console.log(`๐Ÿ” ๋ ˆ์ฝ”๋“œ ์ƒ์„ธ ์กฐํšŒ: ${tableName}/${id}`, { enableEntityJoin: enableEntityJoinFlag, groupByColumns: groupByColumnsArray, + primaryKeyColumn: primaryKeyColumnStr, }); - // ๋ ˆ์ฝ”๋“œ ์ƒ์„ธ ์กฐํšŒ (Entity Join ์˜ต์…˜ + ๊ทธ๋ฃนํ•‘ ์˜ต์…˜ ํฌํ•จ) + // ๋ ˆ์ฝ”๋“œ ์ƒ์„ธ ์กฐํšŒ (Entity Join ์˜ต์…˜ + ๊ทธ๋ฃนํ•‘ ์˜ต์…˜ + Primary Key ์ปฌ๋Ÿผ ํฌํ•จ) const result = await dataService.getRecordDetail( tableName, id, enableEntityJoinFlag, - groupByColumnsArray + groupByColumnsArray, + primaryKeyColumnStr // ๐Ÿ†• Primary Key ์ปฌ๋Ÿผ๋ช… ์ „๋‹ฌ ); if (!result.success) { diff --git a/backend-node/src/services/dataService.ts b/backend-node/src/services/dataService.ts index 8c6e63f0..60de20db 100644 --- a/backend-node/src/services/dataService.ts +++ b/backend-node/src/services/dataService.ts @@ -490,7 +490,8 @@ class DataService { tableName: string, id: string | number, enableEntityJoin: boolean = false, - groupByColumns: string[] = [] + groupByColumns: string[] = [], + primaryKeyColumn?: string // ๐Ÿ†• ํด๋ผ์ด์–ธํŠธ์—์„œ ์ „๋‹ฌํ•œ Primary Key ์ปฌ๋Ÿผ๋ช… ): Promise> { try { // ํ…Œ์ด๋ธ” ์ ‘๊ทผ ๊ฒ€์ฆ @@ -499,20 +500,30 @@ class DataService { return validation.error!; } - // Primary Key ์ปฌ๋Ÿผ ์ฐพ๊ธฐ - const pkResult = await query<{ attname: string }>( - `SELECT a.attname - FROM pg_index i - JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) - WHERE i.indrelid = $1::regclass AND i.indisprimary`, - [tableName] - ); + // ๐Ÿ†• ํด๋ผ์ด์–ธํŠธ์—์„œ ์ „๋‹ฌํ•œ Primary Key ์ปฌ๋Ÿผ์ด ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ + let pkColumn = primaryKeyColumn || ""; + + // Primary Key ์ปฌ๋Ÿผ์ด ์—†์œผ๋ฉด ์ž๋™ ๊ฐ์ง€ + if (!pkColumn) { + const pkResult = await query<{ attname: string }>( + `SELECT a.attname + FROM pg_index i + JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) + WHERE i.indrelid = $1::regclass AND i.indisprimary`, + [tableName] + ); - let pkColumn = "id"; // ๊ธฐ๋ณธ๊ฐ’ - if (pkResult.length > 0) { - pkColumn = pkResult[0].attname; + pkColumn = "id"; // ๊ธฐ๋ณธ๊ฐ’ + if (pkResult.length > 0) { + pkColumn = pkResult[0].attname; + } + console.log(`๐Ÿ”‘ [getRecordDetail] ์ž๋™ ๊ฐ์ง€๋œ Primary Key:`, pkResult); + } else { + console.log(`๐Ÿ”‘ [getRecordDetail] ํด๋ผ์ด์–ธํŠธ ์ œ๊ณต Primary Key: ${pkColumn}`); } + console.log(`๐Ÿ”‘ [getRecordDetail] ํ…Œ์ด๋ธ”: ${tableName}, Primary Key ์ปฌ๋Ÿผ: ${pkColumn}, ์กฐํšŒ ID: ${id}`); + // ๐Ÿ†• Entity Join์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ if (enableEntityJoin) { const { EntityJoinService } = await import("./entityJoinService"); diff --git a/frontend/components/common/ScreenModal.tsx b/frontend/components/common/ScreenModal.tsx index 44685dc0..fdd104df 100644 --- a/frontend/components/common/ScreenModal.tsx +++ b/frontend/components/common/ScreenModal.tsx @@ -374,8 +374,9 @@ export const ScreenModal: React.FC = ({ className }) => { const editId = urlParams.get("editId"); const tableName = urlParams.get("tableName") || screenInfo.tableName; const groupByColumnsParam = urlParams.get("groupByColumns"); + const primaryKeyColumn = urlParams.get("primaryKeyColumn"); // ๐Ÿ†• Primary Key ์ปฌ๋Ÿผ๋ช… - console.log("๐Ÿ“‹ URL ํŒŒ๋ผ๋ฏธํ„ฐ ํ™•์ธ:", { mode, editId, tableName, groupByColumnsParam }); + console.log("๐Ÿ“‹ URL ํŒŒ๋ผ๋ฏธํ„ฐ ํ™•์ธ:", { mode, editId, tableName, groupByColumnsParam, primaryKeyColumn }); // ์ˆ˜์ • ๋ชจ๋“œ์ด๊ณ  editId๊ฐ€ ์žˆ์œผ๋ฉด ํ•ด๋‹น ๋ ˆ์ฝ”๋“œ ์กฐํšŒ if (mode === "edit" && editId && tableName) { @@ -414,6 +415,11 @@ export const ScreenModal: React.FC = ({ className }) => { params.groupByColumns = JSON.stringify(groupByColumns); console.log("โœ… [ScreenModal] groupByColumns๋ฅผ params์— ์ถ”๊ฐ€:", params.groupByColumns); } + // ๐Ÿ†• Primary Key ์ปฌ๋Ÿผ๋ช… ์ „๋‹ฌ (๋ฐฑ์—”๋“œ ์ž๋™ ๊ฐ์ง€ ์‹คํŒจ ์‹œ ์‚ฌ์šฉ) + if (primaryKeyColumn) { + params.primaryKeyColumn = primaryKeyColumn; + console.log("โœ… [ScreenModal] primaryKeyColumn์„ params์— ์ถ”๊ฐ€:", primaryKeyColumn); + } console.log("๐Ÿ“ก [ScreenModal] ์‹ค์ œ API ์š”์ฒญ:", { url: `/data/${tableName}/${editId}`, diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index ab387348..9b8e7cf0 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -1590,21 +1590,40 @@ export const SplitPanelLayoutComponent: React.FC // ์ปค์Šคํ…€ ๋ชจ๋‹ฌ ํ™”๋ฉด ์—ด๊ธฐ const rightTableName = componentConfig.rightPanel?.tableName || ""; - // Primary Key ์ฐพ๊ธฐ (์šฐ์„ ์ˆœ์œ„: id > ID > ์ฒซ ๋ฒˆ์งธ ํ•„๋“œ) + // Primary Key ์ฐพ๊ธฐ (์šฐ์„ ์ˆœ์œ„: ์„ค์ •๊ฐ’ > id > ID > non-null ํ•„๋“œ) + // ๐Ÿ”ง ์„ค์ •์—์„œ primaryKeyColumn ์ง€์ • ๊ฐ€๋Šฅ + const configuredPrimaryKey = componentConfig.rightPanel?.editButton?.primaryKeyColumn; + let primaryKeyName = "id"; let primaryKeyValue: any; - if (item.id !== undefined && item.id !== null) { + if (configuredPrimaryKey && item[configuredPrimaryKey] !== undefined && item[configuredPrimaryKey] !== null) { + // ์„ค์ •๋œ Primary Key ์‚ฌ์šฉ + primaryKeyName = configuredPrimaryKey; + primaryKeyValue = item[configuredPrimaryKey]; + } else if (item.id !== undefined && item.id !== null) { primaryKeyName = "id"; primaryKeyValue = item.id; } else if (item.ID !== undefined && item.ID !== null) { primaryKeyName = "ID"; primaryKeyValue = item.ID; } else { - // ์ฒซ ๋ฒˆ์งธ ํ•„๋“œ๋ฅผ Primary Key๋กœ ๊ฐ„์ฃผ - const firstKey = Object.keys(item)[0]; - primaryKeyName = firstKey; - primaryKeyValue = item[firstKey]; + // ๐Ÿ”ง ์ฒซ ๋ฒˆ์งธ non-null ํ•„๋“œ๋ฅผ Primary Key๋กœ ๊ฐ„์ฃผ + const keys = Object.keys(item); + let found = false; + for (const key of keys) { + if (item[key] !== undefined && item[key] !== null) { + primaryKeyName = key; + primaryKeyValue = item[key]; + found = true; + break; + } + } + // ๋ชจ๋“  ํ•„๋“œ๊ฐ€ null์ด๋ฉด ์ฒซ ๋ฒˆ์งธ ํ•„๋“œ ์‚ฌ์šฉ + if (!found && keys.length > 0) { + primaryKeyName = keys[0]; + primaryKeyValue = item[keys[0]]; + } } console.log("โœ… ์ˆ˜์ • ๋ชจ๋‹ฌ ์—ด๊ธฐ:", { @@ -1629,7 +1648,7 @@ export const SplitPanelLayoutComponent: React.FC hasGroupByColumns: groupByColumns.length > 0, }); - // ScreenModal ์—ด๊ธฐ ์ด๋ฒคํŠธ ๋ฐœ์ƒ (URL ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ID + groupByColumns ์ „๋‹ฌ) + // ScreenModal ์—ด๊ธฐ ์ด๋ฒคํŠธ ๋ฐœ์ƒ (URL ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ID + groupByColumns + primaryKeyColumn ์ „๋‹ฌ) window.dispatchEvent( new CustomEvent("openScreenModal", { detail: { @@ -1638,6 +1657,7 @@ export const SplitPanelLayoutComponent: React.FC mode: "edit", editId: primaryKeyValue, tableName: rightTableName, + primaryKeyColumn: primaryKeyName, // ๐Ÿ†• Primary Key ์ปฌ๋Ÿผ๋ช… ์ „๋‹ฌ ...(groupByColumns.length > 0 && { groupByColumns: JSON.stringify(groupByColumns), }), @@ -1650,6 +1670,7 @@ export const SplitPanelLayoutComponent: React.FC screenId: modalScreenId, editId: primaryKeyValue, tableName: rightTableName, + primaryKeyColumn: primaryKeyName, groupByColumns: groupByColumns.length > 0 ? JSON.stringify(groupByColumns) : "์—†์Œ", });