코드 분할 (Code Splitting)

지은·2022년 11월 28일
0

번들링

: 웹 애플리케이션 동작에 필요한 HTML, CSS, JavaScript 등의 파일들을 묶어서 제공하는 것

대부분의 React 앱들은 Webpack이나 Rollup과 같은 툴을 사용해 앱을 번들링(Bundling)한다.

하지만 모던 웹으로 발전하면서 JavaScript의 코드 양이 많아지고 무거워지면서, 점차 페이지를 로드하는 시간이 길어지게 되었다.

➡️ 이렇게 번들이 거대해지는 것을 방지하기 위해 번들을 물리적으로 나누자는 아이디어가 등장했다.

코드 분할 (Code Splitting)

: 런타임에서 여러 번들을 동적으로 만들고 불러오는 것으로 Webpack, Rollup과 같은 번들러가 지원하는 기능

코드 분할을 하게 되면 지금 당장 필요한 코드만 불러와서 사용할 수 있으며, 이를 통해 페이지 로딩 속도를 개선할 수 있다.

React에서의 코드 분할

React는 SPA(Single Page Application)이다.
SPA는 사용하지 않는 모든 컴포넌트까지 한 번에 불러오기 때문에 처음 화면을 렌더링하는 데에 시간이 오래 걸린다.
➡️ 그래서 사용하지 않는 컴포넌트는 나중에 불러오기 위해 코드 분할 개념을 도입했다.
React에서 코드 분할을 하는 법은 동적 불러오기(Dynamic import)를 사용하는 것이다.

정적 불러오기(Static import)

: 코드 파일의 최상위에서 사용하고자 하는 라이브러리 및 파일을 불러오는 방법

import moduleA from "library"; // Static import

form.addEventListener("submit", e => {
  e.preventDefault();
  someFunction();
});

const someFunction = () => {
  // 불러온 moduleA를 사용한다.
}

이 방식은 import 구문이 항상 문서의 상위에 위치해야한다는 점과, 블록문 안에서는 위치할 수 없다는 제약이 있었다.

➡️ 이러한 제약이 있는 이유는...
번들링을 할 때 코드 구조를 분석하여 모듈을 한 데 모으고, 사용하지 않는 모듈은 제거하는 등의 작업을 하는데, 이 작업은 코드 구조가 간단하고 고정이 되어있을 때만 가능하기 때문이다.


동적 불러오기(Dynamic import)

: 구문 분석 및 컴파일해야 하는 스크립트의 양을 최소화하기 위해 코드 안에서 사용하고자 하는 라이브러리 및 파일을 불러오는 방법

form.addEventListener("submit", e => {
  e.preventDefault();
  
  import('library.moduleA') // Dynamic import
    .then(module => module.default)
    .then(someFunction())
    .catch(handleError());
});

const someFunction = () => {
  // 불러온 moduleA를 사용한다.
}
  • 이 방식은 사용자가 form을 통해 양식을 제출한 경우에만 모듈을 import해오도록 할 수 있다.
  • Dynamic import는 then 메소드를 사용해 필요한 코드만 가져올 수 있다.
  • 가져온 코드에 대한 호출은 해당 함수 내부에 있어야 한다.

React.lazy()

: 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.Suspense

이렇게 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에서 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')); // 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>
  );
}
  • 라우터가 분기되는 App 컴포넌트에서 React.lazy() 사용해서 Home 컴포넌트와 About 컴포넌트를 import 한다.
  • Route 컴포넌트들을 <Suspense> 컴포넌트로 감싼 후, 로딩 화면으로 사용할 컴포넌트를 fallback 속성으로 설정해준다.

이 방법은 초기 렌더링 시간이 줄어든다는 장점이 있지만, 페이지를 이동하는 과정마다 로딩 화면이 보여진다는 단점이 있기 때문에 서비스에 따라서 적용 여부를 결정해야 한다.

profile
개발 공부 기록 블로그

0개의 댓글