# [계획서] 버튼 아이콘화 - 화면 디자이너 버튼 표시 모드 확장 > 관련 문서: [맥락노트](./BIC[맥락]-버튼-아이콘화.md) | [체크리스트](./BIC[체크]-버튼-아이콘화.md) ## 개요 화면 디자이너에서 버튼을 텍스트 모드(현행), 아이콘 모드, 아이콘+텍스트 모드 중 선택할 수 있도록 확장한다. 아이콘 모드 선택 시 버튼 액션에 맞는 아이콘 후보군이 제시되고, 관리자가 원하는 아이콘을 선택한다. 아이콘 크기 비율(버튼 높이 대비 4단계 프리셋), 아이콘 색상, 텍스트 위치(4방향), 아이콘-텍스트 간격 설정을 제공한다. 관리자가 lucide 검색 또는 외부 SVG 붙여넣기로 커스텀 아이콘을 추가/삭제할 수 있다. --- ## 현재 동작 - 버튼은 항상 **텍스트 모드**로만 표시됨 - `ButtonConfigPanel.tsx`에서 "버튼 텍스트" 입력 → 실제 화면에서 해당 텍스트가 버튼에 표시 - 아이콘 표시 기능 없음 ### 현재 코드 위치 | 구분 | 파일 | 설명 | |------|------|------| | 설정 패널 | `frontend/components/screen/config-panels/ButtonConfigPanel.tsx` | 버튼 텍스트, 액션 설정 (784~854행) | | 뷰어 렌더링 | `frontend/components/screen/InteractiveScreenViewerDynamic.tsx` | 실제 버튼 렌더링 (961~983행) | | 뷰어 렌더링 | `frontend/components/screen/InteractiveScreenViewer.tsx` | 실제 버튼 렌더링 (2041~2059행) | | 위젯 렌더링 | `frontend/components/screen/widgets/types/ButtonWidget.tsx` | 위젯 기반 버튼 렌더링 (67~86행) | | 최적화 컴포넌트 | `frontend/components/screen/OptimizedButtonComponent.tsx` | 최적화된 버튼 컴포넌트 (643~674행) | --- ## 변경 후 동작 ### 1. 표시 모드 선택 (라디오 그룹) ButtonConfigPanel에 "버튼 텍스트" 입력 위에 표시 모드 선택 UI 추가: - **텍스트 모드** (기본값, 현행 유지): 버튼에 텍스트만 표시 - **아이콘 모드**: 버튼에 아이콘만 표시 - **아이콘+텍스트 모드**: 버튼에 아이콘과 텍스트를 함께 표시 ``` [ 텍스트 | 아이콘 | 아이콘+텍스트 ] ← 라디오 그룹 (토글 형태) ``` ### 2. 텍스트 모드 선택 시 - 현재와 동일하게 "버튼 텍스트" 입력 필드 표시 - 변경 사항 없음 ### 2-1. 아이콘+텍스트 모드 선택 시 - 아이콘 선택 UI (3장과 동일) + 버튼 텍스트 입력 필드 **둘 다 표시** - 렌더링: 텍스트 위치에 따라 아이콘과 텍스트 배치 방향이 달라짐 - 텍스트 위치 4방향: 오른쪽(기본), 왼쪽, 위쪽, 아래쪽 - 예시: `[ ✓ 저장 ]` (오른쪽), `[ 저장 ✓ ]` (왼쪽), 세로 배치 (위쪽/아래쪽) - 아이콘과 텍스트 사이 간격: 기본 6px, 관리자가 0~무제한 조절 가능 (슬라이더 0~32px + 직접 입력) ### 3. 아이콘 모드 선택 시 #### 3-1. 버튼 액션별 추천 아이콘 목록 버튼 액션(`action.type`)에 따라 해당 액션에 어울리는 아이콘 후보군을 그리드로 표시: | 버튼 액션 | 값 | 추천 아이콘 (lucide-react) | |-----------|-----|---------------------------| | 저장 | `save` | Check, Save, CheckCircle, CircleCheck, FileCheck, ShieldCheck | | 삭제 | `delete` | Trash2, Trash, XCircle, X, Eraser, CircleX | | 편집 | `edit` | Pencil, PenLine, Edit, SquarePen, FilePen, PenTool | | 페이지 이동 | `navigate` | ArrowRight, ExternalLink, MoveRight, Navigation, CornerUpRight, Link | | 모달 열기 | `modal` | Maximize2, PanelTop, AppWindow, LayoutGrid, Layers, FolderOpen | | 데이터 전달 | `transferData` | SendHorizontal, ArrowRightLeft, Repeat, PackageCheck, Upload, Share2 | | 엑셀 다운로드 | `excel_download` | Download, FileDown, FileSpreadsheet, Sheet, Table, FileOutput | | 엑셀 업로드 | `excel_upload` | Upload, FileUp, FileSpreadsheet, Sheet, ImportIcon, FileInput | | 즉시 저장 | `quickInsert` | Zap, Plus, PlusCircle, SquarePlus, FilePlus, BadgePlus | | 제어 흐름 | `control` | Settings, SlidersHorizontal, ToggleLeft, Workflow, GitBranch, Cog | | 바코드 스캔 | `barcode_scan` | ScanLine, QrCode, Camera, Scan, ScanBarcode, Focus | | 운행알림 및 종료 | `operation_control` | Truck, Car, MapPin, Navigation2, Route, Bell | | 이벤트 발송 | `event` | Send, Bell, Radio, Megaphone, Podcast, BellRing | | 복사 | `copy` | Copy, ClipboardCopy, Files, CopyPlus, Duplicate, ClipboardList | **적절한 아이콘이 없는 액션 (숨김 처리된 deprecated 액션들):** | 버튼 액션 | 값 | 안내 문구 | |-----------|-----|----------| | 연관 데이터 버튼 모달 열기 | `openRelatedModal` | 안내 문구 표시 + 커스텀 아이콘 추가 가능 | | (deprecated) 데이터 전달 + 모달 | `openModalWithData` | 안내 문구 표시 + 커스텀 아이콘 추가 가능 | | 테이블 이력 보기 | `view_table_history` | 안내 문구 표시 + 커스텀 아이콘 추가 가능 | | 코드 병합 | `code_merge` | 안내 문구 표시 + 커스텀 아이콘 추가 가능 | | 공차등록 | `empty_vehicle` | 안내 문구 표시 + 커스텀 아이콘 추가 가능 | > 안내 문구: "적절한 추천 아이콘이 없습니다. 텍스트 모드를 사용하거나 아래에서 아이콘을 직접 추가하세요." > 안내 문구 아래에 커스텀 아이콘 목록 + lucide 검색/SVG 붙여넣기 버튼이 표시됨 #### 3-2. 아이콘 선택 UI - 액션별 추천 아이콘을 4~6열 그리드로 표시 - 각 아이콘은 32x32 크기, 호버 시 하이라이트, 선택 시 ring 표시 - 아이콘 아래에 이름 표시 (`text-[10px]`) - 관리자가 추가한 커스텀 아이콘이 있으면 "커스텀 아이콘" 구분선 아래 함께 표시 #### 3-3. 아이콘 크기 비율 설정 버튼 높이 대비 비율로 아이콘 크기를 설정 (정사각형 유지): **프리셋 (ToggleGroup, 4단계):** | 이름 | 버튼 높이 대비 | 설명 | |------|--------------|------| | 작게 | 40% | 컴팩트한 아이콘 | | 보통 | 55% | 기본값, 대부분의 버튼에 적합 | | 크게 | 70% | 존재감 있는 크기 | | 매우 크게 | 85% | 아이콘 강조, 버튼에 꽉 차는 느낌 | - px 직접 입력은 제거 (비율 기반이므로 버튼 크기 변경 시 아이콘도 자동 비례) - 저장: `icon.size`에 프리셋 문자열(`"보통"`) 저장 - 렌더링: `height: N%` + `aspect-ratio: 1/1`로 정사각형 유지 #### 3-4. 아이콘 색상 설정 아이콘 크기 아래에 아이콘 전용 색상 설정: - **컬러 피커**: 기존 버튼 색상 설정과 동일한 UI 사용 - **기본값**: 미설정 (= `textColor` 상속, 기존 동작과 동일) - **설정 시**: lucide 아이콘은 지정한 색상으로 덮어쓰기 - **외부 SVG**: 고유 색상이 하드코딩된 SVG는 이 설정의 영향을 받지 않음 (원본 유지) - **초기화 버튼**: "텍스트 색상과 동일" 버튼으로 별도 색상 해제 가능 | 상황 | iconColor 설정 | 결과 | |------|---------------|------| | lucide 아이콘, iconColor 미설정 | 없음 | textColor 상속 (기존 동작) | | lucide 아이콘, iconColor 설정 | `#22c55e` | 초록색 아이콘 | | 외부 SVG (고유 색상), iconColor 설정 | `#22c55e` | SVG 원본 색상 유지 (무시) | | 외부 SVG (currentColor), iconColor 설정 | `#22c55e` | 초록색 아이콘 | #### 3-5. 텍스트 위치 설정 (아이콘+텍스트 모드 전용) 아이콘 대비 텍스트의 배치 방향을 4방향으로 설정: | 위치 | 값 | 레이아웃 | 설명 | |------|-----|---------|------| | 왼쪽 | `left` | `텍스트 ← 아이콘` | 텍스트가 아이콘 왼쪽 (가로) | | 오른쪽 | `right` | `아이콘 → 텍스트` | 기본값, 아이콘 뒤에 텍스트 (가로) | | 위쪽 | `top` | 텍스트 위, 아이콘 아래 | 세로 배치 | | 아래쪽 | `bottom` | 아이콘 위, 텍스트 아래 | 세로 배치 | - 기본값: `"right"` (아이콘 오른쪽에 텍스트) - 저장: `componentConfig.iconTextPosition` - 아이콘 모드에서는 이 옵션이 숨겨짐 (텍스트가 없으므로 불필요) #### 3-6. 아이콘-텍스트 간격 설정 (아이콘+텍스트 모드 전용) 아이콘+텍스트 모드에서 아이콘과 텍스트 사이 간격을 조절: - **슬라이더**: 0~32px 범위 시각적 조절 - **직접 입력**: px 수치 직접 입력 (최솟값 0, 최댓값 제한 없음) - **기본값**: 6px - 아이콘 모드에서는 이 옵션이 숨겨짐 (텍스트가 없으므로 불필요) #### 3-7. 아이콘 모드 레이아웃 안내 아이콘만 표시하면 텍스트보다 좁은 공간으로 충분하므로 안내 문구 표시: ``` ℹ 아이콘만 표시할 때는 버튼 영역의 가로 폭을 줄여 정사각형에 가깝게 만들면 더 깔끔합니다. ``` - `bg-blue-50 dark:bg-blue-950/20` 배경의 안내 박스 - 아이콘 모드(`"icon"`)에서만 표시, 아이콘+텍스트 모드에서는 숨김 #### 3-8. 디폴트 아이콘 자동 부여 아이콘/아이콘+텍스트 모드 전환 시 아이콘이 미선택 상태이면 **디폴트 아이콘을 자동으로 부여**한다. | 상황 | 디폴트 아이콘 | |------|-------------| | 추천 아이콘이 있는 액션 (save, delete 등) | 해당 액션의 **첫 번째 추천 아이콘** (예: save → Check) | | 추천 아이콘이 없는 액션 (deprecated 등) | 범용 폴백 아이콘: `SquareMousePointer` | **커스텀 아이콘 삭제 시:** - 현재 선택된 커스텀 아이콘을 삭제하면 **디폴트 아이콘으로 자동 복귀** (텍스트 모드로 빠지지 않음) - 아이콘 모드를 유지한 채 디폴트 아이콘이 캔버스에 즉시 반영됨 #### 3-9. 커스텀 아이콘 추가/삭제 **방법 1: lucide 아이콘 검색으로 추가** - "아이콘 추가" 버튼 클릭 시 lucide 아이콘 전체 검색 가능한 모달/팝오버 표시 - 검색 입력 → 아이콘 이름으로 필터링 → 선택하면 커스텀 목록에 추가 **방법 2: 외부 SVG 붙여넣기로 추가** - "SVG 붙여넣기" 버튼 클릭 시 텍스트 입력 영역(textarea) 표시 - 외부에서 복사한 SVG 코드를 붙여넣기 → 미리보기로 확인 → "추가" 버튼으로 등록 - SVG 유효성 검사: `; action: { type: string; // 기존: 버튼 액션 타입 // ...기존 action 속성들 유지 }; } ``` ### 저장 예시 ```json { "text": "저장", "displayMode": "icon", "icon": { "name": "Check", "type": "lucide", "size": "보통", "color": "#22c55e" }, "customIcons": ["Rocket", "Star"], "customSvgIcons": [ { "name": "회사로고", "svg": "..." } ], "action": { "type": "save" } } ``` --- ## 시각적 동작 예시 ### ButtonConfigPanel (디자이너 편집 모드) ``` 표시 모드: [ 텍스트 | (아이콘) | 아이콘+텍스트 ] ← 아이콘 선택됨 아이콘 선택: ┌──────────────────────────────────┐ │ 추천 아이콘 (저장) │ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │ │ ✓ │ │ 💾 │ │ ✓○ │ │ ○✓ │ │ │ │Check│ │Save│ │Chk○│ │○Chk│ │ │ └────┘ └────┘ └────┘ └────┘ │ │ ┌────┐ ┌────┐ │ │ │📄✓│ │🛡✓│ │ │ │FChk│ │ShCk│ │ │ └────┘ └────┘ │ │ │ │ ── 커스텀 아이콘 ── │ │ ┌────┐ ┌────┐ ┌────┐ │ │ │ 🚀 │ │ ⭐ │ │[로고]│ │ │ │Rckt │ │Star│ │회사 │ │ │ │ ✕ │ │ ✕│ │ ✕ │ │ │ └────┘ └────┘ └────┘ │ │ [+ lucide 검색] [+ SVG 붙여넣기]│ └──────────────────────────────────┘ 아이콘 크기 비율: [ 작게 | (보통) | 크게 | 매우 크게 ] 텍스트 위치: [ 왼쪽 | (오른쪽) | 위쪽 | 아래쪽 ] ← 아이콘+텍스트 모드에서만 표시 아이콘-텍스트 간격: [━━━━━○━━] [6] px ← 아이콘+텍스트 모드에서만 표시 아이콘 색상: [■ #22c55e] [텍스트 색상과 동일] ℹ 아이콘만 표시할 때는 버튼 영역의 가로 폭을 줄여 정사각형에 가깝게 만들면 더 깔끔합니다. ``` ### 실제 화면 렌더링 | 모드 | 표시 | |------|------| | 텍스트 모드 | `[ 저장 ]` | | 아이콘 모드 (보통, 55%) | `[ ✓ ]` | | 아이콘 모드 (매우 크게, 85%) | `[ ✓ ]` | | 아이콘+텍스트 (텍스트 오른쪽) | `[ ✓ 저장 ]` (간격 6px) | | 아이콘+텍스트 (텍스트 왼쪽) | `[ 저장 ✓ ]` | | 아이콘+텍스트 (텍스트 아래쪽) | 아이콘 위, 텍스트 아래 (세로) | | 아이콘+텍스트 (색상 분리) | `[ 초록✓ 검정저장 ]` | --- ## 변경 대상 ### 수정 파일 | 파일 | 변경 내용 | |------|----------| | `ButtonConfigPanel.tsx` | 표시 모드 3종 라디오, 아이콘 그리드, 크기, 색상, 간격 설정, 레이아웃 안내, 커스텀 아이콘 UI | | `InteractiveScreenViewerDynamic.tsx` | `displayMode` 3종 분기 → 아이콘/아이콘+텍스트/텍스트 렌더링 | | `InteractiveScreenViewer.tsx` | 동일 분기 추가 | | `ButtonWidget.tsx` | 동일 분기 추가 | | `OptimizedButtonComponent.tsx` | 동일 분기 추가 | | `ScreenDesigner.tsx` | 입력 필드 포커스 시 키보드 단축키 기본 동작 허용 (Ctrl+A/C/V/Z) | | `RealtimePreviewDynamic.tsx` | 버튼 컴포넌트 position wrapper에서 border 속성 분리 (이중 테두리 방지) | ### 신규 파일 | 파일 | 내용 | |------|------| | `frontend/lib/button-icon-map.ts` | 버튼 액션별 추천 아이콘 매핑 + 아이콘 동적 렌더링 유틸 | --- ## 설계 원칙 - 기본값은 `"text"` 모드 → 기존 모든 버튼은 변경 없이 동작 - `displayMode`가 없거나 `"text"`이면 현행 텍스트 렌더링 유지 - 아이콘/아이콘+텍스트 모드 전환 시 아이콘 미선택이면 **디폴트 아이콘 자동 부여** (빈 상태 방지) - 커스텀 아이콘 삭제 시 텍스트 모드로 빠지지 않고 **디폴트 아이콘으로 자동 복귀** - 아이콘 모드에서도 `text` 값은 유지 (접근성 aria-label로 활용) - 기본 아이콘은 lucide-react 사용 (프로젝트 일관성) - 외부 SVG 붙여넣기도 지원 → 관리자가 회사 로고 등 자체 아이콘을 등록 가능 - lucide 커스텀 아이콘은 `componentConfig.customIcons`에, SVG 아이콘은 `componentConfig.customSvgIcons`에 저장 - lucide 아이콘 렌더링: 아이콘 이름 → 컴포넌트 매핑, SVG 아이콘 렌더링: `dangerouslySetInnerHTML` + DOMPurify 정화