
👇🏻 실제 영상이 재생되지 않고 렌더링만 될 때 발생하는 리소스 요청

👇🏻 문제점: isShown이 true가 되는 순간 바로 iframe이 렌더링되어 불필요한 요청이 모두 발생
const YoutubeVideo = ({
embedUrl,
isShown,
}: {
embedUrl: string
isShown?: boolean
}) => {
if (!isShown) {
return (
<div className="flex relative justify-center items-center w-full bg-black aspect-video">
<div className="text-center text-white">
<p>Loading video...</p>
</div>
</div>
)
}
return (
<div
className="relative w-full aspect-video"
style={{ touchAction: "pan-y" }}
>
<iframe
className="w-full h-full"
src={embedUrl}
title="YouTube video"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
style={{ pointerEvents: "auto" }}
></iframe>
</div>
)
}
export default YoutubeVideo
isShown은 슬라이드에서 "현재 화면에 보이는 비디오만 렌더링/재생"하도록 만드는 상태값
이를 통해 불필요한 네트워크·메모리 낭비를 막고, 슬라이드 UX를 자연스럽게 만든다.
예를 들어, 좌우로 넘기는 슬라이드(Swiper, Carousel 등)에서 여러 개의 동영상 카드가 있을 때 항상 “딱 한 개(현재 인덱스)”만 isShown=true가 되고 나머지는 모두 false가 된다.
const getYoutubeThumbnailFromEmbed = (embedUrl: string) => {
// /embed/ 뒤 11자 추출
const match = embedUrl.match(/embed\/([\w-]{11})/)
return match ? `https://img.youtube.com/vi/${match[1]}/hqdefault.jpg` : null
}
const embedUrl = "https://www.youtube.com/embed/AbCdEfG1234"
const thumbnailUrl = getYoutubeThumbnailFromEmbed(embedUrl)
// 결과: "https://img.youtube.com/vi/AbCdEfG1234/hqdefault.jpg"
import { useEffect, useState } from "react"
interface Props {
embedUrl: string
isShown?: boolean
}
const YoutubeVideo = ({ embedUrl, isShown }: Props) => {
const [localActive, setLocalActive] = useState(false)
useEffect(() => {
if (!isShown) setLocalActive(false)
}, [isShown])
const isActive = !!isShown && localActive
const thumbnailUrl = getYoutubeThumbnailFromEmbed(video)
if (!isActive) {
return (
<button
type="button"
className="flex relative justify-center items-center w-full bg-black aspect-video group"
onClick={() => setLocalActive(true)}
style={{ cursor: "pointer" }}
>
{thumbnailUrl ? (
<>
<img
src={thumbnailUrl}
alt="YouTube Thumbnail"
className="object-cover absolute inset-0 w-full h-full transition-opacity group-hover:opacity-80"
/>
<span className="absolute p-4 text-white rounded-full bg-black/60">
<svg
width="48"
height="48"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M8 5v14l11-7z" />
</svg>
</span>
</>
) : (
<div className="z-10 text-center text-white">
<p>Loading video...</p>
</div>
)}
</button>
)
}
return (
<div
className="relative w-full aspect-video"
style={{ touchAction: "pan-y" }}
>
<iframe
className="w-full h-full"
src={embedUrl + (embedUrl.includes("?") ? "&" : "?") + "autoplay=1"}
title="YouTube video"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
style={{ pointerEvents: "auto" }}
></iframe>
</div>
)
}
export default YoutubeVideo
1. 최초 렌더링
2. 플레이 버튼 클릭
3. 슬라이드 이동

사용자가 플레이버튼을 누르기 전 단순 렌더링 시에는 썸네일이 보이며 네트워크 요청이 발생하지 않음
➡ 클릭 시 iframe이 렌더링되어 영상 자동 재생과 리소스 요청이 시작
iframe으로 유튜브, 페이스북 등 외부 영상을 렌더링할 때,
처음부터 iframe을 바로 띄우면 불필요한 네트워크 요청이 과도하게 발생한다.
이를 해결하기 위해서 최초에는 썸네일과 플레이 버튼만 보여주고,
사용자가 재생을 원하는 순간에만 iframe을 동적으로 렌더링하여
실제로 네트워크 요청과 영상 재생이 시작되도록 개선했다.
참고✨
위 코드는 유튜브 임베드 영상을 기준으로 작성되었습니다.
(페이스북 등 다른 플랫폼의 경우 썸네일 추출/iframe URL 패턴만 다르게 적용하면 동일한 방식으로 사용할 수 있습니다.)