React.lazy
, Suspense
컴포넌트를 통해 비동기 로딩을 구현할 수 있다.create-react-app
으로 React 프로젝트를 셋팅하고 Dynamic Import 부터 Code Splitting을 사용해보자.yarn create react-app
src/add.js
export default function add(a, b) {
return a + b;
}
src/App.js
import { useState } from "react";
import add from "./add";
function App() {
const [state, setState] = useState(0);
const clickHandler = () => {
setState(add(1, 2));
};
return (
<div>
<p>1 + 2 는? {state !== 0 && state}</p>
<button onClick={clickHandler}>계산해보기</button>
</div>
);
}
export default App;
src/add.js
변경 없음src/App.js
import { useState } from "react";
function App() {
const [state, setState] = useState(0);
const clickHandler = async () => {
const add = await import("./add.js").then((res) => res.default);
setState(add(1, 2));
};
return (
<div>
<p>1 + 2 는? {state !== 0 && state}</p>
<button onClick={clickHandler}>계산해보기</button>
</div>
);
}
export default App;
여기까지의 내용을 React에서 제공하는 React.lazy, Suspense를 사용하여 더 간편하게 구현할 수 있다.
React.lazy
: 컴포넌트를 렌더링하는 시점에 비동기적으로 로딩할 수 있게 해주는 유틸 함수Suspense
: React 내장 컴포넌트로 코드 스플리팅된 컴포넌트를 로딩하도록 발동하거나 로딩이 끝나지 않았을 때 보여줄 UI를 설정할 수 있다.Suspense 컴포넌트는 React.lazy로 불러올 컴포넌트를 감싸는 형태이다.
React.lazy는 현재 default exports만 지원한다.
src/SplitComponent.js
export default function SplitComponent() {
return <div>React.lazy로 지연 로딩된 컴포넌트</div>;
}
src/App.js
import React, { Suspense, useState } from "react";
const SplitComponent = React.lazy(() => import("./SplitComponent"));
function App() {
const [visible, setVisible] = useState(false);
return (
<Suspense fallback={<div>Loading...</div>}>
{visible && <SplitComponent />}
<button onClick={() => setVisible(true)}>나타내기</button>
</Suspense>
);
}
export default App;
코드 분할(Code Splitting)로 번들링 되는 Javascript 파일의 크기를 줄여주고 필요할 때 요청하기에 유저가 아예 진입하지 않는 페이지나 사용하지 않는 컴포넌트는 요청하지 않아 성능을 최적화할 수 있다.
그렇다면 필요한 대부분의 곳에서 코드 분할을 사용하는 것이 좋은 것일까?
React 공식 문서에는 사용자의 경험을 해치지 않는 선에서 적당한 코드 분할을 권장하고 있는데, 컴포넌트마다 코드 분할을 적용하는 것이 아니라 Route 기반의 코드가 코드 분할을 설정하기 좋은 장소라고 설명하고 있다.
import React, { Suspense, lazy } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
);