기존에 Youtube Data API에서 비디오 데이터를 가져올 때, videos API를 통해 요청하면 채널 썸네일 URL이 제공되지 않았습니다.
그래서 비디오 데이터에서 채널 ID를 추출한 후 별도로 채널 정보를 요청하여 썸네일 URL을 가져오는 로직을 구현했습니다.
또한, VideoDetail 라우터를 설정하여 비디오 카드를 클릭했을 때 해당 상세 페이지로 이동하며, 데이터를 함께 전달하는 기능을 구현했습니다.
1. youtubeAPI 수정
Youtube Data API 하루 할당량을 조금이라도 아끼려고, new Set()을 사용하여 중복된 채널 ID를 제거 한 후 채널 정보를 요청했습니다.
import axios from "axios";
const YoutubeApi = {
httpClient: axios.create({
baseURL: "https://www.googleapis.com/youtube/v3",
params: { key: process.env.REACT_APP_YOUTUBE_API_KEY },
}),
async getVideosByKeyword(keyword: string, pageToken: string) {
const response = await this.httpClient.get("search", {
params: {
part: "snippet",
maxResults: 20,
q: keyword,
pageToken: pageToken,
},
});
const videoIds = response.data.items.map((item: any) => item.id.videoId).join(',');
const channelIds = Array.from(new Set(response.data.items.map((item: any) => item.snippet.channelId)));
const videosResponse = await this.httpClient.get("videos", {
params: {
part: "snippet,statistics",
id: videoIds,
},
});
// 채널 썸네일을 가져오기 위한 요청
const channelsResponse = await this.httpClient.get("channels", {
params: {
part: "snippet",
id: channelIds.join(','),
},
});
// 채널 썸네일을 매핑
const channelThumbnails: { [key: string]: string } = {};
channelsResponse.data.items.forEach((channel: any) => {
channelThumbnails[channel.id] = channel.snippet.thumbnails.default.url;
});
// 비디오 정보와 채널 썸네일을 결합하여 반환
return {
items: videosResponse.data.items.map((item: any) => ({
...item,
id: item.id,
channelThumbnail: channelThumbnails[item.snippet.channelId],
})),
nextPageToken: response.data.nextPageToken,
};
},
};
export default YoutubeApi;
생략했지만 getPopularVideos 함수에서도 동일하게 중복된 채널 ID를 제거한 후 채널 정보를 요청하도록 코드를 수정했습니다.
2. Videos 컴포넌트에서 스타일 적용
Videos 컴포넌트에서는 검색어(keyword)의 유무에 따라 리스트 또는 그리드 형식으로 비디오를 표시하도록 스타일을 다르게 적용했습니다.
현재 무한 스크롤이 구현되어 있어 그리드 형식에서는 조금만 내려도 무한 스크롤로 인해 다시 API 요청이 발생합니다.
검색 데이터는 할당량이 많이 소모되기 때문에 이를 보완하기 위해 리스트 형식으로 변경하여 스크롤을 더 많이 내려야 API 요청이 이루어지도록 하였습니다.
export default function Videos() {
const listStyle = keyword ? styles.list : styles.grid;
return (
<ul className={listStyle}>
...
</ul>
);
}
3. VideoCard 컴포넌트의 스타일 및 기능 구현
VideoCard 컴포넌트에서도 마찬가지로 검색어 유무에 따라 다른 스타일을 적용하고, li 요소를 클릭했을 때 비디오 상세 페이지로 이동하도록 navigate 함수를 활용해 라우팅을 추가했습니다.
이때, 비디오 데이터를 함께 전달하여 상세 페이지에서 사용할 수 있게 했습니다.
const VideoCard = ({ video }, ref) => {
const cardClassName = keyword ? styles['card-search'] : styles.card;
const navigate = useNavigate();
return (
<li
className={cardClassName}
ref={ref}
onClick={() => navigate(`videos/watch/${video.id}`, { state: { video } })}
>
...
</li>
);
};
export default function VideoDetail() {
const { state: { video } } = useLocation();
console.log(video);
return <div>VideoDetail</div>;
}
VideoDetail 페이지에서는 전달받은 비디오 데이터를 단순히 콘솔에 출력만 해두었습니다.
이후 이 데이터를 기반으로 UI를 추가할 예정입니다.
.img-container {
position: relative;
background-color: #f0f0f0;
border-radius: 16px;
overflow: hidden;
transition: border-radius 0.5s, transform 0.3s;
}
.card .img-container {
width: 100%;
padding-top: 56.25%;
}
.card-search .img-container {
width: 36%;
padding-top: 20.25%;
}
/* ... */
.card-search {
display: flex;
gap: 22px;
justify-content: center;
}
.card 클래스와 .card-search 클래스는 카드 레이아웃과 검색 시 적용되는 레이아웃을 다르게 적용했습니다.
'React > Youtube' 카테고리의 다른 글
YouTube Data API를 이용한 Youtube Clone - 7 (4) | 2024.09.07 |
---|---|
YouTube Data API를 이용한 Youtube Clone - 6 (0) | 2024.09.04 |
YouTube Data API를 이용한 Youtube Clone - 4 (5) | 2024.09.01 |
YouTube Data API를 이용한 Youtube Clone - 3 (0) | 2024.08.29 |
YouTube Data API를 이용한 Youtube Clone - 2 (0) | 2024.08.28 |