'use client'; import { useState, useEffect, useRef } from 'react'; import { cn } from '@/lib/utils'; import { registerServiceWorker, getCacheStatus } from '@/lib/service-worker/register'; interface VideoBackgroundProps { videos: string[]; className?: string; overlayClassName?: string; showIndicators?: boolean; showCacheStatus?: boolean; onVideoChange?: (index: number) => void; } /** * VideoBackground 컴포넌트 * * 특징: * - 영상 프리로드로 부드러운 전환 * - 자동 재생 및 순환 * - Service Worker를 통한 영구 캐싱 * - HTTP 캐시 헤더 지원 * - 인디케이터 지원 */ export function VideoBackground({ videos, className, overlayClassName, showIndicators = true, showCacheStatus = false, onVideoChange, }: VideoBackgroundProps) { const [currentVideoIndex, setCurrentVideoIndex] = useState(0); const [isVideoLoaded, setIsVideoLoaded] = useState(false); const [cachedVideos, setCachedVideos] = useState(0); const videoRef = useRef(null); const preloadedVideos = useRef([]); // Service Worker 등록 useEffect(() => { const setupServiceWorker = async () => { const registration = await registerServiceWorker(); if (registration) { console.log('✅ Service Worker registered - Videos will be cached'); // 캐시 상태 확인 setTimeout(async () => { const status = await getCacheStatus(); if (status) { setCachedVideos(status.cached); console.log(`📦 Cached videos: ${status.cached}/${status.total}`); } }, 2000); } }; setupServiceWorker(); }, []); // 초기 랜덤 비디오 선택 useEffect(() => { const randomIndex = Math.floor(Math.random() * videos.length); setCurrentVideoIndex(randomIndex); }, [videos.length]); // 모든 비디오 프리로드 useEffect(() => { // 이미 프리로드된 경우 스킵 if (preloadedVideos.current.length > 0) return; const preloadVideos = async () => { const loadedVideos: HTMLVideoElement[] = []; for (let i = 0; i < videos.length; i++) { const video = document.createElement('video'); video.preload = 'auto'; video.muted = true; video.playsInline = true; video.src = videos[i]; // 메타데이터 로드 대기 await new Promise((resolve) => { video.addEventListener('loadedmetadata', () => resolve(), { once: true }); // 타임아웃 설정 (10초) setTimeout(() => resolve(), 10000); }); loadedVideos.push(video); } preloadedVideos.current = loadedVideos; setIsVideoLoaded(true); }; preloadVideos(); // 컴포넌트 언마운트 시 비디오 리소스 정리 return () => { preloadedVideos.current.forEach((video) => { video.src = ''; video.load(); }); preloadedVideos.current = []; }; }, [videos]); // 비디오 변경 시 콜백 호출 useEffect(() => { if (onVideoChange) { onVideoChange(currentVideoIndex); } }, [currentVideoIndex, onVideoChange]); // 비디오 종료 시 다음 비디오로 전환 const handleVideoEnd = () => { setCurrentVideoIndex((prevIndex) => (prevIndex + 1) % videos.length); }; // 인디케이터 클릭 핸들러 const handleIndicatorClick = (index: number) => { setCurrentVideoIndex(index); // 비디오 즉시 재생 if (videoRef.current) { videoRef.current.currentTime = 0; videoRef.current.play(); } }; return (
{/* 비디오 배경 */} {/* 오버레이 */}
{/* 캐시 상태 표시 (개발용) */} {showCacheStatus && cachedVideos > 0 && (
Cached: {cachedVideos}/{videos.length}
)} {/* 비디오 인디케이터 */} {showIndicators && isVideoLoaded && (
{videos.map((_, index) => (
)}
); }