서비스가 심미적이나 기능적으로 모두 구현되었다고 끝이 아님을 대부분 개발자들이 알고 있을 것이라 생각한다. 그중에 꼭 짚고 넘어가야 할 것이 어셋들(image, video, sound 등)에 대한 성능 최적화 작업이다. 성능 최적화 작업은 프론트엔드, 벡엔드, 디자이너 단에서 모두 각자의 방식으로 작업이 가능하나 그중 나는 프론트엔드 쪽에서 작업한 경험을 간단하게 정리해 보았다.
이미지 최적화하기
svg
svg 이미지는 sources로 사용하는 것보다 svg그대로 사용하는 것이 성능에 좋다.
import { ReactComponent as Logo } from "assets/icons/logo_bg.svg";
const LogoComponent = () => {
return (
<Logo title="logo image" />
);
};
sources
하나의 소스를 제공하는 것이 아니라, 최적화를 위해 여러 이미지는 제공하고 브라우저가 렌더링 할 수 있는 이미지를 사용한다.
아래의 예시에서는 브라우저가 AVIF 이미지를 렌더링할 수 있는 경우 선택한 이미지 파일이다. 그렇지 않으면 다음 요소로 이동해 두 번째 요소는 WebP 형식source 의 이미지를 가리키게 된다. 브라우저가 WebP 이미지를 렌더링할 수 있는 경우 해당 이미지 파일을 사용하고 그렇지 않으면 img에 있는 이미지 파일(JPEG)로 대체된다.
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="A description of the image."
width="300" height="200" loading="lazy" decoding="async">
</picture>
WebP 파일이 PNG 파일보다 26% 작고 JPEG 파일보다 최대 34% 작기 때문에 매우 빠른 로딩 시간을 제공한다.
Can I Use 에 따른 글로벌 WebP 및 AVIF 지원 수치는 다음과 같다.
- WebP : 웹 사용자의 약 96.30%가 WebP를 지원하는 브라우저를 사용하고 있습니다.- AVIF : 웹 사용자의 약 79.81%가 AVIF를 지원하는 브라우저를 사용하고 있습니다.
lazy
true로 설정하면 페이지에서의 위치에 관계없이 리소스를 즉시 로드하지 않고 뷰포트로부터 계산된 거리에 도달할 때까지 리소스 로딩을 지연시킨다. 스크롤 해야 보이는 이미지의 경우는 lazy를 처리하는 것이 첫 로딩에 부하를 줄일 수 있다.
<img src="image.png" loading="lazy" alt="…" width="200" height="200">
srcSet
디스플레이 사양(1x, 2x, 3x) 에 따라 w 디스크립터(Width descriptor)를 사용해 포트 너비에 최적화된 이미지를 선택해 출력할 수 있다. 다른 해상도의 이미지를 각각 제공하는 것이 시각적으로도 좋고 데이터 낭비도 줄여서 유저 경험에 긍정적인 효과를 줄 수 있다.
다른 확장자와 dpr에 따른 이미지가 필요하다면 아래와 같이 구현이 가능하다.
위의 이미지처럼 html 코드가 길어지기 때문에 공통적으로 사용할 수 있는 image 컴포넌트를 따로 만들어 구현하는 것이 서비스 전반적으로 적용하기에 훨씬 나았어서 구현했던 코드를 간단히 공유해본다. -> GitHub Link
import ImagePng from "assets/images/fallback.png";
import ImageAvif1x from "assets/images/image_1.avif";
import ImageAvif2x from "assets/images/image_2.avif";
import ImageAvif3x from "assets/images/image_3.avif";
import ImageWebp1x from "assets/images/image_1.webp";
import ImageWebp2x from "assets/images/image_2.webp";
import ImageWebp3x from "assets/images/image_3.webp";
const MainImage = () => {
return (
<Image
src={ImagePng} // fallback 이미지
sources={[
{ srcSet: `${ImageAvif1x} 1x, ${ImageAvif2x} 2x, ${ImageAvif3x} 3x`, type: "avif" },
{ srcSet: `${ImageWebp1x} 1x, ${ImageWebp2x} 2x, ${ImageWebp3x} 3x`, type: "webp" },
]}
width="100%"
/>
);
};
출처
- https://web.dev/learn/design/picture-element/#image-formats
- https://themeisle.com/blog/avif-vs-webp
- https://web.dev/browser-level-image-lazy-loading/
'Developer > React' 카테고리의 다른 글
i18n(국제화 언어)리액트에서 적용하기 (0) | 2024.03.11 |
---|---|
리액트에서 DOM 사이즈 줄이는 최적화 방법 (0) | 2024.01.10 |
useEffect와 useLayoutEffect의 차이 예제로 쉽게 이해하기 (0) | 2023.01.13 |
React Clean Code 를 위한 팁 (0) | 2022.09.04 |
Cannot use JSX unless the '--jsx' flag is provided.ts(17004) 빨간줄 없애기 (0) | 2022.02.09 |