# 🎨 μ œμ–΄κ΄€λ¦¬ - 데이터 μ—°κ²° μ„€μ • UI μž¬μ„€κ³„ κ³„νšμ„œ ## πŸ“‹ ν”„λ‘œμ νŠΈ κ°œμš” ### λͺ©ν‘œ - κΈ°μ‘΄ λͺ¨λ‹¬ 기반 ν•„λ“œ 맀핑을 메인 ν™”λ©΄μœΌλ‘œ 톡합 - μ€‘λ³΅λœ ν…Œμ΄λΈ” 선택 κ³Όμ • 제거 - μ‹œκ°μ  ν•„λ“œ μ—°κ²° λ§€ν•‘ κ΅¬ν˜„ - 쒌우 λΆ„ν•  λ ˆμ΄μ•„μ›ƒμœΌλ‘œ 정보 κ°€μ‹œμ„± ν–₯상 ### ν˜„μž¬ 문제점 - ❌ **이쀑 μž‘μ—…**: ν…Œμ΄λΈ”μ„ 3번 선택해야 함 (더블클릭 β†’ λͺ¨λ‹¬ β†’ μž¬μ„ νƒ) - ❌ **ν˜Όλž€μŠ€λŸ¬μš΄ UX**: 사전 μ„ νƒμ˜ μ˜λ―Έκ°€ 없어짐 - ❌ **λΆˆν•„μš”ν•œ λͺ¨λ‹¬**: μ—°κ²° 섀정이 메인 κΈ°λŠ₯인데 숨겨져 있음 - ❌ **μ‹œκ°μ  ν”Όλ“œλ°± λΆ€μ‘±**: ν•„λ“œ λ§€ν•‘ 관계가 λͺ…ν™•ν•˜μ§€ μ•ŠμŒ ## 🎯 μƒˆλ‘œμš΄ UI ꡬ쑰 ### λ ˆμ΄μ•„μ›ƒ ꡬ성 ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ μ œμ–΄κ΄€λ¦¬ - 데이터 μ—°κ²° μ„€μ • β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ 쒌츑 νŒ¨λ„ (30%) β”‚ 우츑 νŒ¨λ„ (70%) β”‚ β”‚ - μ—°κ²° νƒ€μž… 선택 β”‚ - 단계별 μ„€μ • UI β”‚ β”‚ - λ§€ν•‘ 정보 λͺ¨λ‹ˆν„°λ§ β”‚ - μ‹œκ°μ  ν•„λ“œ λ§€ν•‘ β”‚ β”‚ - 상세 μ„€μ • λͺ©λ‘ β”‚ - μ‹€μ‹œκ°„ μ—°κ²°μ„  ν‘œμ‹œ β”‚ β”‚ - μ•‘μ…˜ λ²„νŠΌ β”‚ - λ“œλž˜κ·Έ μ•€ λ“œλ‘­ 지원 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## πŸ”§ κ΅¬ν˜„ 단계 ### Phase 1: κΈ°λ³Έ ꡬ쑰 ꡬ좕 - [ ] 쒌우 λΆ„ν•  λ ˆμ΄μ•„μ›ƒ μ»΄ν¬λ„ŒνŠΈ 생성 - [ ] κΈ°μ‘΄ λͺ¨λ‹¬ μ»΄ν¬λ„ŒνŠΈλ“€μ„ 메인 ν™”λ©΄μš©μœΌλ‘œ λ¦¬νŒ©ν† λ§ - [ ] μ—°κ²° νƒ€μž… 선택 μ»΄ν¬λ„ŒνŠΈ κ΅¬ν˜„ ### Phase 2: 쒌츑 νŒ¨λ„ κ΅¬ν˜„ - [ ] μ—°κ²° νƒ€μž… 선택 (데이터 μ €μž₯ / μ™ΈλΆ€ 호좜) - [ ] μ‹€μ‹œκ°„ λ§€ν•‘ 정보 ν‘œμ‹œ - [ ] λ§€ν•‘ 상세 λͺ©λ‘ μ»΄ν¬λ„ŒνŠΈ - [ ] κ³ κΈ‰ μ„€μ • νŒ¨λ„ ### Phase 3: 우츑 νŒ¨λ„ κ΅¬ν˜„ - [ ] 단계별 μ§„ν–‰ UI (μ—°κ²° β†’ ν…Œμ΄λΈ” β†’ λ§€ν•‘) - [ ] μ‹œκ°μ  ν•„λ“œ λ§€ν•‘ μ˜μ—­ - [ ] SVG 기반 μ—°κ²°μ„  μ‹œμŠ€ν…œ - [ ] λ“œλž˜κ·Έ μ•€ λ“œλ‘­ λ§€ν•‘ κΈ°λŠ₯ ### Phase 4: κ³ κΈ‰ κΈ°λŠ₯ - [ ] μ‹€μ‹œκ°„ 검증 및 ν”Όλ“œλ°± - [ ] λ§€ν•‘ 미리보기 κΈ°λŠ₯ - [ ] μ„€μ • μ €μž₯/뢈러였기 - [ ] ν…ŒμŠ€νŠΈ μ‹€ν–‰ κΈ°λŠ₯ ## πŸ“ 파일 ꡬ쑰 ### μƒˆλ‘œ 생성할 μ»΄ν¬λ„ŒνŠΈ ``` frontend/components/dataflow/connection/redesigned/ β”œβ”€β”€ DataConnectionDesigner.tsx # 메인 μ»¨ν…Œμ΄λ„ˆ β”œβ”€β”€ LeftPanel/ β”‚ β”œβ”€β”€ ConnectionTypeSelector.tsx # μ—°κ²° νƒ€μž… 선택 β”‚ β”œβ”€β”€ MappingInfoPanel.tsx # λ§€ν•‘ 정보 ν‘œμ‹œ β”‚ β”œβ”€β”€ MappingDetailList.tsx # λ§€ν•‘ 상세 λͺ©λ‘ β”‚ β”œβ”€β”€ AdvancedSettings.tsx # κ³ κΈ‰ μ„€μ • β”‚ └── ActionButtons.tsx # μ•‘μ…˜ λ²„νŠΌλ“€ β”œβ”€β”€ RightPanel/ β”‚ β”œβ”€β”€ StepProgress.tsx # 단계 μ§„ν–‰ ν‘œμ‹œ β”‚ β”œβ”€β”€ ConnectionStep.tsx # 1단계: μ—°κ²° 선택 β”‚ β”œβ”€β”€ TableStep.tsx # 2단계: ν…Œμ΄λΈ” 선택 β”‚ β”œβ”€β”€ FieldMappingStep.tsx # 3단계: ν•„λ“œ λ§€ν•‘ β”‚ └── VisualMapping/ β”‚ β”œβ”€β”€ FieldMappingCanvas.tsx # μ‹œκ°μ  λ§€ν•‘ μΊ”λ²„μŠ€ β”‚ β”œβ”€β”€ FieldColumn.tsx # ν•„λ“œ 컬럼 μ»΄ν¬λ„ŒνŠΈ β”‚ β”œβ”€β”€ ConnectionLine.tsx # SVG μ—°κ²°μ„  β”‚ └── MappingControls.tsx # λ§€ν•‘ μ œμ–΄ 도ꡬ └── types/ └── redesigned.ts # νƒ€μž… μ •μ˜ ``` ### μˆ˜μ •ν•  κΈ°μ‘΄ 파일 ``` frontend/components/dataflow/connection/ β”œβ”€β”€ DataSaveSettings.tsx # μƒˆ UI둜 ꡐ체 β”œβ”€β”€ ConnectionSelectionPanel.tsx # μž¬μ‚¬μš©μ„ μœ„ν•œ λ¦¬νŒ©ν† λ§ β”œβ”€β”€ TableSelectionPanel.tsx # μž¬μ‚¬μš©μ„ μœ„ν•œ λ¦¬νŒ©ν† λ§ └── ActionFieldMappings.tsx # λ ˆκ±°μ‹œ 처리 ``` ## 🎨 UI μ»΄ν¬λ„ŒνŠΈ 상세 ### 1. μ—°κ²° νƒ€μž… 선택 (ConnectionTypeSelector) ```typescript interface ConnectionType { id: "data_save" | "external_call"; label: string; description: string; icon: React.ReactNode; } const connectionTypes: ConnectionType[] = [ { id: "data_save", label: "데이터 μ €μž₯", description: "INSERT/UPDATE/DELETE μž‘μ—…", icon: , }, { id: "external_call", label: "μ™ΈλΆ€ 호좜", description: "API/Webhook 호좜", icon: , }, ]; ``` ### 2. μ‹œκ°μ  ν•„λ“œ λ§€ν•‘ (FieldMappingCanvas) ```typescript interface FieldMapping { id: string; fromField: ColumnInfo; toField: ColumnInfo; transformRule?: string; isValid: boolean; validationMessage?: string; } interface MappingLine { id: string; fromX: number; fromY: number; toX: number; toY: number; isValid: boolean; isHovered: boolean; } ``` ### 3. λ§€ν•‘ 정보 νŒ¨λ„ (MappingInfoPanel) ```typescript interface MappingStats { totalMappings: number; validMappings: number; invalidMappings: number; missingRequiredFields: number; estimatedRows: number; actionType: "INSERT" | "UPDATE" | "DELETE"; } ``` ## πŸ”„ 데이터 ν”Œλ‘œμš° ### μƒνƒœ 관리 ```typescript interface DataConnectionState { // κΈ°λ³Έ μ„€μ • connectionType: "data_save" | "external_call"; currentStep: 1 | 2 | 3; // μ—°κ²° 정보 fromConnection?: Connection; toConnection?: Connection; fromTable?: TableInfo; toTable?: TableInfo; // λ§€ν•‘ 정보 fieldMappings: FieldMapping[]; mappingStats: MappingStats; // UI μƒνƒœ selectedMapping?: string; isLoading: boolean; validationErrors: ValidationError[]; } ``` ### 이벀트 핸듀링 ```typescript interface DataConnectionActions { // μ—°κ²° νƒ€μž… setConnectionType: (type: "data_save" | "external_call") => void; // 단계 μ§„ν–‰ goToStep: (step: 1 | 2 | 3) => void; // μ—°κ²°/ν…Œμ΄λΈ” 선택 selectConnection: (type: "from" | "to", connection: Connection) => void; selectTable: (type: "from" | "to", table: TableInfo) => void; // ν•„λ“œ λ§€ν•‘ createMapping: (fromField: ColumnInfo, toField: ColumnInfo) => void; updateMapping: (mappingId: string, updates: Partial) => void; deleteMapping: (mappingId: string) => void; // 검증 및 μ €μž₯ validateMappings: () => Promise; saveMappings: () => Promise; testExecution: () => Promise; } ``` ## 🎯 μ‚¬μš©μž κ²½ν—˜ (UX) κ°œμ„ μ  ### Before (κΈ°μ‘΄) 1. ν…Œμ΄λΈ” 더블클릭 β†’ 화면에 ν‘œμ‹œ 2. λͺ¨λ‹¬ μ—΄κΈ° β†’ λ‹€μ‹œ ν…Œμ΄λΈ” 선택 3. μ™ΈλΆ€ 컀λ„₯μ…˜ μ„€μ • β†’ 또 λ‹€μ‹œ ν…Œμ΄λΈ” 선택 4. ν•„λ“œ λ§€ν•‘ β†’ ν…μŠ€νŠΈ 기반 λ§€ν•‘ ### After (κ°œμ„ ) 1. **μ—°κ²° νƒ€μž… 선택** β†’ λͺ©μ  λͺ…ν™•ν™” 2. **μ—°κ²° 선택** β†’ ν•œ λ²ˆμ— FROM/TO μ„€μ • 3. **ν…Œμ΄λΈ” 선택** β†’ μ¦‰μ‹œ ν•„λ“œ 정보 λ‘œλ“œ 4. **μ‹œκ°μ  λ§€ν•‘** β†’ λ“œλž˜κ·Έ μ•€ λ“œλ‘­μœΌλ‘œ 직관적 μ—°κ²° ## πŸš€ κ΅¬ν˜„ μš°μ„ μˆœμœ„ ### πŸ”₯ High Priority 1. **κΈ°λ³Έ λ ˆμ΄μ•„μ›ƒ** - 쒌우 λΆ„ν•  ꡬ쑰 2. **μ—°κ²° νƒ€μž… 선택** - 데이터 μ €μž₯/μ™ΈλΆ€ 호좜 3. **단계별 μ§„ν–‰** - μ—°κ²° β†’ ν…Œμ΄λΈ” β†’ λ§€ν•‘ 4. **κΈ°λ³Έ ν•„λ“œ λ§€ν•‘** - λ“œλž˜κ·Έ μ•€ λ“œλ‘­ 없이 클릭 기반 ### πŸ”Ά Medium Priority 1. **μ‹œκ°μ  μ—°κ²°μ„ ** - SVG 기반 라인 ν‘œμ‹œ 2. **μ‹€μ‹œκ°„ 검증** - νƒ€μž… ν˜Έν™˜μ„± 체크 3. **λ§€ν•‘ 정보 νŒ¨λ„** - 톡계 및 μƒνƒœ ν‘œμ‹œ 4. **λ“œλž˜κ·Έ μ•€ λ“œλ‘­** - κ³ κΈ‰ λ§€ν•‘ κΈ°λŠ₯ ### πŸ”΅ Low Priority 1. **κ³ κΈ‰ μ„€μ •** - νŠΈλžœμž­μ…˜, 배치 μ„€μ • 2. **미리보기 κΈ°λŠ₯** - 데이터 λ³€ν™˜ 미리보기 3. **μ„€μ • ν…œν”Œλ¦Ώ** - 자주 μ‚¬μš©ν•˜λŠ” λ§€ν•‘ μ €μž₯ 4. **μ„±λŠ₯ μ΅œμ ν™”** - λŒ€μš©λŸ‰ ν…Œμ΄λΈ” 처리 ## πŸ“… 개발 일정 ### Week 1: κΈ°λ³Έ ꡬ쑰 - [ ] λ ˆμ΄μ•„μ›ƒ μ»΄ν¬λ„ŒνŠΈ 생성 - [ ] μ—°κ²° νƒ€μž… 선택 κ΅¬ν˜„ - [ ] κΈ°μ‘΄ μ»΄ν¬λ„ŒνŠΈ λ¦¬νŒ©ν† λ§ ### Week 2: 핡심 κΈ°λŠ₯ - [ ] 단계별 μ§„ν–‰ UI - [ ] μ—°κ²°/ν…Œμ΄λΈ” 선택 톡합 - [ ] κΈ°λ³Έ ν•„λ“œ λ§€ν•‘ κ΅¬ν˜„ ### Week 3: μ‹œκ°μ  κ°œμ„  - [ ] SVG μ—°κ²°μ„  μ‹œμŠ€ν…œ - [ ] λ“œλž˜κ·Έ μ•€ λ“œλ‘­ λ§€ν•‘ - [ ] μ‹€μ‹œκ°„ 검증 κΈ°λŠ₯ ### Week 4: μ™„μ„± 및 ν…ŒμŠ€νŠΈ - [ ] κ³ κΈ‰ κΈ°λŠ₯ κ΅¬ν˜„ - [ ] 톡합 ν…ŒμŠ€νŠΈ - [ ] μ‚¬μš©μž ν…ŒμŠ€νŠΈ 및 ν”Όλ“œλ°± 반영 ## πŸ” 기술적 고렀사항 ### μ„±λŠ₯ μ΅œμ ν™” - **가상화**: λŒ€μš©λŸ‰ ν•„λ“œ λͺ©λ‘ 처리 - **λ©”λͺ¨μ΄μ œμ΄μ…˜**: λΆˆν•„μš”ν•œ λ¦¬λ Œλ”λ§ λ°©μ§€ - **μ§€μ—° λ‘œλ”©**: ν•„μš”ν•œ μ‹œμ μ—λ§Œ 데이터 λ‘œλ“œ ### μ ‘κ·Όμ„± - **ν‚€λ³΄λ“œ λ„€λΉ„κ²Œμ΄μ…˜**: λͺ¨λ“  κΈ°λŠ₯을 ν‚€λ³΄λ“œλ‘œ μ ‘κ·Ό κ°€λŠ₯ - **슀크린 리더**: μ‹œκ°μ  λ§€ν•‘μ˜ λŒ€μ²΄ ν…μŠ€νŠΈ 제곡 - **색상 λŒ€λΉ„**: μ—°κ²°μ„ κ³Ό μƒνƒœ ν‘œμ‹œμ˜ λͺ…ν™•ν•œ ꡬ뢄 ### ν™•μž₯μ„± - **ν”ŒλŸ¬κ·ΈμΈ ꡬ쑰**: μƒˆλ‘œμš΄ μ—°κ²° νƒ€μž… μ‰½κ²Œ μΆ”κ°€ - **μ»€μŠ€ν…€ λ³€ν™˜**: μ‚¬μš©μž μ •μ˜ 데이터 λ³€ν™˜ κ·œμΉ™ - **API ν™•μž₯**: μ™ΈλΆ€ μ‹œμŠ€ν…œκ³Όμ˜ 연동 지원 --- ## 🎯 λ‹€μŒ 단계 이 κ³„νšμ„œλ₯Ό λ°”νƒ•μœΌλ‘œ **Phase 1λΆ€ν„° 순차적으둜 κ΅¬ν˜„**을 μ‹œμž‘ν•˜κ² μŠ΅λ‹ˆλ‹€. **첫 번째 μž‘μ—…**: 쒌우 λΆ„ν•  λ ˆμ΄μ•„μ›ƒκ³Ό μ—°κ²° νƒ€μž… 선택 μ»΄ν¬λ„ŒνŠΈ κ΅¬ν˜„ κ΅¬ν˜„μ„ μ‹œμž‘ν•˜μ‹œκ² μ–΄μš”? πŸš€