hmwebviewer — 3D 뷰어

Three.js 기반 하이브리드(SSR + CSR) 웹 3D 모델 뷰어. 설계 원본: 3d_viewer_architecture_spec.pdf

지원 포맷: GLB · GLTF · OBJ · FBX · DAE(Collada) · IFC(BIM)  |  로드 경로: SSR + CSR  |  성능: 화면 FPS 표시 + 60fps 미만 시 적응형 품질 강등

문서 갱신: 2026-06-19 — 매 작업 프롬프트 종료 시 이 파일을 함께 업데이트합니다.

1. 개요 / 두 가지 로드 경로

Path A — 서버 에셋 (SSR + CSR + Hydration)

서버가 미리 렌더링한 360° Animated WebP 자리표시자 + HTML/CSS 골조를 먼저 보냄(빠른 체감 로드). 뒤에서 modelLoader.loadModel(url)이 확장자로 디스패치하여 실제 모델을 비동기 로드/디코드. WebGL 준비 and 모델 로드 완료 둘 다 충족 시 WebP 캔버스로 opacity 0.5s 페이드.

트리거: ?model=<url> · 프리뷰: /previews/<base>.webp (포맷 무관)

Path B — 로컬 파일 (Drag & Drop, CSR)

사용자가 .glb .gltf .obj .fbx .dae .ifc 드롭. URL.createObjectURL(file) loadModel 디코드 씬 추가 URL.revokeObjectURL(성공/에러 .finally() 양쪽). 검증: extOf() 확장자.

트리거: 파일 드롭 / 클릭-탐색

두 경로 모두 단일 디스패처 src/viewer/modelLoader.tsloadModel(url, renderer, onProgress)를 거쳐 THREE.Object3D로 정규화 → ThreeDViewer.onLoaded()는 원본 포맷을 알 필요 없음.

2. 지원 파일 포맷

포맷확장자로더비고SSRCSR검증(체감 로드)
glTF (Binary/JSON).glb .gltf GLTFLoader
(+DRACO +KTX2 싱글톤)
Draco 지오메트리 + KTX2/Basis·WebP 텍스처. 기준 경로. PASS 229ms
Wavefront OBJ.obj OBJLoader (지연 청크 8.8KB) 지오메트리 전용. blob: URL에선 외부 .mtl/텍스처 미해결 → 기본 머티리얼. PASS 173ms
Autodesk FBX.fbx FBXLoader (지연 청크 47.8KB) 바이너리(자체 포함). 애니메이션 가능(.animations) — 현재 정적 표시. PASS 158ms
COLLADA.dae ColladaLoader (지연 청크 41KB) XML(DOMParser 필요). 외부 텍스처는 OBJ와 동일 제약. PASS 178ms
IFC (BIM).ifc web-ifc IfcAPI
(단일스레드 wasm, 지연 청크 3.5MB)
StreamAllMeshes BufferGeometry(인터리브 stride-6 pos+normal). 단일스레드라 COOP/COEP 헤더 불필요. geom.delete()+CloseModel로 wasm 메모리 해제. PASS 463ms
wasm Init 포함

검증: 빌드 dist + vite preview에 대해 헤드리스 Chrome으로 포맷별 1개 샘플 로드, 네비게이션→hmw:ready 측정. 전체 <3000ms PASS (tools/perf-smoke.mjs).

3. 기술 스택 (확정)

영역선택비고
렌더러three.js WebGLRenderer (r0.169)WebGPU는 차후 옵션
로더(기준)GLTFLoader + DRACOLoader + KTX2Loader각 1개 싱글톤 — 다중 인스턴스 크래시(#22445)
로더(확장)OBJ / FBX / Collada — three example loaders지연 import() → 별도 Vite 청크(초기 번들 미증가)
로더(IFC)web-ifc IfcAPI (v0.0.77)단일스레드 web-ifc.wasm 자가호스팅(/web-ifc/), SetWasmPath(...,true)
적응형 품질FPS 측정 + pixelRatio 사다리외부 의존성 0 — ui/fps.ts + viewer/adaptiveQuality.ts
지오메트리 압축Draco~73% 축소 검증됨
텍스처 압축WebP(런타임) / KTX2·Basis(고급)KTX2는 별도 CLI 단계(toktx)
빌드Vite + TypeScript (strict)three vendor + 포맷별 로더 청크 분리
디코더 호스팅Vite hashed Draco·Basis asset + public/web-ifcThree.js import로 생성, single-thread IFC WASM만 postinstall 복사
에셋 파이프라인gltf-transform + draco3dtools/preprocess.mjs (GLB 전용)

4. 런타임 파일 구조

src/
  main.ts                 # 진입점 — ThreeDViewer + Dropzone 와이어링, ?model= Path A
  viewer/
    loaders.ts            # getLoaders() 싱글톤 (GLTF+DRACO+KTX2)
    modelLoader.ts        # ★ loadModel() 포맷 디스패치 (glb/gltf/obj/fbx/dae/ifc) → Object3D
    ThreeDViewer.ts       # 핵심 클래스: 렌더러/씬/카메라/OrbitControls/로드/dispose + FPS·적응형 와이어링
    adaptiveQuality.ts    # ★ pixelRatio 사다리 + 히스테리시스 (60fps↓ 강등)
    hydration.ts          # createHydrationGate() — 양쪽 마크 시 페이드
  dnd/dropzone.ts         # HTML5 DnD + 클릭탐색 + extOf 검증 → loadLocalFile
  ui/
    progress.ts           # showProgress/hideProgress/setProgress/setStatus
    fps.ts                # ★ 화면 FPS 오버레이 (EMA, 색상코딩)
public/
  web-ifc/                # ★ web-ifc.wasm (단일스레드, 자동 복사)
  samples/                # Box/Duck/Avocado/Duck.optimized + Cube.obj/dae/fbx/ifc
  previews/               # Duck.webp + Cube.{obj,dae,fbx,ifc}.webp (24프레임 360°)
tools/
  preprocess.mjs          # gltf-transform Draco+WebP 압축 (GLB 전용)
  copy-decoders.mjs       # postinstall: single-thread web-ifc WASM 복사
  prerender.mjs           # 360° 턴테이블 → Animated WebP (포맷 무관)
  perf-smoke.mjs          # 헤드리스 체감 로드 측정 (<3s)
  adaptive-smoke.mjs      # ★ CPU 스로틀로 60fps↓ 강등 실증

5. 멀티에이전트 + 다이나믹 워크플로우

에이전트가 PLAN.md + PROGRESS.md를 매 시작 시 읽어 다음 작업을 결정. 병렬 안전(파일 스코프 분리).

Phase 6/7은 다이나믹 Workflow multiformat-viewer로 구현 (7 에이전트, 296k 토큰): Research(3 병렬·읽기전용: web-ifc API / 적응형 성능 / three 로더 형태) Core(viewer-core) Enhance(FPS ‖ 에셋) Verify(빌드 green). git 미사용 환경이라 worktree 격리 대신 병렬 웨이브별 파일 스코프 분리로 충돌 방지.

에이전트

이름역할
task-leadPLAN/PROGRESS 읽고 다음 작업 분배
viewer-coresrc/viewer/* 씬·로더·하이드레이션·FPS
dnd-handlersrc/dnd/* 드래그드롭·검증
asset-pipelinetools/* 압축·프리렌더·샘플
hydrationSSR 자리표시자→캔버스 페이드
reviewer누수/에러/성능 감사(읽기 위주)

명령 / 스킬 / 훅

명령: /bootstrap /next-task /report /optimize-asset /verify-viewer

스킬: threejs-viewer(로드 패턴), task-graph(조정 프로토콜)

훅: session-start.sh(PLAN/PROGRESS 읽기 알림), progress-nudge.sh(src 편집 시 알림)

CLAUDE.md가 두 파일 선독을 강제 + 에이전트/스킬/명령 매핑표 + 세션 런북 포함.

6. 계획 진행 상태 (PLAN.md)

Phase범위상태
P0 BootstrapVite/TS 셋업, 디코더, 기본 씬done · 빌드 검증
P1 Core 뷰어싱글톤 로더, loadServer/Local, OrbitControls, 프레이밍done · 빌드 검증
P2 Drag & Drop드롭존 UI, 검증, loadLocalFile 와이어링done · 빌드 검증
P3 HydrationWebP 자리표시자, 게이트, UI 토글done
P4 에셋 파이프라인preprocess.mjs, 샘플셋, 프리렌더P4-1/2/3 done
P5 Hardening누수/에러 감사, 퍼포먼스, 리뷰P5-1/2/3/4 done 0 critical
P6 멀티포맷 로더OBJ/FBX/DAE/IFC, SSR+CSR, modelLoader 디스패치, 샘플+프리뷰P6-1..5 done 6포맷 검증
P7 FPS + 적응형 품질화면 FPS, pixelRatio 사다리, animate 와이어링P7-1/2/3 done 강등 실증

7. FPS 표시 + 적응형 품질 (<60fps → 최적화)

참고: CPU 스로틀 하에선 병목이 JS/지오메트리(필레이트 아님)라 pixelRatio 강등의 FPS 회복폭이 작음(4→7) — 메커니즘은 정상 발화하나, 이 노브는 GPU 필레이트 병목 씬에서 효과가 큼.

8. 현재 구현 상태

9. 실행 명령

# 개발 서버 (HMR)
npm run dev            # → http://127.0.0.1:3333

# 프로덕션 빌드 + 미리보기
npm run build         # tsc --noEmit && vite build → dist/
npm run preview       # → http://127.0.0.1:4173

# 사용
#  CSR(Path B): .glb .gltf .obj .fbx .dae .ifc 를 드롭존에 드롭 / 클릭 선택
#  SSR(Path A): http://127.0.0.1:4173/?model=/samples/Cube.ifc

# 검증/도구 (Git-Bash는 MSYS_NO_PATHCONV=1 접두 필요)
node tools/perf-smoke.mjs                                   # 포맷별 체감 로드 <3s
node tools/adaptive-smoke.mjs --throttle 6 --seconds 30    # 60fps↓ 강등 실증
node tools/preprocess.mjs <file.glb>                        # Draco+WebP 압축(GLB)
node tools/prerender.mjs                                    # 360° WebP 프리뷰 생성

10. 다음 단계 (선택)


작업 로그: PROGRESS.md · 작업 그래프: PLAN.md · 지침: CLAUDE.md