코드 분할(code spliting)
대부분의 리액트 앱은 webpack이나 rollup과 같은 툴을 사용해 번들링을 한다. 번들링을 하면 html웹 페이지에 js를 쉽게 추가할 수 있다.
하지만 예전과 달리 지금은 js엔진이 해석해야할 js코드양이 많아지면서 번들링했을때 특정지점에서 코드를 해석하고 실행하는 정도가 느려졌다.
어느페이지에서 코드를 해석하고 실행하는 정도가 느려졌는지 파악해 번들을 나눈 뒤 필요한 코드만 불러올 수 수는 없을까? 라는 아이디어에서 시작한 것이 코드분할이다.
번들이 거대해지는 것을 방지하기 위한 좋은 해결방법은 번들을 물리적으로나누는 것이다.
코드분할은 런타임 시 여러 번들을 동적으로 만들고 불러오는 것으로 번들러가 지원하는 기능이다.
코드분할을 하게 되면 지금 당장 필요한 코드가 아니면 따로 분리시키고, 나중에 필요할 때 불러와 사용할 수 있다.
번들 분할 / 줄이는 법
번들링 되는 파일에는 서드파티 라이브러리도 포함된다.
( 서드파티 라이브러리 : 개인 개발자, 프로젝트 팀, 업체 등 에서 개발하는 라이브러리, 제 3자 라이브러리 )
서드파티 라이브러리는 사용자에게 다양한 메서드를 제공해주기 때문에 코드 양이 많고 번들링 시 많은 공간을 차지한다.
그래서 사용중인 라이브러리의 전부를 불러오는 것보다 따로따로 불러와서 사용하는게 공간 차지를 줄일 수 있다./* 이렇게 lodash 라이브러리를 전체를 불러와서 그 안에 들은 메서드를 꺼내 쓰는 것은 비효율적입니다.*/ import _ from 'lodash'; ... _.find([]); /* 이렇게 lodash의 메서드 중 하나를 불러와 쓰는 것이 앱의 성능에 더 좋습니다.*/ imprt find from 'lodash/find'; find([]);
lodash라는 라이브러리는 하나의 폴더와같고, 그 폴더 안에는 개발 시 다양한 상황에 쓰기 좋은 함수 코드들이 있다.
이 함수 코드들의 양이 많아 정말로 필요한 것 한두 개만 쓰이면 나머지는 그냥 쓰이지 않고 덩어리로 앱 내부에 남게 된다.
그래서 필요한 것 한 두개만 가져다 쓰는 방식으로 개발하는 것이 좋다.
React에서의 코드 분할
리액트에서 코드분할 방법은 dynamic import(동적 불러오기)를 사용하는 것이다. 그전에는 코드파일의 최상위에 import 지시자를 사용하는 static import(정적 불러오기)를 사용했다./* 기존에는 파일의 최상위에서 import 지시자를 이용해 라이브러리 및 파일을 불러왔습니다. */ import moduleA from "library"; form.addEventListener("submit", e => { e.preventDefault(); someFunction(); }); const someFunction = () => { /* 그리고 코드 중간에서 불러온 파일을 사용했습니다. */ }
기존에는 항상
import
를 문서의 상위에 위치했고, 블록문 안에서는 사용할 수 없는 제약이 있었다.
하지만 이제는 구문 분석과 컴파일 해야하는 스크립트의 양을 최소화하기 위해 dynamic import 구문을 지원한다.form.addEventListener("submit", e => { e.preventDefault(); /* 동적 불러오기는 이런 식으로 코드의 중간에 불러올 수 있게 됩니다. */ import('library.moduleA') .then(module => module.default) .then(someFunction()) .catch(handleError()); }); const someFunction = () => { /* moduleA를 여기서 사용합니다. */ }
dynamic import는
then
함수를 사용해 필요한 코드만 가져온다.
가져온 코드에 대한 모든 호출은 해당 함수 내부에 있어야 한다.
dynamic import는React.lazy
와 함께 사용할 수 있다.
React.lazy()
이 함수를 사용하면 dynamic import를 사용해 컴포넌트를 렌더링할 수 있다.
컴포넌트를 동적으로 import 할 수 있어서 초기 렌더링 지연시간을 어느정도 줄일 수 있다.import Component from './Component'; /* React.lazy로 dynamic import를 감쌉니다. */ const Component = React.lazy(() => import('./Component'));
React.lazy
로 감싼 컴포넌트는 단독으로 사용할 수 없고React.suspense
컴포넌트 하위에서 렌더링해야 한다.
React.Suspense
suspense
는 아직 렌더링이 준비되지 않은 컴포넌트가 있을 때 로딩 화면을 보여주고, 로딩이 완료되면 렌더링이 준비된 컴포넌트를 보여주는 기능이다./* suspense 기능을 사용하기 위해서는 import 해와야 합니다. */ import { Suspense } from 'react'; const OtherComponent = React.lazy(() => import('./OtherComponent')); const AnotherComponent = React.lazy(() => import('./AnotherComponent')); function MyComponent() { return ( <div> {/* 이런 식으로 React.lazy로 감싼 컴포넌트를 Suspense 컴포넌트의 하위에 렌더링합니다. */} <Suspense fallback={<div>Loading...</div>}> {/* Suspense 컴포넌트 하위에 여러 개의 lazy 컴포넌트를 렌더링시킬 수 있습니다. */} <OtherComponent /> <AnotherComponent /> </Suspense> </div> ); }
Suspense
컴포넌트의fallback
prop은 컴포넌트가 로드될때까지 기다리는동안 로딩 화면으로 보여줄 리액트 엘리먼트를 받아들인다.
Suspense
컴포넌트 하나로 여러 개의 lazy 컴포넌트들을 보여줄 수 있다.
React.lazy & Suspense적용
중간에 적용시키는 것보다 웹 페이지를 불러오고 진입하는 단계인Route
에 이 두 기능을 적용하는 것이 좋다.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> );
내용 출처, 참조 : 코드스테이츠
이미지 출처 : 코드스테이츠