리액트 프로젝트를 사용자에게 제공할 때는 빌드 작업을 거쳐서 배포해야 한다.
빌드 작업에선
과 같은 일을 한다.
빌드 작업은 웹팩 이라는 도구가 담당한다.
스플리팅이란?
파일을 분리하는 작업
CRA를 통해 React 프로젝트를 만들 경우 기본으로 탑재되어있는 SplitChunks 기능을 통해 스플리팅이 되지만, 단순한 캐싱 효과만 있을 뿐이다.
애플리케이션의 규모가 커질 경우에 기본적인 스플리팅 기능만 이용하면 로딩이 오래 걸리고 사용자 경험도 안 좋아지고 트래픽도 나오게 된다.
이를 해결하는 방법은 코드 비동기 로딩!
일반 자바스크립트 함수 스플리팅 하기)
export default function notify() {
alert("안녕하세요!");
}
App.js 수정)
import React from "react";
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>
);
}
export default App;
→ 빌드 시 notify 코드가 main 파일에 들어감
App.js 수정)
import React from "react";
import logo from "./logo.svg";
import "./App.css";
function App() {
const onClick = () => {
import("./notify").then((result) => result.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;
→ Hello React!를 클릭하는 시점에서 notify 불러옴
→ 빌드시 새로 시작하는 숫자 파일 안에 notify 관련 코드가 들어가게 됨(스플리팅!)
React.lazy → 컴포넌트를 렌더링하는 시점에서 비동기적으로 로딩할 수 있게 해주는 유틸 함수
Suspense → 리액트 내장 컴포넌트로서, 코드 스플리팅 된 컴포넌트를 로딩하도록 발동시킬 수 있다. 로딩이 끝나지 않았을 때 보여 줄 UI를 설정할 수 있다.
React.lazy를 사용하지 않고 코드 스플리팅 하기)
import React from "react";
const SplitMe = () => {
return <div>SplitMe</div>;
};
export default SplitMe;
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;
React.lazy와 Suspense를 사용하면 코드 스플리팅을 위한 state를 따로 선언하지 않아도 된다.
React.lazy 컴포넌트 사용법)
const SplitMe = React.lazy(() => import('./SplitMe'));
Suspense 사용법)
import React, { Suspense } from 'react';
(...)
<Suspense fallback={<div>loading...</div>}>
<SplitMe />
</Suspense>
적용하기)
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);
const onClick = () => {
setVisible(true);
};
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p onClick={onClick}>Hello React!</p>
<Suspense fallback={<div>loading…</div>}>
{visible && <SplitMe />}
</Suspense>
</header>
</div>
);
}
export default App;
Loadable Components → 코드 스플리팅을 편하게 하도록 도와주는 서드파티 라이브러리
※ React.lazy와 Suspense는 서버 사이드 렌더링을 지원하지 않지만, Loadable Components 은 서버 사이드 렌더링을 지원한다.
라이브러리 설치)
$ yarn add @loadable/component
적용하기)
import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';
import loadable from '@loadable/component';
const SplitMe = loadable(() => import('./SplitMe'));
function App() {
const [visible, setVisible] = useState(false);
const onClick = () => {
setVisible(true);
};
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p onClick={onClick}>Hello React!</p>
{visible && <SplitMe />}
</header>
</div>
);
}
export default App;
만약, 로딩 중 다른 UI를 보여 주고 싶다면, loadable를 사용하는 부분을 아래와 같이 수정한다.
const SplitMe = loadable(() => import('./SplitMe'), {
fallback: <div>loading…</div>
});
preload 하기)
preload → 컴포넌트를 미리 불러오기
import React, { useState } from "react";
import logo from "./logo.svg";
import "./App.css";
import loadable from "@loadable/component";
const SplitMe = loadable(() => import("./SplitMe"));
function App() {
const [visible, setVisible] = useState(false);
const onClick = () => {
setVisible(true);
};
const onMouseOver = () => {
SplitMe.preload();
};
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p onClick={onClick} onMouseOver={onMouseOver}>
Hello React!
</p>
{visible && <SplitMe />}
</header>
</div>
);
}
export default App;
더 다양한 loadable-components 알아보기)
Q1) 컴포넌트를 렌더링하는 시점에서 비동기적으로 로딩할 수 있게 해 주는 유틸 함수는?
Q2) 리액트 내장 컴포넌트로써 코드 스플리팅 된 컴포넌트를 로딩하도록 발동시킬 수 있는 함수는?
[리액트를 다루는 기술] 20장 서버 사이드 렌더링 (0) | 2022.01.31 |
---|---|
18장 리덕스 미들웨어를 통한 비동기 작업 관리 (0) | 2022.01.24 |
[리액트를 다루는 기술] 16장 리덕스 라이브러리 이해하기 (0) | 2022.01.17 |
[리액트를 다루는 기술] 17장 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 (0) | 2022.01.17 |
[리액트를 다루는 기술] 15장 Context API (0) | 2022.01.10 |