코드 분할(Code Splitting)

Rosevilleage·2023년 3월 23일
0

Code Splitting

번들된 파일이나 코드를 여려개로 분리하는 것을 의미하며, 번들이 여러개로 나누어져 있기 때문에 특정 컴포넌트만 골라서 로딩하거나 여러 컴포넌트를 병렬로 로딩할 수 있게 된다.

코드의 양과 서드파티 라이브러리의 양이 늘어나면 번들된 파일의 용량도 필연적으로 늘어나게된다.

번들된 파일의 용량이 커지면 브라우저에서 페이지를 로드할때 걸리는 시간이 늘어나기 때문에 고안된 것이 code splitting이다.

webpack 등의 번들러에서 제공하는 기능을 통해 번들을 나눠 개별 번들의 크기를 줄이면 로딩에 필요한 기능이나 컴포넌트를 우선적으로 가져올 수 있으며, 이후 상호작용이 일어날 때 필요한 기능 등을 추가적으로 가져올 수 있게된다.

서드파티 라이브러리

서드파티 라이브러리는 개발을 위한 다양한 기능들을 포함하고 있기 때문에 기본적으로 용량이 크고, 번들링 시에 많은 공간을 차지하게 된다.

이를 개선하기 위한 방법중 하나는 라이브러리 전체를 import하는것이 아닌 필요한 기능만 import해서 사용하는 것이다.

import _ from 'lodash';
_.find()

위와 같이 lodash 라이브러리 전체를 가져와 하나의 메서드만 사용하는 것은 번들의 공간 낭비가 되므로

import find from 'lodash;
find()

사용할 메서드만 가지고 오는 것이 좀 더 효율적이다.

동적 import

기본적으로 import를 사용할 때 컴포넌트 외부인 파일의 최상단에서 진행하지만, 동적 import 문법은 컴포넌트 내부에서 필요한 상황에 import를 하는 방식이다.

// 기존 static 방식
import _ from 'lodash'

function static() {
  const element = document.createElement('div')
  element.innerHTML = _.join(['hello', 'world'], ' ');
  return element;
}
document.body.appendChild(static());
// 동적 방식
function dynamic() {
  return import('lodash')
    .then(({default: _}) => {
      const element = document.createElement('div');
      element.innerHTML = _.join(['hello', 'world'], ' ');
      return element;
})
.catch(err => 'error message');
 }

dynamic().then(element => document.body.appendChild(element));

webpack4 부터 CommonJs 모듈을 가져올 때 module.exports가 아닌 CommonJs 모듈에 대한 인공 네임스체이스 객체를 생성하기 때문에 import()에서 모듈 전체를 가져오길 원한다면, {default: 'name'} 방식으로 default 모듈을 가져와야 한다.

import()는 promise를 반환하기 때문에 async/await를 같이 사용할 수도 있다.

async function asyncDynamic() {
  const element = document.createElement('div');
  const {default: _} = await import('lodash')
  element.innerHTML = _.join(['hello', 'world'], ' ');
  return element;
}

asyncDynamic().then(element => document.body.appendChild(element));

babel을 같이 사용하는 경우에는 @babel/plugin-syntax-dynamic-import 를 설치후 세팅해서 import()가 변환되지 않도록 해야 한다.

라고 react 공식 문서에서 안내하고 있으나

이 플러그인은 이미 ES2020의 @babel/preset-env 포함되어 있기 때문에 preset-env를 설치했다면 추가적으로 설치할 필요는 없다.
다만 구형 브라우저를 위한 promise, iterator poltfill을 추가 해야 브라우저 호환성을 챙길 수 있다.

다음과 같이 install해 webpack.config.js에 추가한다.

npm install -D @babel/plugin-syntax-dynamic-import
//webpack.config.js
module.export = {
  //...
  origins: {
    plugins: ['@babel/plugin-syntax-dynamic-import']
  }
}

React.lazy, Subpense

React에서 lazy()를 사용하면 import()를 통해서 컴포넌트를 렌더링 할 수 있다.
인자로 들어온 구성요소의 로드를 해당 요소가 처음 렌더링 될때까지 연기하는 역할을 하기 때문에 컴포넌트를 사용하는 시점에 가져올 수 있게된다.

lazy()는 import()를 호출하는 함수를 인자로 받으며, 반환값은 React 컴포넌트를 default export로 가지는 모듈을 출력값으로 하는 Promise다.

때문에 Suspense 하위에 위치해야 하는 규칙을 가지고 있다.

import {Suspense} from 'react';

const Component = React.lazy(() => import('./Component'));

function Top() {
  return (
    <div>
      <Suspense fallback={<div>...Loading</div>}>
        <Component />
      </Suspense>
    </div>
  );
}

Suspense는 하위 컴포넌트에게 필요한 모든 코드와 데이터가 로드될때 까지 fallback 속성에 할당된 컴포넌트를 화면에 표시하며,

Relay, Next.js와 같은 suspense 지원 프레임워크나 lazy()를 포함한 suspense 지원 데이터 소스에만 구성 요소를 활성화 한다.
이는 이벤트 핸들러에 의해 데이터를 가져오는 상황에서는 활성화가 되지 않는다는 것을 의미한다.


Reference

React-문서-코드 분할

Reack-APIReference-<Suspense>

babel-ES2020-@babel/plugin-syntax-dynamic-import

webpack-Guides-Code Splitting
*https://webpack.js.org/guides/code-splitting/

0개의 댓글