[React] 리액트 이미지 프리로딩 적용

박태규·2023년 12월 23일
0
post-thumbnail

이 프로젝트는 불가피하게 고화질의 gif, png 등을 사용하게 되었다.
이러한 소스들은 무거울수록 사용자 경험 측면에서 비효율적이기 때문에, 이를 개선한 과정과 고민에 대해 글로 작성하려 한다.

브라우저 렌더링 과정

고화질 소스들은 그냥 사용했을 때 렌더링 시 뚝 끊기는 현상이 발생됐고, 이는 사용자 경험 측면에서 비효율적이라 판단했다.

과연 어떤 이유로 뚝 끊기는 현상이 발생했을까?

먼저 브라우져 렌더링은 아래와 같다.

웹은 기본적으로 동기화(Synchronos) 속성을 갖고 있기 때문에 HTML렌더링 하는 과정에서 HTML태그 등에서 src atrribute 가 사용된다면 즉시 파싱을 멈추고 브라우저는 해당 소스를 가져오게 된다.

그럼 js 상에서 고화질 소스를 가져온다면?

해결법은 js 상에서 소스가 무거운 gif 파일을 미리 프리로딩을 하여 미리 소스를 불러오면 된다.

HTML 렌더링이 끝난 후 미리 로드한 gif 파일을 동기적으로 가져오게 된다면 재렌더링이 되므로 로딩을 만들어 로딩 동안 파일을 전부 불러오게 만들었다.

이렇게 된다면 끊기지 않고 유저 친화적으로 이미지 프리로딩을 적용할 수 있다.

먼저 기본적인 프리로딩 소스 코드이다.

/*const images = [
    "http://example.com/image1.png",
    "http://example.com/image2.png",
    "http://example.com/image3.png"
]; */

const preload = (images) => () => {
    images.forEach((image) => {
        const img = new Image();
        img.src = image;
    });
};

이미지가 로딩되고 나서 추가적인 작업 사항이 있다면, 이미지 자체에 onload, onerror를 추가적으로 걸 수 있다.

위 코드를 리액트로 적용하면 다음과 같다.

const gif = [............]

  const preLoadGif = useCallback(() => {
    const loadImg= gif.map((url) => {
      return new Promise((resolve) => {
        const img = new Image()
        img.onload = () => resolve()
        img.src = url
      })
    })
    return Promise.all(loadImg)
  }, [gif])

useEffect(() => {
    const preLoad = async () => await preLoadGif()
    preLoad()
		
		// 로딩 종료

  }, [preLoadGif])
  • 로딩 컴포넌트를 실행한다.
  • 리액트에 모든 컴포넌트를 불러오면 preLoadGif가 실행된다.
  • 다수의 gif파일을 promise를 이용해 병렬적으로 받아온다.
  • 파일을 받아오면 로딩을 종료하고 보야주고 싶은 gif소스를 불러온다.

이렇게 받아온 이미지 소스들은 개발자도구 > 퍼포먼스 탭에서 확인 가능하다.

<프리로딩 적용 전>

<프리로딩 적용 후>

각각 비교해 보면 프리로딩 전과 후의 로드되는 시점이 다르다는 것을 확인할 수 있다.

정리

만약 무거운 소스들을 사용자 경험을 고려하여 설계를 하고 있다면 이미지 프리로딩을 적용해 보는 것을 추천한다.

만약 고화질의 소스들이 다수 있다면 로딩 스패너를 만들어 구현해 보자.

0개의 댓글