Frontend

Javascript / HTML / CSS에서의 이미지 fallback 처리

둉이 2024. 8. 8. 19:36

프론트엔드 개발을 하다 보면 이미지를 다룰 일이 많다.

 

단순히 이미지를 화면에 보여주는 것뿐만 아니라 요구사항에 따라 에러 처리, 크로스 브라우징 지원, 미리보기 이미지 노출 등 여러 작업이 필요하다.

 

이미지를 안전하게 다루고, 사용자에게 적절한 이미지를 보여줄 수 있는 다양한 방법에 대해 알아보자.

 

 


 

 

Javascript에서의 fallback 처리

자바스크립트의 onload, onerror 핸들러를 사용하여 이미지 fallback 처리가 가능하다.

<img
  src="https://placehold.co/600x400?text=Preview+Image"
  alt="이미지"
  onload="console.log('이 이벤트는 매번 실행됩니다')"
  onerror="console.log('이 이벤트는 에러시 실행됩니다')"
/>

 

 

onload 이벤트 다루기

onload는 이미지 로드가 완료된 직후 실행되며, 이미지가 로드될 때 스르륵 나타나는 애니메이션을 적용할 때 주로 사용한다.

onload 이벤트 활용 예시

 

 

주의해야 할 점은, Next.js 서버 컴포넌트에서는 사용이 불가능하고, 캐싱된 이미지 로드 시에는 onload/onerror 이벤트가 발생하지 않는다.

 

이미지의 onload/onerror 이벤트가 최소 한 번은 실행되도록 하고 싶다면, 아래와 같이 별도의 Image 객체를 생성하여 요청하면 된다.

<div>
  <img
    id="target"
    src="https://placehold.co/600x400?text=Preview+Image"
    alt="안녕하세요."
    onload="console.log('이 이벤트는 매번 실행됩니다')"
    onerror="console.log('이 이벤트는 에러시 실행됩니다')"
  />
</div>

<script type="module">
  const targetImage = document.getElementById('target');

  const img = new Image();
  img.src = 'https://placehold.co/600x400/FFFF00/FF0000';
  img.onload = () => {
    targetImage.src = img.src;
  };
</script>

 

 

React를 사용하는 경우에는 useEffect 내에서 간단하게 기능 구현이 가능하다.

// React에서 Image 인스턴스 생성
const ImageBox = ({ src, previewSrc }: { src: string; previewSrc: string }) => {
  const [imageSrc, setImageSrc] = useState(previewSrc);

  useEffect(() => {
    if (!src) return;

    const img = new Image();
    img.src = src;
    img.onload = () => setImageSrc(src);
  }, [src]);

  return (
    <div>
      <img
        src={imageSrc}
        alt="안녕하세요."
        onLoad={() => {
          console.log('이 이벤트는 매번 실행됩니다');
        }}
        onError={() => {
          console.log('이 이벤트는 에러시 실행됩니다.');
        }}
      />
    </div>
  );

 

 

onerror 이벤트 다루기

onerror는 이미지 로드 도중 에러가 발생한 경우 실행되며, 에러시 fallback 이미지를 대신 보여주기 위해 주로 사용한다.

<img
  <!-- 잘못된 이미지 주소 -->
  src="https://placehold.c/600x400"
  alt="안녕하세요."
  onerror="this.src='https://pics.iacstatic.co.kr/common/static/noimg_318.jpg'"
/>

 

 

주의할 점으로는 fallback 이미지 요청이 실패하는 경우에도 onerror 이벤트가 발생하기 때문에, 무한 콜백 지옥에 빠질 수 있다.

 

이를 막고 싶다면 아래처럼 this.src를 변경하기 전에 this.onerror=null 조건을 추가하면 된다.

<img
  src="https://placehold.c/600x400?text=Preview+Image"
  alt="안녕하세요."
  <!-- this.onerror=null를 추가하지 않으면 무한 onerror 발생 -->
  onerror="this.onerror=null; this.src='https://pics.iacstatic.co.kr/no_image.jpg'"
/>

 

 

Javascript를 이용한 fallback 처리는 간단하지만 브라우저가 Javascript를 지원하지 않는 경우에는 동작하지 않는다.

 

이러한 경우를 위한 HTML을 이용한 fallback 처리 방법을 알아보자.

 

 

 

HTML에서의 fallback 처리

HTML5의 <object> 태그를 이용하여 이미지를 포함한 다양한 리소스의 fallback 처리가 가능하다.

<object data="https://placehold.com/no-image" type="images/*">
  <img src="https://placehold.com/600x400" alt="fallback" />
</object>

 

만약 data 속성으로 지정한 리소스 요청이 실패하면, object 태그 내 요소가 화면에 노출된다.

 

object를 중첩해서 사용하면 if ~ else 문처럼 복합적인 fallback 처리도 가능하다. 

<object data="interactive-content.swf" type="application/x-shockwave-flash">
  <object data="video-alternative.mp4" type="video/mp4">
    <object data="image-alternative.jpg" type="image/jpeg">
      <p>콘텐츠를 표시할 수 없습니다. <a href="text-version.html">텍스트 버전 보기</a></p>
    </object>
  </object>
</object>

 

 

 

비슷한 기능으로 <source> 태그도 있다.

 

<source>는 크로스 브라우징을 목적으로 여러 파일 확장자로 미디어 콘텐츠를 제공하기 위해 사용되는 태그이다.

<!-- 1. 이미지 요소 fallback 지정 -->
<picture>
  <source src="foo.jpeg" type="image/jpeg" />
  <source src="foo.png" type="image/png" />
  <source src="foo.png" type="image/png" />
  <img src="" />
</picture>

<!-- 2. 비디오 요소 fallback 지정 -->
<video>
  <source src="foo.webm" type="video/webm" />
  <source src="foo.ogg" type="video/ogg" />
  <source src="foo.mov" type="video/quicktime" />
  <p>Your browser doesn't support video.</p>
</video>

 

첫 번째 <source>에 지정한 리소스가 브라우저에서 지원하지 않는 확장자인 경우, 다음 <source>의 리소스를 요청한다.

 

만약 모두 지원하지 않는 경우에는 맨 마지막 요소(img 혹은 텍스트)를 노출한다.

 

이미지의 경우 <picture>, 동영상의 경우 <video>, 오디오의 경우 <audio> 태그로 감싸서 사용한다.

 

<picture>와 함께 사용하는 경우에는 마지막 자식 요소가 반드시 <img> 태그 요소여야 정상적으로 동작한다.

 

<video>, <audio> 태그는 텍스트 요소도 가능하다.

 

Javascript가 지원되지 않는 경우를 포함한 어떠한 브라우저 환경에서도 fallback 이미지(혹은 텍스트)를 보장할 수 있다는 장점이 있다.

 

 

 

CSS에서의 fallback 처리

CSS에서는 background-image 속성(background)을 사용하여 요소에 이미지를 삽입할 수 있다.

.image {
    background: url("//script.gmarket.co.kr/build/mobile/image/sprite/home/sp-home.png")
    	no-repeat, 324px 306.333333333px;
    display: inline-block;
    overflow: hidden;
}

 

여러 이미지 에셋을 하나의 스프라이트 이미지로 관리하는 경우에 주로 사용하는 방법이며,각 요소마다 background 속성에 이미지 url, size, position 등 속성을 지정해서 사용한다.

 

물론 사용자의 네트워크 상태나 cdn 이슈로 이미지를 받아올 수 없는 경우가 생길 수도 있다.

 

아래 코드처럼 url(링크1), url(링크2), ... 형태로 여러 개의 url을 지정하면 첫 번째 이미지 요청이 실패했을 때 자동으로 두 번째 이미지가 요청된다.

.image {
    background: url("//script.gmarket.co.kr/build/mobile/image/sprite/no-image.png"),
    	url("//script.gmarket.co.kr/build/mobile/image/sprite/home/sp-home.png")
        no-repeat, 324px 306.333333333px;
    display: inline-block;
    overflow: hidden;
}

 

 

의사 요소(= pseudo element)에 fallback 이미지를 보여주는 방법도 많이 사용된다.

.item_list .thumb::before {
  content: url(//pics.iacstatic.co.kr/common/static/noimg_318.jpg);
}

 

 

 

참고 링크

 

HTML only image fallback

Some days ago I was doing something to personalize my Github profile page and I needed a way to...

dev.to

 

Image 태그 작성시 onError의 처리(무한루프)

이미지 태그를 이용할 시 참조하는 이미지가 없을 경우를 대비해 대체할 이미지를 보여주는 기능이 있다. 바로 onError라는 속성인데 작성하는 방식은 아래와 같다. 하지만, 이 부분에서도 무한루

short-developer.tistory.com