# DefVideo — 이슈별 해결 정리

- **날짜**: 2026-06-18
- **작성 시각**: 14:00 KST
- **범위**: 폴더-구동 데이터화, RouteInfo 디자인 이식, StationBar 표시/레이아웃, 재생 커서 매끄러움
- **공통 원칙**: 모든 표시 데이터는 선택한 폴더에서 읽는다(하드코딩 0). 폴더 CSV에 없는 정보는 `<영상명>.route.json`. mock/dead 코드는 삭제하지 않고 보존.

---

## 1. 환경: Node 12 → 실행 실패 (`Unexpected token '?'`)
- **증상**: `npm run dev` 시 `??`(nullish) SyntaxError.
- **원인**: 시스템 Node 12. 프로젝트는 Node 20+ 필요(Vite/better-sqlite3 등).
- **해결**: nvm으로 Node 20 설치, `.nvmrc`(`20`) 추가, `node_modules` 재설치.

## 2. 환경: PM2 `errored` / `ffprobe ENOENT`
- **원인①**: FFmpeg 미설치 → 서버가 기동 직후 죽음.
- **원인②(버그)**: `checkFFmpegInstalled`가 `ffprobe -version`의 **텍스트** 출력을 `JSON.parse` → 항상 실패해 "FFmpeg not found" 오탐.
- **해결**: `apt install ffmpeg`. 감지 함수를 JSON 파싱 대신 **exit code 0 확인**으로 수정(server/src/services/ffmpeg.ts). ecosystem.config.js는 `__dirname` 기준 상대경로 + PATH 명령어로 정리.

## 3. 한글 파일명 깨짐
- **원인**: 파일명이 CP949(EUC-KR) 인코딩(Windows 한글). 시스템 locale UTF-8이라 `???`.
- **해결**: `iconv`로 파일명 일괄 UTF-8 변환(내용은 멀쩡, 파일명만).

## 4. 클라이언트 빌드 실패 (`manualChunks is not a function`)
- **원인**: Vite 8(rolldown)은 `manualChunks`를 **함수 형식**만 허용. 기존은 객체.
- **해결**: `client/vite.config.ts`의 `manualChunks` 객체 → 함수로 변경.

---

## 5. 폴더 선택 → 연관 데이터 자동 로드
- **요구**: "파일 선택"으로 영상 고르면 같은 폴더+`building/`의 측점·POI·center·srt를 함께 읽어 화면 세팅. 어떤 영상이든 동작.
- **제약**: `<input type=file>`로 고른 단일 파일은 브라우저 보안상 형제 파일을 못 읽음.
- **결정/해결**: **폴더 선택(webkitdirectory) + 클라이언트 파싱** 방식 채택.
  - `client/src/utils/geoData.ts`: 폴더 파일들에서 드론CSV·측점·POI·center·route.json 파싱(UTF-8/EUC-KR 처리).
  - `client/src/store/geoStore.ts`: 파싱 결과(frames/stations/pois/centerline/routeMeta/loaded) 보관.
  - 영상은 `URL.createObjectURL`로 재생. **서버 업로드/저장 없음.**

## 6. 측점 패널(StationBar) 안 보임
- **원인**: 리팩터링 시 StationBar가 누락되어 여전히 서버 `/api/geo/*`를 직접 fetch → 폴더 데이터를 못 읽고, 서버 없으면 빈 화면.
- **해결**: StationBar를 다른 컴포넌트처럼 **geoStore 구독**으로 변경(서버 fetch 제거). 클라이언트 전체 live `/api/geo` fetch 0건 확인.

## 7. "폴더 선택 시 업로드되나?"
- **답**: 아니오. webkitdirectory의 **브라우저 경고 문구**일 뿐 전송 없음. 코드에 fetch/POST/FormData 0건, 전부 브라우저 메모리 파싱.

## 8. UI 정리: "파일 선택" 제거 / 도구·좌우 패널 숨김
- "파일 선택" 버튼 제거, **가운데 반투명 "폴더 선택"** 버튼 추가(`VideoPlayer.tsx`).
- 하단 도구 패널: `SHOW_TOOLBAR=false`로 **UI 숨김(코드 보존)**.
- 좌·우 패널: `SHOW_LEFT_PANEL`/`SHOW_RIGHT_PANEL=false`로 **UI 숨김(코드 보존)**.

## 9. StationBar 완전 폴더-구동화 (하드코딩/mock 제거)
- **원인**: 노선명·터미널명(신탄진/대전)·구조물·이정이 mock(`mocks/route.ts` ROUTE_LEGS, Timeline 하드코딩, segments.ts)에서 나옴.
- **해결**:
  - `<영상명>.route.json`(routeInfo + structures) 도입 + 샘플 생성. geoStore `routeMeta`.
  - 커서 이정 배지: mock `mileageAtPx` 제거 → 폴더 데이터(드론 GPS 투영 체이니지).
  - 터미널명: route.json → 폴백 첫/끝 측점 title. Timeline 하드코딩 제거(props).
  - 구조물: route.json `structures`(없으면 POI category 폴백).
  - RoutePanel 양끝 라벨도 route.json 이름.

## 10. 좌상단 노선정보 배너 (videoplayer 디자인 이식)
- **요구**: 좌상단에 방향/노선명/연장/소요 배너.
- **과정**: 이모지 버전 → "느낌이 다름" → **videoplayer `RouteInfo` 1:1 이식**(배경이미지 `title-panel-bg@2x.png` + 절대좌표 + Noto Sans KR) → "크기 차이" → **영상 폭/1920 비율 스케일** 적용.
- **데이터**: 연장=route.json 우선→측점 구간 계산, 소요=route.json 우선→실제 영상 길이, 방향·노선명=route.json. (`RouteInfoOverlay.tsx`, `RouteInfo.module.css`)

## 11. 텍스트 배율 / 화면 채움
- **배율 질문**: 퍼블리싱(px+transform)은 텍스트 줌 무반응, DefVideo(Tailwind rem)는 반응. 사이니지엔 고정+화면비례 권장.
- **꽉 채움**: Video.js `fluid`→`fill`, `.vjs-fill/.vjs-tech { object-fit: cover }`, 오버레이 하단 앵커 → 검은 여백/하단 잘림 해결(`VideoPlayer.tsx`, `useVideoPlayer.ts`, `index.css`).

---

## 12. 커서 이정 10m 단위 표시
- **원인**: 배지가 최근접 측점 km(100m 양자화). `formatMileage`도 100m 반올림.
- **해결**: 연속 체이니지(`chain`) + `formatMileage10`(10m). 바 라벨은 **턴 지점만 10m**, 시·종점 등 기본은 100m.

## 13. 초반 감소구간이 주황(증가)으로 표시
- **원인**: 구간 방향 계산이 시작 방향을 `dir=1`(증가)로 가정.
- **해결**: 첫 유의미 이동(±100m)으로 **시작 방향을 실데이터 판정** → 감소면 하늘색.

## 14. 시설물(구조물) 표시 = videoplayer 아이콘
- **요구**: 텍스트만 → videoplayer 3-slice 아이콘으로, 재생바 위에 겹쳐, 통과 시 색 변경.
- **해결**:
  - DefVideo에 이미 있던 `RouteSegment`(3-slice PNG, `public/assets/route-segment/`) 사용.
  - 아이콘을 **재생바 위 레이어(z-index)** 에 배치(top≈바 중앙).
  - **원본 치수 적용**: 교량 22×16/cap6/top32.5, 터널 24×14/cap10/top33.75.
  - 라벨 폰트 **Noto Sans KR** 추가(`index.html`) — 글자 스타일 원본 일치.
  - 커서가 아이콘 **좌측 끝에 닿는 순간** `passed`로 전환 → **아이콘+라벨 색 동시 변경**(neutral→accent).

---

## 15. 재생 커서가 끊김/물결/흔들림 (핵심 성능 이슈)
단계적으로 진단·수정:

1. **끊김(250ms 점프)**: `timeupdate`(250ms)로만 갱신 → rAF 도입.
2. **물결(출렁임)**: rAF 60fps로 `currentTime()`을 읽는데 미디어 시계는 29.97fps → 같은값→점프 반복. `requestVideoFrameCallback` 시도(여전히 프레임 단위 계단).
3. **흔들림 잔존(최종 해결)**: 원인 = **4K HEVC 디코딩 + 60fps React 리렌더 경쟁 + 보간 역방향 보정**.
   - **단조(monotonic) 보간**: 벽시계 기반 보간, 작은 역행 무시(흔들림 제거), 뒤처짐/시크만 재동기화(`VideoPlayer.tsx`).
   - **커서/진행바를 React에서 분리**: 라이브 시간(`smoothTimeRef`)을 StationBar가 rAF로 읽어 **CSS 변수(`--cursor-x`,`--pos-px`)만 직접 갱신**. React 상태는 배지/색용으로 ~10fps throttle.
   - 커서 `left`→**`transform: translateX`**(GPU 합성, reflow 제거) + `will-change`. 진행바 폭은 CSS 변수 `calc()`.
   - 결과: 합성 스레드에서 부드럽게 이동, 4K 디코딩 부하와 분리. **해결 확인됨.**
- **참고**: 비교 대상 videoplayer는 실제 영상이 없는 순수 애니메이션(17px/s)이라 본래 매끄러움. DefVideo는 실영상 동기라 위 최적화가 필요했음.

---

## 사용자 목적 적합성 확인
- "접속자가 자기 영상 폴더를 올려 재생" → **클라이언트 방식이라 사용자별 독립·무업로드로 적합.**
- 실전 점검: ① **HEVC 코덱 호환성(최우선)** ② 폴더 파일명 규칙 ③ CSV 인코딩.

## 토큰 사용량(추정)
- 본 세션 누적 ≈ **600k+ tokens** (오케스트레이터 + 서브에이전트 Explore×2/DEV×1). 정확 집계 도구 없어 추정.
