- 텍스트(측점/POI) 전 프레임 사전 계산 Map (requestIdleCallback 백그라운드) - 드론 데이터 이동 평균 스무딩 (smoothFrame ±N프레임) - 30fps→60fps 프레임 간 선형 보간 (performance.now() 기반) - EMA(지수이동평균) 표시 위치 스무딩 (α=0.01 기본값) - 글씨 2배 크기, bold, strokeText 테두리, 배경 박스 제거 - 카메라 파라미터 패널에 smooth/EMA α 슬라이더 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11 KiB
11 KiB
PLAN.md — abcvideo 구현 계획
이 파일은 전체 구현 계획과 각 단계별 세부 태스크를 정의합니다. 에이전트는 작업 시작 전 반드시 이 파일을 읽고, 자신이 담당할 단계를 확인합니다.
단계 1: 프로젝트 구조 세팅
목표
모노레포 구조 생성, TypeScript/빌드 설정, 개발 환경 구축
세부 태스크
- 루트
package.json(npm workspaces: client, server, shared) client/— Vite + React 18 + TypeScript 초기화server/— TypeScript + ts-node/tsx 설정shared/— 공유 타입 패키지 설정- Tailwind CSS 설치 및 설정 (client)
- ESLint + Prettier 설정 (루트)
tsconfig.json— 루트/client/server/shared 각각 설정 (경로 별칭 포함)storage/디렉토리 구조 생성 +.gitignore설정server/.env.example작성- 개발 서버 동시 실행 스크립트 (
concurrently또는 npm scripts) - 빈 앱 기동 확인 (client dev + server dev 동시 실행)
산출물 (완료 기준)
npm install성공npm run dev로 client(5173) + server(3001) 동시 기동- TypeScript 컴파일 에러 없음
- 빈 React 앱이 브라우저에 표시됨
다음 단계 의존성
- 단계 2, 3 모두 이 단계 완료 후 시작 가능
단계 2: 백엔드 서버
목표
Express 서버 + Range Request 스트리밍 + FFmpeg spawn 래퍼 + tus 업로드 + HLS 변환 + SSE 진행률
세부 태스크
2-1. 서버 기본 골격
- Express 앱 생성 (
server/src/app.ts) - CORS 미들웨어 설정 (개발: localhost:5173 허용)
- 보안 미들웨어: Path traversal 방어 (
middleware/security.ts) - 에러 핸들링 미들웨어
- 환경변수 로드 (dotenv +
server/.env) storage/하위 디렉토리 자동 생성 로직
2-2. Range Request 스트리밍
routes/stream.ts— GET /api/stream/:videoIdservices/streaming.ts— Range 파싱, createReadStream, 206 응답- Accept-Ranges 헤더, Content-Range 헤더 정확한 구현
- highWaterMark 1MB, 청크 크기 10MB 제한
- 테스트: curl로 Range Request 동작 확인
2-3. FFmpeg spawn 래퍼
services/ffmpeg.ts— runFFmpeg(), runFFprobe() 함수- FFmpeg 설치 확인 로직 (서버 시작 시)
- stderr 진행률 파싱 (
time=HH:MM:SS.ms패턴) - 코덱 감지: FFprobe로 H.264 여부 확인 →
-c copyvs 트랜스코딩 분기 - 프레임 추출:
-accurate_seek -ss {time} -i {file} -frames:v 1
2-4. HLS 변환
routes/hls.ts— POST /api/hls/:videoId/convert, GET 플레이리스트/세그먼트- 변환 작업 관리 (Map 기반 인메모리 상태: idle/converting/done/error)
- H.264 →
-c copy(빠른 리먹스), 비-H.264 → 트랜스코딩 - 세그먼트 6초, 키프레임 2초 간격
- SSE 진행률 엔드포인트: GET /api/hls/:videoId/progress
2-5. tus 업로드
@tus/server+@tus/file-store설정routes/upload.ts— /api/upload 경로에 tus 서버 마운트- 업로드 완료 훅:
onUploadFinish에서 파일 이동 + FFprobe 메타데이터 추출 - 업로드 크기 제한: MAX_UPLOAD_SIZE (기본 20GB)
- MIME 타입 + FFprobe 실제 코덱 검증
2-6. 메타데이터 & 영상 관리
routes/meta.ts— GET /api/meta/:videoId (FFprobe 기반)- GET /api/videos — 업로드된 영상 목록
- DELETE /api/videos/:videoId — 원본+HLS+프레임+썸네일+DB 일괄 삭제
2-7. SQLite + 주석 API
db/schema.sql— annotations 테이블 스키마- better-sqlite3 초기화 (
services/storage.ts) routes/annotations.ts— CRUD + 내보내기 (VTT, SRT, JSON, CSV)
2-8. 파일 정리
- 서버 시작 시 24시간 이상 된 임시 파일 정리
- 주기적 정리 (setInterval, 1시간마다)
- 디스크 잔여 공간 체크 (check-disk-space, 10GB 미만 시 로그 경고)
산출물 (완료 기준)
- 서버 기동 성공 (FFmpeg 감지 로그 출력)
- curl로 Range Request 스트리밍 동작 확인
- curl로 영상 업로드 (tus) 성공
- HLS 변환 시작/진행률/완료 확인
- 주석 CRUD API 동작 확인
다음 단계 의존성
- 단계 3 (기본 플레이어)은 최소 2-1, 2-2 완료 시 시작 가능
- 단계 4 (프레임 추출)은 2-3 완료 필요
- 단계 6 (텍스트 오버레이)은 2-7 완료 필요
단계 3: 기본 플레이어
목표
Video.js + 이중 경로 재생 (로컬 File API + 서버 Range Request/HLS) + 기본 UI
세부 태스크
3-1. Video.js 통합
- Video.js 8.23.x + 타입 정의 설치
components/player/VideoPlayer.tsx— ref + useEffect 패턴hooks/useVideoPlayer.ts— 초기화/제어/이벤트 추상화- Video.js 옵션: fluid, responsive, playbackRates 설정
- 전체화면: 컨테이너 div 기준 fullscreen (오버레이 유지)
3-2. 서버 파일 재생
- 서버 영상 목록 조회 UI
- Range Request URL로 즉시 재생
- HLS 변환 트리거 + SSE 진행률 표시
- HLS 준비 완료 시 hls.js로 소스 전환
3-3. 로컬 파일 재생
- 파일 드래그앤드롭 / 파일 선택 UI
URL.createObjectURL()→ Video.js src 설정- 재생 종료/파일 변경 시
URL.revokeObjectURL()호출
3-4. hls.js 설정
- hls.js 1.6.x 설치 및 Video.js VHS와의 역할 분담 결정
- backBufferLength: 30, maxBufferSize: 60MB 등 필수 설정 적용
- 에러 복구 로직 (QuotaExceededError 대응)
3-5. 기본 UI 레이아웃
- 메인 레이아웃: 플레이어 영역 + 사이드 패널 (영상 목록/주석)
- 반응형 기본 구조 (Tailwind)
- Zustand 스토어 기본 구조 (현재 영상, 재생 상태, 소스 타입)
산출물 (완료 기준)
- 로컬 mp4 파일 드래그앤드롭 → 즉시 재생
- 서버 영상 선택 → Range Request로 재생
- HLS 변환 완료 후 HLS로 전환 재생
- 전체화면 동작 확인
다음 단계 의존성
- 단계 4, 5, 6 모두 이 단계 완료 후 시작 가능
단계 4: 프레임 추출
목표
Canvas 즉시 미리보기 + 서버 FFmpeg API를 통한 정확한 프레임 추출
세부 태스크
utils/frameCapture.ts— Canvas 기반 현재 프레임 캡처 (로컬 파일용 즉시 미리보기)- 단일 Canvas 재사용 패턴 적용
routes/frame.ts연동 — 서버 FFmpeg 프레임 추출 요청- 프레임 추출 결과 표시 UI (모달 또는 패널)
- 프레임 이미지 다운로드 기능
- Shift+S 단축키 → 현재 프레임 캡처
산출물 (완료 기준)
- 일시정지 상태에서 Shift+S → 프레임 이미지 표시 + 다운로드
- 서버 영상: FFmpeg 정확 추출
- 로컬 파일: Canvas 즉시 캡처
다음 단계 의존성
- 단계 5에서 FPS 감지 로직과 연계
단계 5: 단위 이동
목표
프레임/초/장면 단위 seek + requestVideoFrameCallback FPS 감지 + 키보드 단축키
세부 태스크
hooks/useFrameStep.ts— requestVideoFrameCallback으로 FPS 동적 감지 (fallback 30fps)- 프레임 이동:
,(이전) /.(다음) — 일시정지 상태에서만 - 초 이동: ←/→(5초), J/L(10초)
- 장면 이동:
[/]— 키프레임 기반 또는 일정 간격(30초) 이동 - 10% 단위 탐색: 0~9 키
- 재생 속도: +/- 키 (0.25x ~ 4x)
- 전체화면 토글: F 키
- 음소거 토글: M 키
- 키보드 이벤트: 플레이어 포커스 시에만 활성, 텍스트 입력 시 비활성
- 현재 프레임 번호 / 타임코드 표시 UI
산출물 (완료 기준)
- 모든 키보드 단축키 동작 확인
- 프레임 단위 이동 시 타임코드 정확 갱신
- FPS 감지 로그 출력
다음 단계 의존성
- 없음 (단계 6과 병렬 진행 가능)
단계 6: 텍스트 오버레이
목표
자막형(WebVTT track) + 메모형(interact.js DOM 오버레이) + 내보내기(subsrt)
세부 태스크
6-1. 자막형
- WebVTT
<track kind="subtitles">연동 - 자막 추가/편집 UI (시작 시간, 종료 시간, 텍스트)
- 서버 주석 API 연동 (type: "subtitle")
- VTT/SRT 내보내기 (subsrt 라이브러리)
6-2. 메모형
components/overlay/MemoOverlay.tsx— DOM 오버레이 컨테이너- interact.js 드래그/리사이즈 연동
- 좌표 정규화: px → % 변환/저장
- 메모 추가 UI: Shift+M → 현재 시점에 메모 생성
- 메모 편집/삭제 UI
- 표시 로직: requestAnimationFrame 루프 + 이진 탐색
- 서버 주석 API 연동 (type: "memo")
- JSON/CSV 내보내기
6-3. 주석 관리 패널
hooks/useAnnotations.ts— 주석 CRUD + 서버 동기화- 사이드 패널: 시간순 주석 목록, 클릭 시 해당 시점으로 이동
- 타임라인 위 주석 마커 표시
산출물 (완료 기준)
- 자막 추가 → 영상 재생 시 하단에 표시
- 메모 추가 → 드래그로 위치 조정 가능
- VTT/SRT/JSON/CSV 내보내기 동작 확인
다음 단계 의존성
- 없음 (단계 5와 병렬 진행 가능)
단계 7: UI/UX 통합
목표
전체 기능 통합, UI 다듬기, 썸네일 미리보기, 디스크 관리
세부 태스크
- 전체 레이아웃 정리 (플레이어 + 컨트롤 + 사이드패널 통합)
- 탐색바 썸네일 미리보기 (서버 FFmpeg 스프라이트 + VTT)
- HLS 변환 진행률 UI (프로그레스 바 + 상태 텍스트)
- 영상 목록 UI 개선 (업로드 상태, HLS 변환 상태, 용량)
- 디스크 사용량 표시 + 경고
- 로딩/에러 상태 처리 (스켈레톤, 에러 바운더리)
- 키보드 단축키 도움말 오버레이 (? 키)
- 전체 기능 통합 테스트
- README.md 작성 (설치, 실행, 사용법)
산출물 (완료 기준)
- 전체 기능이 하나의 UI에서 동작
- 서버 파일 + 로컬 파일 모두 테스트 완료
- README.md로 프로젝트 세팅/실행 가능
다음 단계 의존성
- 모든 이전 단계 완료 필요
단계 의존성 다이어그램
단계 1 (프로젝트 구조)
├── 단계 2 (백엔드 서버)
│ ├── 단계 4 (프레임 추출) ──┐
│ └── 단계 6-자막 (주석 API) │
└── 단계 3 (기본 플레이어) │
├── 단계 4 (프레임 추출) ──┤
├── 단계 5 (단위 이동) ────┤ → 단계 7 (UI/UX 통합)
└── 단계 6 (텍스트 오버레이)┘
※ 단계 5와 6은 병렬 진행 가능