# 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를 함께 읽어 화면 세팅. 어떤 영상이든 동작. - **제약**: ``로 고른 단일 파일은 브라우저 보안상 형제 파일을 못 읽음. - **결정/해결**: **폴더 선택(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). 정확 집계 도구 없어 추정.