코드분할 & React.lazy() & Suspense

이유정·2022년 11월 28일
0

코드스테이츠 TIL

목록 보기
54/62

React는 계속 업데이트되는 오픈소스 라이브러리다.
1) React 18 버전은 더이상 ReactDOM.render를 지원하지 않는다
이전의 index.js

const rootElement = document.getElementById("root"); 
ReactDOM.render(<AppTest />, rootElement)

이제는 createRoot API를 사용한다.

import {createRoot} from "react-dom/client"

const rootElement = document.getElementById("root")
const root = createRoot(rootElement)

root.render(
	<App />
)

2) Concurrent Feature
Suspense 기능을 이용해 독립적으로 렌더링을 할 수 있도록 했다.

코드분할 (Code Spliting)

대부분 react 앱들은 Webpack, Rollup 과 같은 툴을 사용해 번들링을 한다. html웹 페이지에 js를 쉽게 추가할 수 있기 때문이다. 번들된 앱은 모든 js가 한곳에 있기 때문에 설정하는데 필요한 호출 수가 적은 링크 하나만 필요하게 된다. 이 방법은 과거에는 무리가 없었다. 모던 웹 이전의 웹 js코드는 최소한의 수준으로 작성됐기 때문이다.

모던웹으로 발전하면서 점점 DOM을 다루는 정도가 정교해지면 JS 코드 자체가 방대해지고 무거워졌다.

번들을 나눈뒤 필요한 코드만 불러오면 어떨까???

  • 지금 필요한 코드만 불러오고 나중에 필요한 코드는 나중에 불러오자!
  • 번들이 거대해지는 것을 방지하기 위해 번들을 물리적으로 나누자!

코드분할

  • 런타임 시 여러 번들을 동적으로 만들고 불러오는것
  • Webpack. Rollup과 같은 번들러가 지원하는 기능이다.

번들 분할 혹은 줄이는 법

third party

  • npm을 통해 다운받는 서드파티 라이브러리도 번들링되는 파일이다.
  • third party 라이브러리는 개인 개발자나 프로젝트 팀 혹은 업체 등에서 개발하는 라이브러리로, 제 3자 라이브러리다.
  • 서드파티 라이브러리는 플러그인이나 라이브러리, 프레임워크등이 존재한다.
  • 이 라이브러리를 잘 활용하면 편하고 효율적이다.
  • 사용자에게 다양한 메소드를 제공하기 때문에 코드의 양이 많고, 번들링시 많은 공간을 차지한다.
    => 사용중인 라이브러리의 전부를 불러와서 사용하는 것보다, 따로 따로 불러와서 사용할 수 있다면 많은 공간을 차지하지 않을 수 있다.

lodash에서의 코드분할

  • 라이브러리의 종류다.
  • 배열, 숫자, 객체, 문자열을 사용할 때 반복적인 작업을 할때 사용하기 좋다.
  • 하나의 폴더와 같다. 그 폴도 안에는 개발시 다양한 상황에 쓰기 좋은 메소드들, 함수코드가 들어있다.
  • 이 함수코드의 양이 엄청나고, 전부 가져올 경우에 쓰이지 않는 코드 뭉치로 앱 내부에 남게된다. 이는 앱의 성능을 저하시킨다.
    => 필요한 것 한두개만 가져다 쓰는 방법이 좋다!!!

1) lodash 라이브러리 전체를 불러와서 그 안에 들은 메소드 꺼낸 방식

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

2) lodash의 메소드 중 하나를 불러와 쓰는 방식 (이게 좋아!!!)

import find from 'lodash/find'
find([])

React에서의 코드 분할

react는 SPA라, 사용하지 않는 모든 컴포넌트까지 한번에 불러온다
=> 첫 화면이 렌더링 될때까지의 시간이 오래걸린다.
=> 코드분할 개념을 도입해보자! (사용하지 않는 컴포넌트는 나중에 불러와~)

React에서 코드 분할하는 방법 2가지

static import

  • 옛날 방식
  • 코드 파일의 가장 최상위에서 import지시자를 사용해 사용하고자 하는 라이브러리 및 파일을 불러오는 방법
  • import 구문은 문서의 상위에 위치한다. 블록문 안에는 위치할 수 없다. (번들링 시 코드 구조를 분석해 모듈을 한 데 모으고, 사용하지 않는 모듈은 제거하는 등의 작업을 하는데 이를 위해선 코드 구조가 간단하고 고정이 되어있어야 하기 때문이다.)
/* 기존에는 파일의 최상위에서 import 지시자를 이용해 라이브러리 및 파일을 불러왔습니다. */
import moduleA from "library"

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

const someFunction = () => {
  /* 그리고 코드 중간에서 불러온 파일을 사용했습니다. */

dynamic import

  • 구문 분석 및 컴파일해야 하는 스크립트의 양을 최소화하기 위해 사용
form.addEventListener("submit", e => {
  e.preventDefault();
	/* 동적 불러오기는 이런 식으로 코드의 중간에 불러올 수 있게 됩니다. */
  import('library.moduleA')
    .then(module => module.default)
    .then(someFunction())
    .catch(handleError());
});

const someFunction = () => {
    /* moduleA를 여기서 사용합니다. */
}
  • moduleA가 다른 곳에서 사용되지 않는 경우, 사용자가 form 을 통해 양식을 제출한 경우에만 가져오도록 할 수 있다.
  • then 함수를 사용해 필요한 코드만 가져온다. 가져온 코드에 대한 모든 호출은 해당 함수 내부에 있어야 한다.
  • 번들링 시 분할된 코드를 지연 로딩시키거나 요청 시에 로딩할 수 있다.
  • React.lazy와 함께 사용할 수 있다.

React.lazy() & Suspense

React.lazy()

  • dynamic import를 사용해 컴포넌트를 렌더링할 수 있다.
  • 컴포넌트를 동적으로 import할 수 있으므로 초기 렌더링 지연시간을 어느정도 줄일 수 있다. (react의 spa 특성 때문에 한번에 사용하지 않는 컴포넌트까지 불러오는 단점을 커버)
  • 이 React.lazy로 감싼 컴포넌트는 단독으로 쓰일 수는 없고, React.suspense 컴포넌트의 하위에서 렌더링을 해야 한다.
import Component from './Component';

/* React.lazy로 dynamic import를 감쌉니다. */
const Component = React.lazy(() => import('./Component'));

React.Suspense

  • 아직 렌더링이 준비되지 않은 컴포넌트가 있을 때 로딩 화면을 보여주고, 로딩이 완료되면 렌더링이 준비된 컴포넌트를 보여주는 기능이다.
    (Router로 분기가 나눠진 컴포넌트들을 lazy를 통해 import 하면 해당 path로 이동할 때 컴포넌트를 불러오기 때문에 로딩하는 시간이 발생한다.)
  • Supense 컴포넌트의 fallback prop은 컴포넌트가 로드될 때까지 기다리는 동안 로딩 화면으로 보여줄 React 엘리먼트를 받아들인다.
  • Suspense 컴포넌트 하나로 여러 개의 lazy 컴포넌트를 보여줄 수 있다.
/* suspense 기능을 사용하기 위해서는 import 해와야 합니다. */
import { Suspense } from 'react';

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

function MyComponent() {
  return (
    <div>
			{/* 이런 식으로 React.lazy로 감싼 컴포넌트를 Suspense 컴포넌트의 하위에 렌더링합니다. */}
      <Suspense fallback={<div>Loading...</div>}>
				{/* Suspense 컴포넌트 하위에 여러 개의 lazy 컴포넌트를 렌더링시킬 수 있습니다. */}
        <OtherComponent />
				<AnotherComponent />
      </Suspense>
    </div>
  );
}

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'));
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>
);
  • 라우터가 분기되는 컴포넌트에서 각 컴포넌트에 React.lazy를 사용하여 import한다.
  • Route 컴포넌트들을 Suspense로 감싼 후 로딩 화면으로 사용할 컴포넌트를 fallback 속성으로 설정해주면 된다.
profile
팀에 기여하고, 개발자 생태계에 기여하는 엔지니어로

0개의 댓글