리액트 프로젝트를 완성 후 사용자에게 제공할 대는 빌드 작업을 거쳐서 배포를 해야한다.
빌드 작업을 통해 프로젝트에서 사용되는 자바 스크립트 파일안에 필요하지 않은 주석이나 경고 공백 등은 제거
파일 크기 최소화, 브라우저에서 JSX 문법이나 다른 JS 문법이 원할하게 실행되도록 코드 트랜스 파일 작업도 할 수 있다.
프로젝트 내의 이미지 같은 정적 파일의 경로도 설정
이 작업은 웹팩이라는 도구가 담당하는데 웹팩에서는 별도의 설정 없이도 프로젝트에서 사용되는 모든 JS 파일을 하나의 파일로 합치고 모든 CSS 파일도 하나의 파일로 합친다
비 동기 로딩
파일을 여러 개로 분리하는 작업을 코드 스플리팅이라고 한다.
SplitChunks 기능을 통한 코드 스플리팅은 효율적으로 캐싱하는 효과가 있다
예를 들어 abc라는 페이지로 구성된 싱글 페이지 spa가 있을 때
사용자가 a 페이지 방문시 b c 페이지의 컴포넌트 정보가 필요 하지 않아도 리액트 프로젝트에 별도로 설정하지 않으면
ABC 컴포넌트에 대한 코드가 모두 한 파일에 저장됩니다.
이러한 문제를 해결하는 것이 코드 비동기 로딩입니다.코드 비공기 로딩을 통하면 JS함수 객체 컴포넌트를 처음에는 불러오지 않고 필요할 때만 불러오루 수 있다.
<기존 방식>import를 상단에 기록해서 컴포넌트를 가져오는 기존의 방식은 notyfy 코드는 build 안의 main 파일로 들어갑니다.
import notify from './notify';
function App() {
const onClick = () => {
notify();
}
...
<수정 방식 >
import를 함수 안의 메서드 형식으로 넣게 되면 파일을 따로 분리시켜서 저장하고, 실제 함수가 필요한 지점에 파일을 불러와서 함수를 사용 가능
function App() {
const onClick = () => {
import('./notify').then(result => result.default());
}
...
import를 함수로 사용할 때 Promise를 반환하는데, 이렇게 import를 함수로 사용하는 문법은 표준 js는 아니지만
dynamic import 문법이라고 하고, 웹팩에서 지원하고 있습니다.
중요) 이 함수를 통해 모듈을 불러온다면 모듈(JS 파일)에서 default로 내보낸 것을 result.default()를 참조해서 사용한다.
개발자 모드에서 Network 탭을 열고 dynamic import 문법으로 파일을 가져오면
그 가져온 파일의 코드 정보가 보여진다.
React.lazy 와 Suspense를 통해 컴포넌트 코드 스플리팅
코드 스플리팅을 위해 리액트에 내장된 기능인 유틸 함수 React.lazy와 컴포넌트인 Suspense가 있다.
리액트 16.6 버전부터이니 이전 버전은 import함수를 불려와ㅓㅅ 컴포넌트를 state에 넣는 방식으로 구현
React.lazy 없이 구현
handleClick = async () => {
const loadedModule = await import('./SplitMe')
this.setState({
SplitMe : loadedModule.default
});
};
핸들러 비동기 이벤트가 발생했을 때 Splitme라는 모듈(JS파일)을 포함시키고 그 결과를 state에 저장해서 출력
이렇게 되면 처음 파일을 가져올 때 전부 다 가져오는것이 아니라 이벤트 발생 시에 Splitme라는 파일이 가져와진다.
이렇게 state으로 코드 스플리팅하는 것도 나쁘진 않지만 매번 state를 따로 선언해야 해서 약간 불편
// 클래스 형 컴포넌트
import logo from './logo.svg';
import './App.css';
import React, { Component } from 'react';
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는 리액트 내장 컴포넌트로 코드 스플리팅 된 컴포넌트를 로딩하도록 발동 혹은 로딩이 끝나지 않았을 때의 UI를 설정 할 수도 있습니다.
import React, { Suspense } from 'react';
(...)
// 실패했을 때 화면
<Suspense fallback={<div>loading...</div>}>
<SplitMe />
</Suspense>
Suspense에서 fallback props를 통해 로딩 중에 보여 줄 JSX를 지정할 수 있습니다.
import React, { useState, Suspense } from 'react';
import logo from './logo.svg';
import './App.css';
const SplitMe = React.lazy(() => import('./SplitMe'));
const 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를 통한 코드 스플리팅
코드 스플리팅을 도와주는 서드파티 라이브러리
앞의 2개와는 다르게 서버 사이드 렌더링을 지원하고 렌더링 전 필요한 스플리팅 파일을 미리 불러올 수 있다.
// SplitMe 파일을 실행 할 때 가져옴 - 코드 비동기
const SplitMe = loadable(() => import('./SplitMe'));
// 필요한 위치에 출력
...
{visible && <SplitMe />}
...
const SplitMe = loadable(() => import('./SplitMe'),{
fallback: <div>loading</div>
});
Suspense 처럼 가져오는 동안 다른 화면을 보여주고 싶다면 똑같이 fallback 사용
// SplitMe 라는 컴포넌트 자체를 가져옴
const onMouseOver = () => {
SplitMe.preload();
};
// 마우스를 위에 올리기 시작하면 SplitMe 컴포넌트를 가져옴
<p onClick={onClick} onMouseOver={onMouseOver}>
Hello React!</p>
{visible && <SplitMe />}