본문으로 건너뛰기

마이콕 시스템

문서 정보

  • 작성일: 2026-02-27
  • 최종 업데이트: 2026-04-05
  • 버전: v1.2.0

TL;DR

마이콕은 PLUS 멤버십 전용 영구 클립 보관소입니다. 사용자가 콕한 순간을 영원히 보관하기 위해, 원본 영상 만료 전 자동으로 15초 클립을 생성하여 독립 저장합니다. 모든 카메라 앵글의 클립이 하나의 마이콕에 포함됩니다.


목차

  1. 마이콕이란?
  2. 핵심 개념
  3. 영상 상태 전이
  4. 마이콕 저장 흐름
  5. 배치 클립 생성 파이프라인
  6. VM 프로비저닝과 작업 관리
  7. 멀티카메라 통합
  8. PLUS 멤버십과 제한
  9. 원본 삭제와 영구 보존
  10. 다운로드 구매
  11. FAQ

마이콕이란?

마이콕(MyKok)은 "내 콕 영상을 영원히 보관" 하는 기능입니다.

체육관에서 촬영된 원본 영상은 일정 기간이 지나면 만료되어 삭제됩니다. 하지만 사용자가 콕을 찍은 순간 중 특별히 보관하고 싶은 것이 있다면, 마이콕으로 저장할 수 있습니다. 마이콕으로 저장된 영상은 원본이 삭제되더라도 독립적인 15초 클립으로 영구 보존됩니다.

구분콕(Kok)마이콕(MyKok)
성격운동 중 특정 순간을 표시한 타임스탬프영구 보관을 위해 선택한 콕
보존 기간원본 영상 만료 시 함께 접근 불가영구 보존
영상 파일없음 (원본에서 해당 구간만 재생)15초 독립 클립 파일 보유
멤버십패스 보유자PLUS 멤버십 전용
개수 제한패스별 상이최대 100개
비유로 이해하기

콕이 "포스트잇으로 책의 중요한 페이지를 표시"하는 것이라면, 마이콕은 "그 페이지를 복사해서 별도의 파일에 영구 보관"하는 것입니다. 원본 책이 도서관에 반납되더라도 복사본은 남아 있습니다.


핵심 개념

Video와 MyKok의 관계

하나의 Video(10분 원본 영상)에 여러 사용자의 여러 마이콕이 연결될 수 있습니다.

Video (10분 원본, 카메라 4~6대)

│ 1 : N 관계

├── MyKok A (김철수, 5분 32초 시점)
├── MyKok B (김철수, 8분 10초 시점)
├── MyKok C (이영희, 3분 45초 시점)
└── MyKok D (박민수, 5분 32초 시점)
  • 각 마이콕은 정확히 하나의 Video를 참조합니다 (1:1 모델)
  • 서로 다른 사용자가 같은 시점을 마이콕으로 저장할 수 있습니다
  • 마이콕 1개 = 모든 카메라 앵글의 15초 클립 + 대표 썸네일 1장

클립 구간

콕 시점을 기준으로 앞 10초 + 뒤 5초 = 총 15초 구간을 클립으로 생성합니다.

        콕 시점

◄──10초──┤──5초──►
─────────┼────────
시작 콕 끝

영상 시작/끝 경계에 걸리면 부족한 만큼 반대쪽으로 늘려서 가능한 한 15초를 확보합니다.

데이터 모델

마이콕 레코드에 저장되는 핵심 데이터입니다.

필드설명
videoId원본 Video FK (1:1 모델)
kokTimestampId콕 타임스탬프 ID (중복 방지)
kokInfo콕 메타데이터 스냅샷 (타임슬롯, 오프셋 등)
videoState영상 보존 상태 (ORIGINAL / CLIPPING / CLIPPED / FAILED)
mediaInfo클립 영상 정보 (카메라별 URL + 썸네일) — 배치 완료 후 채워짐
memo사용자 메모 (최대 200자, 선택)
expiresAt만료 시각 (구독 해지 시 설정)

영상 상태 전이

마이콕의 videoState는 클립 생성 진행 상태를 나타내며, 클라이언트가 어떤 소스로 영상을 재생할지 결정합니다.

상태별 상세

상태재생 소스설명
ORIGINAL원본 Video의 10분 영상에서 해당 구간 재생마이콕 저장 직후. 실제 클립 파일은 아직 없음
CLIPPING원본 Video의 10분 영상에서 해당 구간 재생배치 처리 진행 중. ORIGINAL과 동일하게 동작
CLIPPED마이콕 자체 클립 파일로 직접 재생독립된 15초 클립 보유. 원본 삭제 후에도 재생 가능
FAILED재생 불가클립 생성과 FALLBACK 모두 실패. 운영팀 수동 개입 필요
ORIGINAL과 CLIPPING의 차이

두 상태 모두 원본 영상을 참조하지만, CLIPPING은 "곧 독립 클립이 생성될 예정"이라는 의미입니다. 클라이언트 관점에서는 동일하게 동작합니다.


마이콕 저장 흐름

사용자가 콕 목록에서 마이콕으로 저장하는 과정입니다.

토글 방식 (Redis Debounce)

마이콕 저장/삭제는 토글 버튼으로 동작합니다. 빠르게 여러 번 눌러도 마지막 상태만 반영되도록 Redis Debounce 패턴을 적용합니다.

저장 시 검증 순서

마이콕 저장이 실행되면 다음 순서로 검증합니다.

  1. PLUS 멤버십 확인 (Controller Guard)
  2. 마이콕 최대 개수 확인 (100개 제한)
  3. 스케줄 존재 여부 + 소유권 + 접근 기간 확인
  4. 콕 데이터 일치 여부 확인
  5. 중복 저장 체크
  6. 해당 타임슬롯의 Video 조회
  7. 마이콕 레코드 생성 (videoState = ORIGINAL)
저장 즉시 클립을 만들지 않는 이유

원본 영상이 아직 살아있으므로, 굳이 지금 클립을 만들 필요가 없습니다. 사용자가 마이콕을 추가했다가 삭제하는 경우를 고려하면, 원본 만료 직전에 한꺼번에 처리하는 것이 효율적입니다.


배치 클립 생성 파이프라인

원본 영상이 만료되기 전, 마이콕으로 저장된 구간을 실제 15초 클립으로 생성하는 배치 시스템입니다.

전체 흐름

스케줄러 실행 순서

매일 새벽 두 개의 스케줄러가 순차 실행됩니다.

시각스케줄러역할
03:00 KSTExpiredVideoStrategy마이콕이 없는 만료 Video를 삭제 (R2/Stream 리소스 제거 + DB soft delete)
03:30 KSTMyKokGenerationStrategy마이콕이 있는 만료 Video에서 클립 생성 배치 시작

이 순서가 중요합니다. 03:00에 마이콕이 없는 영상을 먼저 정리하고, 03:30에 마이콕이 필요한 영상만 배치에 포함합니다.

파이프라인 단계별 역할

단계담당역할
대상 조회MyKokGenerationStrategy만료 Video 중 ORIGINAL 마이콕이 있는 것 조회
그룹핑MyKokGenerationStrategy체육관별 Video 그룹핑 + R2 인증 정보 복호화
매니페스트ManifestGenerator클립 시간 범위, 카메라별 출력 경로 등 작업 지시서 생성
VM 할당VmAllocator체육관별 데이터 용량(50GB) 기준으로 VM 분할
작업 저장AssignmentWriterMongoDB vm_assignments 컬렉션에 작업 지시서 저장
프로비저닝VmProvisionerVultr VM 생성 + videoState CLIPPING 업데이트

VM 프로비저닝과 작업 관리

Vultr VM

배치 클립 생성은 Vultr 클라우드 서버(VM)에서 수행됩니다.

항목
리전도쿄 (nrt)
플랜vx1-g-2c-8g-120s (2 CPU, 8GB RAM, 120GB SSD)
OSUbuntu 24.04 LTS
실행 방식cloud-init 스크립트로 Docker 컨테이너 자동 실행
수명작업 완료 후 자동 삭제 (사용 시간만큼만 비용 발생)

VM 생명주기

MongoDB 작업 관리

배치 진행 상황은 MongoDB 두 개 컬렉션으로 추적합니다.

컬렉션역할핵심 필드
vm_assignmentsVM별 작업 지시 + 진행 상태status (PENDING → RUNNING → COMPLETED), completedCount / totalMyKoks
batch_logs이벤트 단위 상세 로그event (VM_PROVISIONED, FILE_UPLOADED, BATCH_COMPLETED 등)

배치 이벤트 흐름

VM과 서버 사이에 발생하는 이벤트 순서입니다.

순서이벤트발신자설명
1VM_PROVISIONED스케줄러VM 생성 완료 기록
2VM_STARTEDVM WorkerVM 부팅 후 첫 보고
3CLIP_COMPLETEDVM Worker마이콕 1건의 클립 생성 완료
4FILE_UPLOADEDCF WorkerR2에 파일 1개 업로드 감지 (per-file)
5UPLOAD_COMPLETED서버 (내부)마이콕 1건의 모든 파일 도착 확인
6BATCH_COMPLETED서버 (내부)VM의 모든 마이콕 처리 완료
7BATCH_CLEANUP_COMPLETED스케줄러원본 리소스 삭제 완료

완료 판정

마이콕 1건의 완료는 FILE_UPLOADED 카운팅으로 판정합니다.

카메라 6대인 코트의 마이콕 1개:
필요한 파일 = 6개 클립(.mp4) + 1개 썸네일(.webp) = 7개

FILE_UPLOADED x 1 → 1/7 (대기)
FILE_UPLOADED x 2 → 2/7 (대기)
...
FILE_UPLOADED x 7 → 7/7 도달!
→ UPLOAD_COMPLETED 트리거
→ PostgreSQL: videoState → CLIPPED, mediaInfo 저장
→ MongoDB: vm_assignments.completedCount +1

VM의 모든 마이콕이 완료되면(completedCount === totalMyKoks):

  • VM 삭제 (Vultr API)
  • 원본 리소스 삭제 큐잉 (5분 딜레이 후 실행)
  • Discord 리포트 전송

멀티카메라 통합

1개 마이콕 = 모든 앵글

체육관 코트에는 보통 4~6대의 카메라가 설치되어 있습니다. 마이콕은 사용자가 카메라를 선택하는 것이 아니라, 모든 카메라 앵글의 클립을 하나의 마이콕에 자동 포함합니다.

마이콕 1개의 구성:
├── 카메라 1 클립 (15초 .mp4)
├── 카메라 2 클립 (15초 .mp4)
├── 카메라 3 클립 (15초 .mp4)
├── 카메라 4 클립 (15초 .mp4)
├── 카메라 5 클립 (15초 .mp4)
├── 카메라 6 클립 (15초 .mp4)
└── 대표 썸네일 (.webp) ← 기본 카메라에서 추출

카메라별 미디어 타입

클립 생성 결과에 따라 각 카메라에 타입이 부여됩니다.

타입의미파일 크기클라이언트 동작
CLIPPED정상적으로 15초 클립 생성 완료수 MB파일 그대로 재생
FALLBACK클립 실패 후 원본 전체를 복사250~430MB15초 구간만 seek+play
FALLBACK이 발생하는 경우

특정 카메라의 클립 생성이 실패하면, VM Worker가 최대 1시간까지 재시도합니다. 그래도 실패하면 해당 카메라의 원본 전체를 my-koks 버킷으로 복사합니다. 용량이 크지만 영상이 아예 없는 것보다 낫기 때문입니다. FALLBACK은 극히 드물어 스토리지 비용 영향은 미미합니다.

R2 버킷 구조

raw-videos 버킷 (원본 저장소, 만료 후 삭제)
└── {gymCode}/{date}/{hour}/{timeSlot}/{cameraCode}.mp4

my-koks 버킷 (마이콕 영구 저장소)
└── {userUuid}/{myKokUuid}/{cameraCode}.mp4
{userUuid}/{myKokUuid}/thumbnail.webp

원본은 체육관 기준으로 정리되고, 마이콕은 사용자 기준으로 정리됩니다.


PLUS 멤버십과 제한

필수 조건

마이콕은 PLUS 멤버십 전용 기능입니다. 모든 마이콕 API 엔드포인트에 @RequiresPlus 가드가 적용되어 있어, PLUS가 아닌 사용자는 접근할 수 없습니다.

개수 제한

항목
최대 보관 개수100개
제한 기준PLUS 멤버십의 benefitPolicy.myKokMaxCount
초과 시저장 거부 (MY_KOK_LIMIT_EXCEEDED 에러)

구독 해지 시 동작

단계설명
해지 즉시마이콕 접근 차단 (API 가드에서 거부)
유예 기간마이콕에 expiresAt 설정 (기본 10일)
유예 종료 후마이콕 데이터 삭제
재구독 시유예 기간 내라면 데이터 복원 가능

원본 삭제와 영구 보존

왜 만료 직전에 클립을 만드는가?

마이콕 저장 시점에 바로 클립을 만들지 않고, 원본 만료 직전까지 미루는 전략을 사용합니다.

전략장점
원본 있는 동안은 원본으로 재생불필요한 클립 생성 방지 (저장 후 삭제 반복 시)
만료 직전 한꺼번에 클립 생성VM 효율적 활용 (같은 Video의 여러 마이콕 일괄 처리)
클립 완성 후 원본 삭제원본과 클립 동시 존재 기간 최소화 → 스토리지 절약

타임라인

[원본 생성] ─────────────── [만료 시점] ─── [삭제]
│ │ │
│ ORIGINAL 상태 │ CLIPPING │
│ (원본으로 재생) │ (클립 생성) │
│ │ │
│ 마이콕 자유 추가/삭제 │ 배치 처리 │ CLIPPED
│ │ (VM 작업) │ (독립 클립 보유)
│ │ │
▼ ▼ ▼
마이콕 저장 03:30 배치 시작 원본 삭제
+ VM 삭제

삭제 순서

원본 삭제는 해당 VM의 모든 마이콕이 완료된 후 일괄 수행합니다.

개별 삭제가 안 되는 이유

마이콕 하나가 완료될 때마다 해당 Video 원본을 삭제하면, 같은 Video의 다른 마이콕이 아직 처리 중일 수 있습니다. 원본이 사라지면 남은 마이콕의 클립 생성이 불가능해집니다. 반드시 모든 마이콕이 완료된 후 일괄 삭제해야 합니다.


다운로드 구매

마이콕 클립은 앱 내 스트리밍으로 시청할 수 있지만, 기기에 저장하려면 톨(Grain)으로 구매해야 합니다. 1회 구매당 10회 다운로드 사이클이 제공되며, 구매 즉시 다운로드 URL이 반환됩니다.

가격 산정

원본 비디오와 동일한 초당 비율로 가격을 산정합니다.

콘텐츠길이가격초당 비용
원본 비디오10분 (600초)2,000 grain3.33 grain/초
마이콕 클립15초50 grain3.33 grain/초

산정식: 2000 × (15 / 600) = 50 grain

다운로드 사이클

항목
1회 결제당 다운로드 횟수10회
만료 기간무기한
사이클 소진 시동일 비용(50 grain)으로 재결제 → 새 10회 사이클

카메라별 개별 구매

마이콕 1개에는 모든 카메라 앵글이 포함되어 있지만, 다운로드는 카메라별로 개별 구매합니다. 각 카메라 앵글마다 별도의 다운로드 사이클이 생성됩니다.

클라이언트 플로우

  1. 이력/상태 조회canDownload 확인
  2. canDownload: true → 다운로드 URL 요청 (횟수 차감)
  3. canDownload: false → 구매 또는 재구매 (Grain 차감 + URL 즉시 반환)

엔드포인트

메서드경로설명
POST/my-koks/:myKokUuid/download구매 + 다운로드 URL 즉시 반환
GET/my-koks/:myKokUuid/download-status구매 상태 + 사이클 정보 조회
GET/my-koks/:myKokUuid/download-url다운로드 URL 요청 (횟수 차감)
GET/my-koks/:myKokUuid/download-history다운로드 이력 조회

다운로드 이력

GET /my-koks/:myKokUuid/download-history?cameraCode=xxx
Response: {
currentCycle: { uuid, downloadCount, maxDownloads, remainingDownloads, purchasedAt },
histories: [{ uuid, downloadedAt, ip, success }],
previousCycles: [...]
}
멤버십 만료 후에도 구매 가능

PLUS 구독이 만료되어도, 기존에 저장된 마이콕에 대한 다운로드 구매는 가능합니다. MEMBER 역할이면 충분합니다.


FAQ

Q: 마이콕을 저장하면 바로 15초 클립이 만들어지나요?

아닙니다. 저장 즉시에는 원본 영상의 해당 구간을 재생하여 동일한 경험을 제공합니다. 실제 15초 클립은 원본 영상 만료 직전 배치로 생성됩니다.

Q: 원본 영상이 삭제되면 마이콕은 어떻게 되나요?

CLIPPED 상태가 된 마이콕은 독립적인 클립 파일을 보유하므로 원본 삭제 영향을 받지 않습니다. 배치 파이프라인이 원본 삭제 전에 반드시 클립을 생성합니다.

Q: 마이콕에서 특정 카메라만 선택할 수 있나요?

현재는 모든 카메라 앵글이 자동으로 포함됩니다. 사용자는 재생 시 원하는 카메라를 선택하여 시청합니다.

Q: 마이콕 100개가 꽉 찬 상태에서 추가 저장하려면?

기존 마이콕을 삭제하여 여유 공간을 만든 후 저장할 수 있습니다. 삭제된 마이콕은 soft delete로 처리되며, 같은 콕을 다시 저장할 수 있습니다.

Q: PLUS 멤버십을 해지하면 마이콕이 바로 사라지나요?

접근은 즉시 차단되지만, 데이터는 유예 기간(기본 10일) 후에 삭제됩니다. 유예 기간 내 재구독하면 복원됩니다.

Q: 배치 클립 생성이 실패하면 어떻게 되나요?

특정 카메라의 클립 생성이 실패하면 최대 1시간 재시도 후 FALLBACK(원본 복사)을 수행합니다. FALLBACK마저 실패하면 FAILED 상태가 되며, Discord 알림으로 운영팀에 통보됩니다.

Q: 배치는 매일 실행되나요?

네. 매일 새벽 03:30 KST에 자동 실행됩니다. 배치 대상 Video가 없으면 아무 작업 없이 종료되며, Discord로 "배치 대상 없음" 알림을 전송합니다.

Q: 마이콕에 메모를 남길 수 있나요?

네. 각 마이콕에 최대 200자의 메모를 추가, 수정, 삭제할 수 있습니다. 비속어 필터링이 적용됩니다.


관련 문서


변경 이력

버전날짜변경 내용
v1.0.02026-02-27초기 문서 작성
- 마이콕 시스템 전체 개요
- 영상 상태 전이 설명 (ORIGINAL → CLIPPING → CLIPPED)
- 배치 클립 생성 파이프라인 설명
- VM 프로비저닝 (Vultr) + MongoDB 작업 관리
- 멀티카메라 통합 구조
- PLUS 멤버십 조건 및 제한
- FAQ 8항목 작성
v1.1.02026-04-04다운로드 구매 섹션 추가
- 가격 산정 (원본 비례 50 grain)
- 카메라별 개별 구매, 무료 재다운로드 정책
- 엔드포인트 정보
v1.2.02026-04-05다운로드 사이클 시스템 도입
- 10회/1결제 사이클 정책 (무기한)
- 구매 시 다운로드 URL 즉시 반환
- 다운로드 이력 조회 API 추가
- purchase-status 응답에 사이클 정보 포함