Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AAtcmKKtqDUe4NyVgy1aLQ
6.8 KiB
6.8 KiB
Phase 3a 음악 서비스 공개화 — "나의 이야기를 음악으로" 설계
- 날짜: 2026-07-03
- 선행: Phase 2(사주/타로)·2.5/2.6(사주 재스킨) main 머지 완료
- 배경: 운영 비전 2축 3번째 서비스(음악). 숨김 상태의 Suno 스튜디오를 공개·무료화하고, "스토리→음악" 흐름과 회원 저장을 추가. 영상화 유료는 Phase 3b로 분리.
결정 사항 (CEO 확정, 2026-07-03)
| 결정 | 내용 |
|---|---|
| 영상화 | Phase 3b로 분리 — 계좌이체 발주(관리자 수동 제작·납품). 이번 3a 범위 밖 |
| 음악 노출·과금 | 공개+무료 — 페이지 공개(숨김 해제), 생성은 로그인+일일제한(무료). 사주·타로 패턴 |
| 스토리→음악 | Gemini가 스토리→{가사·스타일·무드} 변환 후 Suno 생성 |
| 일일 제한 | 음악 생성 1회/일(Suno 비용) |
| callback | /api/studio/callback 폴링 전용 확정 — 최소 200 응답 route로 댕글링 해소, 회원 저장은 폴링 완료 후 클라 트리거 |
확인된 기존 구조
POST /api/studio/generate: 무인증,SUNO_API_KEY미설정 시 503, custom/simple 모드,callBackUrl=${origin}/api/studio/callback(부재), Suno/api/v1/generate호출 → task 반환GET /api/studio/status?taskId=: 무인증, Sunorecord-info폴링lib/ai-usage.ts:AiService='saju'|'tarot',getTodayUsage/recordUsage, KST 일일 집계.ai_usage_logCHECK는('saju','tarot')(phase2 마이그, auto-nameai_usage_log_service_check)app/music/: layout(가드isServiceVisible('music')), page(72), samples(102), studio(543, 다크 테마 Suno UI)- 저장·마이페이지 통합 패턴: 타로
interpret→readings save, 마이페이지 'AI 기록' 탭(사주·타로)
워크스트림 5개
WS1. 음악 공개화 + 무료화
app/music/layout.tsx의isServiceVisible('music')+notFound()+import 제거 → 공개lib/service-visibility.tsHideableService에서'music'제거 →'gyeol'|'lotto'app/api/admin/services/route.tsDEFAULT_SERVICES music 행 제거- 마이그레이션에서
service_settingsmusic 행 DELETE - Suno 키 미설정 시 503 유지(예시 폴백 없음)
WS2. 스토리 → 음악 (Gemini + Suno)
- 신규
lib/music/story-prompt.ts:STORY_SYSTEM_PROMPT+buildStoryUserMessage(story)+parseStoryJson/validateStory. Gemini가 스토리 텍스트 →{ title, lyrics, style, mood }strict JSON. 타로 prompt.ts 방어(코드블록 스트립·검증·reroll 1회, 45s 가드) 패턴 재사용 - 신규
POST /api/studio/story: 인증(401) → Gemini 변환(GEMINI_API_KEY, 미설정 503) →{ title, lyrics, style, mood }반환(사용자 편집 가능). story 단계는 일일제한 미집계(값싼 단계) - 기존
POST /api/studio/generate(Suno) 수정: 인증(401) 추가 + 일일제한(429,MUSIC_DAILY_LIMIT) + Suno task 생성 성공 시에만recordUsage('music'). body에title/lyrics/tags(style)를 story 결과에서 채워 custom 모드로 호출 GET /api/studio/status유지(무인증 폴링 가능 — taskId만 필요, 민감정보 없음)- callback 정리: 신규
POST /api/studio/callback이 최소{ ok: true }200 반환(Suno webhook 404 방지). 회원 저장은 콜백이 아니라 폴링 완료 후 클라가POST /api/studio/tracks트리거
WS3. 회원 저장 + 일일제한
- 마이그레이션
supabase/migrations/2026-07-03-phase3a-music.sql:의존성: phase2-saju-tarot 마이그(ai_usage_log 생성)가 먼저 적용돼야 함 — 스펙·플랜·CEO 안내에 명시CREATE TABLE IF NOT EXISTS music_tracks ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, title text, story text, lyrics text, style text, audio_url text, task_id text, created_at timestamptz NOT NULL DEFAULT now() ); ALTER TABLE music_tracks ENABLE ROW LEVEL SECURITY; CREATE POLICY music_select_own ON music_tracks FOR SELECT USING (auth.uid() = user_id); -- ai_usage_log CHECK에 'music' 추가 (phase2 마이그 적용 후 실행 전제) ALTER TABLE ai_usage_log DROP CONSTRAINT IF EXISTS ai_usage_log_service_check; ALTER TABLE ai_usage_log ADD CONSTRAINT ai_usage_log_service_check CHECK (service IN ('saju','tarot','music')); DELETE FROM service_settings WHERE id = 'music'; lib/ai-usage.ts:AiService에'music'추가,export const MUSIC_DAILY_LIMIT = 1;- 신규
POST/GET /api/studio/tracks: 저장(admin insert, user_id 세션)·본인 조회(세션 client RLS). 타로 readings 패턴
WS4. 라이트 재스킨 + 스토리 UI
app/music/{page,samples,studio}.tsx다크 →--jsm라이트 재스킨(사주 2.5/2.6 패턴, gradient/blur/보라/이모지 0건). navy 밴드 무테두리 flat 관용구- 스토리 UI 흐름(studio 재구성): ①스토리 textarea →
POST /studio/story→ ②가사·스타일 미리보기(편집 가능) → ③"음악 만들기" →POST /studio/generate→ ④status폴링 → ⑤플레이어 + (로그인 시) 자동 저장(POST /studio/tracks). 비로그인 생성 시도 → 로그인 CTA(/login?next=/music/studio) - 셔플류 클라 이슈 없음(폼 기반)
WS5. 진입점 + AI기록 통합 + 문서
- TopNav
음악링크 추가(외주/소프트웨어/제작사례/사주/타로/음악 = 6링크) - 마이페이지 'AI 기록' 탭에 음악 트랙 통합(
GET /api/studio/tracks) — 사주·타로·음악 3종 병합 리스트, 음악은 제목·스토리 요약·오디오 링크 - CLAUDE.md: 음악 공개 서비스·스토리→음악·music_tracks·studio API 반영, 숨김 서비스 표에서 music 제거
- 최종 검증:
grep -rnE "gradient|violet|purple|blur" app/music/**/*.tsx0건, build+test 30, sajumusic 라우트 존재
범위 밖 (Phase 3b)
- 영상화 유료 발주(계좌이체 order로 video 신청·관리자 제작·납품·다운로드)
- 음악 자동 영상 생성 API 연동
리스크·주의
- Suno 실동작은
SUNO_API_KEY운영 설정 의존 — 로컬 미설정 시 503(사주 Gemini와 동일 정책). 수동 E2E는 운영 키 있는 환경에서 ai_usage_logCHECK ALTER는 phase2 마이그 DB 적용 후 실행돼야 함(미적용 시 ALTER 실패) — CEO 안내- studio 페이지가 큼(543줄) — 재스킨 + 스토리 UI 재구성은 태스크 분할(라이트 재스킨과 스토리 흐름 분리 가능)
- Suno 응답 스키마(task/record-info)는 기존 status route가 이미 다룸 — 저장 시 audio_url 추출 지점은 구현 시 record-info 응답 구조 확인
- 생성은 비동기(task) — recordUsage는 task 생성 성공(Suno 202/200) 시점 집계(완료 아님). 완료 실패해도 1회 소진되나 개인 서비스 규모에서 허용(사주·타로와 동일 기조)