이번 포스팅에서는 코드 분할에 대한 개념과 활용법에 대해서 정리하겠습니다.
모던 웹에서 자바스크립트 코드의 크기가 커지게 되면서, 그에 따라 번들을 나누어 필요한 코드를 적시적소에 불러와 사용하는 방법에 대한 고민이 생겨나게 되었습니다. 이러한 코드 분할을 통해서 웹 페이지의 로딩 속도를 개선할 수 있습니다.
코드 분할의 방법 중 하나는 써드파티 라이브러리를 분리하는 것입니다. 웹 개발 과정에서 다양한 서드파티 라이브러리를 사용하게 되는데 이 라이브러리들은 번들링 시 많은 공간을 차지하게 됩니다. 그렇기 때문에 불러오는 라이브러리의 규모를 줄이는 것이 중요합니다.
아래의 코드처럼 라이브러리의 전체를 불러와서 사용하는 것보다 따로 따로 불러와서 사용할 수 있다면 많은 공간을 차지하지 않을 수 있게 됩니다. 이 방법만으로도 불필요한 라이브러리로 인한 앱의 성능저하를 일부 막을 수 있습니다.
import find from 'lodash/find'; // import find from 'lodash 보다 적은 공간을 사용합니다
find([])
리액트는 기본적으로 SPA(Single-Page-Application)이므로 한 번 로드될 때, 사용하지 않는 컴포넌트까지 불러오게 됩니다. 그렇기 때문에 규모가 큰 어플리케이션은 초기 로딩이 느립니다.
이러한 문제를 해결하기 위해서 Dynamic import를 통한 코드 분할이 가능합니다. 기존에 코드 최상위에 import 키워드를 사용해 라이브러리나 파일을 불러왔으나, Dynamic import를 사용하면 코드의 필요한 지점에서만 불러와 사용할 수 있게 됩니다. 이러한 방식으로 구문 분석 및 컴파일해야하는 스크립트 양을 최소화할 수 있습니다.
정적 불러오기는 파일의 최상위에 코드를 작성해야하며 블록 내부에서 라이브러리나 파일을 불러오는 것이 불가능 합니다.
// 파일의 최상위에서 import 지시자를 이용해 라이브러리 및 파일을 불러왔습니다.
import moduleA from "library";
form.addEventListener("submit", e => {
e.preventDefault();
someFunction();
});
const someFunction = () => {
// 라이브러리 및 파일 사용
}
동적 불러오기는 블록 내부와 같이 코드 중간에 필요한 라이브러리나 파일을 불러와서 사용할 수 있습니다.
form.addEventListener("submit", e => {
e.preventDefault();
//동적 불러오기는 이런 식으로 코드의 중간에 불러올 수 있게 됩니다.
import('library.moduleA')
.then(module => module.default) // then 함수를 통해서 필요한 코드만 불러 옵니다.
.then(someFunction())
.catch(handleError());
});
const someFunction = () => {
/* moduleA를 여기서 사용합니다. */
}
React.lazy 함수를 사용하면 dynamic import를 사용해 컴포넌트를 렌더링할 수 있습니다. 이 함수를 사용하려면 React.suspense 컴포넌트 하위에서 렌더링 해야합니다.
Suspense는 아직 렌더링이 준비되지 않은 컴포넌트가 있을 때 로딩 화면을 보여주고, 로딩이 완료되면 렌더링이 준비된 컴포넌트를 보여주는 기능입니다.
아래의 코드에서는 웹 페이지를 불러오고 진입하는 단계인 Route에 lazy 함수를 이용하여 불러온 Home과 About 컴포넌트를 연결해 줍니다.
import { Suspense, lazy } from 'react';
import { BrowserRouter as Router, 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>
);
라우터를 사용한 페이지 연결 뿐만아니라 lazy 함수를 이용하여 컴포넌트를 바로 렌더링 하는 것도 가능합니다.
import { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent /> // lazy 함수를 통해 불러온 컴포넌트
<AnotherComponent /> // lazy 함수를 통해 불러온 컴포넌트
</Suspense>
</div>
);
}
번들이 거대해지는 것을 방지하기 위해서 코드 분할을 사용하며, 동적 불러오기를 통해서 번들에 포함되는 써드 파티 라이브러리의 사이즈를 줄일 수 있습니다. 또한 리액트에서는 많은 컴포넌트들을 불러와 사용하기 때문에, 각각의 컴포넌트들을 동적으로 불러와 사용하는 방법으로 번들링 사이즈를 줄일 수 있습니다.