ERP-node/docs/화면설정모달_개선_완료_보고서.md

536 lines
20 KiB
Markdown
Raw Normal View History

# 화면 설정 모달 개선 완료 보고서
## 개요
화면 관리에서 화면 노드 우클릭 시 열리는 설정 모달을 대폭 개선하여, 테이블 정보 시각화, 필드 매핑 확인, 컬럼 변경/추가/제거 기능, 조인 설정 기능, 실시간 프리뷰 기능을 강화했습니다.
## 주요 개선 사항
### 1. 화면 개요 탭 통합 개선
#### 1.1 필드 매핑 탭 → 개요 탭 통합
- 기존 "필드 매핑" 탭 제거
- 필드 매핑 정보를 개요 탭의 메인/필터 테이블 아코디언에 통합 표시
- 더 직관적이고 간결한 UI 제공
#### 1.2 메인 테이블 아코디언
- 메인 테이블(예: `customer_mng`)을 아코디언 형식으로 표시
- 클릭 시 테이블의 모든 컬럼 정보 표시
- **1열 레이아웃**: 컬럼 정보를 세로로 배치
- 화면에서 사용 중인 컬럼은 **파란색 배경 + "필드" 배지**로 강조
- **컬럼 정렬**:
- 사용중인 필드가 상단에 표시
- 화면에 표시되는 순서대로 정렬 (y좌표 기준)
- 미사용 컬럼은 하단에 표시
#### 1.3 필터 테이블 아코디언
- 필터 테이블(예: `customer_item_mapping`)을 아코디언 형식으로 표시
- 클릭 시 테이블의 모든 컬럼 정보 표시
- 컬럼별 색상 구분:
- **파란색**: 화면에서 사용 중인 컬럼 (필드)
- **보라색**: 필터 키 컬럼 (WHERE 절에 사용)
- **주황색**: 조인 키 컬럼 (JOIN 조건에 사용)
- **다중 배지 표시**: 컬럼이 필드이면서 조인/필터 키인 경우 배지 동시 표시
- 필터 연결 정보 표시 (예: `→ customer_mng`)
#### 1.4 컬럼 레이아웃 순서
- **순서**: `컬럼명 | 배지 | 데이터타입`
- 예: `거래처 코드` `[필드]` `character varying`
- 데이터타입은 오른쪽 정렬
#### 1.5 클릭 스타일 개선
- **테두리 제거**: ring-2, ring-offset 등 제거
- **강조 색상 연하게**:
- 선택됨: `bg-blue-100 border-blue-300`
- 미선택: `bg-blue-50 border-blue-200`
- 더 부드러운 시각적 피드백
#### 1.6 패널 높이 동기화
- 왼쪽(컬럼 목록)과 오른쪽(설정 패널) 동일한 `max-h-[350px]` 적용
- `overflow-y-auto`로 스크롤 처리
- `items-stretch`로 양쪽 패널 높이 동기화
### 2. 컬럼 변경 기능
#### 2.1 인라인 컬럼 편집
- 사용중인 필드(파란색 배경)를 클릭하면 우측에 "컬럼 설정" 패널 표시
- 패널 정보:
- **화면 필드**: 컬럼 한글명 표시 (예: "거래처 코드")
- **현재 컬럼**: 영문 컬럼명 표시 (예: `customer_code`)
- **컬럼 변경**: 드롭다운으로 다른 컬럼 선택
- 검색 기능으로 컬럼 빠르게 찾기
#### 2.2 실시간 반영
- 컬럼 변경 후 **페이지 새로고침 없이** 실시간 반영
- `onRefresh` 콜백으로 데이터 리로드 + iframe 새로고침
- 더 빠른 사용자 경험
#### 2.3 변경사항 저장
- `screenApi.saveLayout()` 사용하여 **화면 디자이너와 동일한 테이블에 저장**
- 저장 위치:
- `componentConfig.leftPanel.columns` (분할 패널)
- `componentConfig.rightPanel.columns` (분할 패널)
- `usedColumns` 배열
- `bindField` 필드
- `fieldMapping` 배열
### 3. 필드 추가/제거 기능 (신규)
#### 3.1 필드 추가
- 비필드 컬럼(회색/흰색 배경) 클릭
- "컬럼 설정" 패널에 컬럼 정보 표시
- **"필드로 추가"** 버튼 클릭 → 해당 컬럼이 화면 필드로 추가됨
- 버튼 스타일: `text-blue-600 border-blue-300 hover:bg-blue-50` (파란색 테두리)
#### 3.2 필드 제거
- 기존 필드(파란색 배경) 클릭
- "컬럼 설정" 패널에 필드 정보 표시
- **"필드에서 제거"** 버튼 클릭 → 해당 필드가 화면에서 제거됨
- 버튼 스타일: `text-red-600 border-red-300 hover:bg-red-50` (빨간색 테두리)
#### 3.3 저장 로직
```typescript
// 필드 추가: 배열에 새 컬럼 추가
if (isAddingField) {
return {
...comp,
componentConfig: {
...comp.componentConfig,
leftPanel: {
...comp.componentConfig.leftPanel,
columns: [...leftColumns, { name: newColumn, columnName: newColumn }],
},
},
};
}
// 필드 제거: 배열에서 해당 컬럼 제거
if (isRemovingField) {
const filteredColumns = leftColumns.filter((_, i) => i !== columnIdx);
return {
...comp,
componentConfig: {
...comp.componentConfig,
leftPanel: {
...comp.componentConfig.leftPanel,
columns: filteredColumns,
},
},
};
}
```
#### 3.4 적용 범위
- 메인 테이블 아코디언: 필드 추가/제거 가능
- 필터 테이블 아코디언: 필드 추가/제거 가능
- `usedColumns`, `componentConfig.usedColumns`, `componentConfig.columns`, `leftPanel.columns`, `rightPanel.columns` 모두 지원
### 4. 화면 프리뷰 상시 표시
#### 4.1 레이아웃 변경
- 기존: 탭으로 프리뷰 전환
- 개선: **모달 우측에 프리뷰 상시 표시**
- 모달 크기 확대 (1600px 최대 너비)
- 좌측 40% (탭 콘텐츠) / 우측 60% (프리뷰)
#### 4.2 줌/드래그/클릭 기능 (react-zoom-pan-pinch 라이브러리)
- **휠 스크롤**: 마우스 포인터 위치 기준 확대/축소 (20% ~ 300%)
- **드래그**: 마우스 왼쪽 버튼으로 화면 이동 (5px 이상 이동 시)
- **클릭**: iframe 내부 요소 정상 클릭 가능
- 버튼, 셀렉트박스, 체크박스, 테이블 행 클릭
- 인풋박스/텍스트박스 포커스 및 입력
- X버튼(닫기) 등 SVG 아이콘 버튼 클릭
#### 4.3 클릭 좌표 보정 시스템
- 줌 상태에서도 정확한 클릭 위치 계산
- `designWidth / rect.width` 비율로 좌표 변환
- 오버레이 방식으로 드래그와 클릭 분리 처리
### 5. 필드+조인 컬럼 스타일 개선
#### 5.1 다중 역할 컬럼 표시
- 컬럼이 **필드이면서 조인 키**인 경우:
- **파란색 배경** (필드 기준)
- **왼쪽에 주황색 세로 선** (`border-l-4 border-l-orange-500`)
- 배지: `조인` `필드` 동시 표시
- 컬럼이 **필드이면서 필터 키**인 경우:
- **파란색 배경** (필드 기준)
- **왼쪽에 보라색 세로 선** (`border-l-4 border-l-purple-400`)
- 배지: `필터` `필드` 동시 표시
#### 5.2 조인 컬럼도 필드로 인식
- `filterTableColumnMappings` 생성 시 조인 컬럼(`ft.joinColumnRefs`)도 포함
- 조인 테이블 데이터를 화면에서 보여주므로 필드로 간주
#### 5.3 컬럼 설정 패널 - 조인 정보 표시
- 조인 키 클릭 시 패널에 조인 정보 표시:
- **대상 테이블**: item_info (실제 참조 테이블)
- **연결 컬럼**: item_number (참조 컬럼)
### 6. 조인 관계 설정/수정 기능
#### 6.1 기능 설명
- 컬럼 설정 패널에서 **조인 관계 직접 수정** 가능
- **모든 컬럼에서 조인 설정 가능** (기존 조인 키가 아닌 컬럼도 포함)
- 테이블 타입 관리(`column_labels` 테이블)와 동일한 저장 위치 사용
#### 6.2 저장 테이블
```
column_labels 테이블:
├── reference_table (참조 테이블명)
├── reference_column (참조 컬럼 - 보통 PK)
└── display_column (화면에 표시할 컬럼)
```
#### 6.3 구현된 UI
1. **컬럼 클릭** → 컬럼 설정 패널 표시
2. **"조인" 섹션 확인**:
- 조인 설정 있음: "편집" 버튼
- 조인 설정 없음: "추가" 버튼
3. **드롭다운으로 설정** (모두 검색 가능):
- 대상 테이블: 전체 테이블 목록에서 검색/선택
- 연결 컬럼 (PK): 선택한 테이블의 컬럼 중 검색/선택
- 표시 컬럼: 화면에 표시할 컬럼 검색/선택
4. **저장 버튼**`column_labels` 테이블에 저장
5. **취소 버튼** → 편집 취소
#### 6.4 검색 가능한 드롭다운
- Popover + Command 컴포넌트 사용
- 실시간 텍스트 검색 지원
- 대상 테이블, 연결 컬럼, 표시 컬럼 모두 검색 가능
#### 6.5 API 연동
- **테이블 목록 조회**: `tableManagementApi.getTableList()`
- **컬럼 목록 조회**: `tableManagementApi.getColumnList(tableName)`
- **저장**: `tableManagementApi.updateColumnSettings(tableName, columnName, settings)`
#### 6.6 메인 테이블에도 조인 설정 적용
- 메인 테이블 아코디언에서도 조인 설정 가능
- 필터 테이블과 동일한 UI/기능 제공
#### 6.7 조인 데이터 소스 수정
- 기존 조인 키 클릭 시 `joinRef.refTable` 값을 사용
- 예: `품목 ID``item_info` (실제 참조 테이블)
- `mainTable` 대신 `joinRef.refTable` 사용으로 정확한 테이블 표시
### 7. 배지 순서 및 스타일
- **배지 순서**: `필터``조인``필드` (필드가 맨 뒤)
- **조인 배지**: 주황색 배경 (`bg-orange-200 text-orange-700`)
- **필터 배지**: 보라색 배경 (`bg-purple-200 text-purple-700`)
- **필드 배지**: 파란색 배경 (`bg-blue-500 text-white`)
### 8. 컴포넌트 통합 리팩토링
#### 8.1 `TableColumnAccordion` 통합 컴포넌트
- 기존 `MainTableAccordion``FilterTableAccordion`을 하나의 컴포넌트로 통합
- `tableType` prop으로 "main" 또는 "filter" 구분
- 코드 중복 제거 및 유지보수성 향상
#### 8.2 Props 구조
```typescript
interface TableColumnAccordionProps {
tableName: string;
tableLabel?: string;
tableType: "main" | "filter";
columnMappings?: ColumnMapping[];
onColumnChange?: (fieldLabel: string, oldColumn: string, newColumn: string) => void;
onColumnReorder?: (newOrder: string[]) => void;
onJoinSettingSaved?: () => void;
// 필터 테이블 전용 props
mainTable?: string;
filterKeyMapping?: FilterKeyMapping;
joinColumnRefs?: JoinColumnRef[];
}
```
#### 8.3 동적 테마 적용
- 메인 테이블: 파란색 테마 (`blue`)
- 필터 테이블: 보라색 테마 (`purple`)
- `themeColor`, `themeIcon`, `themeBadge` 변수로 동적 스타일 적용
### 9. 드래그 앤 드롭 컬럼 순서 변경
#### 9.1 기능 설명
- 사용 중인 컬럼(필드)을 드래그하여 순서 변경 가능
- 드래그 중에는 시각적으로만 순서 변경, **드롭 시에만 저장**
- 드래그 취소(영역 밖으로 나간 경우) 시 원래 순서로 복원
#### 9.2 드래그 상태 관리
```typescript
// 드래그 상태
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
const [localColumnOrder, setLocalColumnOrder] = useState<string[] | null>(null);
```
#### 9.3 드래그 핸들러
```typescript
// 드래그 시작: 현재 순서를 로컬 상태로 저장
const handleDragStart = (e: React.DragEvent, index: number) => {
setDraggedIndex(index);
const usedColumns = sortedColumns.filter(col => columnMappingMap.has(col.columnName.toLowerCase()));
setLocalColumnOrder(usedColumns.map(col => col.columnName));
};
// 드래그 중: 로컬 순서만 변경 (저장하지 않음)
const handleDragOver = (e: React.DragEvent, hoverIndex: number) => {
if (draggedIndex === null || draggedIndex === hoverIndex || !localColumnOrder) return;
const newOrder = [...localColumnOrder];
const draggedItem = newOrder[draggedIndex];
newOrder.splice(draggedIndex, 1);
newOrder.splice(hoverIndex, 0, draggedItem);
setDraggedIndex(hoverIndex);
setLocalColumnOrder(newOrder);
};
// 드롭: 최종 순서로 저장
const handleDrop = (e: React.DragEvent) => {
if (localColumnOrder && onColumnReorder) {
onColumnReorder(localColumnOrder);
}
setDraggedIndex(null);
setLocalColumnOrder(null);
};
// 드래그 취소
const handleDragEnd = () => {
setDraggedIndex(null);
setLocalColumnOrder(null);
};
```
#### 9.4 시각적 피드백
- 드래그 가능한 컬럼: `cursor-grab active:cursor-grabbing`
- 드래그 중인 컬럼: `opacity-50 scale-95`
- 드래그 중 실시간 순서 변경 표시
#### 9.5 저장 로직 (`handleColumnReorder`)
```typescript
const handleColumnReorder = async (tableType: "main" | "filter", newOrder: string[]) => {
const currentLayout = await screenApi.getLayout(screenId);
const updatedComponents = currentLayout.components.map((comp: any) => {
// leftPanel.columns 순서 변경
if (comp.componentConfig?.leftPanel?.columns) {
const leftColumns = comp.componentConfig.leftPanel.columns;
const reorderedColumns = newOrder.map(colName =>
leftColumns.find((col: any) => col.name?.toLowerCase() === colName.toLowerCase())
).filter(Boolean);
const remainingColumns = leftColumns.filter((col: any) =>
!newOrder.some(n => n.toLowerCase() === col.name?.toLowerCase())
);
return {
...comp,
componentConfig: {
...comp.componentConfig,
leftPanel: {
...comp.componentConfig.leftPanel,
columns: [...reorderedColumns, ...remainingColumns],
},
},
};
}
return comp;
});
await screenApi.saveLayout(screenId, { ...currentLayout, components: updatedComponents });
onRefresh?.();
};
```
#### 9.6 지원 범위
- 메인 테이블: `onColumnReorder={(newOrder) => handleColumnReorder("main", newOrder)}`
- 필터 테이블: `onColumnReorder={(newOrder) => handleColumnReorder("filter", newOrder)}`
- 지원 배열:
- `componentConfig.leftPanel.columns`
- `componentConfig.rightPanel.columns`
- `componentConfig.usedColumns`
- `componentConfig.columns`
## 기술 스택
### 신규 의존성
```bash
npm install react-zoom-pan-pinch
```
### 사용된 컴포넌트
- `TransformWrapper`, `TransformComponent` - 줌/드래그 기능
- `Accordion`, `AccordionContent`, `AccordionItem`, `AccordionTrigger` - 아코디언 UI
- `Popover`, `PopoverTrigger`, `PopoverContent` - 드롭다운 컨테이너
- `Command`, `CommandInput`, `CommandList`, `CommandItem`, `CommandEmpty` - 검색 가능한 선택 UI
- `tableManagementApi.getColumnList()` - 테이블 컬럼 정보 조회
- `tableManagementApi.getTableList()` - 테이블 목록 조회
- `tableManagementApi.updateColumnSettings()` - 조인 설정 저장
- `screenApi.saveLayout()` - 레이아웃 저장
- `screenApi.getLayout()` - 레이아웃 조회
### 핵심 로직
#### 컬럼 변경/추가/제거
```typescript
const handleColumnChange = async (fieldLabel: string, oldColumn: string, newColumn: string) => {
const isAddingField = fieldLabel === "__NEW_FIELD__";
const isRemovingField = newColumn === "__REMOVE_FIELD__";
const currentLayout = await screenApi.getLayout(screenId);
const updatedComponents = currentLayout.components.map((comp: any) => {
if (comp.componentConfig?.leftPanel?.columns) {
const leftColumns = comp.componentConfig.leftPanel.columns;
// 필드 추가
if (isAddingField) {
return {
...comp,
componentConfig: {
...comp.componentConfig,
leftPanel: {
...comp.componentConfig.leftPanel,
columns: [...leftColumns, { name: newColumn, columnName: newColumn }],
},
},
};
}
// 필드 제거
const columnIdx = leftColumns.findIndex((col: any) => ...);
if (columnIdx !== -1 && isRemovingField) {
return {
...comp,
componentConfig: {
...comp.componentConfig,
leftPanel: {
...comp.componentConfig.leftPanel,
columns: leftColumns.filter((_, i) => i !== columnIdx),
},
},
};
}
// 컬럼 변경
if (columnIdx !== -1) {
return {
...comp,
componentConfig: {
...comp.componentConfig,
leftPanel: {
...comp.componentConfig.leftPanel,
columns: leftColumns.map((col, i) =>
i === columnIdx ? { ...col, name: newColumn } : col
),
},
},
};
}
}
return comp;
});
await screenApi.saveLayout(screenId, { ...currentLayout, components: updatedComponents });
onRefresh?.();
};
```
#### 조인 설정 편집기 (JoinSettingEditor)
```tsx
<JoinSettingEditor
editingJoin={editingJoin}
setEditingJoin={setEditingJoin}
allTables={allTables}
refTableColumns={refTableColumns}
loadingRefColumns={loadingRefColumns}
savingJoinSetting={savingJoinSetting}
loadRefTableColumns={loadRefTableColumns}
handleSaveJoinSetting={handleSaveJoinSetting}
/>
```
## 파일 변경 목록
| 파일 | 변경 내용 |
|------|----------|
| `frontend/components/screen/ScreenSettingModal.tsx` | 전체 UI 개선, 줌/드래그 기능, 컬럼 변경/추가/제거 기능, 조인 설정 기능, 필드 매핑 통합, 실시간 반영 |
| `frontend/components/screen/ScreenRelationFlow.tsx` | `filterKeyMapping`, `joinColumnRefs` 데이터 전달 |
| `frontend/lib/api/entityJoin.ts` | `companyCodeOverride` 파라미터 추가 |
| `frontend/lib/api/screen.ts` | `saveLayout`, `getLayout` API 사용 |
| `frontend/lib/api/tableManagement.ts` | `getTableList`, `getColumnList`, `updateColumnSettings` API |
| `frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx` | `companyCode` prop 추가 |
| `backend-node/src/controllers/entityJoinController.ts` | `companyCodeOverride` 처리 로직 추가 |
## 사용 방법
### 화면 설정 모달 열기
1. 화면 관리 페이지에서 화면 그룹 선택
2. 화면 노드 우클릭 → 컨텍스트 메뉴 표시
3. "화면 설정" 선택 → 모달 열림
4. 좌측 탭에서 정보 확인/수정, 우측에서 실시간 프리뷰
### 프리뷰 영역 조작
- **휠 스크롤**: 확대/축소 (5% 단위)
- **마우스 드래그**: 화면 이동 (5px 이상 움직여야 드래그로 인식)
- **짧은 클릭**: iframe 내부 요소 클릭
### 컬럼 변경
1. 메인/필터 테이블 아코디언 펼치기
2. 파란색 배경의 "필드" 컬럼 클릭
3. 우측 "컬럼 설정" 패널 확인
4. "컬럼 변경" 드롭다운에서 새 컬럼 선택
5. **실시간 반영** (페이지 새로고침 없음)
### 필드 추가
1. 메인/필터 테이블 아코디언 펼치기
2. 회색/흰색 배경의 비필드 컬럼 클릭
3. 우측 패널에서 **"필드로 추가"** 버튼 클릭
4. 해당 컬럼이 화면 필드로 추가됨
### 필드 제거
1. 메인/필터 테이블 아코디언 펼치기
2. 파란색 배경의 필드 컬럼 클릭
3. 우측 패널에서 **"필드에서 제거"** 버튼 클릭
4. 해당 필드가 화면에서 제거됨
### 조인 설정 추가/편집
1. 메인/필터 테이블 아코디언 펼치기
2. 아무 컬럼 클릭 (조인 키가 아니어도 됨)
3. 우측 패널의 "조인" 섹션에서:
- 조인 없음: **"추가"** 버튼 클릭
- 조인 있음: **"편집"** 버튼 클릭
4. 대상 테이블 선택 (검색 가능)
5. 연결 컬럼 (PK) 선택 (검색 가능)
6. 표시 컬럼 선택 (검색 가능)
7. **"저장"** 버튼 클릭
### 컬럼 순서 변경 (드래그 앤 드롭)
1. 메인/필터 테이블 아코디언 펼치기
2. 파란색 배경의 "필드" 컬럼을 드래그 시작
3. 원하는 위치로 드래그하여 이동 (실시간으로 순서 변경 표시)
4. 마우스를 놓으면 (드롭) 순서가 저장됨
5. 드래그 취소하려면 컬럼 영역 밖으로 드래그
**참고:**
- 사용 중인 필드만 드래그 가능 (파란색 배경)
- 미사용 컬럼은 드래그 불가
- 드래그 중에는 저장되지 않고, 드롭 시에만 저장됨
---
## 완료일
2026-01-13
## 변경 이력
- 2026-01-12: 최초 작성 (줌/드래그/클릭, company_code 전달)
- 2026-01-12: 컬럼 변경 기능 추가, 필드 매핑 통합, UI 개선 (1열 레이아웃, 배지 변경)
- 2026-01-12: 실시간 반영 구현 (reload 제거), 레이아웃 순서 변경, 스타일 개선
- 2026-01-12: 필드+조인 컬럼 스타일 개선 (파란배경 + 왼쪽 주황선), 조인 정보 패널 표시
- 2026-01-12: 조인 관계 설정/수정 기능 구현 완료 (column_labels 테이블 저장)
- 2026-01-13: 필드 추가/제거 기능 구현
- 2026-01-13: 검색 가능한 조인 설정 드롭다운 (Command 컴포넌트)
- 2026-01-13: 모든 컬럼에서 조인 설정 가능 (범용성 패치)
- 2026-01-13: 메인 테이블에도 조인 설정 기능 추가
- 2026-01-13: 조인 라인 색상 주황색으로 변경 (`border-l-orange-500`)
- 2026-01-13: 조인 데이터 소스 수정 (`joinRef.refTable` 사용)
- 2026-01-13: 패널 높이 동기화 (`max-h-[350px]`, `items-stretch`)
- 2026-01-13: `MainTableAccordion``FilterTableAccordion``TableColumnAccordion`으로 통합
- 2026-01-13: 드래그 앤 드롭 컬럼 순서 변경 기능 구현
- 2026-01-13: 드래그 중에는 로컬 상태만 변경, 드롭 시에만 저장하도록 최적화