프로젝트가 빌드업됨에 따라 많은 로직들과 Import 라이브러리들이 증가하게 마련이고 우리는 렌더링 최적화를 고민해야하는 시점과 마주하게 된다. 백엔드에서 진행할 수 있는 API 최적화와 별게로 프론트엔드에서도 최적화 할 수 있는 만큼은 진행하는 것이 더 좋은 사용자 경험을 제공한다. 하지만 웹서비스라는 것은 사용자가 어느정도 페이지 로딩이 지연되어도 알게모르게 이해하고 넘어가는 유저들이 많다. 이 정도 기다림 쯤이야.. (본인 포함...) 하지만 그 기다림에도 한계가 분명 있기 때문에 우리들은 개선을 해야한다.
프론트엔드 성능 측정 도구로 크롬 Lighthouse를 통해 성능지표를 확인한다. SSR 방식이 아닌 서비스는 번들링 된 자바스크립트 파일의 분할을 권고한다. 리액트에서는 Lazy, Suspense API를 활용할 수 있다. 리액트 뿐만 아니라 모던 프론트엔드 개발 라이브러리들은 기본적으로 Webpack, Rollup등 다양한 번들러를 사용하여 여러개의 파일들을 하나의 파일로 병합한다. 번들러가 import 구문을 만나게 되면 코드를 분할한다.
번들이 거대해지는 것을 방지하고 번들을 나누는 것이라고 공식문서에서는 설명하고 있다.
코드 분할은 런타임에 여러 번들로 동적으로 만들어지고 불러오게 되는데 이 기능은 번들러가 지원한다.
Create React App or Create Next App 으로 프로젝트를 생성하면 자동으로 설정이 되어 있다.
코드분할을 통해 Lazy하게 로딩할 수 있게 되는 것이고 성능 향상도 가능하다.
초기에 많은 데이터를 불러올 필요 없이 앱 초기화에 필요한 번들만 가져오게 되는 원리이다.
import()
코드 분할을 도입하는 가장 좋은 방법은 Dynamic Import 를 사용하는 방법이 있다.
// Before
import { add } from './math';
console.log( add(15, 30) ); // 45
// After
import('./math').then(math => {
console.log( math.add(15, 30) ); // 45
})
Webpack이 위 구문을 만나게 되면 앱의 코드를 분할한다.
React.lazy
React.lazy 함수를 사용하면 동적 import를 사용해서 컴포넌트를 렌더링할 수 있다.
// Before
import AComponent from './AComponent';
// After
const AComponent = React.lazy(() => import('./AComponent'));
React.lazy는 동적 import()를 호출하는 함수를 인자로 가진다. 이 함수는 default export
로 가진 모듈 객체가 이행되는 Promise
를 반환해야 한다고 설명하고 있다. lazy 컴포넌트는 Suspense
컴포넌트 하위에서 렌더링 되어야 한다. Suspense
는 lazy 컴포넌트가 로드되는 동안 로딩스피너와 같은 예비 컨텐츠를 설정해줄 수 있다.
import React, { Suspense } from 'react';
const AComponent = React.lazy(() => import('./AComponent'));
function MainComponent() {
return (
<>
<Suspense fallback={<Loading Spinner />}>
<AComponent>
</Suspense>
</>
)
}
Error Boundary
네트워크 장애 같은 이유로 모듈 로드에 실패할 경우 에러를 발생시킬 수 있다. 여기서 말하는 에러바운더리는 렌더링 사이클에서의 에러처리를 말한다. Suspense
를 사용하여 네트워크 요청이 내부적으로 이루어 진다고 가정했을때 어떠한 이유로 네트워크 요청에 실패할때 사용할 수 있다. 오류경계를 커스터마이징 하기위해서 React에서 제공하는 static getDerivedStateFromError()
또는 componentDidCatch()
둘 중하나 혹은 둘다 구현하여 에러바운더리 역할을 수행할 수 있다. 이 에러바운더리는 Suspense
보다 상위에 설정하여 Suspense
를 감싸는 형태로 사용할 수 있다.
import React, { Suspense } from 'react';
const AComponent = React.lazy(() => import('./AComponent'));
function MainComponent() {
return (
<>
<CustomErrorBoundary>
<Suspense fallback={<Loading Spinner />}>
<AComponent>
</Suspense>
</CustomErrorBoundary>
</>
)
}
참고문서
'Front > React' 카테고리의 다른 글
[번역] useMemo, useCallback 제대로 알기 (0) | 2022.09.23 |
---|---|
Hydrate 이해하기 (0) | 2021.10.20 |
[React] Create-react-app 없이 Webpack, Babel 빌드 해보기 (4) | 2021.04.22 |
React Hook 이해하기 - useEffect (Effect Hook) (0) | 2020.05.25 |
React Hook 이해하기 - useState (State Hook) (0) | 2020.05.25 |
댓글