티스토리 뷰

빠름의 민족인 우리는 웹사이트가 조금만 느려도 매우 화를 내며 사이트를 이용하지 않는다.( = 는 나) 그래서 프론트앤드 개발자는 성능에 민감하게 반응하는 편이라 사이트가 무거운 것을 두고 볼 수가 없다.(이것도 나)

여러 코드를 통해 SPA를 개발하고 나면 웹팩을 이용하여 번들링 하고, 번들된 자바스크립트 파일을 html에서 불러와 실행하는 코스는 웹을 공부해본 사람이라면 몰라도 아는(?) 개념이다. 번들링이란 여러 작업 코드를 하나의 파일로 병합해주는 것을 의미한다.

그런데 코드가 점점 많아지면 번들 파일도 함께 커지면서 성능이 현저하게 느려질 수 밖에 없다. 여담으로 현직에서 보면 라이브러리를 사용하길 싫어하는 개발자들을 꽤나 만날 수 있는데, 많은 라이브러리 혹은 무거운 라이브러리를 사용하게 되면 번들 크기가 금방 커지고 성능에 영향을 미치기 때문이다.

이럴 때, 해결하는 방안이 code splitting, 즉 코드 분할이다. 코드 분할이라 함은 하나의 무거운 번들 파일의 코드를 분할하여 여러개의 번들 파일로 나누고, 해당 페이지에서 필요한 코드들만 불러오는 개념이다. 코드 분할을 통해 앱 코드의 양을 줄이지 않고, 사용자가 필요로 하지 않은 코드를 불러오지 않아 초기화 로딩에 필요한 비용을 줄여줄 수 있다.

리액트 공식 홈페이지에서는 코드 분할에 대한 여러가지 방법을 제안해준다.

 

dynamic import

동적 import() 문법은 앱에 코드 분할을 도입하는 가장 좋은 방법이다.

// before

import { add } from './math';
console.log(add(16, 26));
// After

import("./math").then(math => {
  console.log(math.add(16, 26));
});

웹팩이 해당 구문을 만나면 코드를 분할하게 되는데, CRA를 사용하고 있다면 즉시 사용이 가능하며, Babel을 사용할 때는 라이브러리를 설치해줘야한다.

더불어 동적 import 문법은 위치에 상관 없이 필요로 하는 부분에서 사용할 수 있다.

 

React.lazy

react lazy는 말 그대로 react component를 lazy loading 시켜주는 것을 의미한다. 웹 사이트를 여러 컴포넌트 단위의 코드로 나누고, 사용자가 당장 필요로 하지 않은 코드는 불러오지 않게 하여 앱의 초기 로딩에 필요한 비용을 줄여주는 효과를 가져온다. 당연히 사용자도 빠르다고 체감할 수 있게 된다.

React.lazy와 Suspense(하단에 설명있음)는 서버 사이드 렌더링에서 동작하지 않으니, 리액트 공식 홈페이지에서는 SSR에서 코드 분할하기를 원한다면 Loadable Components 를 추천해주고 있다.

React.lazy 사용 방법은 매우 간단하다. 기존에 import 방식에서

import OtherComponent from './OtherComponent';

다음과 같은 방식으로 변경해주기만 하면 된다.

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

해당 문법은 동적 import를 통하여 컴포넌트를 랜더링 할 때 사용한다. 다르게 말하면 상단의 동적 import 방법은 컴포넌트 랜더링이 불가하다는 의미이다.

React.lazy는 default export로 가진 모듈 객체가 이행되는 Promise를 반환해야하며, Suspense 컴포넌트 하위에서 렌더링되어야한다. Suspense는 여러개의 컴포넌트를 감쌀 수 있으며, lazy 컴포넌트가 로드되길 기다리는 동안 fallback을 통하여 컴포넌트가 로드될 때까지 기다리는 동안 렌더링해주는 element를 지정할 수 있다.

import React, { Suspense } from 'react';

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

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}
 
Error boundaries
사용자는 네트워크 장애 등 여러 이유로 모듈 로드를 실패할 수 있고, 이는 에러로 이어진다. Error Boundary를 만들고 lazy 컴포넌트를 감싸면 네트워크 장애가 발생했을 때, 에러를 표시 할 수 있다.
import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';

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

const MyComponent = () => (
  <div>
    <MyErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </MyErrorBoundary>
  </div>
);

 

Route based code splitting

사용자의 경험을 해치지 않으면서 번들을 균등하게 분배하기 좋은 곳은 라우트이다.

import React, { 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>
);

 

Named Exports

React.lazy는 default exports에서만 동작한다. named exports를 사용하기 위해서는 중간 모듈 생성을 통하여 적용할 수 있다.

// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";

 

// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));

 

참고 사이트

https://ko.reactjs.org/docs/code-splitting.html

 

코드 분할 – React

A JavaScript library for building user interfaces

ko.reactjs.org

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함