Files
react/docs/advanced-performance-ko-KR.html
T
Paul O’Shannessy 63dff641cf Update website
2016-03-21 11:28:34 -07:00

625 lines
38 KiB
HTML

<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>성능 심화 | React</title>
<meta name="viewport" content="width=device-width">
<meta property="og:title" content="성능 심화 | React">
<meta property="og:type" content="website">
<meta property="og:url" content="https://facebook.github.io/react/docs/advanced-performance-ko-KR.html">
<meta property="og:image" content="https://facebook.github.io/react/img/logo_og.png">
<meta property="og:description" content="A JavaScript library for building user interfaces">
<meta property="fb:app_id" content="623268441017527">
<link rel="shortcut icon" href="/react/favicon.ico">
<link rel="alternate" type="application/rss+xml" title="React" href="https://facebook.github.io/react/feed.xml">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css" />
<link rel="stylesheet" href="/react/css/syntax.css">
<link rel="stylesheet" href="/react/css/codemirror.css">
<link rel="stylesheet" href="/react/css/react.css">
<script src="//use.typekit.net/vqa1hcx.js"></script>
<script>try{Typekit.load();}catch(e){}</script>
<!--[if lte IE 8]>
<script src="/react/js/html5shiv.min.js"></script>
<script src="/react/js/es5-shim.min.js"></script>
<script src="/react/js/es5-sham.min.js"></script>
<![endif]-->
<script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script>
<script src="/react/js/codemirror.js"></script>
<script src="/react/js/javascript.js"></script>
<script src="/react/js/react.js"></script>
<script src="/react/js/react-dom.js"></script>
<script src="/react/js/babel-browser.min.js"></script>
<script src="/react/js/live_editor.js"></script>
</head>
<body>
<div class="container">
<div class="nav-main">
<div class="wrap">
<a class="nav-home" href="/react/index.html">
<img class="nav-logo" src="/react/img/logo.svg" width="36" height="36">
React
</a>
<ul class="nav-site nav-site-internal">
<li><a href="/react/docs/getting-started.html" class="active">Docs</a></li>
<li><a href="/react/support.html">Support</a></li>
<li><a href="/react/downloads.html">Download</a></li>
<li><a href="/react/blog/">Blog</a></li>
<li>
<input id="algolia-doc-search" type="text" placeholder="Search docs..." />
</li>
</ul>
<ul class="nav-site nav-site-external">
<li><a href="https://github.com/facebook/react">GitHub</a></li>
<li><a href="https://facebook.github.io/react-native/">React Native</a></li>
</ul>
</div>
</div>
<section class="content wrap documentationContent">
<div class="nav-docs">
<!-- Docs Nav -->
<div class="nav-docs-section">
<h3>Quick Start</h3>
<ul>
<li>
<a href="/react/docs/getting-started.html">Getting Started</a>
</li>
<li>
<a href="/react/docs/tutorial.html">Tutorial</a>
</li>
<li>
<a href="/react/docs/thinking-in-react.html">Thinking in React</a>
</li>
</ul>
</div>
<div class="nav-docs-section">
<h3>Community Resources</h3>
<ul>
<li>
<a href="/react/docs/conferences.html">Conferences</a>
</li>
<li>
<a href="/react/docs/videos.html">Videos</a>
</li>
<li>
<a href="https://github.com/facebook/react/wiki/Complementary-Tools" class="external">Complementary Tools</a>
</li>
<li>
<a href="https://github.com/facebook/react/wiki/Examples" class="external">Examples</a>
</li>
</ul>
</div>
<div class="nav-docs-section">
<h3>Guides</h3>
<ul>
<li>
<a href="/react/docs/why-react.html">Why React?</a>
</li>
<li>
<a href="/react/docs/displaying-data.html">Displaying Data</a>
<ul>
<li>
<a href="/react/docs/jsx-in-depth.html">JSX in Depth</a>
</li>
<li>
<a href="/react/docs/jsx-spread.html">JSX Spread Attributes</a>
</li>
<li>
<a href="/react/docs/jsx-gotchas.html">JSX Gotchas</a>
</li>
</ul>
</li>
<li>
<a href="/react/docs/interactivity-and-dynamic-uis.html">Interactivity and Dynamic UIs</a>
</li>
<li>
<a href="/react/docs/multiple-components.html">Multiple Components</a>
</li>
<li>
<a href="/react/docs/reusable-components.html">Reusable Components</a>
</li>
<li>
<a href="/react/docs/transferring-props.html">Transferring Props</a>
</li>
<li>
<a href="/react/docs/forms.html">Forms</a>
</li>
<li>
<a href="/react/docs/working-with-the-browser.html">Working With the Browser</a>
<ul>
<li>
<a href="/react/docs/more-about-refs.html">Refs to Components</a>
</li>
</ul>
</li>
<li>
<a href="/react/docs/tooling-integration.html">Tooling Integration</a>
</li>
<li>
<a href="/react/docs/addons.html">Add-Ons</a>
<ul>
<li>
<a href="/react/docs/animation.html">Animation</a>
</li>
<li>
<a href="/react/docs/two-way-binding-helpers.html">Two-Way Binding Helpers</a>
</li>
<li>
<a href="/react/docs/test-utils.html">Test Utilities</a>
</li>
<li>
<a href="/react/docs/clone-with-props.html">Cloning Elements</a>
</li>
<li>
<a href="/react/docs/create-fragment.html">Keyed Fragments</a>
</li>
<li>
<a href="/react/docs/update.html">Immutability Helpers</a>
</li>
<li>
<a href="/react/docs/pure-render-mixin.html">PureRenderMixin</a>
</li>
<li>
<a href="/react/docs/perf.html">Performance Tools</a>
</li>
<li>
<a href="/react/docs/shallow-compare.html">Shallow Compare</a>
</li>
</ul>
</li>
<li>
<a href="/react/docs/advanced-performance.html">Advanced Performance</a>
</li>
<li>
<a href="/react/docs/context.html">Context</a>
</li>
</ul>
</div>
<div class="nav-docs-section">
<h3>Reference</h3>
<ul>
<li>
<a href="/react/docs/top-level-api.html">Top-Level API</a>
</li>
<li>
<a href="/react/docs/component-api.html">Component API</a>
</li>
<li>
<a href="/react/docs/component-specs.html">Component Specs and Lifecycle</a>
</li>
<li>
<a href="/react/docs/tags-and-attributes.html">Supported Tags and Attributes</a>
</li>
<li>
<a href="/react/docs/events.html">Event System</a>
</li>
<li>
<a href="/react/docs/dom-differences.html">DOM Differences</a>
</li>
<li>
<a href="/react/docs/special-non-dom-attributes.html">Special Non-DOM Attributes</a>
</li>
<li>
<a href="/react/docs/reconciliation.html">Reconciliation</a>
</li>
<li>
<a href="/react/docs/webcomponents.html">Web Components</a>
</li>
<li>
<a href="/react/docs/glossary.html">React (Virtual) DOM Terminology</a>
</li>
</ul>
</div>
<div class="nav-docs-section">
<h3>Flux</h3>
<ul>
<li>
<a href="https://facebook.github.io/flux/docs/overview.html" class="external">Flux Overview</a>
</li>
<li>
<a href="https://facebook.github.io/flux/docs/todo-list.html" class="external">Flux TodoMVC Tutorial</a>
</li>
</ul>
</div>
<!-- Tips Nav -->
<div class="nav-docs-section">
<h3>Tips</h3>
<ul>
<li>
<a href="/react/tips/introduction.html">Introduction</a>
</li>
<li>
<a href="/react/tips/inline-styles.html">Inline Styles</a>
</li>
<li>
<a href="/react/tips/if-else-in-JSX.html">If-Else in JSX</a>
</li>
<li>
<a href="/react/tips/self-closing-tag.html">Self-Closing Tag</a>
</li>
<li>
<a href="/react/tips/maximum-number-of-jsx-root-nodes.html">Maximum Number of JSX Root Nodes</a>
</li>
<li>
<a href="/react/tips/style-props-value-px.html">Shorthand for Specifying Pixel Values in style props</a>
</li>
<li>
<a href="/react/tips/children-props-type.html">Type of the Children props</a>
</li>
<li>
<a href="/react/tips/controlled-input-null-value.html">Value of null for Controlled Input</a>
</li>
<li>
<a href="/react/tips/componentWillReceiveProps-not-triggered-after-mounting.html">componentWillReceiveProps Not Triggered After Mounting</a>
</li>
<li>
<a href="/react/tips/props-in-getInitialState-as-anti-pattern.html">Props in getInitialState Is an Anti-Pattern</a>
</li>
<li>
<a href="/react/tips/dom-event-listeners.html">DOM Event Listeners in a Component</a>
</li>
<li>
<a href="/react/tips/initial-ajax.html">Load Initial Data via AJAX</a>
</li>
<li>
<a href="/react/tips/false-in-jsx.html">False in JSX</a>
</li>
<li>
<a href="/react/tips/communicate-between-components.html">Communicate Between Components</a>
</li>
<li>
<a href="/react/tips/expose-component-functions.html">Expose Component Functions</a>
</li>
<li>
<a href="/react/tips/children-undefined.html">this.props.children undefined</a>
</li>
<li>
<a href="/react/tips/use-react-with-other-libraries.html">Use React with Other Libraries</a>
</li>
<li>
<a href="/react/tips/dangerously-set-inner-html.html">Dangerously Set innerHTML</a>
</li>
</ul>
</div>
</div>
<div class="inner-content">
<h1>
성능 심화
<a class="edit-page-link" href="https://github.com/facebook/react/tree/master/docs/docs/11-advanced-performance.ko-KR.md" target="_blank">Edit on GitHub</a>
</h1>
<div class="subHeader"></div>
<p>React를 도입하려 할 때 많은 사람이 묻는 첫 번째 질문은 React를 사용하지 않을 때처럼 애플리케이션이 빠르고 반응성도 좋을 것이냐는 것입니다. 모든 상태변화에 대해 컴포넌트의 하위 트리를 전부 다시 렌더링하는 아이디어에 대해 사람들은 이 프로세스가 성능에 부정적인 영향을 줄 것으로 생각하지만, React는 여러 가지 영리한 방법을 통해 UI를 업데이트하는데 필요한 비싼 DOM 조작을 최소화합니다.</p>
<h2><a class="anchor" name="dom--"></a>DOM 조정 회피 <a class="hash-link" href="#dom--">#</a></h2>
<p>React는 브라우저에서 렌더된 DOM 하위 트리의 서술자 개념인 <em>가상의 DOM</em>을 사용합니다. 이 병렬적인 서술체는 React가 DOM 노드를 생성하거나 이미 존재하는 DOM 노드에 접근하는 것(JavaScript 객체를 조작하는 것보다 느리죠)을 피하게 해 줍니다. 컴포넌트의 props나 state가 변경되면 React는 새로운 가상의 DOM을 구성해 이전의 것과 비교해서 실제 DOM 업데이트가 필요한지 결정합니다. 가능한 적게 변화를 적용하기 위해, React는 둘이 다를 경우에만 DOM을 <a href="/react/docs/reconciliation-ko-KR.html">조정</a>할 것입니다.</p>
<p>이에 더해, React는 컴포넌트 생명주기 함수인 <code>shouldComponentUpdate</code>를 제공합니다. 이는 다시 렌더링하는 프로세스(가상 DOM 비교와 어쩌면 일어날 DOM 조정)가 일어나기 직전에 일어나며 개발자가 프로세스를 중단할 수 있게 합니다. 이 함수의 기본구현은 <code>true</code>를 반환해 React가 업데이트를 수행하도록 합니다.</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">shouldComponentUpdate</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">,</span> <span class="nx">nextState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>React가 이 함수를 자주 호출한다는 것을 명심하십시오. 따라서 구현체는 빨라야 합니다.</p>
<p>대화 스레드가 여럿 돌고 있는 메시지처리 애플리케이션을 생각해 봅시다. 오직 하나의 스레드만이 변경되었다고 가정해 보죠. <code>ChatThread</code><code>shouldComponentUpdate</code>를 구현했다면 React는 다른 스레드의 렌더링 프로세스를 건너뛸 수 있습니다.</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">shouldComponentUpdate</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">,</span> <span class="nx">nextState</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// TODO: 현재의 대화 스레드가 이전의 것과 다른지 아닌지를 반환한다</span>
<span class="p">}</span>
</code></pre></div>
<p>정리하자면, React는 사용자가 <code>shouldComponentUpdate</code>를 사용해 렌더링 프로세스를 중단하고 가상의 DOM과 비교해 업데이트 여부를 결정해서 DOM의 하위 트리를 조정하는 비싼 DOM 조작을 피하도록 합니다.</p>
<h2><a class="anchor" name="shouldcomponentupdate-"></a>shouldComponentUpdate 실전 <a class="hash-link" href="#shouldcomponentupdate-">#</a></h2>
<p>다음은 컴포넌트의 하위 트리입니다. 각각은 <code>shouldComponentUpdate</code>의 반환값(SCU)과 가상의 DOM과의 동일성(vDOMEq)을 표시합니다. 마지막으로, 원의 색은 컴포넌트가 조정되었는지를 표시합니다.</p>
<figure><img src="/react/img/docs/should-component-update.png" /></figure>
<p>위의 예시에서, C2를 루트로 하는 하위 트리에 대해 <code>shouldComponentUpdate</code><code>false</code>를 반환했기 때문에 React는 새로운 가상의 DOM을 만들 필요가 없습니다. 따라서 DOM을 조정할 필요도 없습니다. React가 C4와 C5에는 <code>shouldComponentUpdate</code>를 요청하지도 않은 것을 확인하세요.</p>
<p>C1과 C3의 <code>shouldComponentUpdate</code><code>true</code>를 반환했기 때문에 React는 하위 노드로 내려가 그들을 확인합니다. C6는 <code>true</code>를 반환했네요; 이는 가상의 DOM과 같지 않기 때문에 DOM의 조정이 일어났습니다. 마지막으로 흥미로운 사례는 C8입니다. React가 이 노드를 위해 가상의 DOM을 작동했지만, 노드가 이전의 것과 일치했기 때문에 DOM의 조정을 일어나지 않았습니다.</p>
<p>React가 C6에만 DOM 변경을 수행한 것을 확인하세요. 이는 필연적이었습니다. C8의 경우는, 가상의 DOM과 비교를 해 제외되었고, C2의 하위 트리와 C7은 <code>shouldComponentUpdate</code> 단계에서 제외되어 가상의 DOM은 구동조차 되지 않았습니다.</p>
<p>자 그럼, 어떻게 <code>shouldComponentUpdate</code>를 구현해야 할까요? 문자열 값을 렌더하는 컴포넌트를 생각해보죠.</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">React</span><span class="p">.</span><span class="nx">createClass</span><span class="p">({</span>
<span class="nx">propTypes</span><span class="o">:</span> <span class="p">{</span>
<span class="nx">value</span><span class="o">:</span> <span class="nx">React</span><span class="p">.</span><span class="nx">PropTypes</span><span class="p">.</span><span class="nx">string</span><span class="p">.</span><span class="nx">isRequired</span>
<span class="p">},</span>
<span class="nx">render</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">value</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/div&gt;;</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div>
<p>다음과 같이 간단히 <code>shouldComponentUpdate</code>를 구현해 볼 수 있습니다:</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">shouldComponentUpdate</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">,</span> <span class="nx">nextState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">value</span> <span class="o">!==</span> <span class="nx">nextProps</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>여기까지는 좋습니다. 간단한 props/state 구조를 다루기는 쉽습니다. 단순한 등식비교 구현을 일반화하고 이를 컴포넌트에 혼합할 수도 있습니다. 사실, React는 이미 그런 구현을 제공합니다: <a href="/react/docs/pure-render-mixin-ko-KR.html">PureRenderMixin</a>.</p>
<p>하지만 만약 컴포넌트의 props나 state가 가변적인 데이터 구조로 되어 있다면 어떨까요? 컴포넌트의 prop으로 <code>&#39;bar&#39;</code>같은 문자열 대신에 <code>{ foo: &#39;bar&#39; }</code>처럼 문자열을 포함한 JavaScript 객체를 전달받는다고 해봅시다.</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">React</span><span class="p">.</span><span class="nx">createClass</span><span class="p">({</span>
<span class="nx">propTypes</span><span class="o">:</span> <span class="p">{</span>
<span class="nx">value</span><span class="o">:</span> <span class="nx">React</span><span class="p">.</span><span class="nx">PropTypes</span><span class="p">.</span><span class="nx">object</span><span class="p">.</span><span class="nx">isRequired</span>
<span class="p">},</span>
<span class="nx">render</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">foo</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/div&gt;;</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div>
<p>전에 구현했던 <code>shouldComponentUpdate</code>는 언제나 예상대로 작동하지 않을 것입니다:</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// this.props.value가 { foo: &#39;bar&#39; }라고 가정합니다</span>
<span class="c1">// nextProps.value도 { foo: &#39;bar&#39; }라고 가정하지만,</span>
<span class="c1">// 이 참조는 this.props.value와 다른 것입니다</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">value</span> <span class="o">!==</span> <span class="nx">nextProps</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span> <span class="c1">// true</span>
</code></pre></div>
<p>문제는 prop이 실제로 변경되지 않았을 때도 <code>shouldComponentUpdate</code><code>true</code>를 반환할 거라는 겁니다. 이를 해결하기 위한 대안으로, 아래와 같이 구현해 볼 수 있습니다:</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">shouldComponentUpdate</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">,</span> <span class="nx">nextState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">foo</span> <span class="o">!==</span> <span class="nx">nextProps</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">foo</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>기본적으로, 우리는 변경을 정확히 추적하기 위해서 깊은 비교를 해야 했습니다. 이 방법은 성능 면에서 제법 비쌉니다. 각각의 모델마다 다른 깊은 등식 코드를 작성해야 하므로 확장이 힘들어 집니다. 심지어 객체 참조를 신중히 관리하지 않는다면 작동하지도 않을 수 있습니다. 컴포넌트가 부모에 의해 다뤄지는 경우를 살펴보죠:</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">React</span><span class="p">.</span><span class="nx">createClass</span><span class="p">({</span>
<span class="nx">getInitialState</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span> <span class="nx">value</span><span class="o">:</span> <span class="p">{</span> <span class="nx">foo</span><span class="o">:</span> <span class="s1">&#39;bar&#39;</span> <span class="p">}</span> <span class="p">};</span>
<span class="p">},</span>
<span class="nx">onClick</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">value</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">value</span><span class="p">.</span><span class="nx">foo</span> <span class="o">+=</span> <span class="s1">&#39;bar&#39;</span><span class="p">;</span> <span class="c1">// 안티패턴 입니다!</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="nx">value</span><span class="o">:</span> <span class="nx">value</span> <span class="p">});</span>
<span class="p">},</span>
<span class="nx">render</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span>
<span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">InnerComponent</span> <span class="nx">value</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">value</span><span class="p">}</span> <span class="o">/&gt;</span>
<span class="o">&lt;</span><span class="nx">a</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">onClick</span><span class="p">}</span><span class="o">&gt;</span><span class="err">클릭하세요</span><span class="o">&lt;</span><span class="err">/a&gt;</span>
<span class="o">&lt;</span><span class="err">/div&gt;</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div>
<p>처음엔 내부 컴포넌트(<code>&lt;InnerComponent /&gt;</code>)가 <code>{ foo: &#39;bar&#39; }</code>를 value prop으로 가진 채 렌더될 것입니다. 사용자가 앵커(<code>&lt;a&gt;</code>)를 클릭한다면 부모 컴포넌트의 state는 <code>{ value: { foo: &#39;barbar&#39; } }</code>로 업데이트되고, 내부 컴포넌트 또한 <code>{ foo: &#39;barbar&#39; }</code>를 새로운 value prop으로 전달받아 다시 렌더링 되는 프로세스가 일어날 것입니다.</p>
<p>이 문제는 부모와 내부 컴포넌트가 같은 객체에 대한 참조를 공유하기 때문에 발생합니다. <code>onClick</code> 함수의 두 번째 줄에서 객체에 대한 변경이 일어날 때, 내부 컴포넌트의 prop도 변경될 것입니다. 따라서 다시 렌더링 되는 프로세스가 시작될 때 <code>shouldComponentUpdate</code>가 호출되고 <code>this.props.value.foo</code><code>nextProps.value.foo</code>와 같게 됩니다. 실제로 <code>this.props.value</code><code>nextProps.value</code>와 같은 객체이기 때문입니다.</p>
<p>그에따라 prop의 변경을 놓치게 되어 다시 렌더링하는 프로세스가 중단되고, UI는 <code>&#39;bar&#39;</code>에서 <code>&#39;barbar&#39;</code>로 업데이트되지 않습니다.</p>
<h2><a class="anchor" name="-immutable-js"></a>구원자 Immutable-js <a class="hash-link" href="#-immutable-js">#</a></h2>
<p><a href="https://github.com/facebook/immutable-js">Immutable-js</a>는 Lee Byron이 만들고 Facebook이 오픈소스화 한 JavaScript 컬렉션 라이브러리입니다. 이는 <em>구조의 공유(structural sharing)</em>를 통해 <em>불변의 영속적인(immutable persistent)</em> 컬렉션을 제공합니다. 이러한 속성이 무엇을 의미하는지 살펴보죠:</p>
<ul>
<li><em>불변성(Immutable)</em>: 컬렉션이 한번 생성되면, 이 후 다른 시점에 변경될 수 없습니다.</li>
<li><em>영속성(Persistent)</em>: 새로운 컬렉션이 이전의 컬렉션이나 셋(set) 같은 뮤테이션(mutation)에서 생성될 수 있습니다. 기존의 컬렉션은 새로운 컬렉션이 생성된 후에도 여전히 유효합니다.</li>
<li><em>구조의 공유(Structural Sharing)</em>: 새로운 컬렉션은 가능한 한 원래의 컬렉션과 같은 구조를 사용해 생성됩니다. 공간 효율성과 적절한 성능을 위해 복사를 최소화합니다.</li>
</ul>
<p>불변성은 변경의 추적을 비용을 줄여줍니다; 변경은 항상 새로운 객체에만 발생하기 때문에 객체에 대한 참조가 변경될 때만 확인하면 됩니다. 예를 들어 일반적인 이 JavaScript 코드에서는:</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">x</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">foo</span><span class="o">:</span> <span class="s2">&quot;bar&quot;</span> <span class="p">};</span>
<span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="nx">x</span><span class="p">;</span>
<span class="nx">y</span><span class="p">.</span><span class="nx">foo</span> <span class="o">=</span> <span class="s2">&quot;baz&quot;</span><span class="p">;</span>
<span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span><span class="p">;</span> <span class="c1">// true</span>
</code></pre></div>
<p><code>y</code>가 수정되더라도 여전히 같은 객체인 <code>x</code>를 참조하고 있기 때문에, 이 비교는 <code>true</code>를 반환합니다. 하지만 이 코드를 immutable-js를 사용해 다음과 같이 작성할 수 있습니다:</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">SomeRecord</span> <span class="o">=</span> <span class="nx">Immutable</span><span class="p">.</span><span class="nx">Record</span><span class="p">({</span> <span class="nx">foo</span><span class="o">:</span> <span class="kc">null</span> <span class="p">});</span>
<span class="kd">var</span> <span class="nx">x</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">SomeRecord</span><span class="p">({</span> <span class="nx">foo</span><span class="o">:</span> <span class="s1">&#39;bar&#39;</span> <span class="p">});</span>
<span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="nx">x</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">,</span> <span class="s1">&#39;baz&#39;</span><span class="p">);</span>
<span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span><span class="p">;</span> <span class="c1">// false</span>
</code></pre></div>
<p>이 경우, <code>x</code>가 변경되면 새로운 참조가 반환되기 때문에, 우리는 안전하게 <code>x</code>가 변경되었을 것으로 추정할 수 있습니다.</p>
<p>변경을 탐지할 수 있는 또 다른 방법은 세터(setter)에 의해 설정된 플래그를 더티 체킹(dirty checking)하는 것입니다. 이 방식의 문제는 당신이 세터를 사용할 뿐만 아니라 수많은 추가 코드를 작성하거나 어떻게든 클래스들을 인스트루먼트(instrument) 하도록 강요한다는 것입니다. 혹은 변경(mutations) 직전에 객체를 깊은 복사(deep copy) 한 뒤 깊은 비교(deep compare)를 수행해 변경 여부를 판단할 수 있습니다. 이 방식의 문제점은 deepCopy와 deepCompare 둘 다 비용이 많이 드는 연산이라는 것입니다.</p>
<p>그래서 Immutable 자료구조는 <code>shouldComponentUpdate</code>의 구현에 필요한 객체의 변경사항을 추적할 수 있는 덜 자세하지만 저렴한 방법을 제공합니다. 그에 따라 immutable-js가 제공하는 추상화를 사용해 props와 state 어트리뷰트를 모델링한다면, <code>PureRenderMixin</code>을 사용해 성능을 향상할 수 있습니다.</p>
<h2><a class="anchor" name="immutable-js-flux"></a>Immutable-js와 Flux <a class="hash-link" href="#immutable-js-flux">#</a></h2>
<p><a href="https://facebook.github.io/flux/">Flux</a>를 사용한다면 immutable-js를 사용해 stores를 작성해야 합니다. <a href="https://facebook.github.io/immutable-js/docs/#/">전체 API</a>를 살펴보세요.</p>
<p>Immutable 자료구조를 이용해 스레드를 모델링하는 예제를 살펴봅시다. 먼저 모델링하려는 엔티티마다 <code>Record</code>를 정의해야 합니다. Record는 특정 필드들의 값을 유지하기 위한 불변의 컨테이너입니다:</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">User</span> <span class="o">=</span> <span class="nx">Immutable</span><span class="p">.</span><span class="nx">Record</span><span class="p">({</span>
<span class="nx">id</span><span class="o">:</span> <span class="kc">undefined</span><span class="p">,</span>
<span class="nx">name</span><span class="o">:</span> <span class="kc">undefined</span><span class="p">,</span>
<span class="nx">email</span><span class="o">:</span> <span class="kc">undefined</span>
<span class="p">});</span>
<span class="kd">var</span> <span class="nx">Message</span> <span class="o">=</span> <span class="nx">Immutable</span><span class="p">.</span><span class="nx">Record</span><span class="p">({</span>
<span class="nx">timestamp</span><span class="o">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(),</span>
<span class="nx">sender</span><span class="o">:</span> <span class="kc">undefined</span><span class="p">,</span>
<span class="nx">text</span><span class="o">:</span> <span class="s1">&#39;&#39;</span>
<span class="p">});</span>
</code></pre></div>
<p><code>Record</code> 함수는 필드별로 기본값이 선언된 객체에 대한 정의를 넘겨받습니다.</p>
<p>메시지 store는 두 개의 List를 통해 users와 messages를 추적할 수 있습니다:</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">this</span><span class="p">.</span><span class="nx">users</span> <span class="o">=</span> <span class="nx">Immutable</span><span class="p">.</span><span class="nx">List</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">messages</span> <span class="o">=</span> <span class="nx">Immutable</span><span class="p">.</span><span class="nx">List</span><span class="p">();</span>
</code></pre></div>
<p>각각의 <em>페이로드</em> 타입을 처리하는 기능을 구현하는 것은 꽤 간단합니다. 예를 들면, store가 새 메시지를 나타내는 페이로드를 확인할 때 레코드를 새로 생성하고 메시지 리스트에 추가할 수 있습니다.</p>
<div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">this</span><span class="p">.</span><span class="nx">messages</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">messages</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">new</span> <span class="nx">Message</span><span class="p">({</span>
<span class="nx">timestamp</span><span class="o">:</span> <span class="nx">payload</span><span class="p">.</span><span class="nx">timestamp</span><span class="p">,</span>
<span class="nx">sender</span><span class="o">:</span> <span class="nx">payload</span><span class="p">.</span><span class="nx">sender</span><span class="p">,</span>
<span class="nx">text</span><span class="o">:</span> <span class="nx">payload</span><span class="p">.</span><span class="nx">text</span>
<span class="p">});</span>
</code></pre></div>
<p>자료구조가 불변이기 때문에 push 함수의 결과를 <code>this.messages</code>에 할당할 필요가 있으니 주의하세요.</p>
<p>React 측에서는, 컴포넌트의 state를 보존하기 위해 immutable-js 자료구조를 사용한다면, 모든 컴포넌트에 <code>PureRenderMixin</code>을 혼합해 다시 렌더링하는 프로세스를 중단할 수 있습니다.</p>
<div class="docs-prevnext">
<a class="docs-prev" href="/react/docs/perf-ko-KR.html">&larr; Prev</a>
<a class="docs-next" href="/react/docs/context-ko-KR.html">Next &rarr;</a>
</div>
</div>
</section>
<footer class="wrap">
<div class="left">
A Facebook &amp; Instagram collaboration.<br>
<a href="/react/acknowledgements.html">Acknowledgements</a>
</div>
<div class="right">
&copy; 2013&ndash;2016 Facebook Inc.<br>
Documentation licensed under <a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.
</div>
</footer>
</div>
<div id="fb-root"></div>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-41298772-1', 'facebook.github.io');
ga('send', 'pageview');
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=623268441017527";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
docsearch({
apiKey: '36221914cce388c46d0420343e0bb32e',
indexName: 'react',
inputSelector: '#algolia-doc-search'
});
</script>
</body>
</html>