번들링
: 웹 애플리케이션 동작에 필요한 HTML, CSS, JavaScript 등의 파일들을 묶어서 제공하는 것
대부분의 React 앱들은 Webpack이나 Rollup과 같은 툴을 사용해 앱을 번들링(Bundling)한다.
하지만 모던 웹으로 발전하면서 JavaScript의 코드 양이 많아지고 무거워지면서, 점차 페이지를 로드하는 시간이 길어지게 되었다.
➡️ 이렇게 번들이 거대해지는 것을 방지하기 위해 번들을 물리적으로 나누자는 아이디어가 등장했다.
: 런타임에서 여러 번들을 동적으로 만들고 불러오는 것으로 Webpack, Rollup과 같은 번들러가 지원하는 기능
코드 분할을 하게 되면 지금 당장 필요한 코드만 불러와서 사용할 수 있으며, 이를 통해 페이지 로딩 속도를 개선할 수 있다.
React는 SPA(Single Page Application)이다.
SPA는 사용하지 않는 모든 컴포넌트까지 한 번에 불러오기 때문에 처음 화면을 렌더링하는 데에 시간이 오래 걸린다.
➡️ 그래서 사용하지 않는 컴포넌트는 나중에 불러오기 위해 코드 분할 개념을 도입했다.
React에서 코드 분할을 하는 법은 동적 불러오기(Dynamic import)를 사용하는 것이다.
: 코드 파일의 최상위에서 사용하고자 하는 라이브러리 및 파일을 불러오는 방법
import moduleA from "library"; // Static import
form.addEventListener("submit", e => {
e.preventDefault();
someFunction();
});
const someFunction = () => {
// 불러온 moduleA를 사용한다.
}
이 방식은 import
구문이 항상 문서의 상위에 위치해야한다는 점과, 블록문 안에서는 위치할 수 없다는 제약이 있었다.
➡️ 이러한 제약이 있는 이유는...
번들링을 할 때 코드 구조를 분석하여 모듈을 한 데 모으고, 사용하지 않는 모듈은 제거하는 등의 작업을 하는데, 이 작업은 코드 구조가 간단하고 고정이 되어있을 때만 가능하기 때문이다.
: 구문 분석 및 컴파일해야 하는 스크립트의 양을 최소화하기 위해 코드 안에서 사용하고자 하는 라이브러리 및 파일을 불러오는 방법
form.addEventListener("submit", e => {
e.preventDefault();
import('library.moduleA') // Dynamic import
.then(module => module.default)
.then(someFunction())
.catch(handleError());
});
const someFunction = () => {
// 불러온 moduleA를 사용한다.
}
import
해오도록 할 수 있다.then
메소드를 사용해 필요한 코드만 가져올 수 있다.: Dynamic import를 사용해 컴포넌트를 렌더링할 수 있게 해주는 함수
React는 SPA이므로 사용하지 않는 컴포넌트까지 한 번에 불러온다는 단점이 있다.
이때 React.lazy()
를 사용하면 컴포넌트를 동적으로 import할 수 있기 때문에, 초기 렌더링 지연 시간을 어느정도 줄일 수 있다.
import { lazy } from "react";
import Component from './Component';
// React.lazy() 안의 콜백 함수에서 import 구문을 작성한다.
const Component = React.lazy(() => import('./Component'));
이렇게 React.lazy()
로 감싼 컴포넌트는 리턴문에서 <Suspense>
컴포넌트로 감싸야 렌더링시킬 수 있다.
<Suspense>
컴포넌트로 여러 개의 lazy 컴포넌트를 렌더링시킬 수 있다.<Suspense>
컴포넌트에 fallback
prop을 전달하면, 컴포넌트가 로딩되는 동안 화면에 보여줄 React 엘리먼트를 전달할 수 있다.import { lazy, Suspense } from "react";
const Component1 = React.lazy(() => import('./Component1'));
const Component2 = React.lazy(() => import('./Component2'));
function App() {
return (
<div>
<Suspense fallback={<div>로딩 중...</div>}>
<Component1 />
<Component2 />
</Suspense>
</div>
);
}
앱에서 코드 분할을 도입할 곳을 결정하는 것은 사실 까다롭기 때문에, 앱 중간에 적용시키기보다는 웹 페이지를 불러오고 진입하는 단계인 Route
에 적용시키는 것이 좋다.
import { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home')); // Home 컴포넌트 Dynamic import
const About = lazy(() => import('./routes/About')); // About 컴포넌트 Dynamic import
const App = () => {
return (
<Router>
<Suspense fallback={<div>로딩 중...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
);
}
React.lazy()
사용해서 Home 컴포넌트와 About 컴포넌트를 import 한다.<Suspense>
컴포넌트로 감싼 후, 로딩 화면으로 사용할 컴포넌트를 fallback
속성으로 설정해준다.이 방법은 초기 렌더링 시간이 줄어든다는 장점이 있지만, 페이지를 이동하는 과정마다 로딩 화면이 보여진다는 단점이 있기 때문에 서비스에 따라서 적용 여부를 결정해야 한다.