- Added support for entity joins in the V2Repeater component, allowing for automatic resolution of foreign key references to display data from related tables. - Introduced a new `resolveEntityJoins` function to handle the fetching and mapping of reference data based on configured entity joins. - Enhanced the V2RepeaterConfigPanel to manage entity join configurations, including loading available columns and toggling join settings. - Updated the data handling logic to incorporate mapping rules for incoming data, ensuring that only necessary fields are retained during processing. - Improved user experience by providing clear logging and feedback during entity join resolution and data mapping operations.
10 KiB
10 KiB
v2-table-list Entity 조인 기능 분석
v2-repeater에 동일 기능을 추가하기 위한 상세 분석 문서입니다.
1. 개요
v2-table-list의 Entity 조인 기능은 두 가지 유형으로 구분됩니다:
| 유형 | 설명 | 설정 방식 |
|---|---|---|
| isEntityJoin | 테이블 컬럼이 input_type=entity인 경우 (테이블 타입 관리에서 참조 테이블 설정됨) |
자동 감지 + entityDisplayConfig로 표시 컬럼 선택 |
| additionalJoinInfo | ConfigPanel "Entity 조인 컬럼" 탭에서 수동 추가한 참조 테이블 컬럼 | addEntityColumn으로 추가, additionalJoinInfo 저장 |
2. Entity 조인 설정 UI 구조 (TableListConfigPanel)
2.1 데이터 소스
- entityJoinApi.getEntityJoinColumns(tableName) 호출
- targetTableName 변경 시 useEffect로 재호출
2.2 entityJoinColumns 상태 구조
{
availableColumns: Array<{
tableName: string; // 참조 테이블명 (예: dept_info)
columnName: string; // 참조 테이블 컬럼명 (예: company_name)
columnLabel: string;
dataType: string;
joinAlias: string; // 예: dept_code_company_name (sourceColumn_columnName)
suggestedLabel: string;
}>;
joinTables: Array<{
tableName: string; // 참조 테이블명
currentDisplayColumn: string;
joinConfig: { // 백엔드 entity-join-columns API에서 반환
sourceColumn: string; // 기준 테이블 FK 컬럼 (예: dept_code)
referenceTable: string;
referenceColumn: string;
displayColumn: string;
// ...
};
availableColumns: Array<{
columnName: string;
columnLabel: string;
dataType: string;
inputType?: string;
}>;
}>;
}
2.3 Entity 조인 컬럼 UI (ConfigPanel)
- 위치: 기본 컬럼 선택 영역 아래, "Entity 조인 컬럼" 섹션
- 조건:
entityJoinColumns.joinTables.length > 0일 때만 표시 - 구조: joinTables별로 그룹화 → 각 그룹 내 availableColumns를 체크박스로 표시
- 추가 로직:
addEntityColumn(joinColumn)호출
2.4 addEntityColumn 함수 (핵심)
const addEntityColumn = (joinColumn: availableColumns[0]) => {
// joinTables에서 sourceColumn 추출 (필수!)
const joinTableInfo = entityJoinColumns.joinTables?.find(
(jt) => jt.tableName === joinColumn.tableName
);
const sourceColumn = joinTableInfo?.joinConfig?.sourceColumn || "";
const newColumn: ColumnConfig = {
columnName: joinColumn.joinAlias, // 예: dept_code_company_name
displayName: joinColumn.columnLabel,
// ...
isEntityJoin: false, // 조인 탭에서 추가한 컬럼은 엔티티 타입이 아님
additionalJoinInfo: {
sourceTable: config.selectedTable || screenTableName || "",
sourceColumn: sourceColumn, // dept_code
referenceTable: joinColumn.tableName, // dept_info
joinAlias: joinColumn.joinAlias, // dept_code_company_name
},
};
handleChange("columns", [...config.columns, newColumn]);
};
주의: sourceColumn은 반드시 joinTableInfo.joinConfig.sourceColumn에서 가져와야 합니다. joinColumn에는 없습니다.
3. additionalJoinInfo 데이터 구조
3.1 타입 정의 (types.ts)
additionalJoinInfo?: {
sourceTable: string; // 기준 테이블 (예: user_info)
sourceColumn: string; // 기준 테이블 FK 컬럼 (예: dept_code)
referenceTable?: string; // 참조 테이블 (예: dept_info)
joinAlias: string; // 조인 결과 컬럼 별칭 (예: dept_code_company_name)
};
3.2 네이밍 규칙
- joinAlias:
${sourceColumn}_${referenceTable컬럼명} - 예:
dept_code+company_name→dept_code_company_name - 백엔드가 이 규칙으로 SELECT 시 alias를 생성하고, 응답 row에
dept_code_company_name키로 값이 들어옴
4. 백엔드 API 호출 흐름
4.1 TableListComponent 데이터 로딩
// 1. additionalJoinInfo가 있는 컬럼만 추출
const entityJoinColumns = (tableConfig.columns || [])
.filter((col) => col.additionalJoinInfo)
.map((col) => ({
sourceTable: col.additionalJoinInfo!.sourceTable,
sourceColumn: col.additionalJoinInfo!.sourceColumn,
joinAlias: col.additionalJoinInfo!.joinAlias,
referenceTable: col.additionalJoinInfo!.referenceTable,
}));
// 2. entityDisplayConfig가 있는 컬럼 (isEntityJoin) - 화면별 표시 설정
const screenEntityConfigs: Record<string, any> = {};
(tableConfig.columns || [])
.filter((col) => col.entityDisplayConfig?.displayColumns?.length > 0)
.forEach((col) => {
screenEntityConfigs[col.columnName] = {
displayColumns: col.entityDisplayConfig!.displayColumns,
separator: col.entityDisplayConfig!.separator || " - ",
sourceTable: col.entityDisplayConfig!.sourceTable || tableConfig.selectedTable,
joinTable: col.entityDisplayConfig!.joinTable,
};
});
// 3. API 호출
response = await entityJoinApi.getTableDataWithJoins(tableConfig.selectedTable, {
page, size, sortBy, sortOrder,
search: hasFilters ? filters : undefined,
enableEntityJoin: true,
additionalJoinColumns: entityJoinColumns.length > 0 ? entityJoinColumns : undefined,
screenEntityConfigs: Object.keys(screenEntityConfigs).length > 0 ? screenEntityConfigs : undefined,
dataFilter: tableConfig.dataFilter,
excludeFilter: excludeFilterParam,
});
4.2 entityJoinApi.getTableDataWithJoins 파라미터
additionalJoinColumns?: Array<{
sourceTable: string;
sourceColumn: string;
joinAlias: string;
referenceTable?: string; // 백엔드에서 referenceTable로 기존 조인 찾을 때 사용
}>;
- 전달 방식:
JSON.stringify(additionalJoinColumns)후 쿼리 파라미터로 전달 - 백엔드:
entityJoinController→tableManagementService.getTableDataWithEntityJoins
4.3 백엔드 처리 (tableManagementService)
detectEntityJoins로 기본 Entity 조인 설정 조회additionalJoinColumns가 있으면:sourceColumn또는referenceTable로 기존 joinConfig 찾기joinAlias에서 실제 컬럼명 추출 (예:dept_code_company_name→company_name)- 기존 config에
displayColumns병합 또는 새 config 추가 aliasColumn:${sourceColumn}_${actualColumnName}(예:dept_code_company_name)
additionalJoinColumns가 있으면 full_join 전략 강제 사용 (캐시 미사용)
5. 데이터 표시 시 조인 데이터 매핑
5.1 additionalJoinInfo 컬럼 (조인 탭에서 추가한 컬럼)
- 백엔드 응답: row에
joinAlias키로 값이 직접 들어옴- 예:
row.dept_code_company_name = "개발팀"
- 예:
- 프론트엔드:
column.columnName이joinAlias와 동일하므로rowData[column.columnName]으로 바로 접근 - formatCellValue:
entityDisplayConfig가 없으면 일반 컬럼처럼value사용 (이미 row에 joinAlias로 들어있음)
5.2 entityDisplayConfig 컬럼 (isEntityJoin, 테이블 타입 관리에서 entity 설정된 컬럼)
- formatCellValue 로직:
if (column.entityDisplayConfig && rowData) { const displayColumns = column.entityDisplayConfig.displayColumns; const separator = column.entityDisplayConfig.separator; const values = displayColumns.map((colName) => { const joinedKey = `${column.columnName}_${colName}`; // 예: manager_user_name let cellValue = rowData[joinedKey]; if (cellValue == null) cellValue = rowData[colName]; return cellValue ?? ""; }); return values.filter(v => v !== "").join(separator || " - "); } - 백엔드 alias 규칙:
${sourceColumn}_${displayColumn}(예:manager_user_name)
5.3 joinedColumnMeta (inputType/category 매핑)
- additionalJoinInfo 컬럼도
joinedColumnMeta에 등록됨 actualColumn추출:joinAlias.replace(\${sourceColumn}_`, "")` → 참조 테이블의 실제 컬럼명- 조인 테이블별로
tableTypeApi.getColumnInputTypes호출하여 inputType 로드
6. entity-join-columns API (ConfigPanel용)
- 엔드포인트:
GET /api/table-management/tables/:tableName/entity-join-columns - 역할: 화면 편집기에서 "Entity 조인 컬럼" 탭에 표시할 데이터 제공
- 응답:
joinTables: 각 Entity 조인별joinConfig,tableName,availableColumnsavailableColumns: 모든 조인 컬럼을 flat하게 (joinAlias 포함)
- joinConfig:
entityJoinService.detectEntityJoins결과에서 옴 (테이블 타입 관리의 reference_table 설정 기반)
7. v2-repeater 적용 시 체크리스트
ConfigPanel
entityJoinApi.getEntityJoinColumns(targetTableName)호출entityJoinColumns상태 (availableColumns, joinTables)- "Entity 조인 컬럼" UI 섹션 (joinTables.length > 0일 때)
addEntityColumn함수:joinConfig.sourceColumn사용- RepeaterColumnConfig에
additionalJoinInfo타입 추가
데이터 로딩 (RepeaterComponent)
additionalJoinInfo가 있는 컬럼 추출 →entityJoinColumns배열 생성entityJoinApi.getTableDataWithJoins호출 시additionalJoinColumns전달entityDisplayConfig가 있으면screenEntityConfigs에도 포함 (isEntityJoin 컬럼용)
셀 렌더링
- additionalJoinInfo 컬럼:
rowData[column.columnName](joinAlias와 동일) - entityDisplayConfig 컬럼: displayColumns + separator로 조합,
joinedKey = ${columnName}_${colName}
타입 정의
RepeaterColumnConfig에additionalJoinInfo?: { sourceTable, sourceColumn, referenceTable, joinAlias }추가
8. 참고 파일
| 파일 | 용도 |
|---|---|
frontend/lib/registry/components/v2-table-list/TableListConfigPanel.tsx |
Entity 조인 UI, addEntityColumn |
frontend/lib/registry/components/v2-table-list/TableListComponent.tsx |
데이터 로딩, formatCellValue |
frontend/lib/registry/components/v2-table-list/types.ts |
additionalJoinInfo 타입 |
frontend/lib/api/entityJoin.ts |
getTableDataWithJoins, getEntityJoinColumns |
backend-node/src/controllers/entityJoinController.ts |
entity-join-columns, data-with-joins |
backend-node/src/services/tableManagementService.ts |
additionalJoinColumns 병합 로직 |