코드스테이츠(SEB_FE_42)

[React] 코드 분할, lazy, Suspense

codeyun2 2023. 1. 25. 20:32

코드 분할(Code Spliting)

모던 웹으로 발전하면서 JavaScript 코드가 방대해지고 무거워짐을 개선하기 위해

런타임 시 여러 번들을 동적으로 만들고 불러오는 것

 

당장 필요한 코드가 아니면 따로 분리시키고, 필요할 때 불러와서 사용

-> 대규모 프로젝트 앱의 경우에도 페이지 로딩 속도를 개선할 수 있음

 

번들 분할, 줄이는 법

서드 파티 라이브러리(플러그인, 라이브러리, 프레임워크)는 다양한 메서드를 제공하기 때문에 코드 양이 많아

번들링 시 많은 공간을 차지함

-> 라이브러리를 전부 불러와 사용하지 말고, 필요한 것만 불러와 사용하는 것이 성능 개선에 좋음

// 라이브러리 전부 불러오기
import _ from 'lodash';
_.find([]);

// 필요한 메서드만 불러오기
import find from 'lodash/find';
find([]);

 

React에서의 코드 분할

React는 SPA라서 당장 사용하지 않는 컴포넌트까지 모두 불러오기 때문에

첫화면 렌더링에 시간이 오래 걸림

-> 사용하지 않는 컴포넌트는 나중에 불러오기 위해 코드 분할 개념을 도입함

 

기존: 정적 분할(Static Import)

- 문서의 상위에서 라이브러리와 파일을 import함(코드 구조의 중심을 잡아줌)

- 블록문 안에 위치할 수 없음

-> 번들링 시 코드 구조를 분석해 모듈을 모으고 사용하지 않는 모듈을 제거하기 때문에 코드 구조가 간단하고 고정되어 있어야했음

/* 기존에는 파일의 최상위에서 import를 이용해 라이브러리 및 파일을 불러옴 */
import moduleA from "library";

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

const someFunction = () => {
  /* 코드 중간에서 불러온 파일을 사용 */
}

 

동적 분할(Dynamic Import)

- 코드의 중간에서 모듈을 불러올 수 있음(import, then)

- 가져온 코드의 호출은 해당 함수 내부에 있어야 함

-> 구문 분석 및 컴파일해야하는 스크립트 양을 최소화하기 위해 사용하며 then으로 필요한 코드만 가져옴

 

번들링 시 분할된 코드(청크)를 지연 로딩시키거나, 요청 시 로딩할 수 있음

form.addEventListener("submit", e => {
  e.preventDefault();
	/* 코드의 중간에서 모듈을 불러옴 */
  import('library.moduleA')
    .then(module => module.default)
    .then(someFunction())
    .catch(handleError());
});

const someFunction = () => {
    /* moduleA 사용 */
}

 

import(module)

모듈을 읽어 모듈의 내용을 모두 포함한 객체를 담은 프로미스 반환

import는 함수가 아니기 때문에 변수에 import를 복사하거나 call/apply 사용 불가

 

 

React.lazy()

dynamic import로 컴포넌트를 렌더링할 수 있게 해줌

-> 컴포넌트를 분리하여 초기 렌더링 시간을 줄여줌

 

React.Suspense 하위에서 렌더링해야 함

 

React.Suspense

Router로 분기가 나누어진 컴포넌트들을 lazy를 통해 import하면,

해당 path로 이동할 때 컴포넌트를 불러오는데 이 때 로딩 시간이 생기게 됨

 

아직 렌더링이 준비되지 않은 컴포넌트가 있으면 fallback 속성으로 설정한 로딩화면을 보여줌

 

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>
);

 

동적으로 컴포넌트를 불러오는 것에 장단점이 있기 때문에 서비스에 따라 적용 여부를 결정해야 함

장점: 초기 렌더링 시간이 줄어듦

단점: 페이지 이동 시마다 로딩 화면이 보여짐

 

 

 

 

 

2023.01.25

코스 S4U4 코드 분할, React.lazy, React.Suspense