공부내용 공유하기

[react] 화면에 보일 때 렌더링 되는 이미지 컴포넌트(Lazy Load Image) - IE11 polyfill

이미지 최적화와 웹 사이트 속도는 떼려야 뗄 수 없는 관계이다.

 

이미지의 용량을 압축과 최적화를 통해 줄일 만큼 줄였다면, 이제 페이지 로딩 시간을 향상하기 위한 지연 로딩(Lazy Loading)을 적용해보자.

 

 

지연 로딩이 적용된 이미지는, 사용자의 눈에 충분히 노출되기 전에는 데이터 요청이 일어나지 않게 할 수 있다.

그러므로 이를 통해 페이지의 초기 로딩 속도를 비약적으로 향상시킬 수 있다.

 

React와 TypeScript를 사용한 이미지 컴포넌트 코드는 다음과 같다.

 

Image.tsx

import React, { useEffect, useRef, useState } from "react";
//* polyfill 2개. 아래에 설명있음*//
import "intersection-observer";
import "../polyfill/customEvent"
//* polyfill 2개. 아래에 설명있음*//

let observer: any = null;
// 이미지를 로드할 이벤트 이름은 loadImage로 정했습니다.
const LOAD_IMG_EVENT_TYPE = "loadImage";

function onIntersection(
  entries: IntersectionObserverEntry[],
  io: IntersectionObserver
) {
  entries.forEach((entry: IntersectionObserverEntry) => {
    if (entry.isIntersecting) {
      io.unobserve(entry.target);
      entry.target.dispatchEvent(new CustomEvent(LOAD_IMG_EVENT_TYPE));
    }
  });
}

// 이미지 경로와 alt를 받는다
interface ImageProps {
  src: string;
  alt?: string;
}

const Image: React.FC<ImageProps> = (props: ImageProps) => {
  const { src, alt } = props;
  const imgRef = useRef(null);
  
  const [isLoad, setIsLoad] = useState(false);


  useEffect(() => {
    function loadImage() {
      setIsLoad(true);
    }

    const imgEl: HTMLImageElement | null = imgRef.current;

	// 이미지 엘리먼트에 커스텀 이벤트 리스너 추가
    if (imgEl) {
      // @ts-ignore
      imgEl.addEventListener(LOAD_IMG_EVENT_TYPE, loadImage);
    }

    return () => {
    // 이미지 엘리먼트에 커스텀 이벤트 리스너 제거. clean up
      if (imgEl) {
        // @ts-ignore
        imgEl.removeEventListener(LOAD_IMG_EVENT_TYPE, loadImage);
      }
    };
  }, []);

  useEffect(() => {
  // 이미지 요소의 노출도를 측정할 옵저버를 생성해줍니다.
    if (!observer) {
      observer = new IntersectionObserver(onIntersection, {
        // threshold(0 ~ 1) 0.3 이면 30% 노출시 로드.
        threshold: 0.3,
      });
    }

    // @ts-ignore
    if (imgRef.current) {
      observer.observe(imgRef.current);
    }
  }, []);

  return <img ref={imgRef} src={isLoad ? src : ""} alt={alt} />    
};

Image.defaultProps = {
  alt: "",
};

export default Image;

 

intersectionObserver(요소가 화면에 노출된 범위를 감지하는 옵저버)를 사용해서, 요소가 정해진 threshold를 넘어가는 순간 src를 넣어주는 방식이다.

 

문제는 IE11에서 intersection observer도 지원을 안 하고, customEvent도 지원을 안 한다는 것?

 

따라서 IE11환경에서도 사용하기 위해서는 2가지 polyfill이 필요하다.

 

$yarn add intersection-observer

 

1. intersection observer는 해당 종속성을 설치 후 위 코드처럼 import를 해주면 되고,

 

 

 

src/polyfill/customEvent.js

/* eslint-disable */
(function () {
if (typeof window !== "undefined"){
    if ( typeof window.CustomEvent === "function" ) return false;

    function CustomEvent ( event, params ) {
        params = params || { bubbles: false, cancelable: false, detail: undefined };
        var evt = document.createEvent( 'CustomEvent' );
        evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
        return evt;
    }

    CustomEvent.prototype = window.Event.prototype;

    window.CustomEvent = CustomEvent;
    }
})();

 

2. customEvent는 따로 polyfill 파일을 만들어서 넣어줘야 한다.

 

intersection observer polyfill만 넣고서 안심했는데 작동을 안 해서 당황했던 기억이 난다...

 

IE는 언제쯤 신경을 안 써도 될까...?