제목은 뭔가 완성된 프로그램을 만든 것처럼 보이지만
내가 만든건 프로그레스바(재생바)가 동작하지 않는 반쪽짜리 프로그램이다.
UI에서부터 느껴지는 반푼이 프로그램의 기운...
주제에 반응형으로 늘어났다 줄어들었다 한다 ㅎ
아무튼 이걸 어떻게 만들었냐면
유튜브 ifram API라는걸 이용해서 만들었다.
ifram player API에 대한 자세한 정보는 아래 링크를 참조하면 된다.
https://developers.google.com/youtube/iframe_api_reference?hl=ko
iframe 삽입에 대한 YouTube Player API 참조 문서 | YouTube IFrame Player API | Google for Developers
애플리케이션에 YouTube 플레이어를 삽입합니다.
developers.google.com
그런데 위 레퍼런스대로 영상을 땡겨오면
내가 만든 것 같은 커스텀 미디어 컨트롤러 UI가 적용되지 않은
유튜브 영상 그대로가 퍼와진다.
나도 처음엔 당황했었는데 단순히 크기를 0으로 수정하면 영상이 사라지고
커스텀 컨트롤러 UI를 적용할 수 있다.
아래 코드는 내 코드에서 axios 통신하는 부분과 앨범 커버 부분만 제외한 코드인데
axios 통신을 통해 서버에서 앨범 커버 이미지의 URL을 불러오기 때문에
아래 코드를 그대로 가져다 쓸 경우 오류를 발생할 수 밖에 없으므로 해당 부분은 제외했다.
참고로 MUI를 이용해서 코드를 작성했기 때문에
MUI가 설치되어있지 않다면
npm update를 이용해 패키지를 한 번 업데이트 하고 코드를 실행해야 한다.
import React,{useEffect, useState} from "react";
import { useTheme } from "@mui/material/styles";
import {
Box,
Card,
CardContent,
CardMedia,
IconButton,
} from "@mui/material";
import SkipPreviousIcon from "@mui/icons-material/SkipPrevious";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import PauseIcon from '@mui/icons-material/Pause';
import SkipNextIcon from "@mui/icons-material/SkipNext";
export default function Musiccontroller() {
useEffect(() => {
const tag = document.createElement("script");
tag.src = "https://www.youtube.com/iframe_api";
const firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
window.onYouTubeIframeAPIReady = initializePlayer;
return () => {
delete window.onYouTubeIframeAPIReady;
};
},[]);
const theme = useTheme();
const [player, setPlayer] = useState(null);
const [videoTitle, setVideoTitle] = useState("");
const [progress, setProgress] = useState(0);
const [currentTime, setCurrentTime] = useState(0);
const [totalTime, setTotalTime] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const [volume, setVolume] = useState(50);
const initializePlayer = () => {
setPlayer(
new window.YT.Player("player", {
height: "0", // 영상은 표시하고 싶지 않을때 크기를 0으로 만들면 됨
width: "0",
videoId: "JGwWNGJdvx8", //music id, 원하는 영상의 musicId를 넣으면 해당 영상 재생
playerVars: {
rel: 0,
controls: 0,
autoplay: 1,
loop: 1,
playsinline: 1,
},
events: {
onReady: onPlayerReady,
onStateChange: onPlayerStateChange,
},
})
);
};
const onPlayerReady = (event) => {
setVideoTitle(event.target.getVideoData().title);
setTotalTime(event.target.getDuration());
setInterval(updateProgressBar, 1000);
};
const onPlayerStateChange = (event) => {
if (event.data === window.YT.PlayerState.PLAYING) {
setIsPlaying(true);
} else {
setIsPlaying(false);
}
};
const updateProgressBar = () => {
if(player){
const currentTime = player.getCurrentTime();
const duration = player.getDuration();
setProgress((currentTime / duration) * 100);
setCurrentTime(currentTime);
}
};
const formatTime = (time) => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`;
};
const togglePlayPause = () => {
if (isPlaying) {
player.pauseVideo();
} else {
player.playVideo();
}
};
const seekToTime = (value) => {
const duration = player.getDuration();
const seekTime = (value / 100) * duration;
player.seekTo(seekTime);
};
const changeVolume = (newVolume) => {
player.setVolume(newVolume);
setVolume(newVolume);
};
return (
<>
<div id="player"></div>
<Card sx={{ display: "flex", background: "transparent", color: "white" }}>
<Box
sx={{
display: "flex",
flexDirection: "column",
}}>
<CardContent sx={{ flex: "1 0 auto", textAlign: "center" }}>
<div style={{maxWidth:150}}>{videoTitle}</div>
</CardContent>
<div style={{width: 150}}>
<input
type="range"
value={progress}
onChange={(e) => seekToTime(e.target.value)}
/>
<span>{formatTime(currentTime)}</span> /
<span>{formatTime(totalTime)}</span>
</div>
<div style={{width: 150}}>
<input
type="range"
value={volume}
onChange={(e) => changeVolume(e.target.value)}
min="0"
max="100"
/>
</div>
<Box
sx={{
display: "flex",
alignItems: "center",
pl: 1,
pb: 1,
}}>
<IconButton
aria-label="previous"
sx={{
color: "white",
}}>
{theme.direction === "rtl" ? (
<SkipNextIcon />
) : (
<SkipPreviousIcon />
)}
</IconButton>
<IconButton aria-label="play/pause" onClick={togglePlayPause}>
{isPlaying?<PauseIcon sx={{ height: 38, width: 38, color: "white" }} />: <PlayArrowIcon sx={{ height: 38, width: 38, color: "white" }} />}
</IconButton>
<IconButton
aria-label="next"
sx={{
color: "white",
}}>
{theme.direction === "rtl" ? (
<SkipPreviousIcon />
) : (
<SkipNextIcon />
)}
</IconButton>
</Box>
</Box>
</Card>
</>
);
}
생각보다 코드가 길다...
이 코드를 구현하면서 어려웠던 부분이 있었는데
사실 이 코드는 chatGPT가 만들어준 코드이다ㅎ
그러다보니 최초로 chatGPT가 만들어준 코드는
쌩 날것 그대로의, 눈뜨고 보기 어려운 수준의 UI로 이루어져 있었고
UI를 조금 손보고 싶어서 MUI를 이용해
아이콘도 끼얹고 디자인도 조금 끼얹었다.
근데 커스텀 컨트롤러 UI를 다 만들고 chatGPT의 코드에서
함수 부분과 useState, useEffect 부분을 모조리 다 복붙 했는데도
커스텀 컨트롤러 UI를 이용한 영상 재생이 안되어서 좀 멘붕이었다.
한참 삽질하다가 이유를 발견할 수 있었는데
내가 UI부분을 죄다 뜯어고치느라
chatGPT가 작성한 UI부분 코드를 제대로 가져오지 않아서
오류가 발생하는거였다.
<div id="player"></div>
내가 빼먹은 코드는 이 작고 하찮은 코드 한줄이었다.
이 짧은 코드가 iframe API로부터 불러온
동영상이 위치할 페이지 식별하는 역할을 한다.
영상을 불러와야 재생을 하든 말든 할텐데
아무런 미디어 데이터도 없는 상황에서
재생 버튼을 연타했으니 컴퓨터도 좀 당황스러웠을 것 같다.
발견한 방법도 좀 웃긴데
chatGPT의 코드를 한 10번 쯤 다시 봤을 때
"어 얘는 왜 div태그 안에 아무런 내용이 없지??" 싶어서
리액트 코드쪽에 복붙해봤고,
그제서야 커스텀 컨트롤러 UI가 정상적으로 동작 했다.
아무튼 그랬다...
'코딩 > ReactJS' 카테고리의 다른 글
리액트에서 게시글 목록 클릭해서 해당 게시글 상세 페이지로 이동하기 (0) | 2023.08.27 |
---|---|
리액트에서 useParams를 이용해 유동적으로 페이지 주소 지정하기 (0) | 2023.08.25 |
ReactJS에서 외부 JSON 파일 불러와서 사용하기 (0) | 2023.03.04 |
ReactJS에서 이미지 저장하고 불러오는 방법 (0) | 2023.03.03 |
ReactJS 디자인 초보를 위한 부트스트랩 사용하기 (0) | 2023.03.02 |