리피터 데이터 저장 로직 개선 및 이벤트 처리 추가
- EditModal, InteractiveScreenViewer, SaveModal 컴포넌트에서 리피터 데이터(배열)를 마스터 저장에서 제외하고, 별도로 저장하는 로직을 추가하였습니다. - 리피터 데이터 저장 이벤트를 발생시켜 UnifiedRepeater 컴포넌트가 이를 리스닝하도록 개선하였습니다. - 각 컴포넌트에서 최종 저장 데이터 로그를 업데이트하여, 저장 과정에서의 데이터 흐름을 명확히 하였습니다. 이로 인해 데이터 저장의 효율성과 리피터 관리의 일관성이 향상되었습니다.
This commit is contained in:
@@ -260,9 +260,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
// 객체인 경우 tableName 속성 추출 시도
|
||||
if (typeof finalSelectedTable === "object" && finalSelectedTable !== null) {
|
||||
console.warn("⚠️ selectedTable이 객체입니다:", finalSelectedTable);
|
||||
finalSelectedTable = (finalSelectedTable as any).tableName || (finalSelectedTable as any).name || tableName;
|
||||
console.log("✅ 객체에서 추출한 테이블명:", finalSelectedTable);
|
||||
}
|
||||
|
||||
tableConfig.selectedTable = finalSelectedTable;
|
||||
@@ -353,12 +351,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
});
|
||||
|
||||
console.log("🔍 [TableListComponent] filters → searchValues:", {
|
||||
filtersCount: filters.length,
|
||||
filters: filters.map((f) => ({ col: f.columnName, op: f.operator, val: f.value })),
|
||||
searchValues: newSearchValues,
|
||||
});
|
||||
|
||||
setSearchValues(newSearchValues);
|
||||
setCurrentPage(1); // 필터 변경 시 첫 페이지로
|
||||
}, [filters]);
|
||||
@@ -761,7 +753,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
});
|
||||
|
||||
if (hasChanges) {
|
||||
console.log("🔗 [TableList] 연결된 필터 값 변경:", newFilterValues);
|
||||
setLinkedFilterValues(newFilterValues);
|
||||
|
||||
// searchValues에 연결된 필터 값 병합
|
||||
@@ -817,13 +808,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
componentType: "table",
|
||||
|
||||
receiveData: async (receivedData: any[], config: DataReceiverConfig) => {
|
||||
console.log("📥 TableList 데이터 수신:", {
|
||||
componentId: component.id,
|
||||
receivedDataCount: receivedData.length,
|
||||
mode: config.mode,
|
||||
currentDataCount: data.length,
|
||||
});
|
||||
|
||||
try {
|
||||
let newData: any[] = [];
|
||||
|
||||
@@ -831,13 +815,11 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
case "append":
|
||||
// 기존 데이터에 추가
|
||||
newData = [...data, ...receivedData];
|
||||
console.log("✅ Append 모드: 기존 데이터에 추가", { newDataCount: newData.length });
|
||||
break;
|
||||
|
||||
case "replace":
|
||||
// 기존 데이터를 완전히 교체
|
||||
newData = receivedData;
|
||||
console.log("✅ Replace 모드: 데이터 교체", { newDataCount: newData.length });
|
||||
break;
|
||||
|
||||
case "merge":
|
||||
@@ -853,7 +835,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
});
|
||||
newData = Array.from(existingMap.values());
|
||||
console.log("✅ Merge 모드: 데이터 병합", { newDataCount: newData.length });
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -862,10 +843,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
// 총 아이템 수 업데이트
|
||||
setTotalItems(newData.length);
|
||||
|
||||
console.log("✅ 데이터 수신 완료:", { finalDataCount: newData.length });
|
||||
} catch (error) {
|
||||
console.error("❌ 데이터 수신 실패:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -899,7 +877,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
componentId: component.id,
|
||||
componentType: "table-list",
|
||||
receiveData: async (incomingData: any[], mode: "append" | "replace" | "merge") => {
|
||||
console.log("📥 [TableListComponent] 분할 패널에서 데이터 수신:", {
|
||||
// 분할 패널에서 데이터 수신 처리
|
||||
const receiveInfo = {
|
||||
count: incomingData.length,
|
||||
mode,
|
||||
position: currentSplitPosition,
|
||||
@@ -937,24 +916,12 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
// 컬럼의 고유 값 조회 함수
|
||||
const getColumnUniqueValues = async (columnName: string) => {
|
||||
console.log("🔍 [getColumnUniqueValues] 호출됨:", {
|
||||
columnName,
|
||||
dataLength: data.length,
|
||||
columnMeta: columnMeta[columnName],
|
||||
sampleData: data[0],
|
||||
});
|
||||
|
||||
const meta = columnMeta[columnName];
|
||||
const inputType = meta?.inputType || "text";
|
||||
|
||||
// 카테고리 타입인 경우 전체 정의된 값 조회 (백엔드 API)
|
||||
if (inputType === "category") {
|
||||
try {
|
||||
console.log("🔍 [getColumnUniqueValues] 카테고리 전체 값 조회:", {
|
||||
tableName: tableConfig.selectedTable,
|
||||
columnName,
|
||||
});
|
||||
|
||||
// API 클라이언트 사용 (쿠키 인증 자동 처리)
|
||||
const { apiClient } = await import("@/lib/api/client");
|
||||
const response = await apiClient.get(`/table-categories/${tableConfig.selectedTable}/${columnName}/values`);
|
||||
@@ -965,24 +932,9 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
label: item.valueLabel, // 카멜케이스
|
||||
}));
|
||||
|
||||
console.log("✅ [getColumnUniqueValues] 카테고리 전체 값:", {
|
||||
columnName,
|
||||
count: categoryOptions.length,
|
||||
options: categoryOptions,
|
||||
});
|
||||
|
||||
return categoryOptions;
|
||||
} else {
|
||||
console.warn("⚠️ [getColumnUniqueValues] 응답 형식 오류:", response.data);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("❌ [getColumnUniqueValues] 카테고리 조회 실패:", {
|
||||
error: error.message,
|
||||
response: error.response?.data,
|
||||
status: error.response?.status,
|
||||
columnName,
|
||||
tableName: tableConfig.selectedTable,
|
||||
});
|
||||
} catch {
|
||||
// 에러 시 현재 데이터 기반으로 fallback
|
||||
}
|
||||
}
|
||||
@@ -991,15 +943,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const isLabelType = ["category", "entity", "code"].includes(inputType);
|
||||
const labelField = isLabelType ? `${columnName}_name` : columnName;
|
||||
|
||||
console.log("🔍 [getColumnUniqueValues] 데이터 기반 조회:", {
|
||||
columnName,
|
||||
inputType,
|
||||
isLabelType,
|
||||
labelField,
|
||||
hasLabelField: data[0] && labelField in data[0],
|
||||
sampleLabelValue: data[0] ? data[0][labelField] : undefined,
|
||||
});
|
||||
|
||||
// 현재 로드된 데이터에서 고유 값 추출
|
||||
const uniqueValuesMap = new Map<string, string>(); // value -> label
|
||||
|
||||
@@ -1020,15 +963,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label));
|
||||
|
||||
console.log("✅ [getColumnUniqueValues] 데이터 기반 결과:", {
|
||||
columnName,
|
||||
inputType,
|
||||
isLabelType,
|
||||
labelField,
|
||||
uniqueCount: result.length,
|
||||
values: result,
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -1105,10 +1039,9 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
setSortColumn(column);
|
||||
setSortDirection(direction);
|
||||
hasInitializedSort.current = true;
|
||||
console.log("📂 localStorage에서 정렬 상태 복원:", { column, direction });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 정렬 상태 복원 실패:", error);
|
||||
} catch {
|
||||
// 정렬 상태 복원 실패 - 무시
|
||||
}
|
||||
}
|
||||
}, [tableConfig.selectedTable, userId]);
|
||||
@@ -1124,12 +1057,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
if (savedOrder) {
|
||||
try {
|
||||
const parsedOrder = JSON.parse(savedOrder);
|
||||
console.log("📂 localStorage에서 컬럼 순서 불러오기:", { storageKey, columnOrder: parsedOrder });
|
||||
setColumnOrder(parsedOrder);
|
||||
|
||||
// 부모 컴포넌트에 초기 컬럼 순서 전달
|
||||
if (onSelectedRowsChange && parsedOrder.length > 0) {
|
||||
console.log("✅ 초기 컬럼 순서 전달:", parsedOrder);
|
||||
|
||||
// 초기 데이터도 함께 전달 (컬럼 순서대로 재정렬)
|
||||
const initialData = data.map((row: any) => {
|
||||
@@ -1177,8 +1108,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
onSelectedRowsChange([], [], sortColumn, sortDirection, parsedOrder, initialData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 컬럼 순서 파싱 실패:", error);
|
||||
} catch {
|
||||
// 컬럼 순서 파싱 실패 - 무시
|
||||
}
|
||||
}
|
||||
}, [tableConfig.selectedTable, userId, data.length]); // data.length 추가 (데이터 로드 후 실행)
|
||||
@@ -1336,11 +1267,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const parts = columnName.split(".");
|
||||
targetTable = parts[0]; // 조인된 테이블명 (예: item_info)
|
||||
targetColumn = parts[1]; // 실제 컬럼명 (예: material)
|
||||
console.log("🔗 [TableList] 엔티티 조인 컬럼 감지:", {
|
||||
originalColumn: columnName,
|
||||
targetTable,
|
||||
targetColumn,
|
||||
});
|
||||
}
|
||||
|
||||
const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values`);
|
||||
@@ -1438,8 +1364,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
// 조인 테이블의 컬럼 inputType 정보 가져오기 (이미 import된 tableTypeApi 사용)
|
||||
const inputTypes = await tableTypeApi.getColumnInputTypes(joinedTable);
|
||||
|
||||
console.log(`📡 [TableList] 조인 테이블 inputType 로드 [${joinedTable}]:`, inputTypes);
|
||||
|
||||
for (const col of columns) {
|
||||
const inputTypeInfo = inputTypes.find((it: any) => it.columnName === col.actualColumn);
|
||||
|
||||
@@ -1448,17 +1372,9 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
inputType: inputTypeInfo?.inputType,
|
||||
};
|
||||
|
||||
console.log(
|
||||
` 🔗 [${col.columnName}] (실제: ${col.actualColumn}) inputType: ${inputTypeInfo?.inputType || "unknown"}`,
|
||||
);
|
||||
|
||||
// inputType이 category인 경우 카테고리 매핑 로드
|
||||
if (inputTypeInfo?.inputType === "category" && !mappings[col.columnName]) {
|
||||
try {
|
||||
console.log(`📡 [TableList] 조인 테이블 카테고리 로드 시도 [${col.columnName}]:`, {
|
||||
url: `/table-categories/${joinedTable}/${col.actualColumn}/values`,
|
||||
});
|
||||
|
||||
const response = await apiClient.get(`/table-categories/${joinedTable}/${col.actualColumn}/values`);
|
||||
|
||||
if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
|
||||
@@ -1476,20 +1392,19 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
mappings[col.columnName] = mapping;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`ℹ️ [TableList] 조인 테이블 카테고리 없음 (${col.columnName})`);
|
||||
} catch {
|
||||
// 조인 테이블 카테고리 없음 - 무시
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ [TableList] 조인 테이블 inputType 로드 실패 [${joinedTable}]:`, error);
|
||||
console.error(`조인 테이블 inputType 로드 실패 [${joinedTable}]:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// 조인 컬럼 메타데이터 상태 업데이트
|
||||
if (Object.keys(newJoinedColumnMeta).length > 0) {
|
||||
setJoinedColumnMeta(newJoinedColumnMeta);
|
||||
console.log("✅ [TableList] 조인 컬럼 메타데이터 설정:", newJoinedColumnMeta);
|
||||
}
|
||||
|
||||
// 🆕 카테고리 연쇄관계 매핑 로드 (category_value_cascading_mapping)
|
||||
@@ -1515,26 +1430,17 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
};
|
||||
}
|
||||
}
|
||||
console.log("✅ [TableList] 카테고리 연쇄관계 매핑 로드 완료:", {
|
||||
tableName: tableConfig.selectedTable,
|
||||
cascadingColumns: Object.keys(cascadingMappings),
|
||||
});
|
||||
}
|
||||
} catch (cascadingError: any) {
|
||||
// 연쇄관계 매핑이 없는 경우 무시 (404 등)
|
||||
if (cascadingError?.response?.status !== 404) {
|
||||
console.warn("⚠️ [TableList] 카테고리 연쇄관계 매핑 로드 실패:", cascadingError?.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(mappings).length > 0) {
|
||||
setCategoryMappings(mappings);
|
||||
setCategoryMappingsKey((prev) => prev + 1);
|
||||
} else {
|
||||
console.warn("⚠️ [TableList] 매핑이 비어있어 상태 업데이트 스킵");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ [TableList] 카테고리 매핑 로드 실패:", error);
|
||||
console.error("카테고리 매핑 로드 실패:", error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user