프론트엔드 개발을 하다 보면 이미지를 다룰 일이 많다.
단순히 이미지를 화면에 보여주는 것뿐만 아니라 요구사항에 따라 에러 처리, 크로스 브라우징 지원, 미리보기 이미지 노출 등 여러 작업이 필요하다.
이미지를 안전하게 다루고, 사용자에게 적절한 이미지를 보여줄 수 있는 다양한 방법에 대해 알아보자.
Javascript에서의 fallback 처리
자바스크립트의 onload, onerror 핸들러를 사용하여 이미지 fallback 처리가 가능하다.
<img
src="https://placehold.co/600x400?text=Preview+Image"
alt="이미지"
onload="console.log('이 이벤트는 매번 실행됩니다')"
onerror="console.log('이 이벤트는 에러시 실행됩니다')"
/>
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);
}
참고 링크
'Frontend' 카테고리의 다른 글
텍스트 조각으로 특정 페이지 내 영역 이동, 하이라이팅을 해보자 (2) | 2024.10.30 |
---|---|
직접 겪은 카카오 인앱 브라우저 이슈/해결 방법 (8) | 2024.09.11 |
userAgent가 복잡해진 이유 & 브라우저의 역사 알아보기 (2) | 2023.09.14 |