# PROGRESS.md — abcvideo 진행 상태 > 이 파일은 각 단계의 진행 상태를 기록합니다. > 에이전트는 작업 시작/완료 시 반드시 이 파일을 업데이트합니다. --- ## 현재 상태 요약 - 마지막 완료 단계: 검증 (VERIFICATION) - 현재 진행 단계: 전체 완료 + 검증 완료 - 블로커: FFmpeg 미설치 (HLS/프레임 기능은 FFmpeg 설치 후 동작) - 검증 결과: VERIFICATION.md 참조 (모든 항목 통과, V3-3 RAF 버그 1건 수정 완료) --- ## 단계별 진행 기록 ### 단계 1: 프로젝트 구조 세팅 - 상태: ✅ 완료 (2026-03-24) - 완료 항목: - 루트 package.json (npm workspaces) - shared/src/types.ts (공유 타입) - server/ 스캐폴드 + tsx dev 환경 - client/ Vite + React 18 + TypeScript + Tailwind - storage/ 디렉토리 구조 - npm install 성공 (514 packages) - server TypeScript 컴파일 에러 없음 - GET /api/health 응답 확인 - 미완료/이슈: - better-sqlite3 ^12.8.0으로 업그레이드 (Node 24 prebuilt binary 문제) - 다음 단계 참고: - server/package.json에 better-sqlite3 ^12.8.0 사용 중 - client Vite dev는 아직 미확인 (브라우저 확인 필요) - server/src/app.ts는 최소 스캐폴드 — 단계 2에서 확장 ### 단계 2: 백엔드 서버 - 상태: ✅ 완료 (2026-03-24) - 완료 항목: - server/src/config.ts — 환경변수 기반 설정 - server/src/services/ffmpeg.ts — FFprobe/FFmpeg spawn 래퍼 (probeVideo, runFFmpeg, getVideoFps 등) - server/src/services/streaming.ts — Range Request 스트리밍 (10MB 청크, 1MB highWaterMark) - server/src/services/storage.ts — better-sqlite3 초기화, Annotation CRUD, 임시파일 정리 - server/src/middleware/security.ts — pathTraversalGuard, validateMimeType, securityHeaders - server/src/routes/stream.ts — GET /api/stream/:videoId - server/src/routes/hls.ts — POST /convert, GET /status, GET /progress(SSE), GET /index.m3u8, GET /:segment - server/src/routes/frame.ts — GET /api/frame/:videoId?time=|frame= - server/src/routes/upload.ts — 청크 업로드 (init/chunk/complete/status), @tus/server 대신 자체 구현 - server/src/routes/meta.ts — GET /api/videos, GET /api/meta/:videoId, DELETE /api/videos/:videoId - server/src/routes/annotations.ts — CRUD + export(vtt/srt/json/csv) - server/src/app.ts — 전체 통합, 헬스체크, 에러 핸들러 - server/.env — 환경변수 파일 - server/tsconfig.json — rootDirs 수정 (shared 타입 접근 허용) - GET /api/health, /api/videos, /api/annotations/:videoId 정상 응답 확인 - 미완료/이슈: - @tus/server 2.3.0이 ESM-only("type":"module")라 CommonJS 서버와 호환 불가 → 자체 청크 업로드 구현으로 대체 - FFmpeg 시스템 PATH 미등록 상태 → HLS 변환/프레임 추출은 FFmpeg 설치 후 동작 - 다음 단계 참고: - 청크 업로드 클라이언트는 POST /api/upload/init → POST /api/upload/chunk (X-Upload-Id, X-Chunk-Index 헤더, application/octet-stream body) → POST /api/upload/complete 순서 사용 - HLS 변환 완료 후 /api/hls/:videoId/index.m3u8 로 플레이리스트 접근 - 서버는 포트 3001에서 실행 중 ### 단계 3: 기본 플레이어 - 상태: ✅ 완료 (2026-03-24) - 완료 항목: - client/src/types/player.ts — VideoSource, PlayerState 타입 - client/src/store/playerStore.ts — Zustand 플레이어 상태 스토어 - client/src/store/annotationStore.ts — 주석 Zustand 스토어 - client/src/hooks/useVideoPlayer.ts — Video.js 8 초기화, loadLocalFile, loadServerStream, switchToHls - client/src/components/player/VideoPlayer.tsx — forwardRef + VideoPlayerHandle API - client/src/components/player/HlsConversionStatus.tsx — SSE 진행률 바 - client/src/components/sidebar/VideoList.tsx — 서버 영상 목록 - client/src/App.tsx — 전체 레이아웃 통합 - npm install: video.js@^8.23.0, @videojs/http-streaming, hls.js@^1.6.15, interactjs@^1.10.27, subsrt-ts@^2.0.3 - TypeScript 에러 없음, 빌드 성공 (dist 1.47MB) - 미완료/이슈: - interact.js 패키지명 오류 → interactjs (하이픈 없음) 으로 설치 - 빌드 경고: chunk > 500kB (video.js 특성상 정상, 운영환경 분할 불필요) - 다음 단계 참고: - VideoPlayer는 forwardRef 사용 — App에서 playerRef.current?.loadServerStream() 호출 - hls.js backBufferLength: 30 설정 완료 - MemoOverlay는 interactjs 사용 (import from 'interactjs') ### 단계 4: 프레임 추출 - 상태: ✅ 완료 (2026-03-24) - 완료 항목: - client/src/utils/frameCapture.ts — 단일 Canvas 재사용, captureFrame, downloadDataUrl - client/src/components/player/FrameCaptureButton.tsx — Shift+S 캡처 버튼 - VideoPlayer에 캡처 통합 (getVideoElement → captureFrame → downloadDataUrl) - 미완료/이슈: 서버 FFmpeg 정확 추출(로컬 파일 정확 추출 시)은 미구현 — Canvas 즉시 미리보기만 구현 - 다음 단계 참고: (없음) ### 단계 5: 단위 이동 - 상태: ✅ 완료 (2026-03-24) - 완료 항목: - client/src/hooks/useFrameStep.ts — requestVideoFrameCallback FPS 감지, stepForward/stepBackward - client/src/hooks/useKeyboard.ts — CLAUDE.md 키보드 단축키 전체 구현 - 프레임/초/장면 단위 이동 완성 (Space/←/→/J/L/,/./[/]/F/M/+/-/Shift+S/Shift+M/0-9) - 미완료/이슈: (없음) - 다음 단계 참고: (없음) ### 단계 6: 텍스트 오버레이 - 상태: ✅ 완료 (2026-03-24) - 완료 항목: - client/src/hooks/useAnnotations.ts — 주석 CRUD API 연동 - client/src/components/overlay/MemoOverlay.tsx — interactjs 드래그, x%/y% 좌표 - client/src/components/sidebar/AnnotationPanel.tsx — 자막/메모 탭, 내보내기(VTT/SRT/JSON/CSV) - client/src/components/AddAnnotationModal.tsx — 주석 추가 모달 - client/src/utils/timecode.ts — secondsToTimecode, timecodeToSeconds, frameToSeconds, secondsToFrame - 미완료/이슈: - WebVTT track 요소를 통한 자막형 렌더링은 미구현 (주석 저장/표시는 완성, track 연동은 단계 7) - 다음 단계 참고: - 자막형 WebVTT track 연동은 단계 7 UI/UX 통합 시 추가 가능 - 내보내기는 /api/annotations/:videoId/export?format=vtt|srt|json|csv 백엔드 API 사용 ### 검증 (VERIFICATION) - 상태: ✅ 완료 (2026-03-24) - 완료 항목: - VERIFICATION.md 작성 및 전 항목 검증 실행 - V1 정적 검증: server/client tsc 에러 없음, 빌드 성공 - V2 서버 API: 헬스체크/영상목록/업로드/주석 CRUD/내보내기/보안 모두 통과 - V3 코드 품질: 10/10 통과 (MemoOverlay RAF 버그 발견 및 수정) - V4 파일 구조: 19개 필수 파일 전부 존재 확인 - 수정 사항: - `VideoPlayerHandle`에 `getVideoElement()` 추가 (VideoPlayer.tsx) - App.tsx RAF 루프 추가 → MemoOverlay `currentTime` 60fps 업데이트 ### 기능 확장: 카메라 파라미터 보정 패널 (2026-03-26) - 상태: ✅ 완료 - 완료 항목: - client/src/utils/geoProjection.ts — CameraParams 인터페이스 추가, projectPoint 시그니처 변경 - yawOffset(number) → params(CameraParams) 로 통합 - Roll 회전 수학 추가 (Rodrigues, fwd 축 기준): right_r = right0·cosR − up0·sinR - 주점 오프셋(cx0, cy0) 핀홀 투영에 적용 - 초점 배율(focalScale) 적용: f_eff = focalLen × focalScale - pxRaw/pyRaw 필드 추가 (클램프 전 원본값, FOV 이탈 감지용) - DEFAULT_CAMERA_PARAMS export - client/src/components/overlay/StationOverlay.tsx — 파라미터 패널 UI 전면 개편 - ParamRow 컴포넌트: 슬라이더 + 직접 수치 입력(Enter/blur 커밋) 양방향 동기화 - 자세 보정 섹션: Yaw(-180~+180°), Pitch(-45~+45°), Roll(-45~+45°) - 내부표정 섹션: f×(0.3~3.0배율), cx₀(-0.5~+0.5), cy₀(-0.5~+0.5) - 현재 유효값 실시간 표시: yaw_eff, pitch_eff, f_eff(mm), hFOV(°) - 측점 Canvas 마커 구현: 다이아몬드 ◆ + 측점명 레이블 + 거리(m) - FOV 이탈 측점 → 가장자리 방향 화살표(drawEdgeArrow) - POI 마커: + 심볼 + 레이블 - 지면 가이드선: 측점 → 화면 하단 점선 - 나침반 HUD: hFOV를 focalScale 반영하여 동적 계산 - 초기화 버튼, 드론 경로 체크박스 - 빌드 성공: tsc + vite build 에러 없음 (2026-03-26) - 발견 사항: - SRT gb_roll=0.0 (전 프레임 고정) → Roll 보정은 이 영상에서 효과 없으나 다른 영상 대비 구조 완성 - 자기 편각(-8°) 보정이 yawOffset 슬라이더의 주된 용도 - focalScale로 실효 FOV를 조정할 수 있어 정밀 보정 가능 - 미완료/이슈: - GCP 클릭 기반 자동 Resection 미구현 (yaw/pitch 자동 계산) - 파라미터 설정값 영속 저장(localStorage) 미구현 ### 기능 확장: 지리정보 오버레이 + 측점 검증 (2026-03-25) - 상태: ✅ 완료 - 완료 항목: - server/src/services/geoMatch.ts — 블렌더 방식 월드 ENU 좌표계 구현 - geoToEnu(): 위경도 → ENU(m), 기준점 위도로 cos(lat) 고정 (전 프레임 일관성) - projectEnu(): 상대 ENU 벡터 + 카메라 회전 → 핀홀 투영 - project3D(): geoToEnu + projectEnu 래퍼, origin 파라미터 지원 - findFramesForPoi / findPoisForFrame — frames[0]를 월드 원점으로 전달 - getDroneFrames() 신규 export - server/src/routes/geo.ts — yawOffset 쿼리 파라미터, GET /api/geo/frames 엔드포인트 - client/src/utils/geoProjection.ts — 서버와 동일한 블렌더 방식 ENU 투영, ref 파라미터 추가 - client/src/components/overlay/StationOverlay.tsx — yaw 보정 슬라이더(-180~+180°), 드론 경로 오버레이, pathFrames[0] 원점 전달 - client/src/components/geo/StationVerify.tsx — 측점 목록 + 클릭 검증 패널 (신규) - client/src/App.tsx — 우측 패널에 "측점" 탭 추가 - server/client 빌드 모두 에러 없음 확인 - 발견 사항: - cos(lat) 보정을 각 프레임 위도가 아닌 기준점(frame 0) 위도로 고정해야 전 구간 일관성 보장 - CSV yaw/pitch/roll = 짐벌(카메라) 방향 (pitch=-29.8°는 짐벌 고정 틸트, roll=0은 짐벌 보정) - frame 1791 기준 158K300: px=70.1% (사용자 관측 ~73%와 일치) - 미완료/이슈: - Z값(표고) 미적용 — 측점 z값이 ~0m이라 수직 오차 존재, 실제 표고 데이터 확보 후 보정 필요 - GCP 캘리브레이션 UI 미구현 (클릭으로 yaw/pitch 오프셋 자동 계산) ### 지리정보 오버레이 — Python advanced_tuner_v2 포팅 (2026-03-30) - 상태: ✅ 완료 - 완료 항목: - geoProjection.ts 재구현: R_b2w=Rz(-yaw)*Rx(pitch)*Ry(roll), R_w2c=R_align@R_b2w.T (Python 동치) - pitch/roll 절대값 → 오프셋 방식 (Python spn_pitch/roll 기본값 0과 동일) - sensorH 기본값 20.25mm (=36×9/16, 16:9 영상 기준, 기존 24mm 수직 스케일 오류 수정) - CameraParams에 offX/offY/offZ 추가 (Python off_x/y/z 동치) - geoMatch.ts: center.csv 224점 로더 추가 (getCenterlinePoints) - GET /api/geo/centerline 엔드포인트 추가 - StationOverlay: center.csv 중심선 빨간 선분 시각화 (Python 방식), Catmull-Rom next6 스플라인 제거 - 파라미터 패널: 위치 보정(offX/Y/Z), 센서 크기(senW/H) 슬라이더 추가 - DEFAULT_CAMERA_PARAMS = Python 기본값 (모든 오프셋 0, focal=24, sensor=36) - 다음 단계 참고: - off_x/y/z 슬라이더로 GPS 위치 오차 보정 필요 - swap_xy 옵션 미구현 (Python에는 있음, 필요시 추가) ### 단계 7: UI/UX 통합 - 상태: ✅ 완료 (2026-03-24) - 완료 항목: - client/src/components/ErrorBoundary.tsx — React 에러 바운더리 (오류 발생 시 복구 UI) - client/src/components/HelpOverlay.tsx — 키보드 단축키 도움말 오버레이 - client/src/main.tsx — ErrorBoundary로 App 래핑 - client/src/hooks/useKeyboard.ts — ? 키 (Shift+Slash) → onToggleHelp 콜백 추가 - client/src/components/player/VideoPlayer.tsx — onToggleHelp prop 수신 및 useKeyboard에 전달 - client/src/App.tsx — showHelp 상태 + HelpOverlay 렌더링 통합 - README.md — 프로젝트 설치/실행/단축키/환경변수 문서 작성 - TypeScript 타입 에러 없음, 프로덕션 빌드 성공 (1,480 kB / gzip 453 kB) - 미완료/이슈: - 빌드 경고: chunk > 500kB (video.js 특성상 정상) - 다음 단계 참고: 전체 7단계 완료