React | 코드 스플리팅 (+webpack / build)

Kate Jung·2021년 11월 12일
0

React

목록 보기
16/28
post-thumbnail

🔶 웹팩(webpack)

🔹 빌드 작업 → 담당

  • 리액트 프로젝트 완성 후 → 사용자에게 제공 시, 빌드 작업 거쳐 배포

◼ 빌드 작업을 통해 할 수 있는 것

  • 파일 크기 최소화

    js 파일 내부의 불필요한 주석, 경고 메세지, 공백 등 → 제거

  • 코드 트랜스파일 작업

    브라우저에서 JSX 문법 or 다른 최신 js 문법 → 원활한 실행 되도록

  • 정적 파일(img 등) → 경로 설정

🔹 별도의 설정 하지 않으면

  • 프로젝트에서 사용 중인

    모든 js/css 파일 → 각각 하나의 파일로 합쳐짐 (총 2개)

🔹 CRA로 프로젝트 빌드 시

  • 최소 2개 이상의 JS 파일 생성

  • CRA의 기본 웹팩 설정

    → SplitChunks 기능 적용

🔹 SplitChunks

캐싱 효과

  • node_modules 에서 풀러온 파일
  • 일정 크기 이상의 파일
  • 여러 파일 간에 공유된 파일

→ 자동으로 따로 분리

🔹 빌드 방법

$ yarn build

🔹 build/static

◼ 파일 이름 (해시 값 포함)

  • 해당 파일 내용에 따라 생성

  • 알 수 있는 것

    브라우저 → 새 파일 받아야 할지 여부

  • ex. 7b7f7f25

◼ 파일의 시작

  • 2

    node_modules에서 불러온 라이브러리 관련 코드

    • 자주 안바뀌는 코드들 소속 (SplitChunks을 통해) → 캐싱 이점 더 오래 유지 가능
  • main

    직접 프로젝트에 작성하는 (App 같은) 컴포넌트 코드

🔶 코드 스플리팅

파일 분리하는 작업

🔹 코드 비동기 로딩

필요한 시점에 사용 가능 (js 함수, 객체, 컴포넌트 등)

  • 코드 스플리팅 방법 中 1

◼ 별도 설정 無 → 문제점

  • SplitChunks

    → 단순히 효율적인 캐싱 효과만 有

  • 모든 코드(모든 페이지) → 한 파일(main)에 저장

    → 악순환

    • 파일 크기 ↑ (필요 x 컴포넌트 정보 모두 부름)
    • 로딩 오래 걸림 → 사용자 경험 ↓ → 트래픽 多

◼ 방법 (JS 함수)

  1. import() 함수 형태로 메소드 안에서 사용
  2. 빌드 ( yarn build )

효과

함수가 필요한 지점에 파일 불러와서 사용 가능

  • 파일 → 따로 분리시켜 저장

예시 코드

  • src/notify.js
        export default function notify() {
          alert("안녕하세요!");
        }
  • src/App.js

    ▶ 기존

            import logo from "./logo.svg";
            import "./App.css";
            import notify from "./notify";
            
            function App() {
              const onClick = () => {
                notify();
              };
              return (
                <div className="App">
                  <header className="App-header">
                    <img src={logo} className="App-logo" alt="logo" />
                    <p onClick={onClick}>Hello React!</p>
                  </header>
                </div>
              );

▶ 적용 후

            import logo from "./logo.svg";
            import "./App.css";
            
            function App() {
              const onClick = () => {
                import("./notify").then((res) => res.default());
              };
              return (
                <div className="App">
                  <header className="App-header">
                    <img src={logo} className="App-logo" alt="logo" />
                    <p onClick={onClick}>Hello React!</p>
                  </header>
                </div>
              );
            }
            
            export default App;

원리

  • import를 함수로 사용 시

    → Promise 반환

  • default로 내보낸 모듈 불러올 때

    result.default 를 참조해야 사용 가능

    • 예시
          const onClick = () => {
            import("./notify").then((res) => res.default());
          };
  • 빌드 후

    build/static/js → 3으로 시작하는 파일 생성 (notify 관련 코드 들어감)

[ 확인 ] Network 탭

  1. Hello React를 클릭하는 시점에 새로운 js 파일 불러옴

  2. 파일 내용 확인 시

    notify(필요해서 불러온 코드)관련 코드만 有

🔹 방법 1. state 사용

구현 : import 함수로 부르고 컴포넌트 자체를 state에 넣는 방식

  • 리액트 16.6 버전 이전
  • React.lazy 없이 스플리팅

◼ 방법 (+ 예시 코드)

    1. 코드 스플리팅할 컴포넌트 (SplitMe.js)
    import React from "react";
    
    const SplitMe = () => {
      return <div>SplitMe</div>;
    };
    
    export default SplitMe;
    1. App.js
    • 클래스형 컴포넌트


    1. 스플리팅할 컴포넌트 부를 메소드 내부에서 호출 후, state 넣기 (setState)

    2. render함수

      → 해당 컴포넌트의 유효성 검사 후, 렌더링

    • 코드
        import React, { Component } from "react";
        import logo from "./logo.svg";
        import "./App.css";
        
        class App extends Component {
          state = {
            SplitMe: null,
          };
          handleClick = async () => {
            const loadedModule = await import("./SplitMe");
            this.setState({
              SplitMe: loadedModule.default,
            });
          };
          render() {
            const { SplitMe } = this.state;
            return (
              <div className="App">
                <header className="App-header">
                  <img src={logo} className="App-logo" alt="logo" />
                  <p onClick={this.handleClick}>Hello React!</p>
                  {SplitMe && <SplitMe />}
                </header>
              </div>
            );
          }
        }
        
        export default App;

◼ 단점

매번 state 선언 → 불편

🔹 방법 2. React.lazy & Suspense

  • 간편
  • 함수형 컴포넌트
  • SSR 지원 x

◼ React.lazy

유틸 함수 : 컴포넌트 렌더링 시점에서 비동기적으로 로딩 가능하게 함

  • 코드
    const SplitMe = React.lazy(() => import('./SplitMe'));

◼ Suspense

  • 코드 스플리팅된 컴포넌트 → 로딩하도록 발동 가능
  • 로딩 끝나지 x 때 → 보여줄 UI 설정 가능
  • 리액트 내장 컴포넌트
  • 예시 코드
    import React, { Suspense } from 'react';
    
    (...)
    <Suspense fallback = {<div>loading...</div>}>
    	<SplitMe/>
    </ Suspense>
  • fallback props

    로딩 중 보여줄 JSX 지정 가능

◼ 방법

  1. useState 생성 (의미: 단순히 코드 스플리팅된 컴포넌트의 가시성)

  2. setState → 업데이트

    → 보여짐

  • 예시 코드
    import React, { useState, Suspense } from "react";
    import logo from "./logo.svg";
    import "./App.css";
    const SplitMe = React.lazy(() => import("./SplitMe"));
    
    function App() {
      const [visible, setVisible] = useState(false);
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p onClick={() => setVisible(true)}>Hello React!</p>
            <Suspense fallback={<div>loading...</div>}>
              {visible && <SplitMe />}
            </Suspense>
          </header>
        </div>
      );
    }
    
    export default App;

🔹 방법 3. Loadable Components

서드파티 라이브러리 (코드 스플리팅 더 편하게 도와주는)

  • SSR 지원

◼ 설치

$ yarn add @loadable/component

◼ 예시 코드

import React, { useState, Suspense } from "react";
import logo from "./logo.svg";
import "./App.css";
import loadable from "@loadable/component";
const SplitMe = loadable(() => import("./SplitMe"), {
  fallback: <div>loading...</div>,
});

function App() {
  const [visible, setVisible] = useState(false);
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p onClick={() => setVisible(true)}>Hello React!</p>
        {visible && <SplitMe />}
      </header>
    </div>
  );
}

export default App;

◼ React.lazy 와 비교

  • React.lazy → loadable
  • fallback 위치 → loadable의 인자 (다른 UI 및 생략 가능)
  • Suspense → 無

◼ 컴포넌트 미리 불러오는(preload) 방법

  • (렌더링하기 전에 필요할 때) 스플리팅된 파일 미리 부를 수 있는 기능

  • 방법

    • onMouseOver스플리팅된 컴포넌트.preload()

      ⇒ 마우스 커서를 해당 위치 (Hello React!)에 올린 즉시 → 로딩 시작

      → 클릭 시, 렌더링

    • 코드 예시

        const onMouseOver = () => {
            SplitMe.preload();
          };
        
        (...)
        <p onMouseOver={onMouseOver}>
          Hello React!
        </p>
  • 예시 코드
    import React, { useState, Suspense } from "react";
    import logo from "./logo.svg";
    import "./App.css";
    import loadable from "@loadable/component";
    const SplitMe = loadable(() => import("./SplitMe"), {
      fallback: <div>loading...</div>,
    });
    
    function App() {
      const [visible, setVisible] = useState(false);
      const onMouseOver = () => {
        SplitMe.preload();
      };
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p onClick={() => setVisible(true)} onMouseOver={onMouseOver}>
              Hello React!
            </p>
            {visible && <SplitMe />}
          </header>
        </div>
      );
    }
    
    export default App;
  • 장점
    • 사용자 → 더 좋은 경험 제공

◼ 그 외 다양한 기능 제공

타임아웃, 로딩 UI 딜레이, SSR 렌더링 호환 등

🔹 참고. 느린 네트워크 속도 시뮬레이트 방법

  1. 개발자 도구의 Network 탭 → Online 클릭
  2. Slow 3G 선택
profile
복습 목적 블로그 입니다.

0개의 댓글