Files
dwg-dxf-viewer-sample/docs/sample-test-report-2026-10-19.md
T
minsungandClaude Opus 5.5 ae77253229 docs: sample test and error-handling status report (2026-10-19)
Re-test of every sample in the browser viewer and headless Node, the
history of 13 handled errors with before/after/current captures for the 8
that could be reproduced, newly found issues (Fit cropping, %%% control
codes, symbol-only CP949 strings, broken e2e) and the library packaging
assessment. scripts/parse-dwg.mjs is the headless parser used for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:48:44 +09:00

419 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 샘플 테스트·오류 처리 현황 보고서 (2026-10-19)
> 대상: `dwg-dxf-viewer-sample` (HEAD `1f80442` + 미커밋 작업 트리)
> 근거: 이번 재테스트 실측, git 이력 20커밋, `docs/*.md` 10건, `hmwebviewer/PROGRESS.md`
> 상태 표기: `원인 확정` · `수정 완료·검증됨` · `수정 완료·미검증`
> 이 보고서에는 검증된 결과만 싣는다. 검증하지 못한 항목은 §8 목록에만 적는다.
---
## 1. 핵심
- **고유 샘플 20개(DWG 19 + DXF 1)가 모두 브라우저 뷰어에서 로드에 성공했다.** 콘솔 에러는 404 리소스 1건뿐이다. Node 헤드리스 파싱도 19/19 성공했다.
- **기존 e2e(`e2e_lin_test.cjs`)는 지금 상태 그대로는 실패한다** (`ERR_MODULE_NOT_FOUND`). 끝까지 돌려도 assert 가 없어서 결과를 판정하지 못한다.
- **과거에 처리한 오류 13건 중 8건을 검증했다** (§4.1). 해당 커밋의 수정 전 버전과 수정 후 버전을 실제로 띄워서, 현재 버전까지 3단으로 캡처하고 측정했다.
- 대용량 로드: 213.8초 → 3.6초
- Slug 글자 높이: 150px → 202px (비율 0.743, 커밋 주장 0.744)
- 레이어 토글: 454~622ms → 13~35ms
- 그 외 회색 원 제거, 메쉬 표시, 선가중치 적용, Model/Paper 분리, 한글 복원
- **이번에 새로 찾은 문제는 4건이다.**
- Fit 이 도면 가장자리를 잘라냄
- `%%%` 제어코드가 변환되지 않음
- 기호만 있는 CP949 문자열이 깨진 채 남음
- e2e 스크립트가 고장 나 있음
- **라이선스 2건 (`원인 확정`)**
- 공개한 MPL 패치를 원본 acadrust 0.4.1 에 적용하면 실패한다. 실제 wasm 의 소스를 재현할 수 없다는 뜻이다.
- Visio 저작권("All rights reserved") 라인타입 데이터가 번들에 들어 있다.
- Gitea 와 dist2 는 사내 전용이다. **외부 반출(고객 납품·외부 웹 공개·npm) 전에 해결해야 한다.** 라이선스 고지 버튼 자체는 형식이 맞다. 그 버튼이 가리키는 패치 내용이 틀렸다.
- **라이브러리 배포는 조건부로 가능하다** (§6). 위 라이선스 2건, Vite 전용 코드, Viewer2D 에 `dispose()` 가 없는 문제를 먼저 해결해야 한다.
## 2. 권장 액션
1. **라이선스.**
- MPL 패치를 다시 만든다. 원본 0.4.1 과 실제 vendored 트리의 전체 diff 로 만들고, 원본에 적용해서 성공하는지 확인한다.
- 고지 문서 4곳의 "한 파일만 수정", "unmodified" 문구를 고친다.
- `ICAD_LIN` 은 IntelliCAD(ITC) 계약 범위를 확인한다. 외부 배포가 허용되지 않으면 제거한다.
2. **미커밋 작업 정리.**
- 2.5D 보기, VIEWPORT 채움 변환, Z 붕괴 수정을 커밋하거나 분리한다.
- 루트의 untracked 대용량 파일(`C0060202.json` 31.7MB, `SAN01.json` 16.6MB, `C0060202.zip`)은 `.gitignore` 에 넣거나 삭제한다.
3. **e2e 를 실제 테스트로.**
- `linParser.ts` 의 확장자 없는 import 를 고친다.
- 샘플 경로를 `dist/` 에서 `public/samples/` 로 바꾼다.
- assert 를 추가한다.
- 이번에 쓴 브라우저 e2e 와 3단 비교 스크립트를 레포에 넣는다.
4. **Fit 잘림 수정** (§3.4-①).
5. **텍스트 후처리 2건 수정.**
- `stripMText` 에 `%%%`, `%%nnn`, `%%o`, `%%u` 처리를 추가한다.
- `fixCp949Mojibake` 가 한글이 없는 CP949 기호도 복원하게 한다.
---
## 3. 이번 재테스트 결과
### 3.1 환경
| 항목 | 값 |
|---|---|
| 코드 | HEAD `1f80442` + 미커밋 변경(2.5D 보기, `_bakeFillXf`, `_trimmedContentBox` Z 수정) |
| Node | v24.11.1 |
| 브라우저 | 시스템 Chrome headless, `playwright-core@1.56.1` |
| GPU | ANGLE D3D11 / NVIDIA RTX 3060 (실제 GPU) |
| 타입체크 | `npm run typecheck` 통과 (컴파일 통과일 뿐, 동작 검증은 아님) |
### 3.2 샘플별 결과
열 설명:
- **Node 파싱**: `parse_dwg_json` + `fixDwgKoreanText` 실행 시간.
- **RSS**: 프로세스 전체 메모리.
- **브라우저 로드**: 파일을 선택한 뒤 상태바에 `✓ <파일명>` 이 뜨기까지의 시간.
- **JS heap**: `performance.memory`.
- **notif**: acadrust notification 개수.
| 파일 | MB | 버전 | 엔티티 | Node 파싱 ms | RSS MB | 브라우저 로드 ms | JS heap MB | 콘솔 에러 | notif | 결과 |
|---|---|---|---|---|---|---|---|---|---|---|
| BasicSample.dwg | 0.4 | R2018 | 7,309 | 142 | 147 | 553 | 47 | 1¹ | 4 | 통과 |
| civil.dwg² | 5.4 | R2000 | 17,810 | 389 | 264 | 799 | 98 | 0 | 2 | 통과 |
| C0060203-001-표준단면도(…).dwg | 0.4 | R2007 | 2,068 | 93 | 126 | 567 | 57 | 0 | **78** | 통과 |
| bb.dwg | 0.7 | R2018 | 438 | 75 | 109 | 579 | 46 | 0 | 4 | 통과 |
| simple.dxf | 0.0 | DXF | 6 | — | — | 74 | 34 | 0 | — | 통과 |
| 04manhole1-simplify.dwg | 0.4 | R2018 | 7,035 | 118 | 138 | 575 | 54 | 0 | 4 | 통과 |
| dwg_18.3mb.dwg | 18.4 | R2018 | 82,393 | 1,988 | 1,087 | **4,232** | 372 | 0 | 4 | 통과 |
| test.dwg³ | 18.4 | R2018 | 82,393 | 2,156 | 1,088 | 3,914 | 371 | 0 | 4 | 통과 |
| 대산당진 2공구_노면_….dwg | 37.1 | R2018 | 65,495 | 3,017 | **1,373** | **5,454** | **782** | 0 | 4 | 통과 |
| a. 거더배치 삽도 | 0.2 | R2018 | 1,433 | 118 | 134 | 568 | 139 | 0 | 5 | 통과 |
| a. 교대배치 삽도 | 0.4 | R2018 | 2,138 | 58 | 102 | 565 | 59 | 0 | 4 | 통과 |
| a. 교량시종점 삽도 | 0.4 | R2018 | 4,432 | 84 | 105 | 542 | 56 | 0 | 4 | 통과 |
| a. 교량폭원 삽도 | 0.5 | R2018 | 2,824 | 64 | 106 | 575 | 45 | 0 | 4 | 통과 |
| a. 교면포장 삽도 | 0.2 | R2018 | 1,304 | 54 | 104 | 570 | 44 | 0 | 4 | 통과 |
| a. 중앙분리대 삽도 | 0.4 | R2018 | 2,159 | 63 | 103 | 537 | 44 | 0 | 4 | 통과 |
| a. 횡단경사 삽도 | 0.5 | R2018 | 5,635 | 126 | 138 | 569 | 44 | 0 | 4 | 통과 |
| 산외교- ELEVATION 산출근거도 | 0.6 | R2007 | 11,262 | 162 | 181 | 937 | 111 | 0 | **44** | 통과 |
| 산외교-받침배치도 | 0.4 | R2018 | 6,240 | 106 | 144 | 635 | 63 | 0 | 4 | 통과 |
| 산외교_파이형_교각일반도(울산P11-꺾임) | 1.0 | R2018 | 25,927 | 371 | 219 | 810 | 56 | 0 | 4 | 통과 |
| 산외교_파이형_교각일반도(울산P9) | 1.0 | R2018 | 26,207 | 397 | 220 | 744 | 57 | 0 | 4 | 통과 |
¹ 404 리소스 1건, 첫 페이지에서만 발생. `/favicon.ico` 가 404 를 반환하는 것은 확인했다.
² `civil.dwg` 와 `C0060202-001-평면및종단면도(…).dwg` 는 md5 가 같다. 같은 파일이다.
³ `test.dwg` 는 `dwg_18.3mb.dwg` 와 바이트는 다르지만, 파싱 결과의 엔티티 수와 텍스트 통계가 같다.
"통과" 의 기준: 상태바에 `✓ <그 파일명> · … entities` 가 표시되고, `.err` 가 없고, 페이지 crash 가 없을 것.
### 3.3 기존 e2e (`e2e_lin_test.cjs`)
| 실행 방식 | 결과 |
|---|---|
| `node e2e_lin_test.cjs` (그대로) | **실패, exit 1**. 4단계에서 `ERR_MODULE_NOT_FOUND: …/src/viewer2d/linData` |
| resolve hook 추가 | 끝까지 실행, exit 0. H-DICHL06/06R/02R/01R/05 패턴 5개 출력 |
`원인 확정`: `linParser.ts` 가 `./linData` 를 확장자 없이 import 한다. Vite 는 이것을 해석하지만 Node ESM 은 해석하지 못한다.
그 밖의 문제:
- **assert 가 없다.** 패턴을 못 찾아도 `console.error` 만 찍고, 마지막에 항상 `COMPLETED SUCCESSFULLY` 를 출력한다.
- 샘플 경로가 gitignore 된 `./dist/samples/…` 다. clean checkout 에서는 실패한다.
- 느린 경로인 `parse_dwg` 를 쓰고 `fixDwgKoreanText` 를 적용하지 않는다. 그래서 출력되는 라인타입 설명이 `LÇüÃø±¸` 처럼 깨진다.
- `Found 0 text styles` 가 출력된다. `tables.styles` 가 비어 있다는 문서상 과제가 C0060202 에서도 재현된다.
### 3.4 이번에 새로 확인한 문제
**① Fit 이 도면 가장자리를 잘라냄.** 재현 확인.
- ELEVATION 도면에서 Fit 을 하면 도곽 왼쪽이 화면 밖으로 나가고, 오른쪽에는 약 470px 가 비어 있다 (§4.1 캡처).
- 자동 Fit, Fit 버튼, 창 크기 변경 후 Fit 모두 같은 결과다.
- HEAD(미커밋 변경 제외)에서도 똑같다. WIP 회귀가 아니다.
**② AutoCAD 제어코드 `%%%` 등이 변환되지 않음.** `원인 확정`.
- C0060203 에 `-2 %%%`, civil Layout 에 `S=0.0000%%%` 가 그대로 표시된다.
- [`Viewer2D.js:64`](../src/viewer2d/Viewer2D.js) 의 `stripMText` 는 `%%d`, `%%p`, `%%c` 만 처리한다.
**③ 기호만 있는 CP949 문자열이 깨진 채 남음.** `원인 확정`.
- civil 에 `500¡¿350` 이 표시된다. CP949 로 디코딩하면 `500×350` 이다.
- [`fixDwgKoreanText.ts:64-67`](../src/viewer2d/fixDwgKoreanText.ts) 는 디코딩 결과에 한글이 생길 때만 변환을 받아들인다.
**④ e2e 스크립트 고장.** §3.3 참고.
**기타 (경미)**
- Layout 탭 이름이 `Layout (Paper_Space0)` 처럼 내부 블록명으로 나온다. dwg2model 삽도 7개 전부가 그렇다.
- Layout 탭이 많으면 툴바가 줄바꿈되면서 상태바가 속성 패널과 겹친다.
- `parse_dwg_json` 은 acadrust notification 의 개수만 돌려주고 내용은 돌려주지 않는다 (C0060203 78건, ELEVATION 44건).
---
## 4. 처리된 오류 이력
표기: [M] 커밋 메시지 · [D] diff 로 확인 · [Doc] 문서 기록.
Rust 쪽 수정(dwg-wasm, acadrust)은 `hmwebviewer/rust/dwg-wasm` 에 있다.
| # | 시기 | 증상 | 재현 샘플 | 원인 | 조치 | 커밋 | 이번 검증 |
|---|---|---|---|---|---|---|---|
| 1 | 07-30 | 구형 한글 TEXT/MTEXT 깨짐 | civil 외 | CP949 바이트가 Latin-1 로 올라옴 [Doc] | `fixDwgKoreanText.ts` (뷰어) | be8d52f | **`수정 완료·검증됨`** (§4.1-①) |
| 2 | 07-30 | Model 과 Paper 동시 렌더, Layout 비어 보임, Fit/좌표, 글자 거대, VP 순서 (6건) | C0060202 | VIEWPORT 미투영, minTextH 등 [Doc] | `cadSpaces.ts`, Viewer2D (뷰어) | be8d52f | **`수정 완료·검증됨`** (§4.1-②) |
| 3 | 07-30 | Brave/Firefox 에서 OLE BMP 미표시 | — | `<img>` 가 data:image/bmp 미지원 [M] | 순수 JS BMP 디코더 [D] | 963a903 | §8 |
| 4 | 07-30 | OLE2FRAME 이 하드코딩된 좌표에 고정됨 | — | 정답 좌표 하드코딩 [D] | 파서가 준 ul/lr 사용 | 76f3e20 | §8 |
| 5 | 08-03 | Slug 텍스트 cap-height 약 26% 작음 | BasicSample | 3d3e052 의 회귀 [D] | 되돌림 | fb8f7ca | **`수정 완료·검증됨`** (§4.1-④) |
| 6 | 08-03 | 19MB DWG 로딩 시 탭 프리즈 | dwg_18.3mb | wasm 힙 O(n²) [M] | `parse_dwg_json` 스트리밍 | b047dc4 | **`수정 완료·검증됨`** (§4.1-③) |
| 7 | 08-03 | 도면 위아래 거대 회색 원 | dwg_18.3mb | `sample_arc` 각도 swap 등 [Doc] | 손상 호 감지 후 생략 (파서) | b047dc4 | **`수정 완료·검증됨`** (§4.1-③) |
| 8 | 08-04 | 레이어 토글 지연 | dwg_18.3mb | 토글마다 `load()` 재빌드 [M] | setDrawRange, `.visible` 토글 | 3480945 | **`수정 완료·검증됨`** (§4.1-⑦) |
| 9 | 08-04 | 노면/부체도로면(MESH) 미표시 | 대산당진 노면 | MESH 직렬화 누락, read_mesh 100k 상한 [M] | acadrust 패치 + 직렬화 + `_meshSegs` | eec3382, c67a803 | **`수정 완료·검증됨`** (§4.1-⑤) |
| 10 | 08-04 | 모든 선이 1px | 대산당진 노면 | lineWeight 누락, WebGL linewidth 무시 [M] | LineSegments2 버킷 | c67a803 | **`수정 완료·검증됨`** (§4.1-⑥) |
| 11 | 08-04~05 | 선가중치 배율 (8 → 20 → 11 px/mm) | 대산당진 노면 | 배율 [M] | 기본값 11, 옵션 패널 | cf12987, a924b1e, 1f80442 | 굵기 적용 확인 (§4.1-⑥). 배율 일치는 §8 |
| 12 | 08-04 | MPL-2.0 고지 누락 | — | 고지/원문/소스 경로 없음 [M] | NOTICES, `public/licenses/` | ce3859c | **조치 불충분**: 패치가 원본에 적용되지 않음 (§5 A-1) |
| 13 | 08-04 | `vite build` 가 dist2 테스트 파일을 삭제 | — | `emptyOutDir` 기본값 [M] | `--emptyOutDir false` 절차 문서화 | 6f200ef | — |
분리 전 hmwebviewer 에서 처리한 것 (07-10~14, `PROGRESS.md`):
- solid HATCH 구멍, HIDDEN/CENTER 파선 무시
- MAX_TEXTS 3000 상한
- 회전 도곽 viewTwist, 로고 해치, 폴리라인 폭, MTEXT 배경
**미커밋 수정 2건** (`수정 완료·미검증`):
- VIEWPORT 안 SOLID/HATCH 채움에 model→paper 변환 추가 (`_fillXf`/`_bakeFillXf`)
- `_trimmedContentBox` 의 Z 붕괴 수정
### 4.1 수정 전 / 수정 후 / 현재 비교 캡처
**방법**
- 각 버전은 `git archive <커밋>` 으로 풀어서 실행했다. wasm 이 커밋마다 함께 커밋돼 있어서, 그 시점의 파서와 뷰어가 같이 재현된다.
- 계측용으로 두 가지만 덧붙였다. 둘 다 동작에는 영향이 없다.
- `main.ts` 에 `window.__viewer = viewer` 1줄
- vite 캐시 경로 분리
- "현재" 는 오늘의 작업 트리다.
- 로드 판정은 상태바가 `✓ <그 파일명> ·` 으로 바뀌었는지로 했다.
- 확대 화면은 모든 버전에서 같은 WCS 좌표와 화면 폭으로 카메라를 고정했다. 파일럿 캡처로 목표 객체가 화면 중앙에 오는 것을 먼저 확인했다.
- UI 패널은 숨기고 캔버스만 찍었다. 해상도 1280×800, RTX 3060.
- 24회 실행 모두 로드 성공, 콘솔 에러 0건.
**① 한글 깨짐** (`civil.dwg`)
수정 전 버전의 wasm 이 실제로 내보낸 문자열과, 수정 후 화면을 함께 비교했다:
| handle | 수정 전 `95fa6a4` wasm 출력 | 수정 후 `be8d52f` |
|---|---|---|
| 3232403 | `Ãø Á¡` | `측 점` |
| 3232405 | `°èȹ°í` | `계획고` |
| 3232408 | `Áö¹Ý°í` | `지반고` |
| 3240601 | `õÀÇ1Áö±¸ °èȹ¼±` | `천의1지구 계획선` |
| 3241973 | `(»ç¾÷ ÁøÇà ¿¹Á¤)` | `(사업 진행 예정)` |
| 1009 | `°Ç¼³´Ü°è` | `건설단계` |
| 수정 후 `be8d52f` | 현재 |
|---|---|
| ![](images/sample-test-2026-10-19/01-korean-2after.jpg) | ![](images/sample-test-2026-10-19/01-korean-3current.jpg) |
**② Model/Paper 겹침, Layout 비어 보임** (`civil.dwg`, 로드 직후 기본 화면)
| 수정 전 `95fa6a4` | 수정 후 `be8d52f` | 현재 |
|---|---|---|
| ![](images/sample-test-2026-10-19/02-model-paper-1before.jpg) | ![](images/sample-test-2026-10-19/02-model-paper-2after.jpg) | ![](images/sample-test-2026-10-19/02-model-paper-3current.jpg) |
| 점 3개만 보임. Model(x≈600k·162k)과 Paper(원점)를 한꺼번에 Fit 함 | Layout 이 열리고 VIEWPORT 내용이 투영됨 | 같음 (밝은 종이 배경) |
**③ 대용량 로딩 + 거대 회색 원** (`dwg_18.3mb.dwg`, 로드 직후 기본 화면)
| 수정 전 `7a6edd4` | 수정 후 `b047dc4` | 현재 |
|---|---|---|
| ![](images/sample-test-2026-10-19/03-large-hatch-1before.jpg) | ![](images/sample-test-2026-10-19/03-large-hatch-2after.jpg) | ![](images/sample-test-2026-10-19/03-large-hatch-3current.jpg) |
| 로드 **213.8초**. 회색 SOLID HATCH 가 도면 아래쪽 절반을 덮음 | **3.6초**, 회색 채움 없음 | **3.7초** |
**④ Slug 텍스트 크기** (`BasicSample.dwg`, "정면도" h=140 주변 고정 화면)
| 수정 전 `b047dc4` | 수정 후 `fb8f7ca` | 현재 |
|---|---|---|
| ![](images/sample-test-2026-10-19/04-slug-text-1before.jpg) | ![](images/sample-test-2026-10-19/04-slug-text-2after.jpg) | ![](images/sample-test-2026-10-19/04-slug-text-3current.jpg) |
| 글자 높이 **150px** | **202px** | **202px** |
- 자홍색 픽셀 bbox 높이를 쟀다. 비율 150/202 = **0.743** 으로, 커밋 메시지 "3.6 → 2.68 (0.744)" 와 일치한다.
- 주변 선과 치수의 위치는 세 장 모두 같다.
**⑤ SubDMesh(MESH) 미표시** (노면 도면, 연결부 (156542, 479804), 폭 160)
| 수정 전 `6f200ef` | 수정 후 `c67a803` | 현재 |
|---|---|---|
| ![](images/sample-test-2026-10-19/05-mesh-1before.jpg) | ![](images/sample-test-2026-10-19/05-mesh-2after.jpg) | ![](images/sample-test-2026-10-19/05-mesh-3current.jpg) |
| 삼각망 없음 | 도로면·부체도로면 삼각망 표시 | 같음 |
c67a803 에는 선가중치 수정도 함께 들어 있다.
**⑥ 선가중치** (⑤와 같은 화면)
| 수정 전 `eec3382` | 수정 후 `1f80442` (11 px/mm) | 현재 |
|---|---|---|
| ![](images/sample-test-2026-10-19/06-lineweight-1before.jpg) | ![](images/sample-test-2026-10-19/06-lineweight-2after.jpg) | ![](images/sample-test-2026-10-19/06-lineweight-3current.jpg) |
| 0.50mm 도로마킹 포함 모든 선 1px (메쉬 렌더러 이전 버전) | 굵게 표시 | 같음 |
**⑦ 레이어 토글 속도** (`dwg_18.3mb.dwg`, `setHiddenLayers(CF-PATT)` 숨김→표시 3회, 동기 실행 시간)
| | 수정 전 `fb8f7ca` | 수정 후 `3480945` | 현재 |
|---|---|---|---|
| 숨김 ms | 565 / 454 / 512 | 18 / 17 / 13 | 21 / 18 / 13 |
| 표시 ms | 501 / 622 / 609 | 26 / 5 / 6 | 35 / 5 / 5 |
**새로 확인한 문제 (현재 버전)**
| `%%%` 미변환 (C0060203) | CP949 기호 잔존 (civil) | Fit 잘림 (ELEVATION) |
|---|---|---|
| ![](images/sample-test-2026-10-19/n1-percent-3current.jpg) | ![](images/sample-test-2026-10-19/n2-cp949-symbol-3current.jpg) | ![](images/sample-test-2026-10-19/n3-fit-crop-3current.jpg) |
| `-2 %%%` (정답 `-2 %`) | `500¡¿350` (정답 `500×350`) | 왼쪽 잘림, 오른쪽 약 470px 공백 |
---
## 5. 남은 과제
### A. 우선 처리
| # | 과제 | 근거 |
|---|---|---|
| A-1 | **MPL 패치가 실제 소스를 재현하지 못함** (`원인 확정`)<br>- `public/licenses/acadrust-0.4.1-read_mesh.patch` 를 원본 acadrust 0.4.1 crate 에 `git apply --check` 하면 `entities.rs:2350`(read_ole2frame)에서 실패한다.<br>- 고지의 "One file differs", "exact Source Code Form" 문구와 `acadrust-dwg/package.json` 의 "used unmodified" 가 사실과 다르다. | 이번 실행, `THIRD-PARTY-NOTICES.md` |
| A-2 | **Visio 저작권 데이터가 번들됨**<br>- `linData.ts` 의 `ICAD_LIN` 헤더가 "Copyright (c) 1998 by Visio Corporation. All rights reserved" 다.<br>- `linParser.ts:228` 이 import 될 때 등록하므로 배포본에 반드시 들어간다.<br>- IntelliCAD(ITC) 계약이 자사 웹 뷰어로의 외부 배포를 허용하는지 확인이 필요하다. | 이번 확인 |
| A-3 | 자체 `LICENSE` 파일 없음, NanumGothic OFL 원문 미동봉 | §6 |
| A-4 | 자동 판정 테스트 없음, e2e 고장 | §3.3 |
| A-5 | Fit 이 가장자리를 잘라냄 | §3.4-① |
| A-6 | 미커밋 WIP 정리, `src/topProgress.ts` 어디서도 import 되지 않음, 루트 대용량 untracked 파일 | `git status` |
| A-7 | 원본 hmwebviewer 에 Model/Paper 수정 미이식 (`cadSpaces.ts` 없음) | `model-paper-space.md` §9 |
### B. 기능·품질
| # | 과제 | 근거 |
|---|---|---|
| B-1 | 해치 손상 호의 근본 원인 미확인. 현재 수정은 감지 후 생략. `spurious_r` 5배 임계값은 경험값 | `hatch-boundary-arc-artifact.md` §7 |
| B-2 | 파싱 중 메인 스레드 블로킹. 37MB 도면 로드 5.5초 동안 UI 정지. Web Worker 미적용 | 이번 측정 |
| B-3 | 메쉬 레이어에 굵기가 지정되면 fat 버킷 상한(40만) 124.5% 초과 → 버킷 전체 헤어라인 폴백 | `improvement-results` §5-3 |
| B-4 | `%%%` 등 제어코드, CP949 기호 | §3.4-②③ |
| B-5 | `tables.styles` 비어 있음 → 폰트 매핑 불가 | 한글 문서 §8, 이번 e2e |
| B-6 | 한글 처리가 JS 휴리스틱. 932/936/950 미지원 | 한글 문서 §8 |
| B-7 | Layout 탭 캡션이 내부명 | 이번 실행 |
| B-8 | acadrust notification 내용 비노출 | 이번 실행 |
| B-9 | 40만 상한 초과 시 사용자 알림 없음 (`console.warn` 만) | `lineweight-rendering.md` §6 |
| B-10 | wasm32 4GB 한계. 37MB 도면에서 Node RSS 1.37GB, 브라우저 JS heap 782MB | 이번 측정 |
### C. 나중에
- 선가중치: 중첩 ByBlock 미추적, DXF LAYER 코드 370 무시, `celweight` 가 DWG 인덱스 그대로 나감
- MESH: Catmull-Clark(`subdivisionLevel > 0`), POLYFACE/POLYGON MESH, shaded 패스
- VIEWPORT: 솔리드 HATCH 근사, 비직교 VP, TILEMODE/CTAB 휴리스틱
- OLE BMP 가 24/32bpp 만 지원
- `parse_dwg` export 미정리
- CXGLOGO-EX흑백.dwg 의 얇은 원 (hmwebviewer 시절부터)
### D. 배포·문서 정리
- `build:dist2` 스크립트 없음, `dist2/assets` 에 예전 해시 번들 누적, 테스트 도면이 `dist2/samples` 에 섞여 있음
- 문서 상태 표기가 낡음:
- `subdmesh-rendering.md`: "렌더러 미커밋"
- `lineweight-rendering.md`, `improvement-results`: "미푸시"
- 여러 문서: "acadrust 미수정"
- `Viewer2D.d.ts:24`, `Viewer2D.js:1389` 주석은 "Default 20" 인데 실제 기본값은 11
- README 이슈 표에 hatch, large-dwg 문서 누락
---
## 6. 라이브러리 배포 가능성
### 6.1 결론: 조건부 가능
- 파서는 DOM 없이 동작한다. Node 24 에서 `initSync` 로 19개 도면을 파싱해 실측했다.
- Viewer2D 가 DOM 에 닿는 곳은 생성자로 받는 `container` 하나뿐이다.
- 다만 아래 차단 요소를 먼저 풀어야 한다.
### 6.2 차단 요소
| # | 문제 | 위치 | 영향 |
|---|---|---|---|
| L1 | MPL 패치로 소스 재현 불가 | §5 A-1 | 법적 의무 미충족 |
| L2 | Visio 저작권 lin 데이터 | `linData.ts:2` | 재배포 권리 확인 필요 |
| L3 | 자체 `LICENSE`, `package.json` `license` 필드 없음. `Viewer2D.js:3` 에 "Ported from webviewer/…" | 루트 | 사용 권리 불명확 |
| L4 | NanumGothic OFL 원문 미동봉 | `public/licenses/` | 폰트 동봉 시 위반 |
| B1 | `?url` import 가 Vite 전용 | `acadrustParser.ts:6` | Node·webpack·SSR 에서 실패. Vite lib 모드로 빌드하면 wasm 이 base64 로 두 번 인라인됨 (실측 2.37MB) |
| B2 | 확장자 없는 상대 import | `dwgParser.ts:16`, `parseDxf.ts:7`, `Viewer2D.js:16-18` 외 | Node ESM 에서 `ERR_MODULE_NOT_FOUND` (§3.3 과 같은 원인) |
| B3 | 폰트 경로 `/fonts/…` 하드코딩, 전역 싱글톤 | `slugText.ts:32-49`, `Viewer2D.js:127` | 호스트에 그 경로가 없으면 404 |
| B4 | `dispose()` 없음 | `Viewer2D.js:159,164,4527` | 익명 window 리스너와 rAF 루프를 해제할 수 없음. F 키 리스너는 입력란에서도 Fit 실행 |
| B5 | 타입 불완전 | `Viewer2D.d.ts` | `load(result: unknown)`, 일부 메서드 누락, 기본값 주석 20 ≠ 실제 11 |
| B6 | 패키지 설정 없음 | `package.json` | `private: true`, `exports`/`files`/`peerDependencies` 없음 |
| B7 | 소스 정본이 여러 곳 | 이 레포 / `hmwebviewer/src/viewer2d` / `hmwebviewer/packages/dwg-dxf-viewer` | wasm 바이너리 3개가 모두 다름. `Viewer2D.js` 는 hmwebviewer 와 1,217줄 차이 |
### 6.3 권장 구조: 패키지 2개
```
@scope/cad-parser (headless, three 없음) license: "MIT AND MPL-2.0"
initDwgParser({ wasm?: URL | bytes | Module })
parseDwg(bytes) / parseDxf(buf) / parseCad(buf, nameHint?)
listCadSpaces, pickDefaultSpace, isDwg/isDxf, CadParseResult 타입
./worker (선택: 메인 스레드 비블로킹)
@scope/viewer2d (Viewer2D, slugText, linParser) license: "MIT"
peerDependencies: three ^0.185.0 ← peer + external
dependencies: @scope/cad-parser, opentype.js
```
파서 패키지 `package.json` 핵심:
```json
{
"type": "module",
"license": "MIT AND MPL-2.0",
"exports": {
".": { "types": "./dist/index.d.ts", "node": "./dist/node.js", "default": "./dist/browser.js" },
"./worker": "./dist/worker.js",
"./acadrust_dwg_bg.wasm": "./dist/acadrust_dwg_bg.wasm"
},
"files": ["dist", "licenses", "THIRD-PARTY-NOTICES.md", "LICENSE"],
"sideEffects": false
}
```
wasm 로딩:
1. **기본**: glue 의 `new URL('./acadrust_dwg_bg.wasm', import.meta.url)` 패턴을 유지한다.
2. **탈출구**: `init({ wasm })` 로 URL, bytes, Module 을 직접 넘길 수 있게 한다.
3. **Node**: `initSync({ module: readFileSync(...) })`. `scripts/parse-dwg.mjs` 에서 동작을 확인했다.
### 6.4 작업 순서
1. 라이선스 L1~L4 해결
2. 정본 레포 통일, `rust/dwg-wasm` 과 vendored acadrust 이전, `rust-toolchain.toml` 로 툴체인 고정
3. 파서 분리: `?url` 제거, `init({ wasm })` 추가, Node 진입점, `parseCad()`, 상대 import 에 `.js`
4. Viewer2D: 생성자 옵션(`fontUrl`, `keyboard` 등), `dispose()`, `ResizeObserver`, `localStorage` 잔재 제거, 콜백 해제 함수
5. 타입: 수기 `Viewer2D.d.ts` 보완, `CadParseResult` 구체화
6. 빌드: tsup/rollup/tsc 로 ESM, wasm 은 파일로 복사, `opentype.js` 는 default import (Node ESM 에서 named import 실패 실측)
7. 검증: 소비 예제(Vite, webpack 5, Node)를 CI 로 돌리고, §3 의 샘플 20개 e2e 를 회귀 테스트로
### 6.5 유의할 점
- **크기**
| 파일 | raw | brotli |
|---|---|---|
| wasm | 884KB | 301KB |
| 폰트 | 2.05MB | 432KB |
폰트는 번들하지 말고 `fontUrl` 옵션으로 받는다.
- **메모리**: 37MB 도면에서 Node RSS 1.37GB, 브라우저 JS heap 782MB. wasm32 상한은 4GB 다.
- **런타임**: CSP 에 `'wasm-unsafe-eval'` 이 필요하다. `application/wasm` MIME 이 아니면 느린 폴백으로 떨어진다.
- **한글**: `cp949`/`euc-kr` TextDecoder 가 없는 런타임에서는 `fixDwgKoreanText` 가 아무 표시 없이 보정을 건너뛴다.
- **버전 정책**: 두 패키지는 lockstep 으로 올린다. `CadParseResult` 필드 추가는 minor, 삭제나 변경은 major.
- **upstream drift**: README 의 "본가에서 다시 복사" 절차를 따르면 이 레포의 기능(lineweight, 2.5D, Model/Layout)이 지워진다.
- **acadrust 버전**: 상류는 0.6.x 다. 0.4.1 fork 유지 여부를 결정해야 한다.
- **릴리스**: 깨끗한 태그에서만 배포한다.
---
## 7. 재현 방법
```powershell
# 헤드리스 파싱 (1개 파일)
node scripts/parse-dwg.mjs "<file.dwg>" [out.json]
# 기존 e2e (현재는 실패, §3.3)
node e2e_lin_test.cjs
```
배치 파서, 브라우저 e2e, 3단 비교 캡처 스크립트는 세션 임시 폴더에 있다. 레포 반영은 §2-3. 측정 원본은 `images/sample-test-2026-10-19/results.json` 에 있다.
## 8. 검증 불가 목록
| 항목 | 이유 |
|---|---|
| OLE BMP(#3), OLE2FRAME(#4) | OLE2FRAME 엔티티가 있는 샘플이 없음 |
| 선가중치 배율(#11)이 AutoCAD 와 일치하는지 | 같은 위치의 AutoCAD 정답 캡처가 없음 |
| 한글 깨짐(#1) 수정 전 화면 | 같은 커밋에서 고친 '글자 거대' 버그 때문에 판독 불가. 문자열 비교로 대신 검증함 |
| 수정 전 수치와 문서 기록의 차이 (로드 213.8s vs 문서 354s, 토글 약 0.5s vs 커밋 "5초 이상") | 당시 측정 환경과 방법을 알 수 없음 |
| Fit 잘림의 원인 | 재현만 확인, 원인 미확정 |
| H-DICHL06 의 `patternLen`(1) 과 요소 합(1.588) 중 어느 쪽이 AutoCAD 기준인지 | AutoCAD 대조 수단 없음 |
| 문서상 한글 재현 도면 `C0060203-002-굴착및시공순서도.dwg` | 레포에 없음 |
| 7~8월 테스트 당시의 증상 원문 | 세션 기록이 남아 있지 않음 (가장 오래된 기록이 09-16) |