diff --git a/backend-node/src/services/entityJoinService.ts b/backend-node/src/services/entityJoinService.ts index 25d96927..86762b64 100644 --- a/backend-node/src/services/entityJoinService.ts +++ b/backend-node/src/services/entityJoinService.ts @@ -334,6 +334,10 @@ export class EntityJoinService { ); }); + // 🔧 _label 별칭 중복 방지를 위한 Set + // 같은 sourceColumn에서 여러 조인 설정이 있을 때 _label은 첫 번째만 생성 + const generatedLabelAliases = new Set(); + const joinColumns = joinConfigs .map((config) => { const aliasKey = `${config.referenceTable}:${config.sourceColumn}`; @@ -368,16 +372,26 @@ export class EntityJoinService { // _label 필드도 함께 SELECT (프론트엔드 getColumnUniqueValues용) // sourceColumn_label 형식으로 추가 - resultColumns.push( - `COALESCE(${alias}.${col}::TEXT, '') AS ${config.sourceColumn}_label` - ); + // 🔧 중복 방지: 같은 sourceColumn에서 _label은 첫 번째만 생성 + const labelAlias = `${config.sourceColumn}_label`; + if (!generatedLabelAliases.has(labelAlias)) { + resultColumns.push( + `COALESCE(${alias}.${col}::TEXT, '') AS ${labelAlias}` + ); + generatedLabelAliases.add(labelAlias); + } // 🆕 referenceColumn (PK)도 항상 SELECT (parentDataMapping용) // 예: customer_code, item_number 등 // col과 동일해도 별도의 alias로 추가 (customer_code as customer_code) - resultColumns.push( - `COALESCE(${alias}.${config.referenceColumn}::TEXT, '') AS ${config.referenceColumn}` - ); + // 🔧 중복 방지: referenceColumn도 한 번만 추가 + const refColAlias = config.referenceColumn; + if (!generatedLabelAliases.has(refColAlias)) { + resultColumns.push( + `COALESCE(${alias}.${config.referenceColumn}::TEXT, '') AS ${refColAlias}` + ); + generatedLabelAliases.add(refColAlias); + } } else { resultColumns.push( `COALESCE(main.${col}::TEXT, '') AS ${config.aliasColumn}` @@ -392,6 +406,11 @@ export class EntityJoinService { const individualAlias = `${config.sourceColumn}_${col}`; + // 🔧 중복 방지: 같은 alias가 이미 생성되었으면 스킵 + if (generatedLabelAliases.has(individualAlias)) { + return; + } + if (isJoinTableColumn) { // 조인 테이블 컬럼은 조인 별칭 사용 resultColumns.push( @@ -403,6 +422,7 @@ export class EntityJoinService { `COALESCE(main.${col}::TEXT, '') AS ${individualAlias}` ); } + generatedLabelAliases.add(individualAlias); }); // 🆕 referenceColumn (PK)도 함께 SELECT (parentDataMapping용) @@ -410,11 +430,13 @@ export class EntityJoinService { config.referenceTable && config.referenceTable !== tableName; if ( isJoinTableColumn && - !displayColumns.includes(config.referenceColumn) + !displayColumns.includes(config.referenceColumn) && + !generatedLabelAliases.has(config.referenceColumn) // 🔧 중복 방지 ) { resultColumns.push( `COALESCE(${alias}.${config.referenceColumn}::TEXT, '') AS ${config.referenceColumn}` ); + generatedLabelAliases.add(config.referenceColumn); } } diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index 869d2c3c..ab387348 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -924,10 +924,15 @@ export const SplitPanelLayoutComponent: React.FC const { entityJoinApi } = await import("@/lib/api/entityJoin"); // 복합키 조건 생성 + // 🔧 entity 타입 컬럼은 코드 값으로 정확히 매칭해야 하므로 operator: 'equals' 사용 const searchConditions: Record = {}; keys.forEach((key) => { if (key.leftColumn && key.rightColumn && leftItem[key.leftColumn] !== undefined) { - searchConditions[key.rightColumn] = leftItem[key.leftColumn]; + // 연결 필터는 정확한 값 매칭이 필요하므로 equals 연산자 사용 + searchConditions[key.rightColumn] = { + value: leftItem[key.leftColumn], + operator: "equals", + }; } }); @@ -1033,16 +1038,24 @@ export const SplitPanelLayoutComponent: React.FC if (keys && keys.length > 0) { // 복합키 + // 🔧 entity 타입 컬럼은 코드 값으로 정확히 매칭해야 하므로 operator: 'equals' 사용 keys.forEach((key) => { if (key.leftColumn && key.rightColumn && leftItem[key.leftColumn] !== undefined) { - searchConditions[key.rightColumn] = leftItem[key.leftColumn]; + searchConditions[key.rightColumn] = { + value: leftItem[key.leftColumn], + operator: "equals", + }; } }); } else { // 단일키 + // 🔧 entity 타입 컬럼은 코드 값으로 정확히 매칭해야 하므로 operator: 'equals' 사용 const leftValue = leftItem[leftColumn]; if (leftValue !== undefined) { - searchConditions[rightColumn] = leftValue; + searchConditions[rightColumn] = { + value: leftValue, + operator: "equals", + }; } }