diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index 366aa05b..9793acd8 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -2688,19 +2688,41 @@ export const TableListComponent: React.FC = ({ const value = row[mappedColumnName]; // 카테고리 매핑된 값 처리 - if (categoryMappings[col.columnName] && value !== null && value !== undefined) { - const mapping = categoryMappings[col.columnName][String(value)]; - if (mapping) { - return mapping.label; + if (value !== null && value !== undefined) { + const valueStr = String(value); + + // 디버그 로그 (카테고리 값인 경우만) + if (valueStr.startsWith("CATEGORY_")) { + console.log("🔍 [엑셀다운로드] 카테고리 변환 시도:", { + columnName: col.columnName, + value: valueStr, + hasMappings: !!categoryMappings[col.columnName], + mappingsKeys: categoryMappings[col.columnName] ? Object.keys(categoryMappings[col.columnName]).slice(0, 5) : [], + }); } + + if (categoryMappings[col.columnName]) { + // 쉼표로 구분된 중복 값 처리 + if (valueStr.includes(",")) { + const values = valueStr.split(",").map((v) => v.trim()).filter((v) => v); + const labels = values.map((v) => { + const mapping = categoryMappings[col.columnName][v]; + return mapping ? mapping.label : v; + }); + return labels.join(", "); + } + // 단일 값 처리 + const mapping = categoryMappings[col.columnName][valueStr]; + if (mapping) { + return mapping.label; + } + } + + return value; } // null/undefined 처리 - if (value === null || value === undefined) { - return ""; - } - - return value; + return ""; }); }); diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index af342a1f..a4b6074c 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -4801,7 +4801,24 @@ export class ButtonActionExecutor { const filteredRow: Record = {}; visibleColumns!.forEach((columnName: string) => { const label = columnLabels?.[columnName] || columnName; - filteredRow[label] = row[columnName]; + let value = row[columnName]; + + // 카테고리 코드를 라벨로 변환 (CATEGORY_로 시작하는 값) + if (value && typeof value === "string" && value.includes("CATEGORY_")) { + // 먼저 _label 필드 확인 (API에서 제공하는 경우) + const labelFieldName = `${columnName}_label`; + if (row[labelFieldName]) { + value = row[labelFieldName]; + } else { + // _value_label 필드 확인 + const valueLabelFieldName = `${columnName}_value_label`; + if (row[valueLabelFieldName]) { + value = row[valueLabelFieldName]; + } + } + } + + filteredRow[label] = value; }); return filteredRow; }); @@ -5074,8 +5091,15 @@ export class ButtonActionExecutor { value = row[`${columnName}_name`]; } // 카테고리 타입 필드는 라벨로 변환 (백엔드에서 정의된 컬럼만) - else if (categoryMap[columnName] && typeof value === "string" && categoryMap[columnName][value]) { - value = categoryMap[columnName][value]; + else if (categoryMap[columnName] && typeof value === "string") { + // 쉼표로 구분된 다중 값 처리 + if (value.includes(",")) { + const values = value.split(",").map((v) => v.trim()).filter((v) => v); + const labels = values.map((v) => categoryMap[columnName][v] || v); + value = labels.join(", "); + } else if (categoryMap[columnName][value]) { + value = categoryMap[columnName][value]; + } } filteredRow[label] = value; diff --git a/frontend/lib/utils/excelExport.ts b/frontend/lib/utils/excelExport.ts index 52c22f5a..6bd97624 100644 --- a/frontend/lib/utils/excelExport.ts +++ b/frontend/lib/utils/excelExport.ts @@ -116,8 +116,10 @@ export async function importFromExcel( return; } - // JSON으로 변환 - const jsonData = XLSX.utils.sheet_to_json(worksheet); + // JSON으로 변환 (빈 셀도 포함하여 모든 컬럼 키 유지) + const jsonData = XLSX.utils.sheet_to_json(worksheet, { + defval: "", // 빈 셀에 빈 문자열 할당 + }); console.log("✅ 엑셀 가져오기 완료:", { sheetName: targetSheetName,