반응형 이미지 적용하기 (feat: 이미지 형식)

aken·2024년 5월 7일
0

만들고 있는 프로젝트를 반응형 웹 사이트로 만들고 싶었다. 이를 구현하기 위한 방법 중 하나로 반응형 이미지가 있다.
매번 img태그로 이미지를 보여줬는데, 반응형 이미지란 무엇이며 어떻게 적용할 수 있는지 궁금해졌다.

반응형 이미지

반응형 이미지는 기기에 맞는 적절한 사이즈의 이미지를 제공한다. 왜그럴까?

사용하는 이유

mdn에서 반응형 이미지를 사용하지 않으면 발생할 수 있는 문제에 대해 크게 2가지로 나눠서 소개한다.

  1. 아트 디렉션 문제
    이미지의 중요한 부분이 데스크탑에서 보이지만 모바일에서 잘 보이지 않는 경우가 있다. 아래 예시에서 사람 2명의 얼굴이 데스크탑에서 또렷하게 잘 보이지만 모바일에서 확인하기 어렵다.
    데스크탑 데스크탑 (출처: mdn - 반응형 이미지)

모바일

모바일 (출처: mdn - 반응형 이미지)
  1. 해상도 전환 문제
    모바일 화면에서 이미지 사이즈가 크지 않기 때문에 굳이 큰 이미지를 다운로드 받을 필요없다. 만약 큰 용량의 이미지를 로드하면 시간이 오래 걸리기 때문에 적절한 사이즈의 이미지를 제공해야 한다.

문제

필자에게 일어났던 문제는 2번으로 실제 이미지의 크기가 2377 x 1507이지만, 화면에 보여줄 크기는 최대 700px이었다. 즉, 이미지가 들어갈 공간보다 훨씬 큰 이미지를 다운로드 받고 있었다.

그럼 이미지 사이즈만 바꾸면 되겠다고 생각했으나 문뜩 'png 이외의 다른 이미지 형식이 있지 않나?'라는 의문이 생겼다.

이미지 형식

기존에 알고 있던 png, jpg 이외의 webp, avif 등 다양한 이미지 형식이 존재한다. 그 중 png를 사용했으나 딱히 선택한 이유는 없었다.

형식에 따라 이미지 압축률이 다르기 때문에 동일한 크기의 이미지더라도 용량이 달라질 수 있다. 즉, 적절한 이미지 형식을 선택하면 이미지 용량을 효과적으로 줄일 수 있다.

손실 압축 vs 무손실 압축

손실 압축은 일부 데이터를 삭제하여 완전히 원본 파일로 복원할 수 없다. 퀄리티는 떨어지더라도 파일 크기를 줄일 수 있다.
반대로, 무손실 압축은 압축된 데이터를 원본 데이터로 되돌릴 수 있다.

png vs jpg vs webp

  • png는 무손실 압축 방식을 사용하여 이미지를 압축한다.
  • jpg는 손실 압축 방식을 사용해 png보다 화질은 떨어지더라도 더 작은 압축 파일을 만들 수 있다.
  • webp는 png와 같이 무손실 압축 방식을 사용하는 예시 중 하나다.
    • webp는 google이 개발한 포맷으로 jpg보다 더 효과적인 압축 알고리즘을 사용한다.
    • google 실험 결과에 따르면, webp는 jpg에 비해 25~34% 더 작은 파일 크기를 가진다.

적용

google에서 만든 squoosh에서 이미지 형식과 사이즈를 변경해보자.

1. 형식 변경

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

2. 사이즈 축소

현재 프로젝트에서 이미지의 최대 가로 사이즈는 700px이다. 그러나 레티나 디스플레이에서 이미지 해상도가 표준 디스플레이 기준으로 2배여야 한다.

  • 레티나 디스플레이에서 이미지 해상도: 700 x 400
  • 표준 디스플레이에서 이미지의 해상도: 1400 x 800

왜냐하면 레티나 디스플레이는 고해상도 디스플레이기 때문에 같은 공간에 더 많은 픽셀이 배치된다.

Resize에서 이미지 사이즈를 2377 x 1508에서 1400 x 888로 줄여 webp로 변환했을 때보다 9% 더 용량이 감소됐다.
small

모바일 환경에서 이미지의 최대 가로 길이가 약 500px이기 때문에 1040 x 660로 줄여 webp로 변환했을 때보다 16% 더 용량이 감소됐다.
moreSmallSize

3. <picture>, <source>

picturesource 태그를 이용해 기기의 가로 길이에 따라 다른 이미지를 보여주도록 했다.

  • 600px 이하일 경우: 1040 x 660
  • 601px 이상일 경우: 1400 x 888

source 태그의 속성을 자세히 살펴보자

  • type: source 태그의 리소스 타입을 명시
  • media: 미디어 쿼리
  • srcSet: 이미지 리소스 세트
    • <picture> 안에 위치할 경우 필수로 명시
    • 브라우저가 이미지를 선택하도록 권한을 넘김
    • 쉼표로 이미지를 구분하고, 이미지_경로 원본_크기로 작성
      ex) srcSet="이미지1_경로 이미지1_사이즈, 이미지2_경로 이미지2_사이즈"

그리고 webp를 지원하지 않는 브라우저에는 jpg 형식의 이미지를 제공하기 위해 <img>의 src에 jpg 이미지 경로를 지정한다.

codeEx

이미지의 최대 가로 사이즈를 제한하기 위해 아래와 같이 스타일 설정했다.

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.jsonincludedeclaration.d.ts를 추가하면 typescript 컴파일러가 jpg, webp 파일을 컴파일 대상으로 삼는다.

결과

반응형 이미지를 적용하기 전에 main.png를 보면 318kb에 로드 시간은 8ms가 걸린 것을 확인할 수 있다.

반응형 이미지 적용 전

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

모바일 환경

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

데스크탑 환경

정리

  • 반응형 이미지를 적용하는 이유는 다음과 같다.
    1. 똑같은 이미지더라도 작은 기기에서 이미지가 잘 보이지 않을 수 있다.
    2. 이미지 넣을 공간보다 훨씬 큰 이미지를 다운로드 받으면 로딩 속도가 저하된다.
  • 똑같은 이미지더라도 형식이 다르면 용량이 다르다. 왜냐하면 압축률이 다르기 때문이다.
    • 용량이 작은 순서는 보통 webp < jpg < png 이다.

0개의 댓글