diff --git a/src/pages/music/components/BatchProgress.jsx b/src/pages/music/components/BatchProgress.jsx
new file mode 100644
index 0000000..5ca15bf
--- /dev/null
+++ b/src/pages/music/components/BatchProgress.jsx
@@ -0,0 +1,48 @@
+const STATUS_LABELS = {
+ queued: '대기 중',
+ generating: '음악 생성 중',
+ generated: '음악 완료, 컴파일 대기',
+ compiling: '컴파일 중',
+ piped: '영상 파이프라인 시작됨 — YouTube 탭 진행 탭에서 확인',
+ failed: '실패',
+ cancelled: '취소',
+};
+
+export default function BatchProgress({ batch }) {
+ if (!batch) return null;
+ const trackList = Array.from({ length: batch.count }, (_, i) => i + 1);
+ return (
+
+
+ 배치 #{batch.id} — {batch.genre} ·{' '}
+ {batch.completed}/{batch.count} 완료 ·{' '}
+ 상태:
+ {STATUS_LABELS[batch.status] || batch.status}
+
+
+ {batch.error &&
에러: {batch.error}
}
+
+ {trackList.map(n => {
+ const completed = n <= batch.completed;
+ const current = n === batch.current_track_index && batch.status === 'generating';
+ const tr = (batch.tracks || [])[n - 1];
+ return (
+ -
+ {completed ? '✓' : current ? '⏳' : '○'}
+ {' '}Track {n}: {tr?.title || (current ? '생성 중...' : '대기')}
+
+ );
+ })}
+
+ {batch.compile_job_id && (
+
📀 컴파일 #{batch.compile_job_id}
+ )}
+ {batch.pipeline_id && (
+
+ 🎬 영상 파이프라인 #{batch.pipeline_id} —{' '}
+ YouTube 탭 → 진행 탭에서 확인
+
+ )}
+
+ );
+}