dwg-dxf-viewer-hm을 public repo로 생성, 로컬 git init 후 최초 push.HMDesign 적용 작업 기록
dwg-dxf-viewer-hm · 2026-10-12 · Gitea 이슈 #2 hmdesign 적용 / #1 디자인팀 디자인 시스템 적용
개요
기존 vanilla TS+Vite+Three.js DWG/DXF 뷰어(dwg-dxf-viewer-sample)를 건드리지 않고,
이 저장소(dwg-dxf-viewer-hm)에 React 19 + HMDesign 디자인 시스템 기반으로
UI/UX를 새로 구성했다. 뷰어 렌더링 엔진(Viewer2D.js + 파서들)은 로직을
그대로 포팅하고, 그 위의 모든 UI(툴바·레이어/속성/옵션 패널·좌표 표시·라이선스 팝업·로딩바)는
HMDesign 컴포넌트로 새로 디자인했다. 기존 UI의 색/아이콘/레이아웃은 가져오지 않았고,
기능만 1:1 유지하는 것을 목표로 했다.
작업 흐름
src/viewer2d/* 포팅, React 컴포넌트 전체 신규 작성.ai-generation-guard.mjs REUSE-004 지적(네이티브 <label>)을 HM Label로 교체.아키텍처: HMDesign 연결
@hm-design/ui는 npm 레지스트리에 배포되지 않고 HMDesign 저장소 자신의 npm workspace
(packages/*) 안에서만 resolve된다. exports는 전부 dist/*를
가리키므로 반드시 빌드가 먼저 끝나 있어야 한다.
git submodule add https://gitea.hmac.kr/b25027/HMDesign.git HMDesign
git submodule update --init --recursive
cd HMDesign && npm ci && npm run build:packages && cd .. # HMDesign 자체 devDependency로 dist/ 생성
npm install # 루트 workspaces(["HMDesign/packages/ui"])가 symlink 연결
npm run dev
HMDesign 내부 빌드는 HMDesign 자신의 node_modules(Storybook/Tailwind/TS7 등)로 수행하고,
외부 앱(이 저장소)은 빌드된 결과물만 workspace symlink로 소비한다 — 두 트리의 의존성 버전이 섞이지 않는다.
@hm-design/center-graphic은 라이선스 미확정으로 사용하지 않았다.
Vite 설정에서 optimizeDeps.entries: ['index.html']을 명시해야 한다 — 지정하지 않으면
Vite의 의존성 스캐너가 submodule(HMDesign) 안의 다른 예제용 *.html까지 크롤링하다
해석할 수 없는 @/... alias에서 죽는다.
구현 내용
디렉토리 구조
src/
├── main.tsx, App.tsx, index.css
├── viewer2d/ ← 기존 엔진 전체, 로직 불변, import 경로만 조정
├── lib/
│ ├── optionsStore.ts ← IndexedDB 영속, 그대로 포팅
│ └── propertyFormat.ts← 속성 포맷터만 추출 포팅 (DOM 렌더는 React로 대체)
├── components/ ← Toolbar / LayerPanel / PropertiesPanel / OptionsPanel /
│ DraggablePanel / CoordReadout / LicenseNotice / LoadProgress
└── hooks/
├── useDwgViewer.ts ← Viewer2D 인스턴스 소유 + 파일 로드 파이프라인 + 옵션 동기화
└── useDraggablePanel.ts ← panelUtils.ts 드래그/collapse 로직의 React 훅 포팅
UI → HMDesign 컴포넌트 매핑 (요약)
| 원본 UI | HMDesign 매핑 |
|---|---|
| Open file / 샘플 3버튼 | Button (+숨김 <input type=file>, HM에 대응 컴포넌트 없음) |
| Model/Layout 탭 | Tabs |
| 2.5D·테마 토글 | Switch + Label |
| 레이어/옵션/속성 패널 열기 | Toggle |
| 레이어 검색/전체켜기끄기 | FilterBar / FilterBarSearch / FilterBarActions |
| 레이어 행(색상/개수/가시성) | 커스텀 — color swatch(동적 color), Badge, lucide Eye/EyeOff |
| 패널 자체(드래그/접기/리사이즈) | DraggablePanel 커스텀(HM Card 기반 + 포인터 드래그 훅, resize는 네이티브 CSS) |
| 슬라이더+숫자입력 3종 | Slider + Input |
| 라이선스/크레딧 | Dialog (THIRD-PARTY-NOTICES / MPL-2.0 / patch 링크 3종 유지) |
| 상단 로딩바 | Progress (원본은 미연결 죽은 코드였음 — HM 추천 방식으로 신규 실연결) |
Navigation(GNB/LNB/Dashboard)은 채용하지 않았다 — 멀티페이지 앱이 아닌 전체화면 캔버스형 단일 도구이기 때문.
발견된 버그 및 수정
증상: 샘플 DWG/DXF 3개 버튼 모두 "로드완료" 상태 텍스트는 뜨지만 캔버스에 아무것도 렌더되지 않음.
원인 확정: Viewer2D(포팅된 vanilla 엔진)는 dispose/teardown API가 없고 생성자에서
window에 영구 리스너를 걸고 자체 requestAnimationFrame 루프를 시작한다.
React <StrictMode>의 개발 모드 mount→unmount→remount 더블 호출이
Viewer2D를 같은 container에 두 번 생성했고, 첫 인스턴스를 정리할 방법이 없어
캔버스가 중첩/렌더가 꼬였다.
수정 완료·미검증: main.tsx에서 <StrictMode> 제거,
useDwgViewer 마운트 effect에서 construction 전에 container.replaceChildren()으로
방어적 클리어 추가. 코드 수정은 했으나 이 세션엔 브라우저 도구가 없어 실제 화면에서 재확인은 못 함 —
사용자 브라우저에서 재검증 필요.
검증 상태
- 검증됨 npm workspace symlink 연결 (
@hm-design/uiresolve 확인) - 검증됨
tsc --noEmit타입체크 통과 - 검증됨 Vite dev 서버에서 전체 모듈 HTTP 200 서빙 확인
- 검증됨
ai-generation-guard.mjsFAIL 0건 (NOT VERIFIED 4건은 HM에 대응 컴포넌트 없는 동적 값 — 문서화된 예외) - 미검증 실제 브라우저 렌더링 (WASM 파싱, Three.js 캔버스, 패널 드래그/접기, IndexedDB 옵션 영속, 390~1440px 반응형) — 이 세션에 브라우저 도구 없음, 컴파일 통과는 동작 검증이 아님
다음 할 일
- StrictMode 제거 수정 후 브라우저에서 샘플 3개 재확인
- 390/768/1280/1440px 반응형 확인
- 기존 프로젝트 기능 체크리스트와 1:1 대조 (파일 로드, pan/zoom/orbit, Fit+F, 2.5D, 그리드, 줌속도, 선가중치, Model/Layout 탭, 엔티티 선택→속성, 줌투셀렉션, 좌표 readout, 패널 영속, 한글 텍스트, 라이선스 링크, 신규 progress bar)