만들고 있는 프로젝트를 반응형 웹 사이트로 만들고 싶었다. 이를 구현하기 위한 방법 중 하나로 반응형 이미지가 있다.
매번 img태그로 이미지를 보여줬는데, 반응형 이미지란 무엇이며 어떻게 적용할 수 있는지 궁금해졌다.
반응형 이미지는 기기에 맞는 적절한 사이즈의 이미지를 제공한다. 왜그럴까?
mdn에서 반응형 이미지를 사용하지 않으면 발생할 수 있는 문제에 대해 크게 2가지로 나눠서 소개한다.
데스크탑 (출처: mdn - 반응형 이미지)

필자에게 일어났던 문제는 2번으로 실제 이미지의 크기가 2377 x 1507이지만, 화면에 보여줄 크기는 최대 700px이었다. 즉, 이미지가 들어갈 공간보다 훨씬 큰 이미지를 다운로드 받고 있었다.
그럼 이미지 사이즈만 바꾸면 되겠다고 생각했으나 문뜩 'png 이외의 다른 이미지 형식이 있지 않나?'라는 의문이 생겼다.
기존에 알고 있던 png, jpg 이외의 webp, avif 등 다양한 이미지 형식이 존재한다. 그 중 png를 사용했으나 딱히 선택한 이유는 없었다.
형식에 따라 이미지 압축률이 다르기 때문에 동일한 크기의 이미지더라도 용량이 달라질 수 있다. 즉, 적절한 이미지 형식을 선택하면 이미지 용량을 효과적으로 줄일 수 있다.
손실 압축은 일부 데이터를 삭제하여 완전히 원본 파일로 복원할 수 없다. 퀄리티는 떨어지더라도 파일 크기를 줄일 수 있다.
반대로, 무손실 압축은 압축된 데이터를 원본 데이터로 되돌릴 수 있다.
google에서 만든 squoosh에서 이미지 형식과 사이즈를 변경해보자.
webp로 변환한 결과 73%만큼 파일 크기를 줄일 수 있었다.

일부 브라우저에서 webp를 지원하지 않는다. webp를 지원하지 않는 브라우저에서 사용할 대체 이미지로 jpg를 최적화한 MozJPEG를 사용한다. 기존 png 파일보다 59%만큼 크기가 감소됐다.

현재 프로젝트에서 이미지의 최대 가로 사이즈는 700px이다. 그러나 레티나 디스플레이에서 이미지 해상도가 표준 디스플레이 기준으로 2배여야 한다.
700 x 4001400 x 800왜냐하면 레티나 디스플레이는 고해상도 디스플레이기 때문에 같은 공간에 더 많은 픽셀이 배치된다.
Resize에서 이미지 사이즈를 2377 x 1508에서 1400 x 888로 줄여 webp로 변환했을 때보다 9% 더 용량이 감소됐다.
모바일 환경에서 이미지의 최대 가로 길이가 약 500px이기 때문에 1040 x 660로 줄여 webp로 변환했을 때보다 16% 더 용량이 감소됐다.

<picture>, <source>picture와 source 태그를 이용해 기기의 가로 길이에 따라 다른 이미지를 보여주도록 했다.
600px 이하일 경우: 1040 x 660601px 이상일 경우: 1400 x 888source 태그의 속성을 자세히 살펴보자
<picture> 안에 위치할 경우 필수로 명시이미지_경로 원본_크기로 작성srcSet="이미지1_경로 이미지1_사이즈, 이미지2_경로 이미지2_사이즈"그리고 webp를 지원하지 않는 브라우저에는 jpg 형식의 이미지를 제공하기 위해 <img>의 src에 jpg 이미지 경로를 지정한다.

이미지의 최대 가로 사이즈를 제한하기 위해 아래와 같이 스타일 설정했다.
const MainImg = styled.img`
${imageStyles}
width: 100%;
max-width: 700px;
`;
jpg, webp 형식의 이미지를 Import하기 위해 프로젝트 최상단에 declaration.d.ts을 생성하여 아래와 같이 작성했다.
// declaration.d.ts
declare module '*.jpg';
declare module '*.webp';
그리고 tsconfig.json의 include에 declaration.d.ts를 추가하면 typescript 컴파일러가 jpg, webp 파일을 컴파일 대상으로 삼는다.
반응형 이미지를 적용하기 전에 main.png를 보면 318kb에 로드 시간은 8ms가 걸린 것을 확인할 수 있다.

반응형 이미지 적용 후, 모바일 환경에서 가로 길이가 1040px인 이미지가 다운로드되어 이전보다 파일 크기와 시간이 줄어들었다.

반면, 데스크탑 환경에서 가로 길이가 1400px인 이미지가 로드됐다.
