[React 최적화] 실습 2. 올림픽 사이트(2)
애니메이션 최적화
<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)
- cra-bundle-analyzer 설치
- 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.
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()
}, [])
...
}
팩토리 패턴에 대해서 더 알아보기:
이미지 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