*Tripus 프로젝트 : 로컬환경에서 Lighthouse를 통해 측정한 성능 지표입니다.
Lighthouse
npm run build
Network
Performance
번들링된 앱은 최적화되지만, 앱이 커지면 번들 크기도 커진다. 즉, 앱의 규모가 커짐에 따라 Bundling되어 제공되는 파일의 크기도 커지게 되어 초기 로딩 속도가 느려진다.
최초 페이지에 진입하면 웹팩에서 압축한 번들파일을 다운받고, 페이지의 전체 리소스를 다운 받게 된다. 당장 보지 않아도 될 페이지의 리소스까지 다운받는다.
이러한 문제를 해결하기 위해 Code Splitting을 적용하여 현재 필요한 모듈만 로딩(lazy-load) 되도록 하여 성능을 향상시킬 수 있다
코드를 분할하는 것은 크기가 큰 번들 파일을 분할하여, 작은 사이즈의 파일로 분할하는 것을 말한다. 예를 들면 맨 처음 mainPage와 postPage를 받아 Bundle.js가 완성되고 렌더링이 된다.
때문에, 코드를 분할하여 Bundle.js가 mainPage만 리소스를 받아도 로딩이 완료되도록 하는 것이다.
앱에 코드 분할을 도입하는 가장 좋은 방법은 동적 import() 문법을 사용하는 방법이다.
React.lazy 함수를 사용하면 동적 import를 사용해서 컴포넌트를 렌더링 할 수 있다.
React.lazy는 동적 import()를 호출하는 함수를 인자로 가진다. 이 함수는 React 컴포넌트를 포함하며 default export를 가진 모듈로 결정되는 Promise로 반환해야 한다.
lazy 컴포넌트는 Suspense 컴포넌트 하위에서 렌더링되어야 하며, Suspense는 lazy 컴포넌트가 로드되길 기다리는 동안 로딩 화면과 같은 예비 컨텐츠를 보여줄 수 있게 해준다.
fallback prop은 컴포넌트가 로드될 때까지 기다리는 동안 렌더링하려는 React 엘리먼트를 받아들인다. Suspense 컴포넌트는 lazy 컴포넌트를 감싼다. 하나의 Suspense 컴포넌트로 여러 lazy 컴포넌트를 감쌀 수도 있다.
앱에 코드 분할을 어느 곳에 도입할지 결정하는 것은 까다롭다.
사용자의 경험을 해치지 않으면서 번들을 균등하게 분배할 곳을 찾아야 한다.
이를 시작하기 좋은 장소는 라우트입니다. 웹 페이지를 불러오는 시간은 페이지 전환에 어느 정도 발생하며 대부분 페이지를 한번에 렌더링하기 때문에 사용자가 페이지를 렌더링하는 동안 다른 요소와 상호작용하지 않습니다.
(React 공식 문서)
const MainPage = lazy(() => import('./pages/MainPage'));
const LoginPage = lazy(() => import('./pages/LoginPage'));
const SignupPage = lazy(() => import('./pages/SignupPage'));
const MapPage = lazy(() => import('./pages/MapPage'));
const ChattingPage = lazy(() => import('./pages/ChattingPage'));
const MyPage = lazy(() => import('./pages/MyPage'));
const AdminPage = lazy(() => import('./pages/AdminPage'));
const Toggle = lazy(() => import('./components/common/Toggle'));
const GoogleCallback = lazy(() => import('./pages/Googlecallback'));
const ManagementPage = lazy(() => import('./pages/ManagementPage'));
const KakaoCallback = lazy(() => import('./pages/KakaoCallback'));
const TourManagementPage = lazy(() => import('./pages/TourManagementPage'));
<Suspense fallback={<div>loading...</div>}>
<Routes>
<Route path='/admin' element={<AdminPage />} />
<Route path='/' element={<MainPage />} />
<Route path='/login' element={<LoginPage />} />
<Route path='/signup' element={<SignupPage />} />
<Route path='/map' element={<MapPage />} />
<Route path='/management' element={<ManagementPage />}>
<Route path='tourlist' element={<TourManagementPage />} />
</Route>
<Route path='/chat' element={<ChattingPage />} />
<Route path='/mypage' element={<MyPage />} />
<Route path='/googlecallback' element={<GoogleCallback />} />
<Route path='/kakaocallback' element={<KakaoCallback />} />
</Routes>
</Suspense>
지표 | 개선 전 | 개선 후 |
---|---|---|
FCP | 4.6s | 1.3s |
TTI | 8.2s | 4.8s |
DOMContentLoaded | 1.51s | 326ms |
Load | 1.80s | 817ms |
npm run build
참고
lighthouse를 이용해 성능 최적화 하기 (code-splitting, lazy-loading)
프론트엔드는 성능 개선을 위해 무엇을 해야 할까?
React의 성능을 최적화하는 10가지 방법