From 8e9daf5b22a15ed159db11287467d223dde97b1f Mon Sep 17 00:00:00 2001 From: kjs Date: Mon, 3 Nov 2025 09:58:04 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=88=98=EC=A0=95=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=20=EC=9E=90=EB=8F=99=20=EB=8B=AB=EA=B8=B0=20=EB=B0=8F=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=83=88=EB=A1=9C=EA=B3=A0?= =?UTF-8?q?=EC=B9=A8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EditModal: 저장 완료 후 자동으로 닫히고 부모 테이블 새로고침 - buttonActions.ts: 저장 성공 후 closeEditModal 이벤트 발생 - InteractiveScreenViewerDynamic: onSave prop 추가하여 EditModal 연동 - InteractiveDataTable: EditModal 열 때 onSave 콜백으로 loadData 전달 - 두 가지 시나리오 모두 지원: 1. InteractiveScreenViewerDynamic 버튼의 onSave 호출 2. DynamicComponentRenderer 버튼의 buttonActions.ts 처리 --- frontend/components/screen/EditModal.tsx | 63 ++- .../screen/InteractiveDataTable.tsx | 26 +- .../screen/InteractiveScreenViewerDynamic.tsx | 17 +- frontend/components/screen/ScreenDesigner.tsx | 38 +- frontend/lib/utils/buttonActions.ts | 6 +- 화면관리_및_테이블관리_개선사항_목록.md | 386 ++++++++++++++++++ 6 files changed, 481 insertions(+), 55 deletions(-) create mode 100644 화면관리_및_테이블관리_개선사항_목록.md diff --git a/frontend/components/screen/EditModal.tsx b/frontend/components/screen/EditModal.tsx index 2d3fb513..76363e4f 100644 --- a/frontend/components/screen/EditModal.tsx +++ b/frontend/components/screen/EditModal.tsx @@ -102,13 +102,6 @@ export const EditModal: React.FC = ({ className }) => { useEffect(() => { const handleOpenEditModal = (event: CustomEvent) => { const { screenId, title, description, modalSize, editData, onSave } = event.detail; - console.log("🚀 EditModal 열기 이벤트 수신:", { - screenId, - title, - description, - modalSize, - editData, - }); setModalState({ isOpen: true, @@ -126,7 +119,16 @@ export const EditModal: React.FC = ({ className }) => { }; const handleCloseEditModal = () => { - console.log("🚪 EditModal 닫기 이벤트 수신"); + // 부모 컴포넌트의 onSave 콜백 실행 (테이블 새로고침) + if (modalState.onSave) { + try { + modalState.onSave(); + } catch (callbackError) { + console.error("⚠️ onSave 콜백 에러:", callbackError); + } + } + + // 모달 닫기 handleClose(); }; @@ -137,7 +139,7 @@ export const EditModal: React.FC = ({ className }) => { window.removeEventListener("openEditModal", handleOpenEditModal as EventListener); window.removeEventListener("closeEditModal", handleCloseEditModal); }; - }, []); + }, [modalState.onSave]); // modalState.onSave를 의존성에 추가하여 최신 콜백 참조 // 화면 데이터 로딩 useEffect(() => { @@ -211,12 +213,6 @@ export const EditModal: React.FC = ({ className }) => { } try { - console.log("💾 수정 저장 시작:", { - tableName: screenData.screenInfo.tableName, - formData, - originalData, - }); - // 변경된 필드만 추출 const changedData: Record = {}; Object.keys(formData).forEach((key) => { @@ -225,26 +221,33 @@ export const EditModal: React.FC = ({ className }) => { } }); - console.log("📝 변경된 필드:", changedData); - if (Object.keys(changedData).length === 0) { toast.info("변경된 내용이 없습니다."); handleClose(); return; } + // 기본키 확인 (id 또는 첫 번째 키) + const recordId = originalData.id || Object.values(originalData)[0]; + // UPDATE 액션 실행 - const response = await dynamicFormApi.updateData(screenData.screenInfo.tableName, { - ...originalData, // 원본 데이터 (WHERE 조건용) - ...changedData, // 변경된 데이터만 - }); + const response = await dynamicFormApi.updateFormDataPartial( + recordId, + originalData, + changedData, + screenData.screenInfo.tableName, + ); if (response.success) { toast.success("데이터가 수정되었습니다."); - // 부모 컴포넌트의 onSave 콜백 실행 + // 부모 컴포넌트의 onSave 콜백 실행 (테이블 새로고침) if (modalState.onSave) { - modalState.onSave(); + try { + modalState.onSave(); + } catch (callbackError) { + console.error("⚠️ onSave 콜백 에러:", callbackError); + } } handleClose(); @@ -335,16 +338,10 @@ export const EditModal: React.FC = ({ className }) => { allComponents={screenData.components} formData={formData} onFormDataChange={(fieldName, value) => { - console.log(`🎯 EditModal onFormDataChange 호출: ${fieldName} = "${value}"`); - console.log("📋 현재 formData:", formData); - setFormData((prev) => { - const newFormData = { - ...prev, - [fieldName]: value, - }; - console.log("📝 EditModal 업데이트된 formData:", newFormData); - return newFormData; - }); + setFormData((prev) => ({ + ...prev, + [fieldName]: value, + })); }} screenInfo={{ id: modalState.screenId!, diff --git a/frontend/components/screen/InteractiveDataTable.tsx b/frontend/components/screen/InteractiveDataTable.tsx index b54df6ad..17662cac 100644 --- a/frontend/components/screen/InteractiveDataTable.tsx +++ b/frontend/components/screen/InteractiveDataTable.tsx @@ -769,7 +769,7 @@ export const InteractiveDataTable: React.FC = ({ setShowSaveModal(true); }, [getDisplayColumns, generateAutoValue, component.addModalConfig]); - // 데이터 수정 핸들러 (SaveModal 사용) + // 데이터 수정 핸들러 (EditModal 사용) const handleEditData = useCallback(() => { if (selectedRows.size !== 1) return; @@ -793,17 +793,25 @@ export const InteractiveDataTable: React.FC = ({ initialData[col.columnName] = selectedRowData[col.columnName] || ""; }); - setEditFormData(initialData); - setEditingRowData(selectedRowData); - // 수정 모달 설정에서 제목과 설명 가져오기 - const editModalTitle = component.editModalConfig?.title || ""; + const editModalTitle = component.editModalConfig?.title || "데이터 수정"; const editModalDescription = component.editModalConfig?.description || ""; - console.log("📝 수정 모달 설정:", { editModalTitle, editModalDescription }); - - setShowEditModal(true); - }, [selectedRows, data, getDisplayColumns, component.editModalConfig]); + // 전역 EditModal 열기 이벤트 발생 + const event = new CustomEvent("openEditModal", { + detail: { + screenId, + title: editModalTitle, + description: editModalDescription, + modalSize: "lg", + editData: initialData, + onSave: () => { + loadData(); // 테이블 데이터 새로고침 + }, + }, + }); + window.dispatchEvent(event); + }, [selectedRows, data, getDisplayColumns, component.addModalConfig, component.editModalConfig, loadData]); // 수정 폼 데이터 변경 핸들러 const handleEditFormChange = useCallback((columnName: string, value: any) => { diff --git a/frontend/components/screen/InteractiveScreenViewerDynamic.tsx b/frontend/components/screen/InteractiveScreenViewerDynamic.tsx index 7ed39353..e85aab58 100644 --- a/frontend/components/screen/InteractiveScreenViewerDynamic.tsx +++ b/frontend/components/screen/InteractiveScreenViewerDynamic.tsx @@ -38,6 +38,7 @@ interface InteractiveScreenViewerProps { id: number; tableName?: string; }; + onSave?: () => Promise; } export const InteractiveScreenViewerDynamic: React.FC = ({ @@ -47,6 +48,7 @@ export const InteractiveScreenViewerDynamic: React.FC { const { isPreviewMode } = useScreenPreview(); // 프리뷰 모드 확인 const { userName, user } = useAuth(); @@ -204,8 +206,7 @@ export const InteractiveScreenViewerDynamic: React.FC { - // 화면 닫기 로직 (필요시 구현) - console.log("🚪 화면 닫기 요청"); + // buttonActions.ts가 이미 처리함 }} /> ); @@ -299,6 +300,18 @@ export const InteractiveScreenViewerDynamic: React.FC { + // EditModal에서 전달된 onSave가 있으면 우선 사용 (수정 모달) + if (onSave) { + try { + await onSave(); + } catch (error) { + console.error("저장 오류:", error); + toast.error("저장 중 오류가 발생했습니다."); + } + return; + } + + // 일반 저장 액션 (신규 생성) if (!screenInfo?.tableName) { toast.error("테이블명이 설정되지 않았습니다."); return; diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index 5a11f325..51364429 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -381,19 +381,37 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD // 실행취소 const undo = useCallback(() => { - if (historyIndex > 0) { - setHistoryIndex((prev) => prev - 1); - setLayout(history[historyIndex - 1]); - } - }, [history, historyIndex]); + setHistoryIndex((prevIndex) => { + if (prevIndex > 0) { + const newIndex = prevIndex - 1; + setHistory((prevHistory) => { + if (prevHistory[newIndex]) { + setLayout(prevHistory[newIndex]); + } + return prevHistory; + }); + return newIndex; + } + return prevIndex; + }); + }, []); // 다시실행 const redo = useCallback(() => { - if (historyIndex < history.length - 1) { - setHistoryIndex((prev) => prev + 1); - setLayout(history[historyIndex + 1]); - } - }, [history, historyIndex]); + setHistoryIndex((prevIndex) => { + let newIndex = prevIndex; + setHistory((prevHistory) => { + if (prevIndex < prevHistory.length - 1) { + newIndex = prevIndex + 1; + if (prevHistory[newIndex]) { + setLayout(prevHistory[newIndex]); + } + } + return prevHistory; + }); + return newIndex; + }); + }, []); // 컴포넌트 속성 업데이트 const updateComponentProperty = useCallback( diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 4c376b09..8b805d93 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -234,9 +234,13 @@ export class ButtonActionExecutor { throw new Error("저장에 필요한 정보가 부족합니다. (테이블명 또는 화면ID 누락)"); } - // 테이블과 플로우 모두 새로고침 + // 테이블과 플로우 새로고침 (모달 닫기 전에 실행) context.onRefresh?.(); context.onFlowRefresh?.(); + + // 저장 성공 후 EditModal 닫기 이벤트 발생 + window.dispatchEvent(new CustomEvent("closeEditModal")); + return true; } catch (error) { console.error("저장 오류:", error); diff --git a/화면관리_및_테이블관리_개선사항_목록.md b/화면관리_및_테이블관리_개선사항_목록.md new file mode 100644 index 00000000..666f41f1 --- /dev/null +++ b/화면관리_및_테이블관리_개선사항_목록.md @@ -0,0 +1,386 @@ +# 화면관리 및 테이블관리 시스템 개선사항 목록 + +## 문서 정보 +- **작성일**: 2025-11-03 +- **목적**: 사용자 피드백 기반 개선사항 정리 +- **우선순위**: 높음 + +--- + +## 1. 화면관리 (Screen Management) 개선사항 + +### 1.1 리스트 컬럼 Width 조절 기능 +**현재 문제**: 리스트 컬럼의 너비가 고정되어 있어 사용자가 조절할 수 없음 + +**요구사항**: +- 사용자가 각 컬럼의 너비를 드래그로 조절할 수 있어야 함 +- 조절된 너비는 저장되어 다음 접속 시에도 유지되어야 함 +- 최소/최대 너비 제한 필요 + +**구현 방안**: +- 컬럼 헤더에 리사이저 핸들 추가 +- `ComponentData` 인터페이스에 `columnWidths` 속성 추가 +- PropertiesPanel에서 개별 컬럼 너비 설정 UI 제공 + +**관련 파일**: +- `frontend/components/screen/ScreenDesigner.tsx` +- `frontend/components/screen/RealtimePreview.tsx` +- `frontend/types/screen.ts` + +--- + +### 1.2 되돌리기(Undo) 단축키 에러 수정 +**현재 문제**: 되돌리기 단축키(Ctrl+Z/Cmd+Z) 실행 시 에러 발생 + +**요구사항**: +- 되돌리기 기능이 안정적으로 작동해야 함 +- 다시 실행(Redo) 기능도 함께 제공 (Ctrl+Y/Cmd+Shift+Z) + +**구현 방안**: +- 히스토리 스택 구현 (최대 50개 상태 저장) +- `useUndo` 커스텀 훅 생성 +- 키보드 단축키 이벤트 리스너 추가 + +**관련 파일**: +- `frontend/hooks/useUndo.ts` (신규 생성) +- `frontend/components/screen/ScreenDesigner.tsx` + +--- + +### 1.3 리스트 헤더 스타일 개선 +**현재 문제**: 리스트 헤더가 눈에 잘 띄지 않음 + +**요구사항**: +- 헤더가 시각적으로 구분되어야 함 +- 배경색, 폰트 굵기, 테두리 등으로 강조 + +**구현 방안**: +- 헤더 기본 스타일 변경: + - 배경색: `bg-muted` → `bg-primary/10` + - 폰트: `font-medium` → `font-semibold` + - 하단 테두리: `border-b-2 border-primary` + +**관련 파일**: +- `frontend/components/screen/RealtimePreview.tsx` +- `frontend/components/screen-viewer/InteractiveScreenViewer.tsx` + +--- + +### 1.4 텍스트 줄바꿈 문제 방지 +**현재 문제**: 화면을 줄였을 때 텍스트가 2줄로 나뉘거나 깨지는 현상 + +**요구사항**: +- 텍스트가 항상 1줄로 표시되어야 함 +- 긴 텍스트는 말줄임표(...) 처리 + +**구현 방안**: +- 모든 텍스트 요소에 다음 클래스 적용: + ```tsx + className="truncate whitespace-nowrap overflow-hidden" + ``` +- 툴팁으로 전체 텍스트 표시 + +**관련 파일**: +- 모든 컴포넌트의 텍스트 렌더링 부분 + +--- + +### 1.5 수정 모달 자동 닫기 +**현재 문제**: 수정 완료 후 모달이 자동으로 닫히지 않음 + +**요구사항**: +- 수정 완료 시 모달이 즉시 닫혀야 함 +- 성공 메시지 표시 후 닫기 + +**구현 방안**: +```typescript +const handleUpdate = async () => { + const result = await updateData(formData); + if (result.success) { + toast.success("수정이 완료되었습니다"); + setIsModalOpen(false); // 모달 닫기 + refreshList(); // 목록 새로고침 + } +}; +``` + +**관련 파일**: +- `frontend/components/screen-viewer/InteractiveScreenViewer.tsx` + +--- + +### 1.6 테이블 Align 조절 기능 +**현재 문제**: 테이블 컬럼의 정렬(align)을 사용자가 조절할 수 없음 + +**요구사항**: +- 각 컬럼의 정렬을 left/center/right로 설정 가능해야 함 +- 숫자 타입은 기본적으로 right 정렬 + +**구현 방안**: +- `TableColumnConfig` 인터페이스에 `align` 속성 추가 +- PropertiesPanel에서 정렬 선택 UI 제공 +- 컬럼 타입별 기본 정렬 설정 + +**관련 파일**: +- `frontend/types/screen.ts` +- `frontend/components/screen/PropertiesPanel.tsx` + +--- + +### 1.7 숫자 천 단위 콤마 표시 +**현재 문제**: 숫자가 콤마 없이 표시됨 + +**요구사항**: +- 모든 숫자는 천 단위마다 콤마(,)를 찍어야 함 +- 예: 1000000 → 1,000,000 + +**구현 방안**: +```typescript +// 유틸리티 함수 생성 +export const formatNumber = (value: number | string): string => { + const num = typeof value === "string" ? parseFloat(value) : value; + if (isNaN(num)) return "0"; + return new Intl.NumberFormat("ko-KR").format(num); +}; +``` + +**관련 파일**: +- `frontend/lib/utils/numberFormat.ts` (신규 생성) +- 모든 숫자 표시 컴포넌트 + +--- + +### 1.8 Drilldown UI 개선 +**현재 문제**: 화면이 횡으로 너무 길게 나열됨 + +**요구사항**: +- 계층적 구조로 정보 표시 +- 펼치기/접기 기능으로 공간 절약 + +**구현 방안**: +- Accordion 컴포넌트 활용 +- 탭 네비게이션 구조 적용 +- 마스터-디테일 레이아웃 패턴 + +**관련 파일**: +- `frontend/components/screen/ScreenDesigner.tsx` +- `frontend/components/ui/accordion.tsx` + +--- + +## 2. 테이블 관리 (Table Management) 개선사항 + +### 2.1 테이블 기본 정보 선택 기능 +**현재 문제**: 테이블 기본 정보를 사용자가 선택할 수 없음 + +**요구사항**: +- 테이블 생성/수정 시 다음 정보를 선택 가능해야 함: + - 테이블 타입 (마스터/트랜잭션/코드) + - 카테고리 + - 로그 사용 여부 + - 버전 관리 여부 + - 소프트 삭제 여부 + +**구현 방안**: +- `TableManagement.tsx`에 선택 UI 추가 +- `CREATE TABLE` DDL 자동 생성 시 옵션 반영 + +**관련 파일**: +- `frontend/components/table/TableManagement.tsx` +- `backend-node/src/controllers/tableController.ts` + +--- + +### 2.2 컬럼 추가 기능 +**현재 문제**: 기존 테이블에 새 컬럼을 추가하는 기능 부족 + +**요구사항**: +- 테이블 수정 시 컬럼을 동적으로 추가할 수 있어야 함 +- `ALTER TABLE ADD COLUMN` DDL 자동 생성 +- 컬럼 순서 조정 기능 + +**구현 방안**: +```typescript +// 컬럼 추가 API +POST /api/table-management/tables/:tableName/columns +{ + "columnName": "new_column", + "dataType": "VARCHAR(100)", + "nullable": true, + "defaultValue": null +} +``` + +**관련 파일**: +- `frontend/components/table/TableManagement.tsx` +- `backend-node/src/controllers/tableController.ts` +- `backend-node/src/services/ddlExecutionService.ts` + +--- + +### 2.3 테이블 복제 기능 +**현재 문제**: 기존 테이블의 구조를 재사용하기 어려움 + +**요구사항**: +- 기존 테이블을 복제하여 새 테이블 생성 +- 다음 정보를 복사: + - 컬럼 구조 (이름, 타입, 제약조건) + - 인덱스 정의 + - 외래키 관계 (선택적) +- 데이터는 복사하지 않음 (구조만) + +**구현 방안**: +```typescript +// 테이블 복제 API +POST /api/table-management/tables/:sourceTableName/clone +{ + "newTableName": "cloned_table", + "includeIndexes": true, + "includeForeignKeys": false, + "copyData": false +} +``` + +**구현 단계**: +1. 원본 테이블 정보 조회 (INFORMATION_SCHEMA) +2. DDL 스크립트 생성 +3. 새 테이블 생성 +4. 인덱스 및 제약조건 추가 +5. 감사 로그 기록 + +**관련 파일**: +- `frontend/components/table/TableManagement.tsx` +- `backend-node/src/controllers/tableController.ts` +- `backend-node/src/services/ddlExecutionService.ts` + +**참고 문서**: +- `/Users/kimjuseok/ERP-node/테이블_복제_기능_구현_계획서.md` + +--- + +### 2.4 채번 Rule 관리 기능 +**현재 문제**: 자동 채번 규칙을 사용자가 관리할 수 없음 + +**요구사항**: +- 채번 규칙 생성/수정/삭제 UI +- 규칙 형식: + - 접두사 (예: "PROD-") + - 날짜 포맷 (예: "YYYYMMDD") + - 일련번호 자릿수 (예: 5자리 → 00001) + - 구분자 (예: "-") +- 예시: `PROD-20251103-00001` + +**구현 방안**: +```typescript +interface NumberingRule { + id: string; + ruleName: string; + prefix: string; + dateFormat?: "YYYY" | "YYYYMM" | "YYYYMMDD" | "YYYYMMDD-HH"; + sequenceDigits: number; + separator: string; + resetPeriod: "none" | "daily" | "monthly" | "yearly"; + currentSequence: number; + tableName: string; + columnName: string; +} +``` + +**관련 파일**: +- `frontend/components/table/NumberingRuleManagement.tsx` (신규 생성) +- `backend-node/src/controllers/numberingRuleController.ts` (신규 생성) +- `backend-node/src/services/numberingRuleService.ts` (신규 생성) + +--- + +## 3. 제어 관리 (Flow Management) 개선사항 + +### 3.1 제목 클릭 시 노드 선택 해제 +**현재 문제**: 제목을 입력할 때 백스페이스를 누르면 노드가 삭제됨 + +**요구사항**: +- 제목(플로우명) 입력란 클릭 시 노드 선택이 해제되어야 함 +- 백스페이스 키가 텍스트 입력으로만 작동해야 함 + +**구현 방안**: +```typescript +const handleTitleClick = (e: React.MouseEvent) => { + e.stopPropagation(); // 이벤트 전파 중지 + setSelectedNodes([]); // 노드 선택 해제 +}; + +const handleTitleKeyDown = (e: React.KeyboardEvent) => { + e.stopPropagation(); // 백스페이스 키가 노드 삭제로 전파되지 않도록 +}; + + setFlowName(e.target.value)} +/> +``` + +**관련 파일**: +- `frontend/components/flow/FlowDesigner.tsx` +- `frontend/components/flow/FlowCanvas.tsx` + +--- + +## 4. 우선순위 및 구현 일정 + +### 높음 (즉시 수정 필요) +1. **되돌리기 단축키 에러 수정** - 기능 오류 +2. **수정 모달 자동 닫기** - 사용자 경험 저해 +3. **제어관리 제목 입력 문제** - 데이터 손실 위험 +4. **숫자 천 단위 콤마 표시** - 가독성 문제 + +### 중간 (2주 내 완료) +5. **리스트 컬럼 Width 조절** +6. **리스트 헤더 스타일 개선** +7. **텍스트 줄바꿈 문제 방지** +8. **테이블 Align 조절** +9. **컬럼 추가 기능** + +### 낮음 (기능 추가) +10. **테이블 기본 정보 선택** +11. **테이블 복제 기능** +12. **Drilldown UI 개선** +13. **채번 Rule 관리** + +--- + +## 5. 테스트 계획 + +각 개선사항 완료 시 다음을 확인: + +### 기능 테스트 +- [ ] 새 기능이 정상 작동함 +- [ ] 기존 기능에 영향 없음 +- [ ] 에러 처리가 적절함 + +### 사용자 경험 테스트 +- [ ] UI가 직관적임 +- [ ] 반응 속도가 빠름 +- [ ] 모바일/태블릿 대응 + +### 성능 테스트 +- [ ] 대량 데이터 처리 시 성능 저하 없음 +- [ ] 메모리 누수 없음 + +--- + +## 6. 참고 문서 + +- [화면관리 시스템 현황](화면관리_및_테이블관리_개선사항_목록.md) +- [테이블 복제 기능 계획서](테이블_복제_기능_구현_계획서.md) +- [Shadcn/ui 레이아웃 패턴](docs/shadcn-ui-레이아웃-패턴-분석-보고서.md) + +--- + +## 변경 이력 + +| 날짜 | 작성자 | 내용 | +|------|--------|------| +| 2025-11-03 | 개발팀 | 초안 작성 | +