Files
jaengseung-made/docs/superpowers/specs/2026-07-09-phase3b-music-video-order-design.md

7.8 KiB

Phase 3b 음악 영상화 유료 발주 설계

  • 날짜: 2026-07-09
  • 선행: Phase 3a(음악 공개화·스토리→음악) main 머지 완료(677012a), /music 카피 정리(6a67a9d)
  • 배경: 음악 서비스 2번째 축 = "영상화"(유료). 회원이 저장한 음악 트랙을 계좌이체 발주하면 관리자가 수동 제작·납품. 자동 영상 생성은 범위 밖.

결정 사항 (CEO 확정, 2026-07-09)

결정 내용
영상화 방식 계좌이체 발주 + 관리자 수동 제작·납품 (Phase 3 스코핑서 확정)
가격 고정가 ₩30,000music_video products 1행, admin/products에서 조정 가능
주문 모델 기존 orders 재사용 + metadata.music_track_id 트랙 참조. music_tracks에 video 상태 추가
납품 music_tracks.video_url — 관리자가 admin/orders에서 완성 영상 URL 직접 입력(별도 업로더 없음)
진입점 마이페이지 AI기록 음악 카드의 "영상화 신청" 버튼

확인된 기존 구조

  • products: { id, name, price, is_active, ... } (lib/supabase/product-files.ts ProductRow, getProductById(admin, id))
  • POST /api/orders: 인증→rate-limit→productId/depositorName 검증→getProductById(active)→중복 pending 가드(user+product)→insert {user_id, product_id, amount:product.price, status:'pending', metadata:{method:'bank_transfer', depositor_name}}→접수 메일
  • admin/orders PATCH: body {id, status∈pending|paid|cancelled} → update; paid 전환 시 sendOrderPaidEmail
  • BankTransferModal({ product, isOpen, onClose }): POST { productId: product.id, depositorName }
  • music_tracks(Phase 3a): { id, user_id, title, story, lyrics, style, audio_url, task_id, created_at }, RLS music_select_own
  • 마이페이지 AI기록 탭: 사주·타로·음악 병합, MusicAiCard(제목·스토리·<audio>)

워크스트림

WS1. DB 마이그레이션 (products 시드 + music_tracks 확장)

  • 신규 supabase/migrations/2026-07-09-phase3b-music-video.sql:
    -- 의존성: 2026-07-03-phase3a-music.sql(music_tracks) 적용 후
    INSERT INTO products (id, name, price, is_active)
    VALUES ('music_video', '음악 영상화', 30000, true)
    ON CONFLICT (id) DO NOTHING;
    
    ALTER TABLE music_tracks
      ADD COLUMN IF NOT EXISTS video_status text NOT NULL DEFAULT 'none'
        CHECK (video_status IN ('none','requested','in_progress','delivered')),
      ADD COLUMN IF NOT EXISTS video_url text,
      ADD COLUMN IF NOT EXISTS video_order_id uuid;
    
    (products 실제 컬럼 확인 후 필수 NOT NULL 컬럼 있으면 값 채워 시드. 클라우드+NAS 양쪽)

WS2. 발주 API 확장 (orders + music_tracks 연결)

  • POST /api/orders 확장: body에 optional musicTrackId(string) 허용
    • productId==='music_video'이고 musicTrackId 있으면: admin으로 music_tracks에서 id=musicTrackId AND user_id=user.id 조회 → 없으면 404(본인 트랙 아님)
    • metadata에 music_track_id 포함해 insert
    • 중복 가드 확장: music_video는 (user_id, product_id='music_video', metadata->>music_track_id = trackId, status='pending')로 조회(트랙별). 일반 상품은 기존 (user, product) 가드 유지
    • 주문 insert 성공 후: music_tracks update video_status='requested', video_order_id=order.id where id=musicTrackId AND user_id=user.id
  • 신규 GET /api/studio/video-product: 로그인, music_video 제품 { id, name, price, is_active } 반환(모달 가격 표시용). 미존재/비활성 시 안내

WS3. 마이페이지 신청 진입점 + 상태 표시

  • BankTransferModal 확장: optional prop musicTrackId?: string → POST body에 musicTrackId 병합(없으면 기존 동작 그대로)
  • 마이페이지 MusicAiCard(AI기록 탭):
    • video_status 뱃지 — none: "영상화 신청" 버튼(→ GET /api/studio/video-product로 제품 로드 후 BankTransferModal 오픈, musicTrackId 전달) / requested: "영상화 접수됨(입금 확인 중)" / in_progress: "영상 제작 중" / delivered: "영상 완료"
    • delivered + video_url 있으면 <video controls src={video_url}> + 다운로드 링크
    • MusicTrackRow 타입에 video_status, video_url 추가(GET /api/studio/tracks가 이 컬럼 반환하도록 select 확장)
  • 신청 완료 후 목록 갱신(status='requested' 반영)

WS4. admin 납품

  • app/admin/orders/page.tsx: product_id==='music_video' 주문에
    • 연결 트랙 표시(metadata.music_track_id로 music_tracks 조회 — GET 응답에 트랙 title/audio_url 조인 포함)
    • 영상 URL 입력 + "납품 완료" 버튼PATCH /api/admin/orders { id, status:'paid', videoUrl }
    • (선택) "제작 중" 버튼 → { id, videoStatus:'in_progress' }
  • admin/orders GET 확장: music_video 주문에 연결 트랙 정보(title, audio_url) 포함(admin이 metadata.music_track_id로 music_tracks 조인)
  • admin/orders PATCH 확장:
    • optional videoUrl, videoStatus 허용
    • status='paid' + music_video 주문이면: 주문 metadata.music_track_id로 music_tracks update video_url=videoUrl, video_status='delivered'
    • videoStatus='in_progress'만 오면 status 변경 없이 트랙 video_status만 갱신
    • 기존 일반 상품 paid 흐름·메일은 무변경

WS5. 메일 + 문서 + 검증

  • 발주 접수/완료 메일 재사용(sendOrderReceivedEmails/sendOrderPaidEmail). 영상화 문구는 제품명("음악 영상화")으로 자연 반영 — 별도 템플릿 추가 없음(YAGNI)
  • CLAUDE.md: 결제/발주 플로우에 영상화(music_video) 추가, music_tracks video 컬럼·admin 납품 반영
  • 검증: npm run build·npm test·가드레일(신규 UI 라이트) grep, 수동 E2E(신청→admin 납품→마이페이지 완료 재생)

향후 방향 기록 (로드맵 — "추후 방향 재설정" 근거)

이 스펙 완료 시점 기준 전체 상태와 다음 후보를 기록한다(메모리 saas-refactor-2026-07에도 반영).

완료(main·양쪽 리모트 푸시): Phase 0(정리) · 1(외주 코어) · 2(사주/타로) · 2.5/2.6(사주 재스킨) · 3a(음악 공개화) · /music 카피 · 3b(영상화 발주 — 이 스펙)

미결 운영(CEO 수동, 코드 불가):

  • DB 마이그레이션 5개 순차 적용(클라우드+NAS): phase0 → phase1 → phase2 → phase3a → phase3b
  • 운영 env: GEMINI_API_KEY(사주·타로·스토리), SUNO_API_KEY(음악), RESEND_API_KEY(메일)
  • 수동 E2E 전 서비스 스모크

향후 후보(다음 방향 잡기용):

  1. 영상 자동 생성 연동 — 수동 제작 → sora/veo/kling 등 API(비용·게이트웨이 설계 필요, NAS video-lab 참고)
  2. NAS 풀 self-host 컷오버 — Vercel/클라우드 Supabase → NAS(진행 중 project nas-selfhost-transition)
  3. 운영 대시보드·정산 — 서비스별 매출/사용량 통합(admin/analytics 확장), AI 사용량·Suno 비용 집계
  4. 회원 등급/구독 재도입 — Phase 0에서 제거한 subscription 인프라 재설계(SaaS 피벗 시)
  5. 사주·타로·음악 크로스 프로모션, 마케팅 채널(admin 광고 관리) 활용 캠페인

범위 밖 (Phase 3b)

  • 영상 자동 생성, 영상 편집 UI, 관리자 파일 업로더(URL 문자열로 충분)
  • 결제 PG(계좌이체 단일 소스 유지)

리스크·주의

  • orders POST의 중복 가드를 music_video만 트랙 단위로 분기 — 일반 상품 가드 회귀 없도록 주의
  • metadata->>music_track_id jsonb 쿼리 문법(Supabase) 정확히
  • BankTransferModal은 다른 상품(products)도 쓰므로 musicTrackId는 optional, 미전달 시 기존 동작 불변
  • video_url은 관리자 신뢰 입력(공개 재생) — 값 검증은 최소(문자열 길이), XSS는 <video src> 특성상 낮으나 URL 형식 권장
  • admin/orders에 music_video 조인 추가 시 기존 일반 주문 렌더 회귀 없도록