코드분할

js·2022년 6월 18일
0
post-thumbnail

코드분할 왜 해야 하는가?

번들링은 필수이나 앱이 커지면 번들도 커지기 마련입니다.

특히, 용량이 큰 서드파티 라이브러리는 로드시간을 길게 만드는 주범이니 주의합시다.

아이러니하게도, 번들이 거대해질수록 번들을 나누어야 합니다. 이럴꺼면 왜 번들링한건데 ㅡ.ㅡ

번들한 코드를 분리하고 나서, 필요에 따라 특정 컴포넌트만 로딩하거나 병렬로 로딩 할 수 있습니다.

코드분할의 장점

1) 초기 로딩이 빠르다
: 코드 총량은 일정하나 초기 로딩에 필요한 코드는 적어진다

2) 지연 로딩이 가능하다
: 결국엔 초기 로딩이 빨라진다

동적 import

적용전

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

동적 import 적용후

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

특징

  • 동적 import는 불러온 모듈을 프로미스 객체로 반환한다.

  • 코드의 위치에 관계 없이 동적 import를 할 수 있다.

  • Webpack이 이 구문을 만나게 되면 앱의 코드를 분할합니다. Create React App을 사용하고 있다면 이미 Webpack이 구성이 되어 있기 때문에 즉시 사용할 수 있습니다. Next.js 역시 지원합니다.

  • Babel을 사용할 때는 Babel이 동적 import를 인식할 수 있지만 변환하지는 않도록 합니다. 이를 위해 @babel/plugin-syntax-dynamic-import를 사용하세요.

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

특징

  • 동적 import를 사용하여 컴포넌트를 렌더링 한다.

  • 매개변수로 동적 import()를 호출하는 함수를 받는다.

  • React.lazy 컴포넌트는 Suspense 컴포넌트 하위에 속해야 한다.

  • Suspense 컴포넌트는 fallback prop은 lazy 컴포넌트를 로딩하는 동안 렌더링하는 요소이다.

  • 하나의 Suspense 컴포넌트로 여러 lazy 컴포넌트를 감쌀 수 있다.

Error boundaries

Suspense를 미리 정의한 MyErrorBoundary 컴포넌트로 감싸면

lazy 컴포넌트에서 네트워크 장애가 발생 했을시 에러를 처리할 수 있다

특징

  • Error boundaries로 감싼 컴포넌트들의 에러만을 캐치한다

  • 클래스 컴포넌트만이 에러 경계가 될 수 있다

  • try catch 구문과 비슷하지만, 컴포넌트에만 적용되며 Error boundaries가 에러를 캐치하지 못하면 에러는 그 위의 가장 가까운 Error boundaries로 전파된다

  • Error boundaries 캐치하지 않는 에러

  1. 이벤트 핸들러
  2. 비동기 코드
  3. 서버사이드 렌더링
  4. Error boundaries의 자식이 아닌 자체에서 발생하는 에러

Error boundaries 레퍼런스

class MyErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 다음 렌더링에서 폴백 UI가 보이도록 상태를 업데이트 합니다.
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 에러 리포팅 서비스에 에러를 기록할 수도 있습니다.
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 폴백 UI를 커스텀하여 렌더링할 수 있습니다.
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

https://ko.reactjs.org/docs/error-boundaries.html

라우터에서의 코드분할

코드분할에 가장 적합한곳은 라우트이다. 페이지 전환시에 로딩이 필요하다.

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

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

React.lazy는 현재 default exports만 지원합니다.

0개의 댓글