이 문서는 "어떻게 여기까지 왔고, 앞으로 뭘 해야 하는가"를 기록한다.
설계 논리, 의사결정 과정, 실행 순서를 모두 포함한다.
새 세션에서 Claude에게 이 문서를 던지면 맥락 100% 복원된다.
ChatGPT와의 대화에서 출발. 핵심 질문은 하나였다:
"PC에 Kontakt + Cubase가 이미 있는데, 이걸로 YouTube 배경음악을 만들 수 있냐?"
당시 보유 자산:
당시 시도:
결과: PC 터짐. 팬 소음, 지연, XRUN 에러. 덮었다.
| 원인 | 설명 | |
| Cubase는 실시간 엔진 | 타임라인 재생 = CPU/RAM 상시 점유 | |
|---|---|---|
| Kontakt는 메모리 괴물 | 라이브러리 하나에 수 GB | |
| 목적 불일치 | "소리 뽑기"가 목적인데, "연주하기" 도구를 사용 |
결론: 도구 선택 미스였지, 실력 부족이 아니었다.
ChatGPT 대화에서 확립된 원칙:
1. 실시간 DAW를 쓸 이유가 없다
2. MIDI만 있으면 오프라인으로 렌더링할 수 있다
3. Kontakt/Pianoteq는 CLI 오프라인 렌더 지원
4. PWA로 가벼운 도구 만들면 FL Studio/Lexis 필요 없다
5. 저작권: 퍼블릭 도메인 MIDI + 본인 렌더 = Master 100% 소유
이 원칙이 Mini-DAW의 설계 근거 전부다.
Parksy Mini-DAW Final Development Whitepaper 확정.
핵심 선언:
"Parksy Mini-DAW는 음악을 만드는 도구가 아니라,
이미 검증된 음악을 최고의 음질로 다시 연주하게 만드는 기계다."
| 항목 | O/X | |
| 작곡 툴 | ❌ | |
|---|---|---|
| 실시간 DAW | ❌ | |
| AI 음악 생성 서비스 | ❌ | |
| 연출용 음원 생산 파이프라인 | ✅ | |
| 1인 운영 | ✅ | |
| 로컬 중심 | ✅ | |
| 저작권 안전 | ✅ |
Audio (원본)
→ ffmpeg 컷 (구간 추출)
→ WAV
→ basic-pitch (MIDI 추출)
→ MIDI
→ (선택) 구매 MIDI 사용
→ Kontakt / Pianoteq (오프라인 렌더)
→ WAV 48kHz / 24bit (최종 출력)
핵심: 실시간 엔진 완전 배제. Cubase 안 켠다.
parksy-audio 레포에 이미 있던 코드:
| 기존 코드 | 위치 | 재활용 가능 여부 | ||
| FastAPI 서버 | `server/app/main.py` | ✅ 그대로 | ||
|---|---|---|---|---|
| ffmpeg 컷/트림 | `local-engine/cli.py` | ✅ 그대로 | ||
| basic-pitch 변환 | `local-engine/cli.py` + `server/main.py` | ✅ 그대로 | ||
| run_ffmpeg 헬퍼 | `local-engine/cli.py` | ✅ 그대로 | ||
| GitHub Actions | `.github/workflows/` | ✅ 유지 |
결론: 파이프라인 앞쪽 절반(서버+컷+MIDI)은 이미 있었다.
→ "새로 만들기"가 아니라 "리팩토링 + 연결"로 Phase 1 완성 가능.
MIDI 품질을 보장하려 하지 않고, 선택지를 구조로 해결:
| Track | 출처 | 품질 기대치 | ||
| Track A (Extract) | WAV → basic-pitch → MIDI | 60~75점 (근사치) | ||
|---|---|---|---|---|
| Track B (Curated) | 구매/선별 퍼블릭도메인 MIDI | 85~95점 |
두 트랙 모두 clean_midi → render → WAV 동일 파이프라인을 탄다.
개발 복잡도 증가 없음.
Step 1 — 현황 파악
GitHub API로 parksy-audio 레포 전체 구조 조회
→ 파일 트리, 커밋 이력, 코드 내용 전부 확인
→ DEV_STATUS.md 최초 생성 (docs/에 저장, GitHub Pages 규칙 준수)
Step 2 — 재활용 가능 코드 분석
server/app/main.py 읽기 → FastAPI + basic-pitch 변환 코드 확인
local-engine/cli.py 읽기 → trim, convert, run_ffmpeg, info, status 확인
requirements.txt 읽기 → 의존성 확인
결론: Phase 1의 3개 항목 중 2개(서버, ffmpeg 컷)는 사실상 완료 상태.
Step 3 — 새 레포 vs 기존 레포 결정
백서에는 parksy-mini-daw/ 새 레포로 설계돼 있었으나,
분석 후 결정: 새 레포 만들지 않는다.
이유:
Step 4 — 폴더 구조 결정
백서의 4단 아키텍처(routes/core/services/utils) 거부.
5개 엔드포인트에 4폴더는 과설계.
확정 구조: 플랫 4파일
local-agent/
server.py ← FastAPI 단일 파일
audio_cut.py ← ffmpeg 컷
extract_midi.py ← basic-pitch 변환
config.py ← 경로/설정
requirements.txt
README.md
Step 5 — 코드 작성 (4파일 동시)
1. config.py — 경로, ffmpeg 바이너리 위치, 확장자 필터, 출력 포맷
2. audio_cut.py — 기존 cli.py의 trim() + run_ffmpeg() 이관
3. extract_midi.py — 기존 cli.py의 convert() + server 로직 이관
4. server.py — FastAPI 5 endpoints, Pydantic 모델, CORS
작성 후 AST 문법 검증 통과. 커밋 d7496b1.
Step 6 — render.py 추가
Phase 3 예정이었으나, 스텁이라도 넣는 게 낫다고 판단.
이유: PC에서 풀사이클 한 번에 테스트 가능.
커밋 a9fdb8e.
Step 7 — 음원 퍼블리싱 전략 확정
Step 8 — DEV_STATUS.md 전면 업데이트
전체 현황, 아키텍처, 코드 이관 맵, 로드맵, 히스토리 반영.
커밋 b976f5a.
Step 9 — PC 매뉴얼 작성
집에서 할 작업 동선, 에러 대응, 도구별 역할 분담.
커밋 e1bbbc7.
| 순서 | 해시 | 내용 | ||
| 1 | `d81179a` | DEV_STATUS.md 최초 생성 | ||
|---|---|---|---|---|
| 2 | `d7496b1` | local-agent/ Phase 1 (server + cut + midi + config) | ||
| 3 | `a9fdb8e` | render.py 스텁 + /render 엔드포인트 | ||
| 4 | `b976f5a` | DEV_STATUS.md 전면 업데이트 | ||
| 5 | `e1bbbc7` | PC 작업 매뉴얼 |
이 프로젝트에서 내린 주요 결정과 그 이유.
| 항목 | 내용 | |
| 결정 | Cubase를 파이프라인에서 완전 제거 | |
|---|---|---|
| 이유 | 실시간 엔진이 목적(오프라인 렌더)과 불일치. PC 리소스 폭발 경험. | |
| 대안 | Pianoteq CLI + Kontakt 오프라인 렌더 | |
| 상태 | 확정. 번복 없음. |
| 항목 | 내용 | |
| 결정 | parksy-mini-daw 새 레포 만들지 않음 | |
|---|---|---|
| 이유 | parksy-audio에 필요한 인프라 전부 있음. 레포 추가 = 관리 비용 2배. | |
| 대안 | parksy-audio 내부에서 local-agent/ 추가 | |
| 상태 | 확정. |
| 항목 | 내용 | |
| 결정 | routes/core/services/utils 4단 분리 거부 | |
|---|---|---|
| 이유 | 엔드포인트 6개에 폴더 4개는 과설계 | |
| 대안 | 파일 5개 플랫 구조 (server, cut, midi, render, config) | |
| 상태 | 확정. 나중에 커지면 그때 분리. |
| 항목 | 내용 | |
| 결정 | Phase 3 예정이던 렌더를 Phase 1에 스텁으로 추가 | |
|---|---|---|
| 이유 | PC에서 풀사이클 한 번에 검증 가능. 따로 붙이면 커밋+테스트 추가 발생. | |
| 상태 | 확정. |
| 항목 | 내용 | |
| 결정 | YouTube 영상을 완전 블랙 대신 느린 그라데이션으로 | |
|---|---|---|
| 이유 | 블랙 화면 = 사용자 이탈("로딩 오류?") → watch time 급락 → 추천 밀림 | |
| 대안 | 다크 그라데이션 (20~30초 주기) + 중앙 고정 텍스트 | |
| 상태 | 확정. |
[✅] 백서 확정 (설계 종료)
[✅] 기존 코드 재활용 분석
[✅] local-agent/ 5파일 작성 (server, cut, midi, render, config)
[✅] API 6개 엔드포인트 정의
[✅] 문법 검증 (AST parse 통과)
[✅] git push (5커밋)
[✅] DEV_STATUS.md 업데이트
[✅] PC 매뉴얼 작성
[✅] 음원 퍼블리싱 전략 확정
[⬜] PC에서 서버 기동 검증
[⬜] /cut → clip.wav 실제 생성 확인
[⬜] /extract-midi → .mid 실제 생성 확인
[⬜] /render → .wav 실제 생성 확인 (Pianoteq)
[⬜] 최종 WAV 재생 → 소리 확인
PC에서 10분이면 끝나는 검증:
git pull origin main
cd local-agent
python -m venv .venv && .venv\Scripts\activate
pip install -r requirements.txt
uvicorn server:app --host 0.0.0.0 --port 8787 --reload
그 다음 curl 3방:
1. POST /cut → clip.wav
2. POST /extract-midi → .mid
3. POST /render → .wav
WAV 재생해서 소리 나오면 Phase 1 완료.
| 작업 | 설명 | |
| clean_midi.py | 0.1초 미만 노트 제거, velocity 보정, quantize | |
|---|---|---|
| merge_midi.py | Track A + Track B 병합 | |
| FFmpeg MP4 생성 | 그라데이션 배경 + 텍스트 → 자동 영상 | |
| CUT PWA UI | 파형 뷰어 + IN/OUT 버튼 | |
| MIDI 편집 PWA UI | Tempo/Key/Velocity 슬라이더 | |
| YouTube 업로드 | API 또는 수동 |
| 작업 | 설명 | |
| Kontakt 프리셋 렌더 | .nkm 사전 저장 → MIDI + 프리셋 → WAV | |
|---|---|---|
| 장르별 프리셋 | 클래식/락/뉴에이지/보사/탱고 | |
| 배치 렌더링 | 여러 MIDI 한 번에 처리 |
이 레포의 docs/WORKFLOW_MANUAL.md를 읽어.
우리가 어디까지 했고 다음에 뭘 해야 하는지 이해한 다음,
Phase 1 PC 검증을 도와줘.
docs/WORKFLOW_MANUAL.md 읽고,
Phase 2 첫 번째 작업인 clean_midi.py를 만들어.
extract_midi.py 출력물(.mid)을 입력으로 받아서
노이즈 제거 + 휴머나이징하는 최소 버전.
docs/WORKFLOW_MANUAL.md 읽고,
local-agent/server.py를 PC에서 돌렸는데 이런 에러가 나:
[에러 로그 붙여넣기]
| 문서 | 위치 | 내용 | ||
| DEV_STATUS.md | `docs/DEV_STATUS.md` | 개발 현황 + 로드맵 | ||
|---|---|---|---|---|
| PC_MANUAL.md | `docs/PC_MANUAL.md` | PC 세팅/실행/에러 대응 | ||
| AUDIT_REPORT.md | `docs/AUDIT_REPORT.md` | v1.0 시스템 감사 보고서 | ||
| MANUAL.md | `docs/MANUAL.md` | v1.0 사용자 매뉴얼 | ||
| CLAUDE.md | `CLAUDE.md` | Claude Code 세션 가이드 | ||
| parksy-logs | 별도 레포 | 2025-12-18 원본 대화 기록 |
"이 문서 하나면 6개월 뒤에 돌아와도 30초 안에 맥락 복원된다."