web

[React 최적화] 실습 2. 올림픽 사이트(2)

Jaaaay 2022. 7. 25. 15:54

애니메이션 최적화

<BarGraph width={props.percent} isSelected={props.isSelected}></BarGraph>

...

const BarGraph = styled.div`
    position: absolute;
    left: 0;
    top: 0;
    width: ${({width}) => width}%;
    transition: width 1.5s ease;
    height: 100%;
    background: ${({isSelected}) => isSelected ? 'rgba(126, 198, 81, 0.7)' : 'rgb(198, 198, 198)'};
    z-index: 1;
`

해당 컴포넌트에 대해 transform을 적용시켜보자

const BarGraph = styled.div`
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    transform: scaleX(${({width}) => width / 100});
    transform-origin: center left;
    transition: transform 1.5s ease;
    height: 100%;
    background: ${({isSelected}) => isSelected ? 'rgba(126, 198, 81, 0.7)' : 'rgb(198, 198, 198)'};
    z-index: 1;
`

컴포넌트 Lazy Loading(Code Splitting)

  1. cra-bundle-analyzer 설치
  2. npx cra-bundle-analyzer 명령어 입력

image-gallery 모듈은 처음부터 포함될 필요가 없다.

  • App.js 수정
...
<Suspense fallback={null}>
            {showModal ? <LazyImageModal closeModal={() => { setShowModal(false) }} /> : null}
            </Suspense>
...
  • 로딩 전

  • 로딩 후

이미지 갤러리가 사용되기 전에는 없던 몇 개의 chunk 파일이 추가됨을 확인할 수 있다.

bundle analyzer의 결과에서도 차이를 확인할 수 있다.


컴포넌트 Preloading

  • Lazy Loading의 단점

최초 페이지에서는 성능이 빨라져도 모달을 띄울 때는 오히려 느림

미리 로드를 하면 된다. 하지만 언제가 적절한 타이밍일까?

  1. 버튼 위에 마우스를 올려 놨을 때
  2. 최초 페이지 로드가 되고, 모든 컴포넌트의 마운트가 끝났을 때

1.

const handleMouseEnter = () => {
        const Component = import('./components/ImageModal')
    }

    return (
        <div className="App">
            <Header />
            <InfoTable />
            <ButtonModal onClick={() => { setShowModal(true) }} onMouseEnter={handleMouseEnter}>올림픽 사진 보기</ButtonModal>
            <SurveyChart />
            <Footer />
            <Suspense fallback={null}>
            {showModal ? <LazyImageModal closeModal={() => { setShowModal(false) }} /> : null}
            </Suspense>
        </div>
    )

마우스를 올릴 때 로딩되도록 함.

( Component는 import 반환값을 담아 오류가 안나도록 하는 것 뿐)

마우스 오버 시 추가 파일이 로드된다.

 

2.

만약 로드할 리소스 양이 많다면? 마우스 오버 시 타이밍은 적합하지 않을 수 있다.

componentDidMount() 순간에 로드를 해주면 적합할 것이다.

...
useEffect(() => {
      const Component = import('./components/ImageModal')
    }, [])
...

useEffect를 활용

다음과 같이 중요한 컴포넌트가 로딩된 후 모달 컴포넌트가 로드되는 것을 확인 가능하다.

 

매번 import를 사용하면 보기에 좋지 않으니 다음과 같이 활용할 수 있다.

...
function lazyWithPreload(importFunction){
    const Component = React.lazy(importFunction)
    Component.preload = importFunction
    return Component
}

const LazyImageModal = lazyWithPreload(()=> import('./components/ImageModal')) 

function App() {
    const [showModal, setShowModal] = useState(false)

    useEffect(() => {
      LazyImageModal.preload()
    }, [])

...
}

팩토리 패턴에 대해서 더 알아보기:

[Design Pattern] 팩토리 패턴


이미지 Preloading

useEffect(() => {
      LazyImageModal.preload();
      
      const img = new Image();
      img.src = '<https://stillmed.olympic.org/media/Photos/2016/08/20/part-1/20-08-2016-Football-Men-01.jpg?interpolation=lanczos-none&resize=*:800>';
    }, [])

useEffect에 Image 객체에 src로 특정 이미지를 선언한다.

해당 이미지가 preload된 것을 확인할 수 있다.

응답 헤더 칸에 보면 cache가 설정된 것도 확인 가능.

 

모든 이미지가 아니라 첫 번째 이미지만 로드한 이유는 모달에서 처음 나타나는 이미지만 빠르게 표현되면 사용하는 입장에서 느리게 로딩된다는 인상을 받지 않기 때문이다.


정리

애니메이션 최적화(reflow, repaint)

→ 컴포넌트 Lazy loading(Code Splitting)

→ 컴포넌트 Preloading

→ 이미지 Preloading