# TableListComponent 개발 가이드 ## 개요 `TableListComponent`는 ERP 시스템의 핵심 데이터 그리드 컴포넌트입니다. DevExpress DataGrid 스타일의 고급 기능들을 구현하고 있습니다. **파일 위치**: `frontend/lib/registry/components/table-list/TableListComponent.tsx` --- ## 핵심 기능 목록 ### 1. 인라인 편집 (Inline Editing) - 셀 더블클릭 또는 F2 키로 편집 모드 진입 - 직접 타이핑으로도 편집 모드 진입 가능 - Enter로 저장, Escape로 취소 - **컬럼별 편집 가능 여부 설정** (`editable` 속성) ```typescript // ColumnConfig에서 editable 속성 사용 interface ColumnConfig { editable?: boolean; // false면 해당 컬럼 인라인 편집 불가 } ``` **편집 불가 컬럼 체크 필수 위치**: 1. `handleCellDoubleClick` - 더블클릭 편집 2. `onKeyDown` F2 케이스 - 키보드 편집 3. `onKeyDown` default 케이스 - 직접 타이핑 편집 4. 컨텍스트 메뉴 "셀 편집" 옵션 ### 2. 배치 편집 (Batch Editing) - 여러 셀 수정 후 일괄 저장/취소 - `pendingChanges` Map으로 변경사항 추적 - 저장 전 유효성 검증 ### 3. 데이터 유효성 검증 (Validation) ```typescript type ValidationRule = { required?: boolean; min?: number; max?: number; minLength?: number; maxLength?: number; pattern?: RegExp; customMessage?: string; validate?: (value: any, row: any) => string | null; }; ``` ### 4. 컬럼 헤더 필터 (Header Filter) - 각 컬럼 헤더에 필터 아이콘 - 고유값 목록에서 다중 선택 필터링 - `headerFilters` Map으로 필터 상태 관리 ### 5. 필터 빌더 (Filter Builder) ```typescript interface FilterCondition { id: string; column: string; operator: "equals" | "notEquals" | "contains" | "notContains" | "startsWith" | "endsWith" | "greaterThan" | "lessThan" | "greaterOrEqual" | "lessOrEqual" | "isEmpty" | "isNotEmpty"; value: string; } interface FilterGroup { id: string; logic: "AND" | "OR"; conditions: FilterCondition[]; } ``` ### 6. 검색 패널 (Search Panel) - 전체 데이터 검색 - 검색어 하이라이팅 - `searchHighlights` Map으로 하이라이트 위치 관리 ### 7. 엑셀 내보내기 (Excel Export) - `xlsx` 라이브러리 사용 - 현재 표시 데이터 또는 전체 데이터 내보내기 ```typescript import * as XLSX from "xlsx"; // 사용 예시 const worksheet = XLSX.utils.json_to_sheet(exportData); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1"); XLSX.writeFile(workbook, `${tableName}_${timestamp}.xlsx`); ``` ### 8. 클립보드 복사 (Copy to Clipboard) - 선택된 행 또는 전체 데이터 복사 - 탭 구분자로 엑셀 붙여넣기 호환 ### 9. 컨텍스트 메뉴 (Context Menu) - 우클릭으로 메뉴 표시 - 셀 편집, 행 복사, 행 삭제 등 옵션 - 편집 불가 컬럼은 "(잠김)" 표시 ### 10. 키보드 네비게이션 | 키 | 동작 | |---|---| | Arrow Keys | 셀 이동 | | Tab | 다음 셀 | | Shift+Tab | 이전 셀 | | F2 | 편집 모드 | | Enter | 저장 후 아래로 이동 | | Escape | 편집 취소 | | Ctrl+C | 복사 | | Delete | 셀 값 삭제 | ### 11. 컬럼 리사이징 - 컬럼 헤더 경계 드래그로 너비 조절 - `columnWidths` 상태로 관리 - localStorage에 저장 ### 12. 컬럼 순서 변경 - 드래그 앤 드롭으로 컬럼 순서 변경 - `columnOrder` 상태로 관리 - localStorage에 저장 ### 13. 상태 영속성 (State Persistence) ```typescript // localStorage 키 패턴 const stateKey = `tableState_${tableName}_${userId}`; // 저장되는 상태 interface TableState { columnWidths: Record; columnOrder: string[]; sortBy: string; sortOrder: "asc" | "desc"; frozenColumns: string[]; columnVisibility: Record; } ``` ### 14. 그룹화 및 그룹 소계 ```typescript interface GroupedData { groupKey: string; groupValues: Record; items: any[]; count: number; summary?: Record; } ``` ### 15. 총계 요약 (Total Summary) - 숫자 컬럼의 합계, 평균, 개수 표시 - 테이블 하단에 요약 행 렌더링 --- ## 캐싱 전략 ```typescript // 테이블 컬럼 캐시 const tableColumnCache = new Map(); const TABLE_CACHE_TTL = 5 * 60 * 1000; // 5분 // API 호출 디바운싱 const debouncedApiCall = ( key: string, fn: (...args: T) => Promise, delay: number = 300 ) => { ... }; ``` --- ## 필수 Import ```typescript import React, { useState, useEffect, useMemo, useCallback, useRef } from "react"; import { TableListConfig, ColumnConfig } from "./types"; import { tableTypeApi } from "@/lib/api/screen"; import { entityJoinApi } from "@/lib/api/entityJoin"; import { codeCache } from "@/lib/caching/codeCache"; import * as XLSX from "xlsx"; import { toast } from "sonner"; ``` --- ## 주요 상태 (State) ```typescript // 데이터 관련 const [tableData, setTableData] = useState([]); const [filteredData, setFilteredData] = useState([]); const [loading, setLoading] = useState(false); // 편집 관련 const [editingCell, setEditingCell] = useState<{ rowIndex: number; colIndex: number; columnName: string; originalValue: any; } | null>(null); const [editingValue, setEditingValue] = useState(""); const [pendingChanges, setPendingChanges] = useState>>(new Map()); const [validationErrors, setValidationErrors] = useState>>(new Map()); // 필터 관련 const [headerFilters, setHeaderFilters] = useState>>(new Map()); const [filterGroups, setFilterGroups] = useState([]); const [globalSearchText, setGlobalSearchText] = useState(""); const [searchHighlights, setSearchHighlights] = useState>(new Map()); // 컬럼 관련 const [columnWidths, setColumnWidths] = useState>({}); const [columnOrder, setColumnOrder] = useState([]); const [columnVisibility, setColumnVisibility] = useState>({}); const [frozenColumns, setFrozenColumns] = useState([]); // 선택 관련 const [selectedRows, setSelectedRows] = useState>(new Set()); const [focusedCell, setFocusedCell] = useState<{ rowIndex: number; colIndex: number } | null>(null); // 정렬 관련 const [sortBy, setSortBy] = useState(""); const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc"); // 페이지네이션 const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(20); const [totalCount, setTotalCount] = useState(0); ``` --- ## 편집 불가 컬럼 구현 체크리스트 새로운 편집 진입점을 추가할 때 반드시 다음을 확인하세요: - [ ] `column.editable === false` 체크 추가 - [ ] 편집 불가 시 `toast.warning()` 메시지 표시 - [ ] `return` 또는 `break`로 편집 모드 진입 방지 ```typescript // 표준 편집 불가 체크 패턴 const column = visibleColumns.find((col) => col.columnName === columnName); if (column?.editable === false) { toast.warning(`'${column.displayName || columnName}' 컬럼은 편집할 수 없습니다.`); return; } ``` --- ## 시각적 표시 ### 편집 불가 컬럼 표시 ```tsx // 헤더에 잠금 아이콘 {column.editable === false && ( )} // 셀 배경색 className={cn( column.editable === false && "bg-gray-50 dark:bg-gray-900/30" )} ``` --- ## 성능 최적화 1. **useMemo 사용**: `visibleColumns`, `filteredData`, `paginatedData` 등 계산 비용이 큰 값 2. **useCallback 사용**: 이벤트 핸들러 함수들 3. **디바운싱**: API 호출, 검색, 필터링 4. **캐싱**: 테이블 컬럼 정보, 코드 데이터 --- ## 주의사항 1. **visibleColumns 정의 순서**: `columnOrder`, `columnVisibility` 상태 이후에 정의해야 함 2. **editInputRef 타입 체크**: `select()` 호출 전 `instanceof HTMLInputElement` 확인 3. **localStorage 키**: `tableName`과 `userId`를 조합하여 고유하게 생성 4. **멀티테넌시**: 모든 API 호출에 `company_code` 필터링 적용 (백엔드에서 자동 처리) --- ## 관련 파일 - `frontend/lib/registry/components/table-list/types.ts` - 타입 정의 - `frontend/lib/registry/components/table-list/TableListConfigPanel.tsx` - 설정 패널 - `frontend/components/common/TableOptionsModal.tsx` - 옵션 모달 - `frontend/lib/registry/components/table-list/SingleTableWithSticky.tsx` - 스티키 헤더 테이블