대부분의 React 앱은 Webpack 이나 Browserify 와 같은 도구를 사용하여 "번들 된"파일을 갖게 됩니다. 번들링은 가져온 파일을 따라 하나의 파일, 즉 "번들"으로 병합하는 프로세스입니다. 이 번들은 웹 페이지에 포함되어 한 번에 전체 앱을로드 할 수 있습니다.
// app.js
import { add } from './math.js';
console.log(add(16, 26)); // 42
// math.js
export function add(a, b) {
return a + b;
}
function add(a, b) {
return a + b;
}
console.log(add(16, 26)); // 42
노트
번들은 이보다 훨씬 많은 내용을 갖고 있게 됩니다.
React Create App, Next.js, Gatsby 또는 이와 유사한 도구를 사용한는 경우 내부에 포함된 Webpack
를 사용하여 앱을 번들로 제공할 수 있습니다.
그렇지 않은 경우 번들 설정을 직접해야 합니다. 관련 내용은 Webpack
문서의 설치 및 시작 설명서를 참고 하세요.
번들링은 훌륭하지만 앱이 커짐에 따라 번들도 커집니다. 특히 큰 third-party
라이브러리를 포함하는 경우. 번들에 포함된 코드를 주의깊게 확인해야 실수로 커진 앱으로 인해 로드시간이 오래 걸리는 문제를 방지 합니다.
큰 번들로 묶이지 않으려면 번들을 code splitting
하는것이 좋습니다. code splitting
기능은 런타임시 동적으로 로드할 수 있는 여러 번들을 만들 수 있는 Webpack
및 Browserify
(factor-bundle)와 같은 번들러에서 지원되는 기능입니다.
code splitting
을 하면 사용자가 현재 필요로하는 것들만 lazy-load
할 수 있으므로 앱의 성능을 크게 향상시킬 수 있습니다. 앱의 전체 코드 양을 줄이지는 않지만 사용자가 필요로하지 않은 코드를 로드하는 것을 피하고, 초기 페이지 로드시 필요한 코드만 받게 됩니다.
어플리케이션에 code splitting
을 도입하는 가장 좋은 방법은 동적 import()
구문을 이용하는 것입니다.
import { add } from './math';
console.log(add(16, 26));
import("./math").then(math => {
console.log(math.add(16, 26));
});
노트
동적import
구문은 현재 표준이 아닌 ECMAScript(JavaScript)의 제안단계 입니다. 가까운 미래에 받아 들여질 것으로 예상됩니다.
Webpack
구문을 보게되면 자동으로 코드 스플릿팅이 시작 됩니다. Create React App
을 사용한다면 이미 설정되어 있어 사용할 수있습니다. Next.js에서도 지원됩니다.
Webpack
을 직접 설정하려면 code splitting
에 관한 Webpack
가이드를 참고하세요. 대략적인 Webpack
설정은 이와 같이 합니다.
Babel을 사용할 때는 Babel
이 dynamic import
구문을 분석하게 하기위해서 babel-plugin-syntax-dynamic-import 플러그인이 필요합니다.
노트
React.lazy
와Suspense
는 아직 서버사이드 랜더링에 사용할 수 없습니다. 서버 사이드 랜더링된 애플리케이션에서code splitting
기능을 사용하고 싶다면, Loadable Components를 추천합니다. 이것은 서버 사이드 랜더링과 번들 스플릿팅에 대한 가이드를 제공하고 있습니다.
React.lazy
함수를 사용하면 dynamic import
를 사용하여 가져온 컴포넌트를 랜더링 할 수 있습니다.
import OtherComponent from './OtherComponent';
function MyComponent() {
return (
<div>
<OtherComponent />
</div>
);
}
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<OtherComponent />
</div>
);
}
MyComponent
컴포넌트가 랜더링되면 OtherComponent
컴포넌트를 포함한 번들이 자동으로 로드됩니다.
React 컴포넌트를 export default
로 해석되는 Promise
로 반환하고 React.lazy
로 dynamic import()
를 할때에는 함수 형태로 사용합니다.
Suspense
컴포넌트를 사용한다면, MyComponent
가 랜더링 될 때까지 동적으로 불러온 OtherComponent
가 아직 로드가 되지 않은경우 로딩중과 같은 fallback content
표현이 가능합니다.
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
fallback
기능은 컴포넌트가 로드 될 때까지 기다리는 동안 랜더링하려는 모든 React요소에 적용가능합니다. Suspense
컴포넌트는 lazy
컴포넌트를 감쌉니다. 하나의 Suspense
컴포넌트로 여러 lazy
컴포넌트를 래핑할 수도 있습니다.
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</div>
);
}
네트워크 장애로 인하여 다른 모듈이 로드에 실패한 경우 오류가 발생할 수 있습니다. 이런 경우 Error Boundaries를 이용하여 사용자 환경을 개선 및 복구 관리할 수 있습니다. Error Boundary
를 만들어 lazy
컴포넌트를 감싸 네트워크 오류가 발생할때 오류 상태를 표시할 수 있습니다.
import MyErrorBoundary from './MyErrorBoundary';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
const MyComponent = () => (
<div>
<MyErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</MyErrorBoundary>
</div>
);
어플리케이션에서 code splitting
을 적용할 위치를 결정하는 것은 까다로울 수 있습니다. 사용자 경험에 지장을 주지않고 번들을 균등하게 분할할 영역을 선택해야 합니다.
적용하기 좋은 곳은 route
입니다. 웹페이지 로드 시간은 대부분 페이지 전환에 발생하며, 페이지를 한번에 렌더링하는 것이 대부분이므로 렌더링 중간에 사용자가 페이지와 상호 작용할 가능성은 거의 없습니다.
다음은 React Router
와 같은 라이브러리를 사용한 어플리케이션에 React.lazy
를 사용하여 경로 기반 code splitting
하는 예제입니다.
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);
React,lazy
는 현재 export default
만 지원합니다. 만약 named export
를 사용한 경우 default
로 이름을 재정의 하는 중간 모듈을 생성 할 수 있습니다. 아래와 같이 한다면 treeshaking
이 계속 작동하고 사용하지 않는 컴포넌트를 가져오지 않게 됩니다.
// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));
와우 좋은정보 감사합니다^^