|
| 1 | +// import React, { useEffect, useRef } from 'react'; |
| 2 | +// // import YouTube from 'react-youtube'; |
| 3 | +// import { useYouTubeStore } from '@/store/youtubeStore'; |
| 4 | +// import { createPortal } from 'react-dom'; |
| 5 | +// // import { twMerge } from 'tailwind-merge'; |
| 6 | + |
| 7 | +// // YouTube Player Props 정의 |
| 8 | +// //1: MusicCard 2: chat 3: post |
| 9 | +// interface YouTubeAudioPlayerProps { |
| 10 | +// playerId: '1' | '2' | '3'; |
| 11 | +// } |
| 12 | + |
| 13 | +// const YouTubeAudioPlayer: React.FC<YouTubeAudioPlayerProps> = ({ playerId }) => { |
| 14 | +// const { isApiReady, players, setIsPlaying } = useYouTubeStore(); |
| 15 | + |
| 16 | +// const playerRef = useRef<YT.Player | null>(null); |
| 17 | +// const videoId = players[playerId]?.videoId || null; |
| 18 | +// const isPlaying = players[playerId]?.isPlaying || false; |
| 19 | + |
| 20 | +// // 플레이어 생성 |
| 21 | +// const createPlayer = () => { |
| 22 | +// if (!isApiReady || !videoId) { |
| 23 | +// if (playerRef.current && playerRef.current.seekTo) { |
| 24 | +// playerRef.current.seekTo(0, false); |
| 25 | +// playerRef.current.pauseVideo(); |
| 26 | +// } |
| 27 | +// return; |
| 28 | +// } // API 준비되지 않았거나 videoId가 없으면 |
| 29 | + |
| 30 | +// if (!playerRef.current) { |
| 31 | +// playerRef.current = new window.YT.Player(`player-${playerId}`, { |
| 32 | +// height: '1px', |
| 33 | +// width: '1px', |
| 34 | +// videoId: videoId, |
| 35 | +// playerVars: { |
| 36 | +// autoplay: 0, |
| 37 | +// controls: 0, |
| 38 | +// playsinline: 1, |
| 39 | +// origin: window.location.origin, // 현재 페이지의 origin을 전달 |
| 40 | +// }, |
| 41 | +// }); |
| 42 | +// } else { |
| 43 | +// playerRef.current.loadVideoById(videoId); // 이미 플레이어가 있으면 비디오를 새로 로드 |
| 44 | +// playerRef.current.pauseVideo(); |
| 45 | +// setIsPlaying(playerId, false); |
| 46 | +// } |
| 47 | +// }; |
| 48 | + |
| 49 | +// // isPlaying 변경 시 실행 |
| 50 | +// useEffect(() => { |
| 51 | +// if (!playerRef.current) return; // 플레이어가 초기화되지 않았으면 실행 안 함 |
| 52 | + |
| 53 | +// if (isPlaying) { |
| 54 | +// playerRef.current.playVideo?.(); |
| 55 | +// } else { |
| 56 | +// playerRef.current.pauseVideo?.(); |
| 57 | +// } |
| 58 | +// }, [isPlaying]); |
| 59 | + |
| 60 | +// useEffect(() => { |
| 61 | +// createPlayer(); |
| 62 | +// }, [videoId, isApiReady]); |
| 63 | + |
| 64 | +// useEffect(() => { |
| 65 | +// const playerElement = document.getElementById(`player-${playerId}`); |
| 66 | +// if (playerElement) { |
| 67 | +// playerElement.style.position = 'absolute'; |
| 68 | +// playerElement.style.top = '0px'; |
| 69 | +// } |
| 70 | +// }, []); |
| 71 | +// return createPortal(<div id={`player-${playerId}`}></div>, document.body); |
| 72 | +// }; |
| 73 | + |
| 74 | +// export default YouTubeAudioPlayer; |
| 75 | + |
1 | 76 | import React, { useEffect, useRef } from 'react'; |
2 | | -// import YouTube from 'react-youtube'; |
3 | | -import { useYouTubeStore } from '@/store/youtubeStore'; |
4 | 77 | import { createPortal } from 'react-dom'; |
5 | | -// import { twMerge } from 'tailwind-merge'; |
| 78 | +import { useYouTubeStore } from '@/store/youtubeStore'; |
6 | 79 |
|
7 | 80 | // YouTube Player Props 정의 |
8 | | -//1: MusicCard 2: chat 3: post |
| 81 | +// 1: MusicCard, 2: chat, 3: post |
9 | 82 | interface YouTubeAudioPlayerProps { |
10 | 83 | playerId: '1' | '2' | '3'; |
11 | 84 | } |
12 | 85 |
|
13 | 86 | const YouTubeAudioPlayer: React.FC<YouTubeAudioPlayerProps> = ({ playerId }) => { |
| 87 | + // 1. 스토어에서 필요한 상태와 함수를 가져옵니다. |
14 | 88 | const { isApiReady, players, setIsPlaying } = useYouTubeStore(); |
15 | | - |
16 | 89 | const playerRef = useRef<YT.Player | null>(null); |
17 | | - const videoId = players[playerId]?.videoId || null; |
18 | | - const isPlaying = players[playerId]?.isPlaying || false; |
19 | 90 |
|
20 | | - // 플레이어 생성 |
21 | | - const createPlayer = () => { |
| 91 | + // players 객체에서 현재 playerId에 맞는 videoId와 isPlaying 상태를 안전하게 추출합니다. |
| 92 | + const { videoId, isPlaying } = players[playerId] || { videoId: null, isPlaying: false }; |
| 93 | + |
| 94 | + // 2. 플레이어 생성, 파괴를 관리하는 메인 useEffect |
| 95 | + useEffect(() => { |
| 96 | + // API가 준비되지 않았거나 재생할 videoId가 없으면 플레이어를 생성하지 않습니다. |
22 | 97 | if (!isApiReady || !videoId) { |
23 | | - if (playerRef.current && playerRef.current.seekTo) { |
24 | | - playerRef.current.seekTo(0, false); |
25 | | - playerRef.current.pauseVideo(); |
26 | | - } |
27 | 98 | return; |
28 | | - } // API 준비되지 않았거나 videoId가 없으면 |
29 | | - |
30 | | - if (!playerRef.current) { |
31 | | - playerRef.current = new window.YT.Player(`player-${playerId}`, { |
32 | | - height: '1px', |
33 | | - width: '1px', |
34 | | - videoId: videoId, |
35 | | - playerVars: { |
36 | | - autoplay: 0, |
37 | | - controls: 0, |
38 | | - playsinline: 1, |
39 | | - origin: window.location.origin, // 현재 페이지의 origin을 전달 |
40 | | - }, |
41 | | - }); |
42 | | - } else { |
43 | | - playerRef.current.loadVideoById(videoId); // 이미 플레이어가 있으면 비디오를 새로 로드 |
44 | | - playerRef.current.pauseVideo(); |
45 | | - setIsPlaying(playerId, false); |
46 | 99 | } |
47 | | - }; |
48 | 100 |
|
49 | | - // isPlaying 변경 시 실행 |
50 | | - useEffect(() => { |
51 | | - if (!playerRef.current) return; // 플레이어가 초기화되지 않았으면 실행 안 함 |
| 101 | + // 새 플레이어 인스턴스를 생성합니다. |
| 102 | + playerRef.current = new window.YT.Player(`player-${playerId}`, { |
| 103 | + height: '0', // 플레이어는 보이지 않도록 처리합니다. |
| 104 | + width: '0', |
| 105 | + videoId: videoId, |
| 106 | + playerVars: { |
| 107 | + playsinline: 1, |
| 108 | + origin: window.location.origin, |
| 109 | + }, |
| 110 | + events: { |
| 111 | + // 플레이어가 준비되면 호출됩니다. |
| 112 | + onReady: (event: any) => { |
| 113 | + // isPlaying 상태가 true이면 비디오를 재생합니다. |
| 114 | + if (isPlaying) { |
| 115 | + event.target.playVideo(); |
| 116 | + } |
| 117 | + }, |
| 118 | + // 플레이어의 상태가 변경될 때마다 호출됩니다. (재생, 일시정지 등) |
| 119 | + onStateChange: (event: any) => { |
| 120 | + // 실제 플레이어 상태를 스토어(전역 상태)에 반영합니다. |
| 121 | + if (event.data === window.YT.PlayerState.PLAYING) { |
| 122 | + setIsPlaying(playerId, true); |
| 123 | + } else if (event.data === window.YT.PlayerState.PAUSED) { |
| 124 | + setIsPlaying(playerId, false); |
| 125 | + } |
| 126 | + }, |
| 127 | + }, |
| 128 | + }); |
52 | 129 |
|
53 | | - if (isPlaying) { |
54 | | - playerRef.current.playVideo?.(); |
55 | | - } else { |
56 | | - playerRef.current.pauseVideo?.(); |
57 | | - } |
58 | | - }, [isPlaying]); |
| 130 | + // 3. Cleanup 함수 (매우 중요) |
| 131 | + // useEffect가 다시 실행되기 전(즉, videoId가 바뀔 때) 또는 컴포넌트가 사라질 때 호출됩니다. |
| 132 | + return () => { |
| 133 | + if (playerRef.current && typeof playerRef.current.destroy === 'function') { |
| 134 | + playerRef.current.destroy(); // 기존 플레이어를 완전히 제거하여 메모리 누수를 방지합니다. |
| 135 | + playerRef.current = null; |
| 136 | + } |
| 137 | + }; |
| 138 | + // isApiReady, videoId가 변경될 때마다 이 로직을 다시 실행합니다. |
| 139 | + }, [isApiReady, videoId, playerId, isPlaying, setIsPlaying]); |
59 | 140 |
|
| 141 | + // 4. 스토어의 isPlaying 상태가 바뀔 때 플레이어를 제어하는 useEffect |
60 | 142 | useEffect(() => { |
61 | | - createPlayer(); |
62 | | - }, [videoId, isApiReady]); |
| 143 | + // 플레이어 인스턴스가 없거나, 아직 제어할 준비가 안 됐으면 아무것도 하지 않습니다. |
| 144 | + if (!playerRef.current || typeof playerRef.current.getPlayerState !== 'function') { |
| 145 | + return; |
| 146 | + } |
63 | 147 |
|
64 | | - useEffect(() => { |
65 | | - const playerElement = document.getElementById(`player-${playerId}`); |
66 | | - if (playerElement) { |
67 | | - playerElement.style.position = 'absolute'; |
68 | | - playerElement.style.top = '0px'; |
| 148 | + const playerState = playerRef.current.getPlayerState(); |
| 149 | + |
| 150 | + // 스토어 상태와 실제 플레이어 상태를 동기화합니다. |
| 151 | + if (isPlaying && playerState !== window.YT.PlayerState.PLAYING) { |
| 152 | + playerRef.current.playVideo(); |
| 153 | + } else if (!isPlaying && playerState !== window.YT.PlayerState.PAUSED) { |
| 154 | + playerRef.current.pauseVideo(); |
69 | 155 | } |
70 | | - }, []); |
71 | | - return createPortal(<div id={`player-${playerId}`}></div>, document.body); |
| 156 | + }, [isPlaying]); // isPlaying 상태가 변경될 때만 실행합니다. |
| 157 | + |
| 158 | + // 5. 플레이어를 body 최상단에 렌더링합니다. |
| 159 | + return createPortal(<div id={`player-${playerId}`} />, document.body); |
72 | 160 | }; |
73 | 161 |
|
74 | 162 | export default YouTubeAudioPlayer; |
0 commit comments