diff --git a/backend-node/src/services/dynamicFormService.ts b/backend-node/src/services/dynamicFormService.ts index cc2fad77..965d2833 100644 --- a/backend-node/src/services/dynamicFormService.ts +++ b/backend-node/src/services/dynamicFormService.ts @@ -338,9 +338,9 @@ export class DynamicFormService { ) { try { parsedArray = JSON.parse(value); - console.log( + console.log( `๐Ÿ”„ JSON ๋ฌธ์ž์—ด Repeater ๋ฐ์ดํ„ฐ ๊ฐ์ง€: ${key}, ${parsedArray?.length || 0}๊ฐœ ํ•ญ๋ชฉ` - ); + ); } catch (parseError) { console.log(`โš ๏ธ JSON ํŒŒ์‹ฑ ์‹คํŒจ: ${key}`); } @@ -348,25 +348,25 @@ export class DynamicFormService { // ํŒŒ์‹ฑ๋œ ๋ฐฐ์—ด์ด ์žˆ์œผ๋ฉด ์ฒ˜๋ฆฌ if (parsedArray && Array.isArray(parsedArray) && parsedArray.length > 0) { - // ์ปดํฌ๋„ŒํŠธ ์„ค์ •์—์„œ targetTable ์ถ”์ถœ (์ปดํฌ๋„ŒํŠธ ID๋ฅผ ํ†ตํ•ด) - // ํ”„๋ก ํŠธ์—”๋“œ์—์„œ { data: [...], targetTable: "..." } ํ˜•์‹์œผ๋กœ ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ์Œ - let targetTable: string | undefined; - let actualData = parsedArray; + // ์ปดํฌ๋„ŒํŠธ ์„ค์ •์—์„œ targetTable ์ถ”์ถœ (์ปดํฌ๋„ŒํŠธ ID๋ฅผ ํ†ตํ•ด) + // ํ”„๋ก ํŠธ์—”๋“œ์—์„œ { data: [...], targetTable: "..." } ํ˜•์‹์œผ๋กœ ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ์Œ + let targetTable: string | undefined; + let actualData = parsedArray; - // ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ์— _targetTable์ด ์žˆ๋Š”์ง€ ํ™•์ธ (ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ „๋‹ฌ) - if (parsedArray[0] && parsedArray[0]._targetTable) { - targetTable = parsedArray[0]._targetTable; - actualData = parsedArray.map( - ({ _targetTable, ...item }) => item - ); - } + // ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ์— _targetTable์ด ์žˆ๋Š”์ง€ ํ™•์ธ (ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ „๋‹ฌ) + if (parsedArray[0] && parsedArray[0]._targetTable) { + targetTable = parsedArray[0]._targetTable; + actualData = parsedArray.map( + ({ _targetTable, ...item }) => item + ); + } - repeaterData.push({ - data: actualData, - targetTable, - componentId: key, - }); - delete dataToInsert[key]; // ์›๋ณธ ๋ฐฐ์—ด ๋ฐ์ดํ„ฐ๋Š” ์ œ๊ฑฐ + repeaterData.push({ + data: actualData, + targetTable, + componentId: key, + }); + delete dataToInsert[key]; // ์›๋ณธ ๋ฐฐ์—ด ๋ฐ์ดํ„ฐ๋Š” ์ œ๊ฑฐ console.log(`โœ… Repeater ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€: ${key}`, { targetTable: targetTable || "์—†์Œ (ํ™”๋ฉด ์„ค๊ณ„์—์„œ ์„ค์ • ํ•„์š”)", @@ -376,6 +376,25 @@ export class DynamicFormService { } }); + // ๐Ÿ”ฅ Repeater targetTable์ด ๋ฉ”์ธ ํ…Œ์ด๋ธ”๊ณผ ๊ฐ™์œผ๋ฉด ๋ถ„๋ฆฌํ•ด์„œ ์ €์žฅ + const separateRepeaterData: typeof repeaterData = []; + const mergedRepeaterData: typeof repeaterData = []; + + repeaterData.forEach(repeater => { + if (repeater.targetTable && repeater.targetTable !== tableName) { + // ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”: ๋‚˜์ค‘์— ๋ณ„๋„ ์ €์žฅ + separateRepeaterData.push(repeater); + } else { + // ๊ฐ™์€ ํ…Œ์ด๋ธ”: ๋ฉ”์ธ INSERT์™€ ๋ณ‘ํ•ฉ (ํ—ค๋”+ํ’ˆ๋ชฉ์„ ํ•œ ๋ฒˆ์—) + mergedRepeaterData.push(repeater); + } + }); + + console.log(`๐Ÿ”„ Repeater ๋ฐ์ดํ„ฐ ๋ถ„๋ฅ˜:`, { + separate: separateRepeaterData.length, // ๋ณ„๋„ ํ…Œ์ด๋ธ” + merged: mergedRepeaterData.length, // ๋ฉ”์ธ ํ…Œ์ด๋ธ”๊ณผ ๋ณ‘ํ•ฉ + }); + // ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ปฌ๋Ÿผ ์ œ๊ฑฐ Object.keys(dataToInsert).forEach((key) => { if (!tableColumns.includes(key)) { @@ -386,9 +405,6 @@ export class DynamicFormService { } }); - // RepeaterInput ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๋กœ์ง์€ ๋ฉ”์ธ ์ €์žฅ ํ›„์— ์ฒ˜๋ฆฌ - // (๊ฐ Repeater๊ฐ€ ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์— ์ €์žฅ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ) - console.log("๐ŸŽฏ ์‹ค์ œ ํ…Œ์ด๋ธ”์— ์‚ฝ์ž…ํ•  ๋ฐ์ดํ„ฐ:", { tableName, dataToInsert, @@ -469,28 +485,95 @@ export class DynamicFormService { const userId = data.updated_by || data.created_by || "system"; const clientIp = ipAddress || "unknown"; - const result = await transaction(async (client) => { - // ์„ธ์…˜ ๋ณ€์ˆ˜ ์„ค์ • - await client.query(`SET LOCAL app.user_id = '${userId}'`); - await client.query(`SET LOCAL app.ip_address = '${clientIp}'`); - - // UPSERT ์‹คํ–‰ - const res = await client.query(upsertQuery, values); - return res.rows; - }); - - console.log("โœ… ์„œ๋น„์Šค: ์‹ค์ œ ํ…Œ์ด๋ธ” ์ €์žฅ ์„ฑ๊ณต:", result); + let result: any[]; + + // ๐Ÿ”ฅ ๋ฉ”์ธ ํ…Œ์ด๋ธ”๊ณผ ๋ณ‘ํ•ฉํ•  Repeater๊ฐ€ ์žˆ์œผ๋ฉด ๊ฐ ํ’ˆ๋ชฉ๋ณ„๋กœ INSERT + if (mergedRepeaterData.length > 0) { + console.log(`๐Ÿ”„ ๋ฉ”์ธ ํ…Œ์ด๋ธ” ๋ณ‘ํ•ฉ ๋ชจ๋“œ: ${mergedRepeaterData.length}๊ฐœ Repeater๋ฅผ ๊ฐœ๋ณ„ ๋ ˆ์ฝ”๋“œ๋กœ ์ €์žฅ`); + + result = []; + + for (const repeater of mergedRepeaterData) { + for (const item of repeater.data) { + // ํ—ค๋” + ํ’ˆ๋ชฉ์„ ๋ณ‘ํ•ฉ + const mergedData = { ...dataToInsert, ...item }; + + // ํƒ€์ž… ๋ณ€ํ™˜ + Object.keys(mergedData).forEach((columnName) => { + const column = columnInfo.find((col) => col.column_name === columnName); + if (column) { + mergedData[columnName] = this.convertValueForPostgreSQL( + mergedData[columnName], + column.data_type + ); + } + }); + + const mergedColumns = Object.keys(mergedData); + const mergedValues: any[] = Object.values(mergedData); + const mergedPlaceholders = mergedValues.map((_, index) => `$${index + 1}`).join(", "); + + let mergedUpsertQuery: string; + if (primaryKeys.length > 0) { + const conflictColumns = primaryKeys.join(", "); + const updateSet = mergedColumns + .filter((col) => !primaryKeys.includes(col)) + .map((col) => `${col} = EXCLUDED.${col}`) + .join(", "); + + mergedUpsertQuery = updateSet + ? `INSERT INTO ${tableName} (${mergedColumns.join(", ")}) + VALUES (${mergedPlaceholders}) + ON CONFLICT (${conflictColumns}) + DO UPDATE SET ${updateSet} + RETURNING *` + : `INSERT INTO ${tableName} (${mergedColumns.join(", ")}) + VALUES (${mergedPlaceholders}) + ON CONFLICT (${conflictColumns}) + DO NOTHING + RETURNING *`; + } else { + mergedUpsertQuery = `INSERT INTO ${tableName} (${mergedColumns.join(", ")}) + VALUES (${mergedPlaceholders}) + RETURNING *`; + } + + console.log(`๐Ÿ“ ๋ณ‘ํ•ฉ INSERT:`, { mergedData }); + + const itemResult = await transaction(async (client) => { + await client.query(`SET LOCAL app.user_id = '${userId}'`); + await client.query(`SET LOCAL app.ip_address = '${clientIp}'`); + const res = await client.query(mergedUpsertQuery, mergedValues); + return res.rows[0]; + }); + + result.push(itemResult); + } + } + + console.log(`โœ… ๋ณ‘ํ•ฉ ์ €์žฅ ์™„๋ฃŒ: ${result.length}๊ฐœ ๋ ˆ์ฝ”๋“œ`); + } else { + // ์ผ๋ฐ˜ ๋ชจ๋“œ: ํ—ค๋”๋งŒ ์ €์žฅ + result = await transaction(async (client) => { + await client.query(`SET LOCAL app.user_id = '${userId}'`); + await client.query(`SET LOCAL app.ip_address = '${clientIp}'`); + const res = await client.query(upsertQuery, values); + return res.rows; + }); + + console.log("โœ… ์„œ๋น„์Šค: ์‹ค์ œ ํ…Œ์ด๋ธ” ์ €์žฅ ์„ฑ๊ณต:", result); + } // ๊ฒฐ๊ณผ๋ฅผ ํ‘œ์ค€ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ const insertedRecord = Array.isArray(result) ? result[0] : result; - // ๐Ÿ“ RepeaterInput ๋ฐ์ดํ„ฐ ์ €์žฅ (๊ฐ Repeater๋ฅผ ํ•ด๋‹น ํ…Œ์ด๋ธ”์— ์ €์žฅ) - if (repeaterData.length > 0) { + // ๐Ÿ“ ๋ณ„๋„ ํ…Œ์ด๋ธ” Repeater ๋ฐ์ดํ„ฐ ์ €์žฅ + if (separateRepeaterData.length > 0) { console.log( - `๐Ÿ”„ RepeaterInput ๋ฐ์ดํ„ฐ ์ €์žฅ ์‹œ์ž‘: ${repeaterData.length}๊ฐœ Repeater` + `๐Ÿ”„ ๋ณ„๋„ ํ…Œ์ด๋ธ” Repeater ์ €์žฅ ์‹œ์ž‘: ${separateRepeaterData.length}๊ฐœ` ); - for (const repeater of repeaterData) { + for (const repeater of separateRepeaterData) { const targetTableName = repeater.targetTable || tableName; console.log( `๐Ÿ“ Repeater "${repeater.componentId}" โ†’ ํ…Œ์ด๋ธ” "${targetTableName}"์— ${repeater.data.length}๊ฐœ ํ•ญ๋ชฉ ์ €์žฅ` @@ -518,24 +601,8 @@ export class DynamicFormService { company_code: data.company_code || company_code, }; - // ๐Ÿ”ฅ ๋ถ€๋ชจ ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™ ๋ณต์‚ฌ (์™ธ๋ž˜ํ‚ค ๊ด€๊ณ„) - // targetTable์ด ๋ฉ”์ธ ํ…Œ์ด๋ธ”๊ณผ ๊ฐ™์œผ๋ฉด ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ - if (targetTableName === tableName) { - console.log( - `โš ๏ธ [Repeater] targetTable์ด ๋ฉ”์ธ ํ…Œ์ด๋ธ”๊ณผ ๊ฐ™์Œ (${tableName}). ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ์ค‘...` - ); - // ๋ฉ”์ธ ํ…Œ์ด๋ธ”์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ Repeater ํ•ญ๋ชฉ์— ๋ณต์‚ฌ - Object.keys(dataToInsert).forEach((key) => { - // ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” ํ•„๋“œ๋งŒ ์ถ”๊ฐ€ - if (itemData[key] === undefined) { - itemData[key] = dataToInsert[key]; - } - }); - console.log( - `โœ… [Repeater] ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ ์™„๋ฃŒ:`, - Object.keys(itemData) - ); - } + // ๐Ÿ”ฅ ๋ณ„๋„ ํ…Œ์ด๋ธ”์ธ ๊ฒฝ์šฐ์—๋งŒ ์™ธ๋ž˜ํ‚ค ์ถ”๊ฐ€ + // (๊ฐ™์€ ํ…Œ์ด๋ธ”์ด๋ฉด ์ด๋ฏธ ๋ณ‘ํ•ฉ ๋ชจ๋“œ์—์„œ ์ฒ˜๋ฆฌ๋จ) // ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ์กด์žฌํ•˜๋Š” ์ปฌ๋Ÿผ๋งŒ ํ•„ํ„ฐ๋ง Object.keys(itemData).forEach((key) => { diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 3edd3e8a..5f825cdc 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -342,15 +342,15 @@ export class ButtonActionExecutor { const writerValue = context.userId; const companyCodeValue = context.companyCode || ""; - console.log("๐Ÿ‘ค [buttonActions] ์‚ฌ์šฉ์ž ์ •๋ณด:", { - userId: context.userId, - userName: context.userName, - companyCode: context.companyCode, // โœ… ํšŒ์‚ฌ ์ฝ”๋“œ - formDataWriter: formData.writer, // โœ… ํผ์—์„œ ์ž…๋ ฅํ•œ writer ๊ฐ’ - formDataCompanyCode: formData.company_code, // โœ… ํผ์—์„œ ์ž…๋ ฅํ•œ company_code ๊ฐ’ - defaultWriterValue: writerValue, - companyCodeValue, // โœ… ์ตœ์ข… ํšŒ์‚ฌ ์ฝ”๋“œ ๊ฐ’ - }); + // console.log("๐Ÿ‘ค [buttonActions] ์‚ฌ์šฉ์ž ์ •๋ณด:", { + // userId: context.userId, + // userName: context.userName, + // companyCode: context.companyCode, + // formDataWriter: formData.writer, + // formDataCompanyCode: formData.company_code, + // defaultWriterValue: writerValue, + // companyCodeValue, + // }); // ๐ŸŽฏ ์ฑ„๋ฒˆ ๊ทœ์น™ ํ• ๋‹น ์ฒ˜๋ฆฌ (์ €์žฅ ์‹œ์ ์— ์‹ค์ œ ์ˆœ๋ฒˆ ์ฆ๊ฐ€) // console.log("๐Ÿ” ์ฑ„๋ฒˆ ๊ทœ์น™ ํ• ๋‹น ์ฒดํฌ ์‹œ์ž‘");