ERP-node/docs/v2-table-list-entity-join-a...

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_namedept_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) 후 쿼리 파라미터로 전달
  • 백엔드: entityJoinControllertableManagementService.getTableDataWithEntityJoins

4.3 백엔드 처리 (tableManagementService)

  1. detectEntityJoins로 기본 Entity 조인 설정 조회
  2. additionalJoinColumns가 있으면:
    • sourceColumn 또는 referenceTable로 기존 joinConfig 찾기
    • joinAlias에서 실제 컬럼명 추출 (예: dept_code_company_namecompany_name)
    • 기존 config에 displayColumns 병합 또는 새 config 추가
    • aliasColumn: ${sourceColumn}_${actualColumnName} (예: dept_code_company_name)
  3. additionalJoinColumns가 있으면 full_join 전략 강제 사용 (캐시 미사용)

5. 데이터 표시 시 조인 데이터 매핑

5.1 additionalJoinInfo 컬럼 (조인 탭에서 추가한 컬럼)

  • 백엔드 응답: row에 joinAlias 키로 값이 직접 들어옴
    • 예: row.dept_code_company_name = "개발팀"
  • 프론트엔드: column.columnNamejoinAlias와 동일하므로 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, availableColumns
    • availableColumns: 모든 조인 컬럼을 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}

타입 정의

  • RepeaterColumnConfigadditionalJoinInfo?: { 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 병합 로직