Translate 11 to Korean
- Up to 83e4409
Jinwoo Oh committed
Mar 3, 2015 at 00:17 UTC
2bc8a36ab991c48331737bc4dd8ffebfbbf248a5
1 file changed
+205
docs/docs/11-advanced-performance.ko-KR.md
new
+205
@@ -0,0 +1,205 @@
1
+---
2
+id: advanced-performance-ko-KR
3
+title: 성능 심화
4
+permalink: advanced-performance-ko-KR.html
5
+prev: perf-ko-KR.html
6
+---
7
+
8
+React를 도입하려 할 때 많은 사람이 묻는 첫 번째 질문은 React를 사용하지 않을 때처럼 애플리케이션이 빠르고 반응성도 좋을 것이냐는 것입니다. 모든 상태변화에 대해 컴포넌트의 하위 트리를 전부 다시 렌더링하는 아이디어에 대해 사람들은 이 프로세스가 성능에 부정적인 영향을 줄 것으로 생각하지만, React는 여러 가지 영리한 방법을 통해 UI를 업데이트하는데 필요한 비싼 DOM 조작을 최소화합니다.
9
+
10
+## DOM 조정 회피
11
+
12
+React는 브라우저에서 렌더된 DOM 하위 트리의 서술자 개념인 *가상의 DOM*을 사용합니다. 이 병렬적인 서술체는 React가 DOM 노드를 생성하거나 이미 존재하는 DOM 노드에 접근하는 것(JavaScript 객체를 조작하는 것보다 느리죠)을 피하게 해 줍니다. 컴포넌트의 props나 state가 변경되면 React는 새로운 가상의 DOM을 구성해 이전의 것과 비교해서 실제 DOM 업데이트가 필요한지 결정합니다. 가능한 적게 변화를 적용하기 위해, React는 둘이 다를 경우에만 DOM을 [조정](/react/docs/reconciliation-ko-KR.html)할 것입니다.
13
+
14
+이에 더해, React는 컴포넌트 생명주기 함수인 `shouldComponentUpdate`를 제공합니다. 이는 다시 렌더링하는 프로세스가 일어나기 직전에 일어나며 개발자가 프로세스를 중단할 수 있게 합니다. 이 함수의 기본구현은 `true`를 반환해 React가 업데이트를 수행하도록 합니다.
15
+
16
+```javascript
17
+shouldComponentUpdate: function(nextProps, nextState) {
18
+ return true;
19
+}
20
+```
21
+
22
+React가 이 함수를 자주 호출한다는 것을 명심하십시오. 따라서 구현체는 빨라야 합니다.
23
+
24
+대화 스레드가 여럿 돌고 있는 메시지처리 애플리케이션을 생각해 봅시다. 오직 하나의 스레드만이 변경되었다고 가정해 보죠. `ChatThread`에 `shouldComponentUpdate`를 구현했다면 React는 다른 스레드의 렌더링 프로세스를 건너뛸 수 있습니다.
25
+
26
+```javascript
27
+shouldComponentUpdate: function(nextProps, nextState) {
28
+ // TODO: 현재의 대화 스레드가 이전의 것과 다른지 아닌지를 반환한다
29
+}
30
+```
31
+
32
+정리하자면, React는 사용자가 `shouldComponentUpdate`를 사용해 렌더링 프로세스를 중단하고 가상의 DOM과 비교해 업데이트 여부를 결정해서 DOM의 하위 트리를 조정하는 비싼 DOM 조작을 피하도록 합니다.
33
+
34
+## shouldComponentUpdate 실전
35
+
36
+다음은 컴포넌트의 하위 트리입니다. 각각은 `shouldComponentUpdate`의 반환값(SCU)과 가상의 DOM과의 동일성(vDOMEq)을 표시합니다. 마지막으로, 원의 색은 컴포넌트가 조정되었는지를 표시합니다.
37
+
38
+<figure><img src="/react/img/docs/should-component-update.png" /></figure>
39
+
40
+위의 예시에서, C2를 루트로 하는 하위 트리에 대해 `shouldComponentUpdate`가 `false`를 반환했기 때문에 React는 새로운 가상의 DOM을 만들 필요가 없습니다. 따라서 DOM을 조정할 필요도 없습니다. React가 C4와 C5에는 `shouldComponentUpdate`를 요청하지도 않은 것을 확인하세요.
41
+
42
+C1과 C3의 `shouldComponentUpdate`가 `true`를 반환했기 때문에 React는 하위 노드로 내려가 그들을 확인합니다. C6는 `true`를 반환했네요; 이는 가상의 DOM과 같지 않기 때문에 DOM의 조정이 일어났습니다. 마지막으로 흥미로운 사례는 C8입니다. React가 이 노드를 위해 가상의 DOM을 작동했지만, 노드가 이전의 것과 일치했기 때문에 DOM의 조정을 일어나지 않았습니다.
43
+
44
+React가 C6에만 DOM 변경을 수행한 것을 확인하세요. 이는 필연적이었습니다. C8의 경우는 가상의 DOM과 비교를 해 제외되었고, C2의 하위 트리와 C7은 `shouldComponentUpdate` 단계에서 제외되어 가상의 DOM은 구동조차 되지 않았습니다.
45
+
46
+자 그럼, 어떻게 `shouldComponentUpdate`를 구현해야 할까요? 문자열 값을 렌더하는 컴포넌트를 생각해보죠.
47
+
48
+```javascript
49
+React.createClass({
50
+ propsTypes: {
51
+ value: React.PropTypes.string.isRequired
52
+ },
53
+
54
+ render: function() {
55
+ return <div>this.props.value</div>;
56
+ }
57
+});
58
+```
59
+
60
+다음과 같이 간단히 `shouldComponentUpdate`를 구현해 볼 수 있습니다:
61
+
62
+```javascript
63
+shouldComponentUpdate: function(nextProps, nextState) {
64
+ return this.props.value !== nextProps.value;
65
+}
66
+```
67
+
68
+여기까지는 좋습니다. 간단한 props/state 구조를 다루기는 쉽습니다. 단순한 등식비교 구현을 일반화하고 이를 컴포넌트에 혼합할 수도 있습니다. 사실, React는 이미 그런 구현을 제공합니다: [PureRenderMixin](/react/docs/pure-render-mixin-ko-KR.html).
69
+
70
+하지만 만약 컴포넌트의 props나 state가 가변적인 데이터 구조로 되어 있다면 어떨까요? 컴포넌트의 prop으로 `'bar'`같은 문자열 대신에 `{ foo: 'bar' }`처럼 문자열을 포함한 JavaScript 객체를 전달받는다고 해봅시다.
71
+
72
+```javascript
73
+React.createClass({
74
+ propsTypes: {
75
+ value: React.PropTypes.object.isRequired
76
+ },
77
+
78
+ render: function() {
79
+ return <div>this.props.value.foo</div>;
80
+ }
81
+});
82
+```
83
+
84
+전에 구현했던 `shouldComponentUpdate`는 언제나 예상대로 작동하지 않을 것입니다:
85
+
86
+```javascript
87
+// this.props.value가 { foo: 'bar' }라고 가정합니다
88
+// nextProps.value도 { foo: 'bar' }라고 가정하지만,
89
+// 이 참조는 this.props.value와 다른 것입니다
90
+this.props.value !== nextProps.value; // true
91
+```
92
+
93
+문제는 prop이 실제로 변경되지 않았을 때도 `shouldComponentUpdate`가 `true`를 반환할 거라는 겁니다. 이를 해결하기 위한 대안으로 아래와 같이 구현해 볼 수 있습니다:
94
+
95
+```javascript
96
+shouldComponentUpdate: function(nextProps, nextState) {
97
+ return this.props.value.foo !== nextProps.value.foo;
98
+}
99
+```
100
+
101
+기본적으로, 우리는 변경을 정확히 추적하기 위해서 깊은 비교를 해야 했습니다. 이 방법은 성능 면에서 제법 비싸고 각각의 모델마다 다른 깊은 등식 코드를 작성해야 하므로 확장이 힘들어 집니다. 심지어 객체 참조를 신중히 관리하지 않는다면 작동하지도 않을 수 있습니다. 컴포넌트가 부모에 의해 다뤄지는 경우를 살펴보죠:
102
+
103
+```javascript
104
+React.createClass({
105
+ getInitialState: function() {
106
+ return { value: { foo: 'bar' } };
107
+ },
108
+
109
+ onClick: function() {
110
+ var value = this.state.value;
111
+ value.foo += 'bar'; // 안티패턴 입니다!
112
+ this.setState({ value: value });
113
+ },
114
+
115
+ render: function() {
116
+ return (
117
+ <div>
118
+ <InnerComponent value={this.state.value} />
119
+ <a onClick={this.onClick}>클릭하세요</a>
120
+ </div>
121
+ );
122
+ }
123
+});
124
+```
125
+
126
+처음엔 내부 컴포넌트(`<InnerComponent />`)가 `{ foo: 'bar' }`를 value prop으로 가진 채 렌더될 것입니다. 사용자가 앵커(`<a>`)를 클릭한다면 부모 컴포넌트의 state는 `{ value: { foo: 'barbar' } }`로 업데이트되고, 내부 컴포넌트 또한 `{ foo: 'barbar' }`를 새로운 value prop으로 전달받아 다시 렌더링 되는 프로세스가 일어날 것입니다.
127
+
128
+이 문제는 부모와 내부 컴포넌트가 같은 객체에 대한 참조를 공유하기 때문에 발생합니다. `onClick` 함수의 두 번째 줄에서 객체에 대한 변경이 일어날 때, 내부 컴포넌트의 prop도 변경될 것입니다. 따라서 다시 렌더링 되는 프로세스가 시작될 때 `shouldComponentUpdate`가 호출되고 `this.props.value.foo`가 `nextProps.value.foo`와 같게 됩니다. 실제로 `this.props.value`는 `nextProps.value`와 같은 객체이기 때문입니다.
129
+
130
+그에따라 prop의 변경을 놓치게 되어 다시 렌더링하는 프로세스가 중단되고, UI는 `'bar'`에서 `'barbar'`로 업데이트되지 않습니다.
131
+
132
+## 구원자 Immutable-js
133
+
134
+[Immutable-js](https://github.com/facebook/immutable-js)는 Lee Byron이 만들고 Facebook이 오픈소스화 한 Javascript 컬렉션 라이브러리입니다. 이는 *구조의 공유(structural sharing)*를 통해 *불변의 영속적인(immutable persistent)* 컬렉션을 제공합니다. 이러한 속성이 무엇을 의미하는지 살펴보죠:
135
+
136
+* *불변성(Immutable)*: 컬렉션이 한번 생성되면, 이 후 다른 시점에 변경될 수 없습니다.
137
+* *영속성(Persistent)*: 새로운 컬렉션이 이전의 컬렉션이나 셋(set) 같은 뮤테이션(mutation)에서 생성될 수 있습니다. 기존의 컬렉션은 새로운 컬렉션이 생성된 후에도 여전히 유효합니다.
138
+* *구조의 공유(Structural Sharing)*: 새로운 컬렉션은 가능한 한 원래의 컬렉션과 같은 구조를 사용해 생성됩니다. 공간 효율성과 적절한 성능을 위해 복사를 최소화합니다.
139
+
140
+불변성은 변경의 추적을 비용을 줄여줍니다; 변경은 항상 새로운 객체에만 발생하기 때문에 객체에 대한 참조가 변경될 때만 확인하면 됩니다. 예를 들어 일반적인 JavaScript 코드에서는:
141
+
142
+```javascript
143
+var x = { foo: "bar" };
144
+var y = x;
145
+y.foo = "baz";
146
+x === y; // true
147
+```
148
+
149
+`y`가 수정되더라도 여전히 같은 객체인 `x`를 참조하고 있기 때문에, 이 비교는 `true`를 반환합니다. 하지만 이 코드를 immutable-js를 사용해 다음과 같이 작성할 수 있습니다:
150
+
151
+```javascript
152
+var SomeRecord = Immutable.Record({ foo: null });
153
+var x = new SomeRecord({ foo: 'bar' });
154
+var y = x.set('foo', 'baz');
155
+x === y; // false
156
+```
157
+
158
+이 경우, `x`가 변경되면 새로운 참조가 반환되기 때문에, 우리는 안전하게 `x`가 변경되었을 것으로 추정할 수 있습니다.
159
+
160
+변경을 탐지할 수 있는 또 다른 방법은 세터(setter)에 의해 설정된 플래그를 더티 체킹(dirty checking)하는 것입니다. 이 방식의 문제는 당신이 세터를 사용할 뿐만 아니라 수많은 추가 코드를 작성하거나 어떻게든 클래스들을 인스트루먼트(instrument) 하도록 강요한다는 것입니다. 혹은 변경(mutations) 직전에 객체를 깊은 복사(deep copy) 한 뒤 깊은 비교(deep compare)를 수행해 변경 여부를 판단할 수 있습니다. 이 방식의 문제점은 deepCopy와 deepCompare 둘 다 비용이 많이 드는 연산이라는 것입니다.
161
+
162
+그래서 Immutable 자료구조는 `shouldComponentUpdate`의 구현에 필요한 객체의 변경사항을 추적할 수 있는 덜 자세하지만 저렴한 방법을 제공합니다. 그에 따라 immutable-js가 제공하는 추상화를 사용해 props와 state 어트리뷰트를 모델링한다면, `PureRenderMixin`을 사용해 성능을 향상할 수 있습니다.
163
+
164
+## Immutable-js와 Flux
165
+
166
+[Flux](http://facebook.github.io/flux/)를 사용한다면 immutable-js를 사용해 stores를 작성해야 합니다. [전체 API](http://facebook.github.io/immutable-js/docs/#/)를 살펴보세요.
167
+
168
+Immutable 자료구조를 이용해 스레드를 모델링하는 예제를 살펴봅시다. 먼저 모델링하려는 엔티티마다 `Record`를 정의해야 합니다. Record는 특정 필드들의 값을 유지하기 위한 불변의 컨테이너입니다:
169
+
170
+```javascript
171
+var User = Immutable.Record({
172
+ id: undefined,
173
+ name: undefined,
174
+ email: undefined
175
+});
176
+
177
+var Message = Immutable.Record({
178
+ timestamp: new Date(),
179
+ sender: undefined,
180
+ text: ''
181
+});
182
+```
183
+
184
+`Record` 함수는 필드별로 기본값이 선언된 객체에 대한 정의를 넘겨받습니다.
185
+
186
+메시지 store는 두 개의 List를 통해 users와 messages를 추적할 수 있습니다:
187
+
188
+```javascript
189
+this.users = Immutable.List();
190
+this.messages = Immutable.List();
191
+```
192
+
193
+각각의 *페이로드* 타입을 처리하는 기능을 구현하는 것은 꽤 간단합니다. 예를 들면, store가 새 메시지를 나타내는 페이로드를 확인할 때 레코드를 새로 생성하고 메시지 리스트에 추가할 수 있습니다.
194
+
195
+```javascript
196
+this.messages = this.messages.push(new Message({
197
+ timestamp: payload.timestamp,
198
+ sender: payload.sender,
199
+ text: payload.text
200
+});
201
+```
202
+
203
+자료구조가 불변이기 때문에 push 함수의 결과를 this.messages에 할당할 필요가 있으니 주의하세요.
204
+
205
+React 측에서는, 컴포넌트의 state를 보존하기 위해 immutable-js 자료구조를 사용한다면, 모든 컴포넌트에 `PureRenderMixin`을 혼합해 다시 렌더링하는 프로세스를 중단할 수 있습니다.