영상 파이프라인
스포클립 서버의 핵심 인프라 - 체육관 영상이 사용자에게 도달하기까지의 전체 흐름
문서 정보
- 작성일: 2026-03-02
- 최종 업데이트: 2026-03-02
- 버전: v1.0.0
- 관련 코드:
src/modules/cloudflare/raw-video-webhook/,src/modules/recording/,src/schedulers/my-kok-pipeline/
이 문서의 위치
스포클립 서버의 모든 기능은 이 영상 파이프라인 위에서 동작합니다.
영상 파이프라인 (이 문서)
↓
예약 & 영상 → 일정 → 콕 → 마이콕 → 활동 보상 → ...
영상이 올라오지 않으면 예약, 시청, 콕, 보상 등 어떤 기능도 작동하지 않습니다. 이 문서는 그 근간이 되는 파이프라인을 설명합니다.
전체 파이프라인 개요
파이프라인 4단계
| 단계 | 설명 | 상세 문서 |
|---|---|---|
| 1. 업로드 | 체육관 로컬 PC가 카메라 영상을 10분 단위로 R2에 업로드 | UPLOAD-FLOW.md |
| 2. Webhook 처리 | R2/Stream 이벤트를 서버가 수신하여 Video 엔티티 생성 | WEBHOOK-FLOW.md |
| 3. 녹화 예약 | 사용자의 WebSocket 예약과 영상 매칭 | RECORDING-FLOW.md |
| 4. 배치 클립 | 만료 영상을 GPU VM에서 클립으로 변환 (MyKok) | BATCH-FLOW.md |
핵심 아키텍처 특성
체육관별 Cloudflare 계정
각 체육관은 독립된 Cloudflare 계정을 보유합니다. R2 버킷, Stream, CF Worker가 모두 체육관 단위로 분리되어 있습니다.
[체육관 A CF 계정] R2 + Stream + Worker → webhook → Spoclip Server
[체육관 B CF 계정] R2 + Stream + Worker → webhook → Spoclip Server
[체육관 C CF 계정] R2 + Stream + Worker → webhook → Spoclip Server
체육관별 크레덴셜(CF Account ID, R2 API Key/Secret, Webhook Secret)은 서버 환경변수가 아닌 gym_media_configs 테이블에 암호화되어 저장됩니다.
gym_media_configs:
- gymId, provider: 'CLOUDFLARE'
- accessKey: CF API Token (암호화)
- secretKey: CF Account ID (암호화)
- providerConfig: { r2: { apiKey, apiSecret }, webhookSecret } (암호화)
Webhook 기반 비동기 처리
체육관 PC는 서버와 직접 통신하지 않습니다. R2 업로드 완료 시 CF Worker가 webhook을 전송하고, 서버는 이를 비동기로 처리합니다.
체육관 PC → R2 (직접 업로드, 서버 미경유)
↓
CF Worker (R2 Event Notification)
↓
Spoclip Server (webhook 수신)
Passthrough 메타데이터
모든 영상에는 출처를 식별하는 메타데이터가 포함됩니다.
| 필드 | 설명 | 예시 |
|---|---|---|
gymCode | 체육관 식별자 | GYM-001 |
courtCode | 코트 식별자 | COURT-A |
cameraCode | 카메라 식별자 | CAM-FRONT |
date | 촬영 날짜 | 2026-03-01 |
hour | 시간 (0-23) | 14 |
timeSlot | 10분 구간 (0-5) | 3 (14:30~14:40) |
분산 락과 캐싱
동일 시간대에 여러 카메라가 동시에 영상을 업로드하므로, Redis 분산 락으로 데이터 일관성을 보장합니다. Context 조회 결과는 10분간 캐싱하여 DB 부하를 줄입니다.
영상의 생명주기
| 상태 | Video.metadata 내 상태 | 설명 |
|---|---|---|
| 업로드 | R2 uploadStatus = UPLOADED | R2에 원본 저장 완료 |
| 스트리밍 준비 | Stream uploadStatus = READY | HLS 트랜스코딩 완료, 재생 가능 |
| 시청 가능 | totalPlayableCount > 0 | 사용자가 앱에서 시청 가능 |
| 만료 | expiresAt < now | MyKok 배치 대상 |
| 클립 생성 | MyKok.videoState = CLIPPED | GPU VM에서 클립 추출 완료 |
관련 시스템
| 시스템 | 역할 |
|---|---|
| Cloudflare R2 | 원본 영상 저장소 (체육관별 계정) |
| Cloudflare Stream | HLS 트랜스코딩 (체육관별 계정) |
| Cloudflare Worker | R2 Event → Spoclip Server webhook 전달 |
| Redis | 분산 락, Context 캐싱 |
| PostgreSQL | Video, Recording, gym_media_configs 엔티티 |
| Vultr | MyKok 배치용 GPU VM 프로비저닝 |
| MongoDB | 배치 작업 상태 (vm_assignments) |
관련 문서
- 소개
- 도메인 맵
- 업로드 흐름 - 체육관 PC → R2
- Webhook 처리 - R2/Stream Webhook
- 녹화 예약 - WebSocket 예약 및 영상 매칭
- 배치 클립 생성 - MyKok 파이프라인
변경 이력
| 버전 | 날짜 | 변경 내용 |
|---|---|---|
| v1.0.0 | 2026-03-02 | 초기 문서 작성 - 전체 파이프라인 개요 - 4단계 파이프라인 구조 정의 - 핵심 아키텍처 특성 (체육관별 CF 계정, Webhook 비동기, Passthrough, 분산 락) - 영상 생명주기 상태 다이어그램 |