웹성능 최적화(1)
웹성능 최적화1
2022-01-01
유동균님의 강의를 보며 정리한 글 입니다.
1. 구글 개발자 도구 Audits(LightHouse) 툴을 이용한 페이지 검사
Opportunities = 리소스의 관점(로딩성능)
Diagnostics = 페이지의 실행 관점(렌더링)
2. 이미지 사이즈 최적화 - 로딩 성능
실제 이미지와 렌더링된 이미지의 크기가 다르다
보통 이미지는 x2의 이미지를 사용
api를 통해 받아온 이미지를 조절하려면?
Image CDN(image processing Contents Delivery Network) - 이미지 가공 → 소비자
CDN 구축은 진행하지 않았다.
3. performance
크롬 개발자도구 -> 성능에서 새로고침을 누르면 페이지의 작업을 분석해준다.
- HTML파싱
- JS 로드
- 첫 페인팅 이후 API 호출
- 리액트는 프레임차트에서 각 컴포넌트마다 실행되는 시간이 보여진다.
4. Route-based code Splitting
코드 스플릿팅은 webpack, rollup 등과 같은 모듈 번들러를 이용하여 만들어진 하나의 번들 파일을 여러 개의 번들 파일로 나누는 것을 의미한다.
하나의 번들파일을 여러개로 나누면 그만큼 로드 속도가 빨라진다.
import React, {Suspense, lazy} from 'react'
import { Switch, Route } from 'react-router-dom'
import './App.css'
// import ListPage from './pages/ListPage/index'
// import ViewPage from './pages/ViewPage/index'
const ListPage = lazy(() => import('./pages/ListPage/index'))
const ViewPage = lazy(() => import('./pages/ViewPage/index'))
function App() {
return (
<div className="App">
<Suspense fallback={<div>로딩 중...</div>}>
<Switch>
<Route path="/" component={ListPage} exact />
<Route path="/view/:id" component={ViewPage} exact />
</Switch>
</Suspense>
</div>
)
}
export default App
import()를 lazy라는 함수 안에 넣고, Suspense라는 컴포넌트로 관리를 해줄 수 있다.
데이터가 로딩중에 유저에게 비어있는 컴포넌트가 보이게 되는데, Suspense를 사용해서 이 로딩중인 상태일 때 다른 컴포넌트를 보여줄 수 있다.
Suspense는 fallback을 받아 import하는 도중에 띄어준다.
페이지가 로드될 때 그 페이지 안에서 보이지 않는 컴포넌트가 존재할 수 있는데, 이 부분을 스플리팅해 관리 할 수도 있다.
5. 애니메이션 최적화
일반적인 모니터는 초당 60FPS, 브라우저도 초당 60FPS로 화면을 그리게 된다.
여기서 브라우저가 60FPS 아래로 그리게 된다면 쟁크 현상이 발생함.
- DOM + CSSOM
- Render Tree
위 DOM과 CSSOM을 조합해 RenderTree를 만들어 낸다.
- Layout
위치나 크기를 계산해 화면에 레이아웃을 그린다.
- Paint
이렇게 그려진 레이아웃위에 색을 채워넣음
- Composite
각 레이어들을 합성하는 과정
이 전체의 과정을 Critical Rendering Path, Pixel Pipeline 이라고 부른다.
이 완성된 상태에서 변화가 생긴다면 처음으로 돌아가 다시 이 전체의 과정을 거치게 된다.
-
width, height가 변경되면, 1 -> 2 -> 3 -> 4 -> 5 모두 실행되게 된다 (Reflow)
-
크기나 위치가 아니라 color, background-color(색깔)이 변경됐을땐 1 -> 2 -> 4 -> 5 (Repaint)
-
transform, opacity 등(GPU가 관여하는 속성) 변경일 때 1 -> 2 -> 5
따라서 transform, opacity 등(GPU가 관여하는 속성)을 사용하는게 가장 빠르고 끊김없이 보여줄 수 있다.
6. 컴포넌트 Lazy Loading, Preload
위에서 했던것 처럼 이미지를 보여주는 모달만 따로 스플릿팅 할 수있다.
import React, { useState, Suspense, lazy } from 'react'
import styled from 'styled-components'
import Header from './components/Header'
import InfoTable from './components/InfoTable'
import SurveyChart from './components/SurveyChart'
import Footer from './components/Footer'
const LazyImageModal = lazy(() => import('./components/ImageModal'))
function App() {
const [showModal, setShowModal] = useState(false)
return (
<div className="App">
<Header />
<InfoTable />
<ButtonModal onClick={() => { setShowModal(true) }}>올림픽 사진 보기</ButtonModal>
<SurveyChart />
<Footer />
<Suspense fallback={null}>
{showModal ? <LazyImageModal closeModal={() => { setShowModal(false) }} /> : null}
</Suspense>
</div>
)
}
Lazy Loading의 단점
우리가 모달을 클릭한순간 모달에 관련된 파일을 불러오고 JS를 Evaluate한다.
이게 끝나고 실제로 모달을 띄우는 코드가 실행된다.
최초 페이지에서는 성능이 조금 빨라졌지만 모달을 띄울 때는 오히려 성능이 더 느려졌다.
- 컴포넌트 Preload
이를 개선하기 위해서 모달을 열기 이전에 모달과 관련된 코드를 미리 로드해 둔다.
문제는 사용자가 모달을 언제 클릭할지 모르기 때문에 언제 미리 로드할지 애매하다.
타이밍
- 버튼 위에 마우스를 올려 놨을 때
const handleMouseEnter = () => {
const Component = import('./components/ImageModal')
}
<ButtonModal onClick={() => { setShowModal(true) }} onMouseEnter={handleMouseEnter}>올림픽 사진 보기</ButtonModal>
- 최초 페이지 로드가 되고, 모든 컴포넌트의 마운트가 끝났을 때(ComponentDidMount)
useEffect(() => {
const Component = import('./components/ImageModal')
}, [])
지금은 단일 컴포넌트를 미리 import 하지만 여러 컴포넌트를 Preload해줘야 할 때는
function lazyWithPreload(importFunction){
const Component = React.lazy(importFunction)
Component.preload = importFunction
return Component
}
const LazyImageModal = lazyWithPreload(() => import('./components/ImageModal'))
useEffect(() => {
LazyImageModal.preload()
}, [])
처럼 사용할 수 있다.
- 이미지 Preload
new Image()
useEffect(() => {
LazyImageModal.preload()
const img = new Image()
img.src = 'url'
}, [])
주의할점은 이미지는 모듈과 다르게 필요할 때 마다 그때그때 매번 보내기 때문에
사용하는 이미지에 캐시가 제대로 걸려있는지 확인할 필요가 있다.