Project Soljik DevLog
  • Project Soljik 소개 문서
  • 프로젝트 소개
    • 솔직한 온라인 쇼핑, Soljik
    • 기술 스택
    • 컨벤션
  • 최적화
    • 이미지 최적화
    • Lazy Loading / Code Splitting
    • 렌더링 최적화
  • 트러블 슈팅
    • BaaS(Firebase) 환경에서의 세션 관리
    • Firebase 저장소의 CORS 에러 해결
    • 다음 우편번호 API 적용하기
Powered by GitBook
On this page
  • 이미지 최적화의 필요성
  • 최적화 방법
  • 코드 예시
  • 성능 측정
  • 결론
  1. 최적화

이미지 최적화

프로젝트에 출력되는 이미지들의 최적화 과정을 정리한 문서입니다.

Previous컨벤션NextLazy Loading / Code Splitting

Last updated 1 month ago

이미지 최적화의 필요성

Store Soljik 프로젝트는 e-commerce 를 주제로 한 프로젝트로, 판매자들이 등록한 상품 이미지들의 출력이 굉장히 많은 프로젝트입니다.

사용자들이 업로드하는 원본 이미지들은 크기가 작게는 KB, 많게는 MB 단위를 가지기 때문에 다음과 같은 문제점이 발생합니다.

  • 초기 페이지 로딩 속도 저하

  • 사용자의 데이터 사용량 증

따라서 원본 이미지를 그대로 사용하기보단, 적절한 크기로 조정해서 사용할 필요가 있습니다.


최적화 방법

크게 2가지 방식을 사용합니다.

1

최신 이미지 포맷 사용하기

  • jpg , png 같은 기존의 이미지 포맷 대신 WebP 포맷을 사용합니다.

  • 최신이미지 포맷을 사용할 때, 브라우저 호환성을 고려하여 <picture> 태그를 사용해 구현합니다.

2

이미지 리사이징

  • 원​본 이미지를 크기의 구간별로 리사이징을 진행해서 각각 저장합니다.

  • 이미지 변환에는 FileReader, Canvas API 를 사용합니다.

이미지 최적화 순서는 다음과 같습니다.


코드 예시

프로젝트 내부적으로 사용되는 로직들은 제외하고 순수하게 이미지 리사이징을 위해 작성된 코드는 다음과 같습니다.

const resizeImage = async (file: File, size: number) => {
  // 이미지 파일 객체를 DataURL 로 변환시킵니다.
  const reader = new FileReader();
  reader.readAsDataURL(file);

  // onload 메서드를 통해 변환된 DataURL 을 사용합니다.
  reader.onload = () => {
    // image 객체의 src 를 DataURL 로 지정합니다.
    let image = new Image();
    image.src = reader.result as string;
    
    // onload 메서드를 통해 DataURL 로 로딩된 이미지를 사용합니다.
    image.onload = () => {
      let { width, height } = image; // 원본 이미지의 가로, 세로 크기

      // Canvas 를 생성하고, 리사이징된 이미지를 그릴 2D 렌더링 Context 를 생성합니다.
      let canvas = document.createElement('canvas');
      const canvasContext = canvas.getContext('2d') as CanvasRenderingContext2D;

      // 원본의 크기가 요구 사이즈보다 크면 리사이징을 진행합니다.
      if (size > 0 && (width > size || height > size)) {
        const ratio = size / Math.max(width, height); // 축소 비율을 계산합니다.
        width = Math.round(width * ratio);
        height = Math.round(height * ratio);
      }

      // 축소된 이미지의 크기에 맞게 Canvas 의 크기를 조정합니다.
      canvas.width = width;
      canvas.height = height;

      // Canvas 에 이미지 크기와 동일하게 투명한 네모를 그립니다.
      canvasContext.fillStyle = 'rgba(0, 0, 0, 0)';
      canvasContext.fillRect(0, 0, width, height);

      // 네모 안에 축소된 이미지를 그려넣습니다.
      canvasContext.drawImage(image, 0, 0, width, height);

      // 축소된 그림이 그려진 canvas 를 Blob 으로 변환하고, 콜백함수를 전달해 후처리를 진행합니다.
      // 그 외에도 이미지 포맷이나, 리사이징 품질(0~1)을 설정할 수 있습니다.
      canvas.toBlob(async (blob) => {
        // 서버에 저장하기 위해 File 객체로 변환합니다.
        const resizedFile = new File([blob as Blob], 'resizedImg.webp', {
          type: 'image/webp',
          lastModified: new Date().getTime(),
        });
        await uploadProductImage(
          'resizedImg.webp',
          resizedFile,
        );
      }, 'image/webp', 0.8);
    };
  };
};

성능 측정

웹 페이지에 이미지 최적화 적용 전/후로 성능의 차이가 얼마나 있는지 성능을 비교합니다.

로컬 서버에서 성능 비교를 진행하기 때문에, 크롬 개발자 도구의 Performance 기능을 활용했습니다.

1. 이미지 최적화 전

가로 그래프 형태로 이미지들이 어떻게 로딩되었는지 표현됩니다.

총 로딩 시간은 첫번째 상품의 이미지 로딩의 시작 시점부터 마지막 상품의 이미지 로딩이 끝날 때 까지의 시간을 살펴보았습니다.

테스트는 16Mbit/s 의 네트워크 환경에서 총 5회에 걸쳐서 반복했고, 결과는 다음과 같습니다.

  • 5277ms, 5522ms, 5365ms, 5576ms, 5644ms = 평균 5476.8 ms

다음으로 그래프에서 색상이 의미하는 바는 다음과 같습니다.

이 두가지 지표의 관점으로 살펴보면, 리소스 요청 후 대기시간은 이미지별로 거의 동일하지만, 이미지 다운로드 시간은 이미지 크기가 제각각이라서 다운로드 완료 시점의 차이가 매우 큰 것을 확인할 수 있습니다.

다음으로 FCP, LCP 지표는 다음과 같습니다.

  • FCP - 311ms, 318ms, 324ms, 333ms, 348ms = 평균 326.8 ms

  • LCP - 3590ms, 2990ms, 1790ms, 1950ms, 3820ms = 평균 2828 ms

2. 이미지 최적화 후

먼저 총 로딩 시간 (첫번째 상품의 이미지 로딩의 시작 ~ 마지막 상품의 이미지 로딩 끝) 은 다음과 같습니다.

  • 888ms, 865ms, 874ms, 812ms, 875ms = 평균 862.8 ms

최적화 전과 비교했을 때 평균적으로 총 로딩시간은 3914ms 가 줄었고, 비율로 따지면 84.24 % 가 줄었습니다.

또 이미지 다운로드 시간이 이전과 다르게 훨씬 줄어들었고, 모든 이미지가 비슷한 다운로드 시간과 다운로드 완료 시점을 가집니다.

다음으로 FCP, LCP 지표입니다.

  • FCP - 353ms, 325ms, 322ms, 488ms, 338ms = 평균 365.2 ms

  • LCP - 1720ms, 1510ms, 1520ms, 1740ms, 1700ms = 평균 1638 ms

최적화 전과 비교해 FCP 는 38.4 ms 증가했고, LCP 는 약 1990 ms 가량 감소했습니다.


결론

기존의 원본 이미지를 webp 이미지 포맷과 용도에 맞는 리사이징을 통해 웹 페이지의 성능을 향상시켰습니다.

최적화 전
최적화 후
비교

이미지 로딩 시간

5476.8 ms

862.8 ms

-4614 ms (-84.24%)

FCP

326.8 ms

365.2 ms

+38.4 ms

LCP

2828 ms

1638 ms

-1190 ms (-42.08%)

이미지 리사이징 순서도
이미지 최적화 이전
그래프 색상의 의미
이미지 최적화 이후
이미지 최적화 전
이미지 최적화 후