Skip to content

Commit 55e8324

Browse files
authored
docs: optimizing performance 추가
1 parent 76da579 commit 55e8324

File tree

1 file changed

+242
-0
lines changed

1 file changed

+242
-0
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
# REACT OPTIMIZING PERFORMANCE
2+
3+
### Why?
4+
5+
빠른 사용자 인터페이스를 위해서 개발자가 고려해야 하는 작업이 많은데, react는 UI를 업데이트하기 위해 비용이 많이 드는 DOM 작업의 수를 최소화하는 기술이 적용되어있으므로 많은 작업을 하지 않아도 된다.
6+
7+
그럼에도 불구하고 이미 적용된 기법을 제외하고 react 애플리케이션의 속도를 높일 수 있는 몇가지 방법을 소개한다.
8+
9+
10+
11+
## Production 빌드 활용
12+
13+
기본적으로 React에는 경고들이 많이 포함되어있다. 개발할 때는 유용하지만, React를 크고 느리게 만들기 때문에 앱을 배포할 때는 프로덕트 버전을 사용해야 한다.
14+
15+
[React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi)를 사용하면 프로덕트 버전인지 쉽게 확인할 수 있다.
16+
17+
다음은 여러가지 프로덕션 용도의 앱을 만드는 방법이다. React의 유용한 경고들을 숨기고, 빌드를 느리게 만들기 때문에 개발 중에 설정하지 않도록 주의하도록 한다.
18+
19+
### Create React App
20+
21+
```shell
22+
npm run build
23+
```
24+
25+
`build/`폴더에 프로덕션 빌드 파일이 만들어진다.
26+
27+
### 단일 파일 빌드
28+
29+
```react
30+
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
31+
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
32+
```
33+
34+
`.production.min.js`로 끝나는 React 파일만이 프로덕션 환경에 적합하다.
35+
36+
### Brunch
37+
38+
`tester-brunch`를 설치한다.
39+
40+
```shell
41+
# npm을 사용한다면
42+
npm install --save-dev terser-brunch
43+
44+
# Yarn을 사용한다면
45+
yarn add --dev terser-brunch
46+
```
47+
48+
```shell
49+
brunch build -p
50+
```
51+
52+
### Browserify
53+
54+
`envify`, `terser`, `uglifyify`를 순서대로 설치한다.
55+
56+
```shell
57+
# npm을 사용하는 경우
58+
npm install --save-dev envify terser uglifyify
59+
60+
# Yarn을 사용하는 경우
61+
yarn add --dev envify terser uglifyify
62+
```
63+
64+
- `envify`변환은 올바른 빌드 환경이 설정되도록 한하고 전역 (`-g`)으로 변환시킨다.
65+
- `uglifyify`변환은 개발에서만 사용하는 package를 제거하고 전역(`-g`)으로 변환시킨다.
66+
- 최종 bundle은 mangling을 위해 `terser`로 연결된다. [참고](https://github.com/hughsk/uglifyify#motivationusage)
67+
68+
###### (mangling? 컴파일러가 식별할 수 있도록 함수의 이름을 변경하는 것)
69+
70+
### Rollup
71+
72+
`replace`, `commonjs`, `terser`을 순서대로 설치한다.
73+
74+
```shell
75+
# npm을 사용하는 경우
76+
npm install --save-dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-terser
77+
78+
# Yarn을 사용하는 경우
79+
yarn add --dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-terser
80+
```
81+
82+
- `replace`는 올바른 빌드 환경이 설정되도록 해준다.
83+
- `commonjs`는 CommonJS를 지원하도록 해준다.
84+
- `terser`는 최종 bundle을 압축하고 mangle 해준다.
85+
86+
### webpack
87+
88+
```react
89+
const TerserPlugin = require('terser-webpack-plugin');
90+
91+
module.exports = {
92+
mode: 'production',
93+
optimization: {
94+
minimizer: [new TerserPlugin({ /* additional options here */ })],
95+
},
96+
};
97+
```
98+
99+
100+
101+
## DevTools Profiler
102+
103+
> 컴포넌트 프로파일링 도구를 사용해서 React 성능을 측정할 수 있다.
104+
>
105+
> [참고](https://medium.com/wantedjobs/react-profiler%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EC%84%B1%EB%8A%A5-%EC%B8%A1%EC%A0%95%ED%95%98%EA%B8%B0-5981dfb3d934): 컴포넌트 분리와 `React.memo`를 이용한 성능 최적화
106+
107+
`react-dom` 16.5+와 `react-native` 0.57+는 React DevTools Profiler를 사용하여 개발 모드에서 향상된 프로파일링이 가능하다.
108+
109+
110+
111+
## Long Lists 가상화
112+
113+
긴 List를 렌더링하는 경우 **windowing**이라는 기법을 사용하는 것이 좋다.
114+
115+
### windowing
116+
117+
> [참고](https://youtu.be/QhPn6hLGljU?t=52)
118+
119+
주어진 시간에 목록의 일부분만 렌더링해서 컴포넌트를 다시 렌더링하는데 걸리는 시간과 생성된 DOM 노드의 수를 크게 줄일 수 있다.
120+
121+
React에서 주로 사용하는 windowing 라이브러리로는 `react-window``react-virtualized`가 있다. 목록, 그리드, 표 형식의 데이터를 표시하기 위해서 재사용 가능한 컴포넌트들을 제공한다.
122+
123+
124+
125+
## Reconciliation 피하기
126+
127+
컴포넌트의 prop이나 state가 변경되면 React는 새로 반환된 엘리먼트를 이전에 렌더링된 엘리먼트와 비교해서 같지 않을 때 실제 DOM을 업데이트한다.
128+
129+
리렌더링이 너무 오래걸릴 때 몇몇 상황에서 컴포넌트를 업데이트 할 필요가 없다는 것을 알고있다면 렌더링 전에 실행되는 생명주기 함수 `shouldComponentUpdate`를 무시함으로써 해결할 수 있다.
130+
131+
**[예시] shouldComponentUpdate**
132+
133+
```react
134+
import React from 'react'
135+
136+
class CustomComponent extends React.Component {
137+
138+
state = {}
139+
140+
constructor() {
141+
super()
142+
this.state = {
143+
id: 1,
144+
name: 'Kim Chaeeun',
145+
birthday: {
146+
year: 1995,
147+
month: 3,
148+
day: 7,
149+
}
150+
}
151+
}
152+
153+
shouldComponentUpdate(nextProps, nextState) {
154+
// 나는 생일 object 가 달라졌을때만 re-render를 할꺼야
155+
if (this.nextState.birthday == this.state.birthday) {
156+
return false
157+
} else {
158+
return true
159+
}
160+
}
161+
162+
render() {
163+
return (
164+
<div>
165+
<div>아이디: {this.state.id}</div>
166+
<div>이름: {this.state.name}</div>
167+
<div>
168+
생년월일: {this.state.birthday.year}
169+
-{this.state.birthday.month}
170+
-{this.state.birthday.day}
171+
</div>
172+
</div>
173+
)
174+
}
175+
}
176+
```
177+
178+
179+
180+
## shouldComponentUpdate 동작
181+
182+
> 가지치기하는 원리이다.
183+
184+
`SCU``shouldComponentUpdate`가 반환한 것을 나타내고, `vDOMEq`는 React 엘리먼트가 동일한지 여부를 표시하고, 원의 색은 컴포넌트를 조정해야 하는지 여부를 나타낸다.
185+
186+
<img src="https://ko.reactjs.org/static/5ee1bdf4779af06072a17b7a0654f6db/cd039/should-component-update.png" width="600">
187+
188+
189+
190+
191+
192+
## React.PureComponent
193+
194+
`PureComponent``this.props.words`의 이전 값과 새로운 값을 간단하게 비교한다. 얕게 탐색함으로써 성능을 최적화할 수 있지만 정상적으로 동작하지 않을 수 있다.
195+
196+
**[예시]**
197+
198+
```react
199+
class ListOfWords extends React.PureComponent {
200+
render() {
201+
return <div>{this.props.words.join(',')}</div>;
202+
}
203+
}
204+
205+
class WordAdder extends React.Component {
206+
constructor(props) {
207+
super(props);
208+
this.state = {
209+
words: ['marklar']
210+
};
211+
this.handleClick = this.handleClick.bind(this);
212+
}
213+
214+
handleClick() {
215+
// This section is bad style and causes a bug
216+
const words = this.state.words;
217+
words.push('marklar');
218+
this.setState({words: words});
219+
}
220+
221+
render() {
222+
return (
223+
<div>
224+
<button onClick={this.handleClick} />
225+
<ListOfWords words={this.state.words} />
226+
</div>
227+
);
228+
}
229+
}
230+
```
231+
232+
이 코드는 `WordAdder``handleClick`메서드에서 `words`배열을 변경시키기 때문에 배열의 실제 단어가 변경되었다 하더라도 `this.props.words`의 이전 값과 새로운 값은 동일하게 비교된다.
233+
234+
따라서 `ListOfWords`는 렌더링 되어야 하는 새로운 단어가 있어도 업데이트되지 않는다.
235+
236+
237+
238+
## 데이터를 Mutating하지 않음으로써 얻는 효과
239+
240+
위와 같이 데이터의 값은 바뀌었는데 참조값이 바뀌지 않아서 리렌더링이 안되는 문제를 해결하는 가장 간단한 방법은 참조값을 바꾸는 것이다.
241+
242+
예를 들어 `handleClick`메서드는 `concat`, 객체는 `Object.assign`를 사용해서 새로운 객체를 반환하는 것을 말한다.

0 commit comments

Comments
 (0)