본문 바로가기
기타

[ASP.NET Core/APOD] img의 src 속성이 이미지가 아닌 비디오일 경우 iframe의 src 속성으로 비디오를 출력하는 기능 구현

by 항붕쿤 2023. 4. 19.

NASA의 APOD api가 제공하는 우주 정보 중에는 이미지가 아닌 영상도 존재한다. 따라서 img 태그만으로 이를 받아들일 경우 

동영상이 위와 같이 출력된다.

그러므로 img 태그의 src 속성이 이미지가 아닐 경우, 즉 src를 로드하는데 문제가 발생할 경우, 다시말해 onerror 속성이 동작할 경우 iframe 태그를 생성하고 iframe의 src 속성에 img의 src 속성 주소를 입력하도록 코딩을 해주어야한다.

그 기능은 아래와 같이 구현할 수 있다.

<div id="media-container">
	<img src="@Html.DisplayFor(model => model.APOD.Url)" onerror="showIframe()"/>
</div>

<script>
	function showIframe() {
		var container = document.getElementById("media-container");
		var iframe = document.createElement("iframe");
		iframe.src = "@Html.DisplayFor(model => model.APOD.Url)";
		
		container.appendChild(iframe);
	}
</script>

그러나

img 태그는 그대로 남아있어 동영상 아래에 저걸 뭐라하노 어쨌든 보기 불편한 이미지가 잔존하게 된다.
이를 없애기 위해 showIframe 함수가 실행되었을 때 기존의 img 태그를 삭제하도록 코딩해 준다.

<div id="media-container">
	<img id="media" src="@Html.DisplayFor(model => model.APOD.Url)" onerror="showIframe()"/>
</div>

<script>
	function showIframe() {
		var container = document.getElementById("media-container");
		var iframe = document.createElement("iframe");
		iframe.src = "@Html.DisplayFor(model => model.APOD.Url)";
		
		var img = document.getElementById("media");

		container.removeChild(img);
		container.appendChild(iframe);
	}
</script>

기존의 img 태그에 id를 붙여주고 showIframe 함수가 실행되면 img 태그의 id를 가진 태그를삭제하도록 코딩하여 그 기능을 구현하였다.

성공적으로 작동한다.

이제 Index 페이지에 img 태그의 src 속성이 이미지가 아니어서 나오는 불편한 이미지를 동영상으로 바꾸기 위해 위와 같은 방법을 이용했지만..

맨 우쪽 하단 카드의 불편한 이미지가 동영상으로 바뀌어야 하는데 생뚱맞게 맨 좌측 상단의 카드 이미지가 동영상으로 대체되었다. 이게 뭐노..

@foreach (var apod in Model)
{
    <div class=" col-3 mb-5">
        <div class="card border-secondary h-100">

            <script>
                function showIframe() {
                    var container = document.getElementById("media-container");
                    var iframe = document.createElement("iframe");
                    iframe.src = "@Html.DisplayFor(item => apod.Url)";

                    var img = document.getElementById("media");
                    iframe.classList.add("card-img-top");

                    container.removeChild(img);
                    container.appendChild(iframe);
                }
            </script>

            <div id="media-container">
                <img id="media" src="@Html.DisplayFor(item => apod.Url)" class="card-img-top" alt="..." style="object-fit: cover; width: 300px; height: 150px" onerror="showIframe()">
            </div>

            <div class="card-body">
                <p class="card-title">@Html.DisplayFor(item => apod.Title)</p>

            </div>
            <div class="card-footer">
                <a asp-action="Details" asp-route-id="@apod.Id">보기</a>
            </div>
        </div>
    </div>
}

코드를 보니 딱 견적이 나온다.
비디오를 출력할 위치로 id 속성의 값이 media-container인 곳을 선정했는데, 반복문을 실행하는 과정에서 이 id가 중복으로 호출되었으나 id 속성의 값은 여러곳에 중복사용이 불가능하여 두번째 이후의 카드부터 id 속성이 생성되지 않게 되어 첫번째 카드에 비디오가 띄워진 것이다.

이를 해결하기 위해  다음과 같이 코드를 수정하였다.

<div class="media-container">
    <img src="@Html.DisplayFor(item => apod.Url)" class="card-img-top" style="object-fit: cover; width: 300px; height: 150px" onerror="showIframe(this)">
</div>

<script>
    function showIframe(img) {
        var container = img.parentElement;
        var iframe = document.createElement("iframe");
        iframe.src = "@Html.DisplayFor(item => apod.Url)";

        iframe.classList.add("card-img-top");

        container.removeChild(img);
        container.appendChild(iframe);
    }
</script>

먼저 media-container라는 값을 class 속성의 값으로 만듦으로써 각각의 카드가 media-container라는 값을 갖게 하였고, showIframe 함수의 인자로 각 img 태그의 요소(this)를 전달하여 해당 요소의 부모 요소인 media-container를 찾도록 하였다.

이후 실행시켜보니

잘 동작한다 ^^b


문제가 발생했다.

동영상이 존재하는 다른 페이지를 확인하여, 동영상이 로드되지 않는 카드를 확인해보니 해당 카드 iframe 태그의 src 속성이 맨 마지막 카드의 src 속성으로 대체되어 있었다.

이게 무슨 일인고 찾아보니..

Razor 문법을 사용한 코드에서는 서버에서 C# 코드(foreach, Html.DiplayFor 등등..)가 먼저 실행되어 HTML 코드로 변환되고, 변환된 HTML 코드가 브라우저로 전달된다. 그 다음으로 브라우저가 HTML과 JavaScript 코드를 해석하면서 script 태그 안의 코드가 실행된다. 즉, 루프가 끝난 이후에 실행되므로, 마지막 카드의 URL 값이 iframe 태그의 src 속성값으로 설정되는 것이다.

그러므로 코드를 다음과 같이 수정해준다.

<script>
    function showIframe(img, url) {
        var container = img.parentElement;
        var iframe = document.createElement("iframe");

        iframe.src = url;
        iframe.allowFullscreen = true;

        container.removeChild(img);
        container.appendChild(iframe);
    }
</script>

<div class="media-container">
    <img src="@Html.DisplayFor(item => apod.Url)" class="card-img-top" style="object-fit: cover; width: 300px; height: 150px" onerror="showIframe(this, '@apod.Url')">
</div>

showIframe 함수의 매개변수로 해당 카드의 src 속성에 해당하는 url을 직접 전달하여 iframe 태그를 생성하는 방식으로 수정하였다.

그 결과..

잘 작동한다 ^^b