Jekyll2021-11-17T10:58:39+09:00https://wonhee009.github.io/feed.xmlI like LasagnaLasagna's iOS BloglasagnaAsync Await - 12021-11-15T01:00:00+09:002021-11-15T01:00:00+09:00https://wonhee009.github.io/async_await/async_await_1<h2 id="intro">Intro</h2>
<p>오늘은 Swift 5.5에서 추가된 Async/Await에 대해서 알아보려고 한다.</p>
<p>거창하게 이걸 어디 어떻게 사용해야하고 이런거 보다는 이번 포스트에서는 어떤 식으로 이게 굴러가는지부터 알아보려 한다.</p>
<p>오늘 포스트는 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md">Async Await 프로포절</a>과 <a href="https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html">Swift 릴리즈 노트</a>를 기준으로 작성하였다.</p>
<hr />
<h1 id="배경">배경</h1>
<p>Async Await에 대해 알려면 어떤 배경으로 추가되었는지 알아야 한다.</p>
<p>Async Await은 <strong>비동기 프로그래밍</strong>과 관련이 깊다.</p>
<p>기존 Swift에서도 비동기 프로그래밍을 지원하기 위해서 클로저나 completion handler를 제공하고 있었다.</p>
<p>어떤 문제점들이 있기에 Async Await이 도입되었을까?</p>
<p><br /></p>
<p>프로포절에는 다음과 같은 문제점들이 나와 있다.</p>
<ul>
<li>비동기 작업이 많아지거나</li>
<li>에러 처리가 필요하거나</li>
<li>비동기 코드 호출 간의 제어 흐름이 복잡해질 때</li>
</ul>
<p>기존의 방식은 복잡도가 올라가고 그에 따라 디버깅도 어려워진다.</p>
<h2 id="기존의-문제점">기존의 문제점</h2>
<p>사실 이런 문제점을 봤을때 기존의 코드에 적응했어서 그런가 그렇게까지 큰 문제인가? 라는 생각이 먼저 들긴 했다;</p>
<p>그럼 예시들을 보면서 어떤 문제점이 있는지 확인해보자.</p>
<h3 id="pyramid-of-doom">Pyramid of doom</h3>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">precessImageData1</span><span class="p">(</span><span class="nv">completionBlock</span><span class="p">:</span> <span class="p">(</span><span class="n">_</span> <span class="nv">result</span><span class="p">:</span> <span class="kt">Image</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"dataprofile.txt"</span><span class="p">)</span> <span class="p">{</span> <span class="n">dataResource</span> <span class="k">in</span>
<span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"imagedata.dat"</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageResource</span> <span class="k">in</span>
<span class="nf">decodeImage</span><span class="p">(</span><span class="n">dataResource</span><span class="p">,</span> <span class="n">imageResource</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageTmp</span> <span class="k">in</span>
<span class="nf">dewarpAndCleanupImage</span><span class="p">(</span><span class="n">imageTmp</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageResult</span> <span class="k">in</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="n">imageResult</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>프로포절에 있던 예시 코드이다.</p>
<p>보고 처음에 들었던 생각은 <strong>depth가 너무 깊고 코드가 잘 안 읽혔다.</strong></p>
<p>코드를 차근차근 뜯어보면</p>
<ol>
<li>텍스트 리소스를 가져오고</li>
<li>이미지 리소스를 가져와</li>
<li>이미지를 디코딩 한 후</li>
<li>그 결과를 재가공 후</li>
<li>completionBlock(completion handler)를 호출</li>
</ol>
<p>해주고 있다.</p>
<p>이런 5가지의 과정을 중첩 클로저로써 표현하고 있다.</p>
<p>그럼 이런 코드의 문제점은 뭘까?</p>
<p><br /></p>
<p>이런 코드는 <strong>실행되는 위치를 읽기 어렵게 만든다.</strong></p>
<p>그에 따라 <strong>코드를 추적하기 어렵게 만든다.</strong></p>
<blockquote>
<p>그에 따라 코드의 복잡도가 올라가고 디버깅 또한 힘들어진다.</p>
</blockquote>
<h3 id="error-handling">Error handling</h3>
<p>그렇다면 에러 핸들링 측면에서는 어떨까?</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">processImageData2a</span><span class="p">(</span><span class="nv">completionBlock</span><span class="p">:</span> <span class="p">(</span><span class="n">_</span> <span class="nv">result</span><span class="p">:</span> <span class="kt">Image</span><span class="p">?,</span> <span class="n">_</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">Error</span><span class="p">?)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"dataprofile.txt"</span><span class="p">)</span> <span class="p">{</span> <span class="n">dataResource</span><span class="p">,</span> <span class="n">error</span> <span class="k">in</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">dataResource</span> <span class="o">=</span> <span class="n">dataResource</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"imagedata.dat"</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageResource</span><span class="p">,</span> <span class="n">error</span> <span class="k">in</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">imageResource</span> <span class="o">=</span> <span class="n">imageResource</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="n">erorr</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="nf">decodeImage</span><span class="p">(</span><span class="n">dataResource</span><span class="p">,</span> <span class="n">imageResource</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageTmp</span><span class="p">,</span> <span class="n">error</span> <span class="k">in</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">imageTmp</span> <span class="o">=</span> <span class="n">imageTmp</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="nf">dewarpAndCleanupImage</span><span class="p">(</span><span class="n">imageTmp</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageResult</span><span class="p">,</span> <span class="n">error</span> <span class="k">in</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">imageResult</span> <span class="o">=</span> <span class="n">imageResult</span> <span class="n">esle</span> <span class="p">{</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="n">imageResult</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>에러를 처리하기 위해 nil을 주입한 <code class="language-plaintext highlighter-rouge">result</code>와 error를 completion handler에 실어서 내보내고 있다.</p>
<p>한 눈에 봐도 코드가 굉장히 길고 한 눈에 안 읽힌다.</p>
<p><br /></p>
<p>그렇다면 비교적? 최근에 나온 <code class="language-plaintext highlighter-rouge">Result</code> 타입을 사용한다면 어떨까?</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">processImageData2b</span><span class="p">(</span><span class="nv">completionBlock</span><span class="p">:</span> <span class="p">(</span><span class="kt">Result</span><span class="o"><</span><span class="kt">Image</span><span class="p">,</span> <span class="kt">Error</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"dataprofile.txt"</span><span class="p">)</span> <span class="p">{</span> <span class="n">dataResourceResult</span> <span class="k">in</span>
<span class="k">do</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">dataResource</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataResourceResult</span><span class="o">.</span><span class="nf">get</span><span class="p">()</span>
<span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"iamgedata.dat"</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageResourceResult</span> <span class="k">in</span>
<span class="k">do</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">imageResource</span> <span class="o">=</span> <span class="k">try</span> <span class="n">imageResourceResult</span><span class="o">.</span><span class="nf">get</span><span class="p">()</span>
<span class="nf">decodeImage</span><span class="p">(</span><span class="n">dataResource</span><span class="p">,</span> <span class="n">imageResource</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageTmpResult</span> <span class="k">in</span>
<span class="k">do</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">imageTmp</span> <span class="o">=</span> <span class="k">try</span> <span class="n">imageTmpResult</span><span class="o">.</span><span class="nf">get</span><span class="p">()</span>
<span class="nf">dewarpAndCleanupImage</span><span class="p">(</span><span class="n">imageTmp</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageResult</span> <span class="k">in</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="n">imageResult</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="n">error</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="n">error</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
<span class="nf">completionBlock</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="n">error</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Result</code> 타입이 에러 처리를 개선해줬기는 했지만 클로저 중첩에 대한 문제점은 그대로 남아 있다.</p>
<h3 id="many-mistakes-are-easy-to-make">Many mistakes are easy to make</h3>
<p>에러 상황에서 completaion handler를 호출하지 않고 바로 return 처리하는 것도 문제이다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">processImageData4a</span><span class="p">(</span><span class="nv">completionBlock</span><span class="p">:</span> <span class="p">(</span><span class="n">_</span> <span class="nv">result</span><span class="p">:</span> <span class="kt">Image</span><span class="p">?,</span> <span class="n">_</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">Error</span><span class="p">?)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"dataprofile.txt"</span><span class="p">)</span> <span class="p">{</span> <span class="n">dataResource</span><span class="p">,</span><span class="n">error</span> <span class="k">in</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">dataResource</span> <span class="o">=</span> <span class="n">datraResource</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// 🤔</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"imagedarta.dat"</span><span class="p">)</span> <span class="p">{</span> <span class="n">imageResource</span><span class="p">,</span> <span class="n">error</span> <span class="k">in</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">imageResource</span> <span class="o">=</span> <span class="n">imageResource</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// 🤔</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="o">...</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위에서 주석 🤔 부분을 확인해보면 <code class="language-plaintext highlighter-rouge">guard</code> 문에서 <code class="language-plaintext highlighter-rouge">else</code>로 빠지는 완벽한 에러 상황이지만 에러 처리를 하지 않고 바로 return을 한다.</p>
<p>이렇게 처리하는게 사실상 에러 처리를 위한 코드를 줄일 수도 있고 개발자 입장에서는 좀 더 편한 처리 방식일수는 있다.</p>
<p>그렇지만 만약 🤔 해당 부분에서 에러가 발생했다면 디버깅하기 어려워진다.</p>
<h2 id="그래서">그래서</h2>
<p>기존의 비동기 코드가 갖는 문제점들을 살펴봤다.</p>
<p>정리하자면</p>
<ul>
<li>비동기 코드가 중첩될 경우 실행되는 위치를 알기 어렵다.</li>
<li>에러 처리가 어렵고 장황하다.</li>
<li>Completion handler 호출을 잊을 수도 있다.</li>
</ul>
<p>라는 문제점들이 있다.</p>
<p><br /></p>
<p>사실 이런 문제점들은 하나로 귀결된다.</p>
<blockquote>
<p>코드의 가독성을 떨어트리고 디버깅을 어렵게 한다.</p>
</blockquote>
<h1 id="async-await">Async Await</h1>
<p>그럼 이런 문제점들 위에 등장하게 된 Async Await은 기존의 방식들과 얼마나 다른 모양일까?</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">loadWebResource</span><span class="p">(</span><span class="n">_</span> <span class="nv">path</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="n">async</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Resource</span>
<span class="kd">func</span> <span class="nf">decodeImage</span><span class="p">(</span><span class="n">_</span> <span class="nv">r1</span><span class="p">:</span> <span class="kt">Resource</span><span class="p">,</span> <span class="n">_</span> <span class="nv">r2</span><span class="p">:</span> <span class="kt">Resource</span><span class="p">)</span> <span class="n">async</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Image</span>
<span class="kd">func</span> <span class="nf">dewarpAndCleanupImage</span><span class="p">(</span><span class="n">_</span> <span class="nv">i</span><span class="p">:</span> <span class="kt">Image</span><span class="p">)</span> <span class="n">async</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Image</span>
<span class="kd">func</span> <span class="nf">processImageData</span><span class="p">()</span> <span class="n">async</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Image</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">dataResource</span> <span class="o">=</span> <span class="k">try</span> <span class="n">await</span> <span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"dataprofile.txt"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">imageResource</span> <span class="o">=</span> <span class="k">try</span> <span class="n">await</span> <span class="nf">loadWebResource</span><span class="p">(</span><span class="s">"imagedata.dat"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">imageTmp</span> <span class="o">=</span> <span class="k">try</span> <span class="n">await</span> <span class="nf">decodeImage</span><span class="p">(</span><span class="n">dataResource</span><span class="p">,</span> <span class="n">imageResource</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">imageResult</span> <span class="o">=</span> <span class="k">try</span> <span class="n">await</span> <span class="nf">dewarpAndCleanupImage</span><span class="p">(</span><span class="n">imageTmp</span><span class="p">)</span>
<span class="k">return</span> <span class="n">imageResult</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위에서 예시로 살펴본 코드를 Async Await으로 구현한 코드이다.</p>
<p>굉장히 간결해지고 가독성도 올라갔다.</p>
<p>프로포절에 의하면 Async Await을 사용하면 비동기 코드를 동기 코드인 것처럼 작성할 수 있다고 한다.</p>
<p><code class="language-plaintext highlighter-rouge">async</code>, <code class="language-plaintext highlighter-rouge">await</code> 키워드를 제외하면 동기 코드처럼 보인다.</p>
<p><br /></p>
<p>위에서 살펴본 문제점들을 다시 살펴 본다면</p>
<blockquote>
<ul>
<li>비동기 코드가 중첩될 경우 실행되는 위치를 알기 어렵다.</li>
<li>에러 처리가 어렵고 장황하다.</li>
<li>Completion handler 호출을 잊을 수도 있다.</li>
</ul>
</blockquote>
<p>현재 코드에서는 중첩될 일이 없어 보인다.</p>
<p>기존에 사용하던 <code class="language-plaintext highlighter-rouge">throws</code>를 통해 깔끔하게 에러 처리가 가능한거 같다.</p>
<p>Async Await의 특징 중 하나는 작업이 종료될 때 <strong>completion handler 호출을 하지 않아도 작업 종료를 알려주는걸 보장</strong>해준다.</p>
<h2 id="proposed">Proposed</h2>
<p>Swift 5.5 릴리즈 노트에 있는 예시로 한 번 알아보자.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">listPhotos</span><span class="p">(</span><span class="n">inGallery</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="n">async</span> <span class="o">-></span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="c1">// ...some asynchronous networking code ...</span>
<span class="k">return</span> <span class="n">result</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">showFirstPhoto</span><span class="p">()</span> <span class="n">async</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">photoNames</span> <span class="o">=</span> <span class="n">await</span> <span class="nf">listPhotos</span><span class="p">(</span><span class="nv">inGallery</span><span class="p">:</span> <span class="s">"Summer Vacation"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">sortedNames</span> <span class="o">=</span> <span class="n">photoNames</span><span class="o">.</span><span class="nf">sorted</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">name</span> <span class="o">=</span> <span class="n">sortedNames</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">let</span> <span class="nv">photo</span> <span class="o">=</span> <span class="n">await</span> <span class="nf">downloadPhoto</span><span class="p">(</span><span class="nv">named</span><span class="p">:</span> <span class="n">name</span><span class="p">)</span>
<span class="nf">show</span><span class="p">(</span><span class="n">photo</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위의 코드는 갤러리에 있는 사진을 모두 가져와 이름순으로 정렬한 후 첫번째 사진을 보여주고 있다.</p>
<p><code class="language-plaintext highlighter-rouge">listPhotos()</code>나 <code class="language-plaintext highlighter-rouge">downloadPhoto()</code>는 완료하는데 시간이 오래 걸릴 수 있는 작업이다.</p>
<p>따라서 Async를 사용해 비동기 메서드로 만든다면 해당 코드의 완료를 기다리는 동안 앱의 다른 코드가 실행될 수 있다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">showFirstPhoto()</code> 메서드의 실행 순서를 살펴보자.</p>
<p>릴리즈 노트에 따르면 해당 메서드는 코드의 첫 줄부터 첫번째 <code class="language-plaintext highlighter-rouge">await</code>까지 실행된다고 한다.</p>
<p><code class="language-plaintext highlighter-rouge">await</code>으로 <code class="language-plaintext highlighter-rouge">listPhoto()</code>를 호출하고 해당 함수가 반환될 때까지 <code class="language-plaintext highlighter-rouge">showFirstPhoto()</code>의 <strong>실행은 일시 중단(suspends execution)</strong>된다.</p>
<p>해당 코드가 일시 중단되는 동안 다른 동시 코드들이 실행된다.</p>
<p><code class="language-plaintext highlighter-rouge">listPhotos()</code>가 반환된 후 <strong>중단된 지점(continues execution)</strong>부터 다시 실행된다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">sortedNames</code>나 <code class="language-plaintext highlighter-rouge">name</code> 프로퍼티는 일반 동기 코드로 <code class="language-plaintext highlighter-rouge">await</code>이 없기 때문에 실행 중지 지점이 없다.</p>
<p><code class="language-plaintext highlighter-rouge">downloadPhoto</code>에는 <code class="language-plaintext highlighter-rouge">await</code>이 있기 때문에 <code class="language-plaintext highlighter-rouge">listPhotos</code>와 동일하게 해당 메서드가 반환될 때까지 실행을 다시 중지한다.</p>
<h3 id="await">await</h3>
<p>위에서 살펴본 예시에서 반복되는 단어들이 있다.</p>
<p><code class="language-plaintext highlighter-rouge">await</code>, <code class="language-plaintext highlighter-rouge">중단(suspend)</code>이다.</p>
<p><br /></p>
<p>프로포절에 따르면</p>
<blockquote>
<p>The possible <strong>suspension points</strong> in your code amrked with <strong>await</strong></p>
</blockquote>
<p><code class="language-plaintext highlighter-rouge">await</code>은 <code class="language-plaintext highlighter-rouge">suspension point</code>를 표기한 키워드라고 한다.</p>
<p><br /></p>
<p><strong>suspension point</strong>는 비동기 메서드가 반환되기를 기다리는 동안 현재 코드 부분의 실행이 일시 중지될 수 있음을 나타내는 지점이다.</p>
<blockquote>
<p>This is also called <strong>yielding the thread</strong></p>
</blockquote>
<p>이를 다른 말로는 스레드 양보라고도 한다.</p>
<p>현재 스레드에서 코드 실행을 일시 중단하고 해당 스레드에서 다른 코드를 실행하기 때문에 이렇게 불린다고 한다.</p>
<h2 id="thread">Thread</h2>
<p>그렇다면 스레드 양보가 무엇인지 알아볼 필요가 있다.</p>
<p>WWDC 영상을 활용하면서 알아보자.</p>
<h3 id="sync">Sync</h3>
<p>비동기에서의 스레드 처리를 알아보기 전에 동기 코드에서 스레드가 어떻게 이용되는지부터 알아보자.</p>
<p>동기 함수는 호출되면 호출이 완료될 때까지 기다린다.</p>
<p>호출이 완료되면 제어 기능이 함수로 돌아가 중단된 부분부터 다시 시작하게 된다.</p>
<p><img src="/assets/images/async_await_1.png" alt="async_await_1" /></p>
<p>편의상 <code class="language-plaintext highlighter-rouge">fetchThumbnail</code>을 A라고 <code class="language-plaintext highlighter-rouge">thumbnailURLRequest</code>를 B라고 지칭하자.</p>
<p>A가 동기 메서드 B를 호출하면 A가 <strong>실행되던 스레드 컨트</strong>롤이 B에게 전달된다.</p>
<p>B는 해당 스레드를 <strong>선점</strong>하게 되어 다른 코드들은 실행될 수 없다.</p>
<p>B의 실행이 끝나면 A에게 스레드 컨트롤을 돌려 준다.</p>
<h3 id="async">Async</h3>
<p>그렇다면 비동기 함수를 호출한다면 어떻게 될까?</p>
<p>프로포절에는 <strong>비동기 함수는 스레드를 포기할 수 있는 특별한 능력을 가진 일반 함수</strong>로 생각하라는 문장이 있다.</p>
<p>이를 생각하면서 한 번 봐보자.</p>
<p><img src="/assets/images/async_await_2.png" alt="async_await_2" /></p>
<p>편의상 <code class="language-plaintext highlighter-rouge">fetchThumbnail</code>을 A라고 <code class="language-plaintext highlighter-rouge">data(for: _)</code>을 B라고 지칭하자.</p>
<p>여기서는 A가 비동기 메서드 B를 호출했다.</p>
<p>동기 함수와 마찬가지로 A가 <strong>실행되던 스레드 컨트롤</strong>이 B에게로 전달된다.</p>
<p>B의 실행이 끝나면 A에게 스레드 컨트롤을 돌려준다.</p>
<p>사실상 이런 과정은 동기 함수의 실행 과정과 다른게 없다.</p>
<p><br /></p>
<p>하지만 비동기 함수는 스레드를 포기할 수 있다고 했다.</p>
<p>즉, B는 비동기 함수로써 스레드를 포기할 수 있다.</p>
<p>여기서 <strong>스레드를 포기</strong>한다는 것은 <strong>스레드 컨트롤을 포기</strong>한다는 뜻과 동일하고 <strong>suspend</strong>되었다고 한다.</p>
<p><br /></p>
<p>위에서 <code class="language-plaintext highlighter-rouge">await</code>은 <code class="language-plaintext highlighter-rouge">suspension points</code>를 표기한거라고 했는데요.</p>
<p>그렇다면 <code class="language-plaintext highlighter-rouge">await</code>은 잠재적으로 여기서는 스레드를 포기할 수 있다고 알려주는거라고 할 수 있다.</p>
<p>B 함수가 스레드를 포기==스레드 컨트롤을 포기==suspend되면 이를 호출한 A도 suspend된다.</p>
<p><br /></p>
<p>A가 비동기 함수 B를 호출했다.</p>
<p><strong>suspension point</strong>인 <code class="language-plaintext highlighter-rouge">await</code>을 만났으니 스레드 컨트롤을 포기한다.</p>
<p>스레드를 포기했으므로 해당 async 작업이나 같은 코드 블록 속 코드들은 바로 실행되지 못한다.</p>
<p>여기서 포기한 스레드 컨트롤은 시스템으로 넘어가고 있다.</p>
<p><br /></p>
<p>시스템은 스레드 컨트롤을 받음으로써 해당 <strong>async 작업을 실행할 역할</strong>을 갖게된다.</p>
<p>이때 시스템은 바로 async 작업을 실행하는게 아닌 <strong>우선순위에 따라 시스템에게 주어진 작업을 실행</strong>한다.</p>
<p>시스템이 정한 우선순위대로 작업들이 실행되다 해당 async 작업(여기서는 B)의 차례가 오면 <code class="language-plaintext highlighter-rouge">resume</code>된다.</p>
<p><code class="language-plaintext highlighter-rouge">resume</code>될 때 스레드 컨트롤을 돌려주게 되는데 처음 B가 실행되던 스레드와 다를 수 있다.</p>
<p>(같을 수도 있고)</p>
<h3 id="그래서-어떤-일이-일어나고-있는데">그래서 어떤 일이 일어나고 있는데?</h3>
<p>모든 스레드는 함수 호출 상태를 저장하기 위해 하나의 Stack을 가지고 있다.</p>
<h4 id="sync-1">sync</h4>
<p><img src="/assets/images/async_await_3.png" alt="async_await_3" /></p>
<p>익명의 스레드는 위와 같은 Stack 상태를 가지고 있다고 가정하자.</p>
<p>우리가 <code class="language-plaintext highlighter-rouge">func4</code>라는 메서드를 호출한다면</p>
<p><img src="/assets/images/async_await_4.png" alt="async_await_4" /></p>
<p>이렇게 Stack에 새로운 프레임이 Push된다.</p>
<p>함수의 실행이 끝나면 해당 프레임이 Stack에서 Pop된다.</p>
<p><br /></p>
<p>사실 이런 형태는 동기 함수일 때에는 별 문제되지 않는다.</p>
<p>A가 B를 호출하면 어차피 해당 스레드는 선점당할테고 해당 스레드에서는 A-B…에 관련된 함수들만 저장될 것이다.</p>
<p>따라서 실행이 완료되어 <code class="language-plaintext highlighter-rouge">func4</code>가 Pop된 후, Stack의 top 프레임인 <code class="language-plaintext highlighter-rouge">func3</code>에 접근해 중지 지점에서 바로 실행을 재개할 수 있다.</p>
<p><strong>그렇다면 비동기 함수는 어떤가?</strong></p>
<h4 id="async-1">async</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">add</span><span class="p">(</span><span class="n">_</span> <span class="nv">newArticles</span><span class="p">:</span> <span class="p">[</span><span class="kt">Article</span><span class="p">])</span> <span class="n">async</span> <span class="k">throws</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">ids</span> <span class="o">=</span> <span class="k">try</span> <span class="n">await</span> <span class="n">database</span><span class="o">.</span><span class="nf">save</span><span class="p">(</span><span class="n">newArticles</span><span class="p">,</span> <span class="nv">for</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span>
<span class="k">for</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">article</span><span class="p">)</span> <span class="k">in</span> <span class="nf">zip</span><span class="p">(</span><span class="n">dis</span><span class="p">,</span> <span class="n">newArticles</span><span class="p">)</span> <span class="p">{</span>
<span class="n">articles</span><span class="p">[</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">article</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">updateDatabase</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="n">async</span> <span class="p">{</span>
<span class="n">await</span> <span class="n">feed</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">articles</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>이 코드를 예시로 한 번 알아보자.</p>
<p><code class="language-plaintext highlighter-rouge">updateDatabase()</code>에서 add를 호출하니 <code class="language-plaintext highlighter-rouge">add()</code>도 Stack에 push 된다.</p>
<p><img src="/assets/images/async_await_5.png" alt="async_await_5" /></p>
<p><code class="language-plaintext highlighter-rouge">add()</code>에는 <code class="language-plaintext highlighter-rouge">await</code>이 있으므로 하나의 <strong>suspension point</strong>가 존재한다.</p>
<p><code class="language-plaintext highlighter-rouge">add()</code>의 for문에서 사용되는 <code class="language-plaintext highlighter-rouge">id</code>, <code class="language-plaintext highlighter-rouge">article</code>은 해당 코드 블럭에서 사용되는 로컬 변수이므로 <strong>add 프레임에 저장</strong>된다.</p>
<p><img src="/assets/images/async_await_6.png" alt="async_await_6" /></p>
<p><br /></p>
<p><img src="/assets/images/async_await_7.png" alt="async_await_7" /></p>
<p>두개의 비동기 프레임이 있는 Heap이 있다.</p>
<p>힙에 저장된 <code class="language-plaintext highlighter-rouge">updateDatabase</code>와 <code class="language-plaintext highlighter-rouge">add</code>는 모두 <code class="language-plaintext highlighter-rouge">await</code>으로 호출되고 있다.</p>
<p><code class="language-plaintext highlighter-rouge">await</code>은 <strong>suspension point</strong>로 스레드를 포기할 수 있음을 나타낸다.</p>
<p>그렇다면 스레드 포기와 Heap은 무슨 상관이 있을까?</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">add</code> 메서드가 suspended(일시 중지)된다면 해당 스레드 컨트롤을 포기한다.</p>
<p>스레드 컨트롤을 포기하고 시스템에게 컨트롤을 넘김으로써 해당 스레드가 다른 작업을 실행할 수 있는 상태로 만든다.</p>
<p><br /></p>
<p><img src="/assets/images/async_await_8.png" alt="async_await_8" /></p>
<p>그럼 이런식으로 suspended된 비동기 코드는 힙에 저장되고 해당 스레드에서는 다른 작업(otherWork2)를 실행할 수 있게 된다.</p>
<p><strong>Suspension Point</strong>에서 유지되는 모든 정보는 Heap에 저장되고 <strong>비동기 메서드가 resume될 때 사용</strong>할 수 있다.</p>
<p>WWDC에서는 힙에 저장된 비동기 프레임을 <strong>continuation에 대한 런타임 표현</strong>이라고 말한다.</p>
<p><strong>continuation</strong>은 비동기 호출 후에 일어나는 일로 <code class="language-plaintext highlighter-rouge">await</code> 호출 아래의 모든 것을 <strong>continuation</strong>이라고 한다.</p>
<p><br /></p>
<p><strong>그렇다면 continuation이 생긴 배경은 뭘까?</strong></p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">beginOperation</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="p">(</span><span class="kt">OperationResult</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span>
</code></pre></div></div>
<p>이처럼 콜백을 사용하는 비동기 메서드가 있다.</p>
<p>Swift에서는 종종 콜백을 통해 비동기 코드 실행을 제공했다.</p>
<p>Async Await이 도입되기 전에 코드 자체가 작성되었거나 이벤트 중심인 다른 시스템과의 연결로도 이런 코드가 있을 수 있다.</p>
<p>이런 경우 내부적으로 콜백을 사용하는 동안 클라이언트에 비동기 인터페이스를 제공할 수 있어야 한다.</p>
<p><br /></p>
<p>즉, 비동기 작업은 자체적으로 일시 중단될 수 있어야 하는 동시에 이벤트 기반 동기 시스템이 이벤트에 대한 응답으로 작업을 재개할 수 있도록 해야한다.</p>
<p>이를 가능하게 해주는 것이 <strong>continuation</strong>이다.</p>
<p>Swift에서는 현재 비동기 작업에 대한 <strong>continuation</strong>을 얻기 위한 API들을 제공한다.</p>
<p>작업의 <strong>continuation</strong>을 가져오면 작업이 일시 중단되고 <strong>동기 코드에서 작업을 재개할 수 있는 값이 생성</strong>된다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">await</code>을 만나면 실행이 일시 중단되고 Heap에 비동기 프레임이 생성된다.</p>
<p>일시 중단되었을때 가장 큰 특징은 스레드 컨트롤을 포기하고 이를 시스템에게 넘긴다는 점이다.</p>
<p>그럼 우리가 위에서 살펴 봤던 특징들을 종합해보자.</p>
<ul>
<li>
<p>스레드는 <strong>함수 호출 상태를 저장</strong>하기 위해 하나의 Stack을 가지고 있다.</p>
</li>
<li>
<p>메서드가 호출되면 stack에 해당 메서드 프레임이 push된다.</p>
</li>
<li><code class="language-plaintext highlighter-rouge">await</code>을 만나면 <strong>실행이 일시 중단이 되고 해당 스레드 컨트롤을 포기</strong>한다.</li>
<li>스레드 컨트롤을 포기하면 해당 <strong>함수 호출 상태를 저장한 Stack 또한 포기</strong>한다.</li>
</ul>
<p><strong>시스템에 의해 해당 비동기 메서드가 <code class="language-plaintext highlighter-rouge">resume</code> 될때 어느 지점에서 실행 재개 되어야 하는지 어떻게 알 수 있을까?</strong></p>
<p>이때 <strong>continuation</strong>가 필요해진다.</p>
<p><br /></p>
<p><img src="/assets/images/async_await_9.png" alt="async_await_9" /></p>
<p>이렇게 Heap에는 continuation 리스트들이 저장되게 되고 <code class="language-plaintext highlighter-rouge">resume</code>을 하면 이 리스트들에서 <strong>Continuation</strong>을 꺼내서 작업을 재개한다.</p>
<p>이를 좀 더 자세하게 본다면</p>
<p><img src="/assets/images/async_await_10.png" alt="async_await_10" /></p>
<p>이렇게 스레드가 해제되었을 때 Heap에 있는 비동기 프레임이 <code class="language-plaintext highlighter-rouge">resume</code>될 수 있다.</p>
<p>해제된 스레드는 이전과 동일한 스레드일수도 아닐 수 도 있지만 중지되었던 포인트를 알고 있기 때문에 해당 포인트부터 다시 작업을 실행할 수 있다.</p>
<hr />
<h1 id="outro">Outro</h1>
<p>Async Await에서 살짝 핥아만 봤다.</p>
<p>사실상 Async Await보다는 스레드 운영에 좀 더 초점이 맞춰져 있지만…</p>
<p>이런거를 이해하고 있어야 이후 스텝도 이해하기 쉽지 않을까 해서 포스팅해본다.</p>lasagnaIntroClean Code - ch.22021-11-15T00:00:00+09:002021-11-15T00:00:00+09:00https://wonhee009.github.io/cleancode/CleanCode_ch.2<h1 id="intro">Intro</h1>
<p>이전에는 클린코드란 무엇인지에 대해 알아봤다.</p>
<p>이번 장에서는 의미 있는 이름이란 무엇인지에 대해서 알아본다.</p>
<hr />
<h2 id="의도를-분명히-밝히는-네이밍">의도를 분명히 밝히는 네이밍</h2>
<p>좋은 이름으로 절약하는 시간이 있다.</p>
<p>변수의 존재 이유/수행 기능/사용 방법을 드러내는 네이밍이 좋다.</p>
<p>따로 주석이 있다면 의도를 분명히 드러내지 못하는 네이밍이다.</p>
<p>의도가 드러나는 이름은 <strong>코드 이해와 변경이 쉬워진다.</strong></p>
<h2 id="그릇된-정보를-피하라">그릇된 정보를 피하라</h2>
<p>코드에 그릇된 단서를 남기지 않는다.</p>
<p>그릇된 단서는 코드의 의미를 흐린다.</p>
<p>예를 들어, 여러 계정을 그룹으로 묶는데 List로 구성되어 있지 않는데 accountList로 네이밍한다던지…</p>
<h2 id="의미-있게-구분하라">의미 있게 구분하라</h2>
<p>컴파일러만 통과하려는 생각으로 코드를 구현하지 마라.</p>
<p>변수의 이름으로 a1, a2, a3… 같은 경우도 여기에 해당한다.</p>
<p>이름이 달라야 한다면 의미도 다르다는 뜻이다.</p>
<p>a1, a2… 같은 경우는 그릇된 정보를 제공하는 것도 아니고 아무런 정보도 제공하지 못하는 이름이다.</p>
<h2 id="클래스-이름">클래스 이름</h2>
<p>클래스 이름이나 객체 이름은 명사/명사구가 적합하다.</p>
<p>Manager, Processor, Data, Info 등과 같은 단어는 피하고 동사는 사용하지 않는다.</p>
<h2 id="메서드-이름">메서드 이름</h2>
<p>메서드 이름은 동사/동사구가 적합하다.</p>
<p>get/set/is를 붙이기도 한다.</p>
<h2 id="한-개념에-한-단어를-사용하라">한 개념에 한 단어를 사용하라</h2>
<p>fetch와 get을 예로 들 수 있다.</p>
<p>API 통신으로 무언가를 가져올 때 fetch나 get을 흔히 쓰는데 이를 같은 기능이라면 하나의 단어로 통일할 필요가 있다.</p>
<hr />
<h1 id="outro">Outro</h1>
<p>네이밍에 대해서 알아봤다.</p>
<p>역시 네이밍이 제일 어렵다.</p>
<p>영어에서 오는 미묘한 뉘앙스 차이도 있고, 복잡해지면 어떤 이름을 가지고 가는게 역할을 확실히 할 수 있을지 항상 고민스럽다.</p>
<p>어느정도 일정한 나만의 룰을 가지고 있으면 좋을거 같은데 아직은 이를 갖기 어려운거 같다.</p>lasagnaIntroClean Code - ch.12021-11-14T22:00:00+09:002021-11-14T22:00:00+09:00https://wonhee009.github.io/cleancode/CleanCode_ch.1<h1 id="intro">Intro</h1>
<p>Clean Code 책을 읽어보기로 해서 기록할 겸 한 장씩 남겨보려고 한다.</p>
<p>거창한건 아니고 그냥 읽으면서 나도 좀 더 명심할거… 아니면 느낀점? 정도 남기려고 한다.</p>
<hr />
<p>제대로 명시한 요구사항은 코드만큼 <strong>정형적</strong>이고 테스트 케이스로 사용해도 좋다.</p>
<p>-> TDD 스터디 했을때 굉장히 많이 나왔던 말인거 같다.</p>
<p>-> TC를 만들어 가는 과정이 요구사항을 좀 더 명확하게 하고 명세의 애매한 부분들을 찾는다는 느낌을 받았었는데 여기서도 그에 비슷한 얘기가 나왔다.</p>
<p><br /></p>
<p>나쁜 코드는 개발 속도를 떨어트린다.</p>
<p>-> 클린 코드는 비용을 절감할 뿐 아니라 전문가로서 살아남는 길이다.</p>
<p><br /></p>
<p>좋은 코드를 사수하는 일은 프로그래머의 책임이다.</p>
<p><br /></p>
<p>프로젝트의 기한을 놓치지 않는 방법 또한 코드를 깨끗하게 유지하는 습관에서 이뤄질 수 있다.</p>
<p><br /></p>
<p>그렇다면 깨끗한 코드는?</p>
<ul>
<li>우아하고 효율적인 코드
<ul>
<li>논리가 간단해야 버그가 숨어들지 못한다.</li>
<li>의존성을 최대한 줄여야 유지보수가 쉽다.</li>
<li>오류는 전략에 의거해 철저하게 처리되어야 한다.</li>
<li>한 가지만 잘 해야한다.</li>
<li>즉, 세세한 사항까지 꼼꼼하게 처리하는 코드이다.</li>
</ul>
</li>
<li>단순하고 직접적이며 잘 읽혀야 한다.
<ul>
<li>설계자의 의도를 숨기지 않는다.</li>
<li>명쾌한 추상화와 단순한 제어문으로 가득하다.</li>
<li>즉, 코드는 추측이 아니라 사실에 기반한다.</li>
</ul>
</li>
<li>작성자가 아니 사람도 읽기 쉽고 고치기 쉽다.
<ul>
<li>의미 있는 이름을 붙인다.</li>
<li>특정 목적을 달성하는 방법을 하나만 제공한다.</li>
<li>의존성은 최소이고 의존성을 명확히 정의한다.</li>
</ul>
</li>
<li>객체가 여러 기능을 수행한다면 여러 객체로 나눈다.</li>
<li>메서드가 여러 기능을 수행한다면 메서드 추출 리팩토링 기법을 적용해 기능을 명확히 기술하는 메서드 하나와 실제로 수행하는 메서드 여러 개로 나눈다.</li>
<li>중복을 줄이고 표현력을 높이고 초반부터 간단한 추상화를 고려한다.</li>
</ul>
<p><strong>즉, 중복을 피하라, 한 기능만 수행해라, 작게 추상화 해라</strong></p>
<p>읽으면서 놀랄 일이 없어야 하고, 독해하느라 고생하지 않아야 한다.</p>
<hr />
<h1 id="outro">Outro</h1>
<p>1장이니 간단하게 클린 코드란 무엇인지에 대해서 얘기한다.</p>
<p>사실 들어보면 다 맞는 말이고 늘 듣는 말이다.</p>
<p>하지만 이런거 지키기 힘들다…. 이유야 많겠지만…. (리소스 부족 등…) 근데 여기서는 그 이유조차도 클린 코드로 해결할 수 있다고 한다.</p>
<p>항상 코드의 역할을 잘 생각하고 그 역할을 명확하게 보이도록 작성하자.</p>lasagnaIntroRxSwift - ch.3 Subject2021-10-26T08:00:00+09:002021-10-26T08:00:00+09:00https://wonhee009.github.io/rxswift/RxSwift_ch.3_Subject<h1 id="intro">Intro</h1>
<p>여기서는 Subject에 대해서 알아보자.</p>
<p>이전에 알아본 Observable과 어떻게 다를까</p>
<p><br /></p>
<p><strong>Observable</strong>은 읽기 전용이다.</p>
<hr />
<h1 id="what-is-an-observables">What is an Observables?</h1>
<p>Observable은 Rx의 핵심이다.</p>
<p>Rx를 보다보면 <code class="language-plaintext highlighter-rouge">Observable</code>, <code class="language-plaintext highlighter-rouge">Observable sequence</code>, <code class="language-plaintext highlighter-rouge">sequence</code>가 등장하는데 이는 모두 같은 것이다.</p>
<p>Observable의 가장 중요한 점은 <strong>비동기적(asynchronous)</strong>이라는 것이다.</p>
<p>Observable은 일정 기간 동안 <strong>이벤트를 생성하고 이를 방출(emit)</strong>한다.</p>
<p>이벤트는 숫자나 사용자가 정의한 타입 인스턴스와 같은 값을 포하고, tap과 같은 제스처도 인식할 수 있다.</p>
<p>이를 가장 잘 보여주는 건 marble diagram이다.</p>
<p><a href="https://rxmarbles.com">marble diagram을 볼 수 있는 사이트</a></p>
<h2 id="lifecycle-of-an-observable">Lifecycle of an observable</h2>
<p><img src="/assets/images/rxSwift_1.png" alt="rxSwift_1" /></p>
<p>위의 marble diagram을 보면 <strong>observable</strong>이 세개의 이벤트를 방출하고 있다.</p>
<p>이 이벤트들은 <strong>next 이벤트</strong>에 의해서 방출된다.</p>
<p><img src="/assets/images/rxSwift_2.png" alt="rxSwift_2" /></p>
<p>위의 marble diagram처럼 오른쪽의 <code class="language-plaintext highlighter-rouge">|</code>수직 막대는 이벤트 종료를 의미한다.</p>
<p><strong>observable</strong>은 3개의 tap 이벤트를 발생시킨 후 종료되었다.</p>
<p>이를 <strong>완료(completed) 이벤트</strong>라고 한다.</p>
<p><strong>observable</strong>은 완료 이벤트가 발생하면 더 이상 아무 것도 방출할 수 없다.</p>
<p><img src="/assets/images/rxSwift_3.png" alt="rxSwift_3" /></p>
<p>빨간색 <code class="language-plaintext highlighter-rouge">X</code> 표시는 에러를 의미한다.</p>
<p><strong>Observable</strong>이 종료된 것은 위와 동일하지만 <strong>error 이벤트</strong>에 의해서 종료되었다.</p>
<ul>
<li>Observable은 어떤 element를 가지는 <strong>next 이벤트</strong>를 방출한다.</li>
<li>종료 이벤트(<strong>error 이벤트</strong> 혹은 <strong>completed 이벤트</strong>)가 발생할 때까지 위의 작업이 계속 될 수 있다.</li>
<li>Observable이 종료되면 더이상 이벤트를 생성할 수 없다.</li>
</ul>
<h3 id="event">Event</h3>
<p>이벤트는 <strong>Enum</strong>으로 표현된다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// Represents a sequence event.</span>
<span class="c1">///</span>
<span class="c1">/// Sequence grammar: </span>
<span class="c1">/// **next\* (error | completed)**</span>
<span class="kd">@frozen</span> <span class="kd">public</span> <span class="kd">enum</span> <span class="kt">Event</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="c1">/// Next element is produced.</span>
<span class="k">case</span> <span class="nf">next</span><span class="p">(</span><span class="kt">Element</span><span class="p">)</span>
<span class="c1">/// Sequence terminated with an error.</span>
<span class="k">case</span> <span class="nf">error</span><span class="p">(</span><span class="kt">Swift</span><span class="o">.</span><span class="kt">Error</span><span class="p">)</span>
<span class="c1">/// Sequence completed successfully.</span>
<span class="k">case</span> <span class="n">completed</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li><strong>next 이벤트</strong>에는 <code class="language-plaintext highlighter-rouge">Element</code> 인스턴스가 포함되어 있다.</li>
<li><strong>error 이벤트</strong>에는 Swift.Error 인스턴스가 포함되어 있다.</li>
<li><strong>error 이벤트와 completed 이벤트는</strong> 데이터가 포함되어 있지 않고 단순히 중지하는 이벤트이다.</li>
</ul>
<hr />
<h1 id="creating-observables1">Creating observables(1)</h1>
<h2 id="just">just</h2>
<p>(이번 챕터의 예제는 플레이그라운드를 사용했다.)</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"just, of, from"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">one</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">let</span> <span class="nv">two</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">three</span> <span class="o">=</span> <span class="mi">3</span>
<span class="c1">//2</span>
<span class="k">let</span> <span class="nv">observable</span><span class="p">:</span><span class="kt">Observable</span><span class="o"><</span><span class="kt">Int</span><span class="o">></span> <span class="o">=</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Int</span><span class="o">>.</span><span class="nf">just</span><span class="p">(</span><span class="n">one</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<ol>
<li>예제에서 사용할 Int 상수를 정의했다.</li>
<li><code class="language-plaintext highlighter-rouge">one</code> 상수와 <code class="language-plaintext highlighter-rouge">just</code> 메서드로 <code class="language-plaintext highlighter-rouge">Int</code> 타입의 <strong>Observable sequence</strong>를 만들었다.</li>
</ol>
<p><code class="language-plaintext highlighter-rouge">just</code>는 Observable의 타입 메서드이다.</p>
<p>단 하나의 Element를 포함하는 Observable sequence를 생성한다.</p>
<p>Rx에서는 메서드를 <code class="language-plaintext highlighter-rouge">operator</code>라고 한다. (즉, <code class="language-plaintext highlighter-rouge">just</code>도 operator이다.)</p>
<p><img src="/assets/images/rxSwift_4.png" alt="rxSwift_4" /></p>
<p><a href="http://reactivex.io/documentation/ko/operators/just.html">출처: rx홈페이지-just</a></p>
<p>rx문서로 <code class="language-plaintext highlighter-rouge">just</code> 정의에 대해 좀 더 알아보자.</p>
<blockquote>
<p><code class="language-plaintext highlighter-rouge">just</code>는 Element를 해당 Element 타입을 방출하는 <strong>Observable로 변환</strong>하는 operator이다.</p>
</blockquote>
<p>이렇게만 보면 이해가 잘 안 간다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">just</span><span class="p">(</span><span class="n">_</span> <span class="nv">element</span><span class="p">:</span> <span class="kt">Element</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="kt">Just</span><span class="p">(</span><span class="nv">element</span><span class="p">:</span> <span class="n">element</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">just</code> operator는 위처럼 구성되어 있다.</p>
<p><code class="language-plaintext highlighter-rouge">static</code>으로 정의되어 있으므로 <strong>타입 메서드</strong>임을 알 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code>의 파라미터 타입과 return 타입을 보면 위의 정의를 이해할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o"><</span><span class="kt">Int</span><span class="o">>.</span><span class="nf">just</span><span class="p">(</span><span class="n">one</span><span class="p">)</span>
</code></pre></div></div>
<p>위의 예시에 적용해 본다면 <code class="language-plaintext highlighter-rouge">just</code> operator는 Int 값을 Int 타입의 <strong>이벤트를 방출하는 Observable로 변환</strong> 해준다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code>에 nil을 전달하면 nil을 Element로 방출하는 Observable이 반환된다.</p>
<p>이는 빈 Observable을 반환하는게 아니다.</p>
<p>(마치 nil과 ““의 차이 같네…)</p>
<p>빈 Observable을 반환하고 싶다면 <code class="language-plaintext highlighter-rouge">Empty</code> operator를 사용할 수 있다.</p>
<h2 id="of">of</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"just, of, from"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">one</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">let</span> <span class="nv">two</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">three</span> <span class="o">=</span> <span class="mi">3</span>
<span class="c1">//2</span>
<span class="k">let</span> <span class="nv">observable</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="n">one</span><span class="p">,</span> <span class="n">two</span><span class="p">,</span> <span class="n">three</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">observable2</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">([</span><span class="n">one</span><span class="p">,</span> <span class="n">two</span><span class="p">,</span> <span class="n">three</span><span class="p">])</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>observable의 타입은 <code class="language-plaintext highlighter-rouge">Observable<Int></code></li>
<li>observable2의 타입은 <code class="language-plaintext highlighter-rouge">Observable<[Int]></code></li>
</ul>
<p><code class="language-plaintext highlighter-rouge">of</code> operator는 주어진 값들의 타입추론을 통해 <strong>Observable sequence</strong>를 생성한다.</p>
<p>어떤 배열을 Observable array로 만들고 싶다면 <code class="language-plaintext highlighter-rouge">of</code> operator에 배열을 넣어주면 된다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="s">"😀 of operator: Observable<Int>"</span><span class="p">)</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="n">array</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
<span class="p">})</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"😀 of operator: Observable<[Int]>"</span><span class="p">)</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">])</span><span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="n">array</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
<span class="p">})</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="n">😀</span> <span class="n">of</span> <span class="nv">operator</span><span class="p">:</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Int</span><span class="o">></span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="mi">3</span>
<span class="n">😀</span> <span class="n">of</span> <span class="nv">operator</span><span class="p">:</span> <span class="kt">Observable</span><span class="o"><</span><span class="p">[</span><span class="kt">Int</span><span class="p">]</span><span class="o">></span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</code></pre></div></div>
<p>위의 코드에서 보이듯 Int 타입의 Element를 <code class="language-plaintext highlighter-rouge">of</code> operator에 넣어주면 <code class="language-plaintext highlighter-rouge">1 2 3</code>으로 Int타입의 시퀀스가 방출된다.</p>
<p>Int 타입의 배열을 <code class="language-plaintext highlighter-rouge">of</code> operator에 넣어주면 배열 자체의 시퀀스가 방출된다.</p>
<h3 id="just-of">just? of?</h3>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">])</span><span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="n">array</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
<span class="p">})</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">just</code> operator에 Int 배열을 넣어줘도 동일한 결과를 얻을 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code>,<code class="language-plaintext highlighter-rouge">of</code> 모두 반환 타입도 <code class="language-plaintext highlighter-rouge">Observable<[Int]></code>로 동일하다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code> 구현부에 가보면 파라미터에 아래와 같이 작성되어 있다.</p>
<blockquote>
<p>Single element in the resulting observable sequence.</p>
</blockquote>
<p>파라미터는 observable sequence로 만들고 싶은 <strong>single element</strong>이다.</p>
<p>아직 이 부분은 명확하지 않은거 같다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code> 는 단일 값을 방출할때 사용하는건 이해하겠는데 단일 값을 어디까지로 봐야할까?</p>
<p>[1, 2, 3] <- 이런 배열도 하나의 배열로써 단일 값을 볼 수 있지 않을까? 라는 생각이 든다.</p>
<h2 id="from">from</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"just, of, from"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">one</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">let</span> <span class="nv">two</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">three</span> <span class="o">=</span> <span class="mi">3</span>
<span class="c1">//2</span>
<span class="k">let</span> <span class="nv">observable</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">from</span><span class="p">([</span><span class="n">one</span><span class="p">,</span> <span class="n">two</span><span class="p">,</span> <span class="n">three</span><span class="p">])</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>observable의 타입은 <code class="language-plaintext highlighter-rouge">Observable<Int></code></li>
</ul>
<p><code class="language-plaintext highlighter-rouge">from</code> operator는 배열 요소들을 하나씩 방출한다.</p>
<p><code class="language-plaintext highlighter-rouge">from</code>은 배열만 입력할 수 있다.</p>
<p><img src="/assets/images/rxSwift_5.png" alt="rxSwift_5" /></p>
<p><a href="http://reactivex.io/documentation/operators/from.html">출처: rx홈페이지-from</a></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">from</span><span class="p">(</span><span class="n">_</span> <span class="nv">array</span><span class="p">:</span> <span class="p">[</span><span class="kt">Element</span><span class="p">],</span> <span class="nv">scheduler</span><span class="p">:</span> <span class="kt">ImmediateSchedulerType</span> <span class="o">=</span> <span class="kt">CurrentThreadScheduler</span><span class="o">.</span><span class="n">instance</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="kt">ObservableSequence</span><span class="p">(</span><span class="nv">elements</span><span class="p">:</span> <span class="n">array</span><span class="p">,</span> <span class="nv">scheduler</span><span class="p">:</span> <span class="n">scheduler</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">from</code> operator는 위와 같이 구성되어 있다.</p>
<p>파라미터 타입을 보면 배열만을 입력 받는걸 알 수 있다.</p>
<hr />
<h1 id="subscribing-to-observables">Subscribing to observables</h1>
<p>iOS 개발자라면 <code class="language-plaintext highlighter-rouge">NotificationCenter</code>에 익숙할 것이다.(observer에게 알림을 브로드캐스팅)</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">let</span> <span class="nv">observer</span> <span class="o">=</span> <span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span>
<span class="nv">forName</span><span class="p">:</span> <span class="o">.</span><span class="kt">UIKeyboardDidChangeFrame</span><span class="p">,</span>
<span class="nv">object</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span>
<span class="nv">queue</span><span class="p">:</span> <span class="kc">nil</span>
<span class="p">)</span> <span class="p">{</span> <span class="n">notification</span> <span class="k">in</span>
<span class="c1">// Handle receiving notification</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위의 코드는 클로저로 <code class="language-plaintext highlighter-rouge">UIKeyboardDidChangeFrame</code> notification observer 코드이다.</p>
<p>RxSwift의 observable을 subscribing하는 방식은 위와 비슷하다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">addObserver()</code> 대신 <code class="language-plaintext highlighter-rouge">subscribe()</code>를 사용한다.</li>
<li><code class="language-plaintext highlighter-rouge">NotificationCenter</code>는 <code class="language-plaintext highlighter-rouge">.default</code> 싱글톤 인스턴스에서만 가능했지만, Rx Observable은 다르다.</li>
<li>핵심은 Observable은 <strong>subscriber가 있어야만 이벤트를 방출하거나 작업을 수행</strong>한다.</li>
</ul>
<p><strong>Observable은 sequence의 정의일 뿐</strong>이며 Observable이 subscriber되어야만 이후의 스텝이 진행된다.</p>
<p>Observable의 구현은 Swift 반복문에서 <code class="language-plaintext highlighter-rouge">.next()</code>를 구현하는 것과 비슷하다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">let</span> <span class="nv">sequence</span> <span class="o">=</span> <span class="mi">0</span><span class="o">..<</span><span class="mi">3</span>
<span class="k">var</span> <span class="nv">iterator</span> <span class="o">=</span> <span class="n">sequence</span><span class="o">.</span><span class="nf">makeIterater</span><span class="p">()</span>
<span class="k">while</span> <span class="k">let</span> <span class="nv">n</span> <span class="o">=</span> <span class="n">iterator</span><span class="o">.</span><span class="nf">next</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="p">}</span>
<span class="cm">/* Prints:
0
1
2
*/</span>
</code></pre></div></div>
<p>Observable subscriber는 이보다 쉽다.</p>
<p>Observable이 <strong>방출하는 이벤트 타입에 대해 handler를 추가</strong>할 수 있다.</p>
<p>–> <code class="language-plaintext highlighter-rouge">.subscribe(onNext:_, onError:_, onCompleted:_)</code>로 이벤트 타입별로 핸들러 추가 가능하다.</p>
<p>Observable은 <code class="language-plaintext highlighter-rouge">next</code>, <code class="language-plaintext highlighter-rouge">error</code>, <code class="language-plaintext highlighter-rouge">completed</code> 이벤트를 방출한다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">next</code>: 핸들러로 방출된 Element를 보낸다.</li>
<li><code class="language-plaintext highlighter-rouge">error</code>: error 인스턴스가 포함된다.</li>
</ul>
<h2 id="subscribe">subscribe()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span> <span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="n">completed</span>
</code></pre></div></div>
<p>Observable은 각각의 Element에 대해서 <code class="language-plaintext highlighter-rouge">next</code> 이벤트를 방출한다.</p>
<p>종료 직전 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트를 방출한다.</p>
<p>이벤트로 감싸진 Element가 아니라 직접 Element에도 접근할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span> <span class="p">{</span> <span class="n">event</span> <span class="k">in</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">element</span> <span class="o">=</span> <span class="n">event</span><span class="o">.</span><span class="n">element</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="mi">3</span>
</code></pre></div></div>
<p>Event 구현부를 보면 아래와 같이 <code class="language-plaintext highlighter-rouge">element</code> 프로퍼티를 확인할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="k">var</span> <span class="nv">element</span><span class="p">:</span> <span class="kt">Element</span><span class="p">?</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">case</span> <span class="o">.</span><span class="nf">next</span><span class="p">(</span><span class="k">let</span> <span class="nv">value</span><span class="p">)</span> <span class="o">=</span> <span class="k">self</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">value</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">next</code> 이벤트에만 <code class="language-plaintext highlighter-rouge">element</code>가 존재하고 옵셔널이다.</p>
<h2 id="subscribeonnext">subscribe(onNext:)</h2>
<p>위에서 살펴본 <code class="language-plaintext highlighter-rouge">subscribe()</code>에 대해서 더 알아보자.</p>
<p>RxSwift에는 Observable에서 방출하는 각 이벤트 유형에 subscribe operator가 존재한다.</p>
<p>위에서 살펴본 코드는 아래와 같이 작성할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span>
<span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">next</code> 이벤트만 처리하고 나머지(<code class="language-plaintext highlighter-rouge">error</code>, <code class="language-plaintext highlighter-rouge">completed</code> 이벤트)에 대해서는 무시한다.</p>
<p><code class="language-plaintext highlighter-rouge">onNext</code> 클로저는 <code class="language-plaintext highlighter-rouge">next</code> 이벤트의 element를 받으므로 위의 코드처럼 element를 따로 뽑지 않아도 된다.</p>
<h3 id="subscribe-subscribeonnext">subscribe()? Subscribe(onNext:)</h3>
<p>위에서 알아본 바와 두 메서드 모두 <code class="language-plaintext highlighter-rouge">next</code> 이벤트의 element를 사용할 수 있다.</p>
<p>그렇다면 둘은 어떤 차이가 있을까?</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">func</span> <span class="nf">subscribe</span><span class="p">(</span><span class="n">_</span> <span class="nv">on</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Event</span><span class="o"><</span><span class="k">Self</span><span class="o">.</span><span class="kt">Element</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="o">-></span> <span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Disposable</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">((</span><span class="k">Self</span><span class="o">.</span><span class="kt">Element</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">onError</span><span class="p">:</span> <span class="p">((</span><span class="kt">Error</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">onCompleted</span><span class="p">:</span> <span class="p">(()</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">onDisposed</span><span class="p">:</span> <span class="p">(()</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">)</span> <span class="o">-></span> <span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Disposable</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">subscribe()</code>의 클로저 입력 타입은 <code class="language-plaintext highlighter-rouge">RxSwift.Event<Self.Element></code>로 element가 이벤트로 감싸져 있다.</p>
<p>-> <code class="language-plaintext highlighter-rouge">subscribe()</code> 예제에서 <code class="language-plaintext highlighter-rouge">next(1)</code> 이런식으로 출력되는 이유이다.</p>
<p><code class="language-plaintext highlighter-rouge">subscribe(onNext:)</code>에서 <code class="language-plaintext highlighter-rouge">next</code> 이벤트 부분을 보면 <code class="language-plaintext highlighter-rouge">Self.Element</code>로 element 자체를 받고 있다.</p>
<hr />
<h1 id="creating-observables2">Creating observables(2)</h1>
<h2 id="empty">empty()</h2>
<p>element를 가지지 않는(갯수가 0인) Observable은 어떻게 만들 수 있을까.</p>
<p><code class="language-plaintext highlighter-rouge">empty</code> operator를 통해 만들 수 있고 element를 방출하지 않고 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트만 방출한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o"><</span><span class="kt">Void</span><span class="o">>.</span><span class="nf">empty</span><span class="p">()</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="n">completed</span>
</code></pre></div></div>
<p>위의 코드에서 보듯 <code class="language-plaintext highlighter-rouge">empty</code> operator는 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트만 방출한다.</p>
<p>empty observable은 어디에 쓰일까?</p>
<ul>
<li>의도적으로 0개의 값을 가지 observable을 반환하고 싶을 때</li>
<li>즉시 종료할 수 있는 observable을 반환하고 싶을 때</li>
</ul>
<p>사용할 수 있다고 한다.</p>
<h3 id="just-empty">just(), empty()</h3>
<p>그렇다면 <code class="language-plaintext highlighter-rouge">just</code>와 <code class="language-plaintext highlighter-rouge">empty</code> 구현부를 비교하면서 어떤 식으로 element를 반환하는지 알아보자.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">empty</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">EmptyProducer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">private</span> <span class="kd">class</span> <span class="kt">EmptyProducer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">:</span> <span class="kt">Producer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">override</span> <span class="kd">func</span> <span class="n">subscribe</span><span class="o"><</span><span class="kt">Observer</span><span class="p">:</span> <span class="kt">ObserverType</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">where</span> <span class="kt">Observer</span><span class="o">.</span><span class="kt">Element</span> <span class="o">==</span> <span class="kt">Element</span> <span class="p">{</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="n">completed</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>먼저 <code class="language-plaintext highlighter-rouge">empty</code>의 구현부이다.</p>
<p><code class="language-plaintext highlighter-rouge">empty</code> operator는 <code class="language-plaintext highlighter-rouge">EmptyProducer</code> 클래스 인스턴스를 반환한다.</p>
<p><code class="language-plaintext highlighter-rouge">EmptyProducer</code>의 <code class="language-plaintext highlighter-rouge">subscribe</code>를 보면 element 반환 없이 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트를 방출하고 종료되는걸 확인할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">just</span><span class="p">(</span><span class="n">_</span> <span class="nv">element</span><span class="p">:</span> <span class="kt">Element</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">Just</span><span class="p">(</span><span class="nv">element</span><span class="p">:</span> <span class="n">element</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">private</span> <span class="kd">class</span> <span class="kt">Just</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">:</span> <span class="kt">Producer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="kd">private</span> <span class="k">let</span> <span class="nv">_element</span><span class="p">:</span> <span class="kt">Element</span>
<span class="nf">init</span><span class="p">(</span><span class="nv">element</span><span class="p">:</span> <span class="kt">Element</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">_element</span> <span class="o">=</span> <span class="n">element</span>
<span class="p">}</span>
<span class="k">override</span> <span class="kd">func</span> <span class="n">subscribe</span><span class="o"><</span><span class="kt">Observer</span><span class="p">:</span> <span class="kt">ObserverType</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">where</span> <span class="kt">Observer</span><span class="o">.</span><span class="kt">Element</span> <span class="o">==</span> <span class="kt">Element</span> <span class="p">{</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="nf">next</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">_element</span><span class="p">))</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="n">completed</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">just</code>의 구현부이다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code> operator는 <code class="language-plaintext highlighter-rouge">Just</code> 클래스 인스턴스를 반환한다.</p>
<p><code class="language-plaintext highlighter-rouge">Just</code> 인스턴스를 생성할때 <code class="language-plaintext highlighter-rouge">just</code> operator로 입력된 값을 주입한다.</p>
<p><code class="language-plaintext highlighter-rouge">Just</code> 클래스의 <code class="language-plaintext highlighter-rouge">subscribe</code>를 보면 이벤트를 2번 방출하고 있다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">.next(self._element)</code> element 값을 주입한 <code class="language-plaintext highlighter-rouge">next</code> 이벤트를 방출한다.</li>
<li><code class="language-plaintext highlighter-rouge">.completed</code> 이벤트를 방출한다.</li>
</ul>
<p>(<code class="language-plaintext highlighter-rouge">just</code> operator는 단 하나의 element만 방출하고 종료되므로 <code class="language-plaintext highlighter-rouge">next</code> 이벤트는 한 번만 호출된다.)</p>
<h3 id="empty와-타입">empty와 타입</h3>
<p><code class="language-plaintext highlighter-rouge">empty</code> 예시 코드에 아무 element도 방출하지 않고 종료될텐데 <code class="language-plaintext highlighter-rouge">Observable<Void></code>로 왜 타입을 지정해주는지 궁금했다.</p>
<p>그래서 아래와 같이 실험을 해봤다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o"><</span><span class="kt">Void</span><span class="o">>.</span><span class="nf">empty</span><span class="p">()</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">empty</span><span class="p">()</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">completed</code>가 두 번 출력될 줄 알았는데 타입을 지정해준 Observable에서만 출력되었다.</p>
<p>책을 쭉 읽는데 <code class="language-plaintext highlighter-rouge">empty</code> operator를 사용할 때에는 Observable의 타입을 지정해줘야 한다고 한다.</p>
<p>앞에서 살펴본 <code class="language-plaintext highlighter-rouge">just</code>, <code class="language-plaintext highlighter-rouge">of</code>, <code class="language-plaintext highlighter-rouge">from</code>과 다르게 가지고 있는 element가 없어 타입 추론을 하지 못한다고 한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">empty</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">EmptyProducer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">empty</code> 구현부를 보면서 좀 이해할 수 있었다.</p>
<p><code class="language-plaintext highlighter-rouge">empty</code>의 반환 타입을 보면 <code class="language-plaintext highlighter-rouge">Observable<Element></code>로 타입이 필요하다.</p>
<p>흠…. 근데 어차피 아무런 element를 방출하지 않고 즉시 completed되는 Observable인데 Void 말고 다른 타입을 쓸 일이 있을까?</p>
<h2 id="never">never()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o"><</span><span class="kt">Void</span><span class="o">>.</span><span class="nf">never</span><span class="p">()</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">never</code> operator는 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트도 방출되지 않는다.</p>
<p>즉, 무한의 Observable을 반환한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">never</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">NeverProducer</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">private</span> <span class="kd">class</span> <span class="kt">NeverProducer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">:</span> <span class="kt">Producer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">override</span> <span class="kd">func</span> <span class="n">subscribe</span><span class="o"><</span><span class="kt">Observer</span><span class="p">:</span> <span class="kt">ObserverType</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">where</span> <span class="kt">Observer</span><span class="o">.</span><span class="kt">Element</span> <span class="o">==</span> <span class="kt">Element</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">never</code>의 구현부를 확인해보자.</p>
<p><code class="language-plaintext highlighter-rouge">NeverProducer</code> 클래스 인스턴스를 반환한다.</p>
<p><code class="language-plaintext highlighter-rouge">NeverProducer</code>의 <code class="language-plaintext highlighter-rouge">subscribe</code>를 확인해보면 어떤 element도 이벤트도 방출하지 않는걸 확인할 수 있다.</p>
<h2 id="range">range()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">range</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">count</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">9</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="n">completed</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">range</code> operator는 이름 그대로이다.</p>
<p><code class="language-plaintext highlighter-rouge">start</code>로 시작 지점을 설정하고 <code class="language-plaintext highlighter-rouge">count</code>로 방출할 갯수를 지정한다.</p>
<hr />
<h1 id="disposing-and-terminating">Disposing and terminating</h1>
<p>Observable은 subscription되기 전까지 아무런 동작도 하지 않는다.</p>
<p>즉, Observable은 subscribe되어야만 동작을 한다.</p>
<p>이 동작은 <code class="language-plaintext highlighter-rouge">error</code>나 <code class="language-plaintext highlighter-rouge">completed</code>로 종료되기 전까지 반복된다.</p>
<p>그렇다면 subscribe를 취소하여 Observable의 동작을 종료할 수 있지 않을까?</p>
<h2 id="dispose">dispose()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// subscribe 구현부</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">subscribe</span><span class="p">(</span><span class="n">_</span> <span class="nv">on</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Event</span><span class="o"><</span><span class="k">Self</span><span class="o">.</span><span class="kt">Element</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="o">-></span> <span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Disposable</span>
<span class="c1">// dispose() 관련 예제</span>
<span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"dispose"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">observable</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="s">"A"</span><span class="p">,</span> <span class="s">"B"</span><span class="p">,</span> <span class="s">"C"</span><span class="p">)</span>
<span class="c1">// 2</span>
<span class="k">let</span> <span class="nv">subscription</span> <span class="o">=</span> <span class="n">observable</span><span class="o">.</span><span class="nf">subscribe</span><span class="p">({</span> <span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="k">in</span>
<span class="c1">// 3</span>
<span class="nf">print</span><span class="p">(</span><span class="n">event</span><span class="p">)</span>
<span class="p">})</span>
<span class="n">subscription</span><span class="o">.</span><span class="nf">dispose</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Dispose() 관련 예제를 살펴보자.</p>
<ul>
<li>
<p>String Observable을 생성한다.</p>
</li>
<li>Observable을 subscribe한다. -> <code class="language-plaintext highlighter-rouge">subscribe()</code>는 <code class="language-plaintext highlighter-rouge">Disposable</code>을 반환한다.</li>
<li>방출된 이벤트들을 출력한다.</li>
</ul>
<p><code class="language-plaintext highlighter-rouge">dispose()</code>를 통해 subscribe를 취소할 수 있다.</p>
<p>subscribe를 취소하거나 dispose한 뒤에는 이벤트 방출이 정지된다.</p>
<p>(<code class="language-plaintext highlighter-rouge">dispose()</code>하면 completed가 방출된 후 종료된다.)</p>
<h2 id="disposebag">DisposeBag()</h2>
<p>각 subscription을 개별적으로 관리하는건 효율적이지 못해서 <code class="language-plaintext highlighter-rouge">DisposeBag</code>을 이용할 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">DisposeBag</code>은 (보통 <code class="language-plaintext highlighter-rouge">disposed(by:)</code>으로 추가된 ) disposables를 가지고 있다.</p>
<p>Dispose bag이 할당 해제되려고 할 때 각각 <code class="language-plaintext highlighter-rouge">dispose()</code>를 호출한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"DisposeBag"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">disposeBag</span> <span class="o">=</span> <span class="kt">DisposeBag</span><span class="p">()</span>
<span class="c1">// 2</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="s">"A"</span><span class="p">,</span> <span class="s">"B"</span><span class="p">,</span> <span class="s">"C"</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="c1">// 3</span>
<span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span> <span class="c1">// 4</span>
<span class="p">}</span>
</code></pre></div></div>
<p>DisposeBag() 관련 예제를 살펴보자.</p>
<p><code class="language-plaintext highlighter-rouge">dispose()</code> 예제와 크게 다르지 않다.</p>
<p><code class="language-plaintext highlighter-rouge">DisposeBag()</code> 인스턴스를 만들어주고 <code class="language-plaintext highlighter-rouge">subscribe</code>로 반환된 <code class="language-plaintext highlighter-rouge">Disposable</code>을 <code class="language-plaintext highlighter-rouge">disposed(by:)</code>로 <code class="language-plaintext highlighter-rouge">disposeBag</code>에 추가한다.</p>
<p><code class="language-plaintext highlighter-rouge">dispose bag</code>에서 subscription 추가를 잊거나 수동으로 <code class="language-plaintext highlighter-rouge">dispose()</code> 호출을 잊어버린다면 메모리 누수가 일어난다.</p>
<h2 id="disposable과-disposebag">Disposable과 DisposeBag</h2>
<p>위에서 살펴본 <code class="language-plaintext highlighter-rouge">Just</code> 구현부와 <code class="language-plaintext highlighter-rouge">DisposeBag</code> 구현부를 확인해보자.</p>
<p>(아직 완벽하게 구현부를 본건 아녀서 겉핥기식으로만 보자….)</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 예제</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
<span class="o">.</span><span class="n">subscribe</span> <span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="c1">// Just 구현부</span>
<span class="kd">final</span> <span class="kd">private</span> <span class="kd">class</span> <span class="kt">Just</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">:</span> <span class="kt">Producer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">override</span> <span class="kd">func</span> <span class="n">subscribe</span><span class="o"><</span><span class="kt">Observer</span><span class="p">:</span> <span class="kt">ObserverType</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">where</span> <span class="kt">Observer</span><span class="o">.</span><span class="kt">Element</span> <span class="o">==</span> <span class="kt">Element</span> <span class="p">{</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="nf">next</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">_element</span><span class="p">))</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="n">completed</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// disposed(by:)</span>
<span class="kd">extension</span> <span class="kt">Disposable</span> <span class="p">{</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">disposed</span><span class="p">(</span><span class="n">by</span> <span class="nv">bag</span><span class="p">:</span> <span class="kt">DisposeBag</span><span class="p">)</span> <span class="p">{</span>
<span class="n">bag</span><span class="o">.</span><span class="nf">insert</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// DisposeBag</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">DisposeBag</span><span class="p">:</span> <span class="kt">DisposeBase</span> <span class="p">{</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_disposables</span> <span class="o">=</span> <span class="p">[</span><span class="kt">Disposable</span><span class="p">]()</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">insert</span><span class="p">(</span><span class="n">_</span> <span class="nv">disposable</span><span class="p">:</span> <span class="kt">Disposable</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="nf">_insert</span><span class="p">(</span><span class="n">disposable</span><span class="p">)?</span><span class="o">.</span><span class="nf">dispose</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">_insert</span><span class="p">(</span><span class="n">_</span> <span class="nv">disposable</span><span class="p">:</span> <span class="kt">Disposable</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span><span class="p">?</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">_lock</span><span class="o">.</span><span class="nf">lock</span><span class="p">();</span> <span class="k">defer</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">_lock</span><span class="o">.</span><span class="nf">unlock</span><span class="p">()</span> <span class="p">}</span>
<span class="k">if</span> <span class="k">self</span><span class="o">.</span><span class="n">_isDisposed</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">disposable</span>
<span class="p">}</span>
<span class="k">self</span><span class="o">.</span><span class="n">_disposables</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">disposable</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="c1">/// This is internal on purpose, take a look at `CompositeDisposable` instead.</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">dispose</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">oldDisposables</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="nf">_dispose</span><span class="p">()</span>
<span class="k">for</span> <span class="n">disposable</span> <span class="k">in</span> <span class="n">oldDisposables</span> <span class="p">{</span>
<span class="n">disposable</span><span class="o">.</span><span class="nf">dispose</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Just</code>의 <code class="language-plaintext highlighter-rouge">subscribe</code>를 보면 ` Disposable`을 반환하고 있다.</p>
<p>-> 예제에서 <code class="language-plaintext highlighter-rouge">.subscribe</code>에서 ` Disposable`을 얻을 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">Disposable</code>에 <code class="language-plaintext highlighter-rouge">disposed(by:)</code>가 구현되어 있다.</p>
<p>-> <code class="language-plaintext highlighter-rouge">.subscribe</code>에서 반환된 <code class="language-plaintext highlighter-rouge">Disposable</code>에 <code class="language-plaintext highlighter-rouge">disposed(by:)</code>를 호출한다.</p>
<p><code class="language-plaintext highlighter-rouge">disposed(by:)</code>를 통해 입력 받은 <code class="language-plaintext highlighter-rouge">DisposeBag</code>에 <code class="language-plaintext highlighter-rouge">Disposable</code> 자체를 넣어준다.</p>
<p><code class="language-plaintext highlighter-rouge">DisposeBag</code>에는 <code class="language-plaintext highlighter-rouge">Disposable</code> 배열 프로퍼티가 존재한다.</p>
<p>-> <code class="language-plaintext highlighter-rouge">disposed(by:)</code>는 해당 배열에 Disposable을 넣는다.</p>
<p>배열에 들어간 <code class="language-plaintext highlighter-rouge">Disposable</code>을 메모리 해제한다.</p>
<hr />
<h1 id="creating-observables3">Creating observables(3)</h1>
<h2 id="create">create()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="n">create</span> <span class="p">{</span> <span class="p">(</span><span class="n">observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">in</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="mi">3</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">create</code>는 탈출 클로저로 <code class="language-plaintext highlighter-rouge">AnyObserver -> Disposable</code> 형태이다.</p>
<p><code class="language-plaintext highlighter-rouge">AnyObserver</code>는 제네릭 타입으로 Observable sequence에 값을 추가할 수 있다.</p>
<p>추가한 값은 subscriber에 방출된다.</p>
<p><code class="language-plaintext highlighter-rouge">.onNext</code>는 <code class="language-plaintext highlighter-rouge">on(.next(_:))</code>와 같은 의미이다.</p>
<p><code class="language-plaintext highlighter-rouge">onCompleted()</code>는 <code class="language-plaintext highlighter-rouge">on(.completed)</code>와 같은 의미이다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="n">create</span> <span class="p">{</span> <span class="p">(</span><span class="n">observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">in</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onCompleted</span><span class="p">()</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span>
<span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onError</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onCompleted</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Completed"</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onDisposed</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Disposed"</span><span class="p">)</span> <span class="p">}</span>
<span class="p">)</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="kt">Completed</span>
<span class="kt">Disposed</span>
</code></pre></div></div>
<p>위의 코드를 확인해보자.</p>
<p><code class="language-plaintext highlighter-rouge">onNext</code>로 1, 2, 3을 방출했지만 2까지만 나온다.</p>
<p><code class="language-plaintext highlighter-rouge">completed</code> 이벤트가 발생하여 dispose되었기 때문에 3은 방출되지 않는다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">TestError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">error</span>
<span class="p">}</span>
<span class="kt">Observable</span><span class="o">.</span><span class="n">create</span> <span class="p">{</span> <span class="p">(</span><span class="n">observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">in</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onError</span><span class="p">(</span><span class="kt">TestError</span><span class="o">.</span><span class="n">error</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onCompleted</span><span class="p">()</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span>
<span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onError</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onCompleted</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Completed"</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onDisposed</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Disposed"</span><span class="p">)</span> <span class="p">}</span>
<span class="p">)</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="n">error</span>
<span class="kt">Disposed</span>
</code></pre></div></div>
<p>위의 예시와 동일하게 2까지만 출력된다.</p>
<p><code class="language-plaintext highlighter-rouge">error</code> 이벤트가 발생한 후 dispose되었기 때문에 뒤의 다른 이벤트들은 방출되지 않는다.</p>
<hr />
<h1 id="traits">Traits</h1>
<p><code class="language-plaintext highlighter-rouge">Trait</code>은 일반적인 Observable보다 좁은 범위의 Observable로 선택적으로 사용할 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">Trait</code>을 사용해 가독성을 높일 수 있다.</p>
<h2 id="single">Single</h2>
<p><code class="language-plaintext highlighter-rouge">Single</code>은 <code class="language-plaintext highlighter-rouge">success(value)</code> 또는 ` error(error)` 이벤트를 방출한다.</p>
<p><code class="language-plaintext highlighter-rouge">success(value)</code>는 <code class="language-plaintext highlighter-rouge">next</code> 이벤트와 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트의 조합니다.</p>
<p>성공 혹은 실패로 확인 가능한 프로세스에 사용한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">typealias</span> <span class="kt">Single</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="o">=</span> <span class="kt">PrimitiveSequence</span><span class="o"><</span><span class="kt">SingleTrait</span><span class="p">,</span> <span class="kt">Element</span><span class="o">></span>
<span class="kd">public</span> <span class="kd">enum</span> <span class="kt">SingleEvent</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">success</span><span class="p">(</span><span class="kt">Element</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">error</span><span class="p">(</span><span class="kt">Swift</span><span class="o">.</span><span class="kt">Error</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">PrimitiveSequenceType</span> <span class="k">where</span> <span class="kt">Trait</span> <span class="o">==</span> <span class="kt">SingleTrait</span> <span class="p">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">create</span><span class="p">(</span><span class="nv">subscribe</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kd">@escaping</span> <span class="kt">SingleObserver</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Single</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">source</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">>.</span><span class="n">create</span> <span class="p">{</span> <span class="n">observer</span> <span class="k">in</span>
<span class="k">return</span> <span class="n">subscribe</span> <span class="p">{</span> <span class="n">event</span> <span class="k">in</span>
<span class="k">switch</span> <span class="n">event</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="k">let</span> <span class="nv">element</span><span class="p">):</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="nf">next</span><span class="p">(</span><span class="n">element</span><span class="p">))</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="n">completed</span><span class="p">)</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="n">error</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kt">PrimitiveSequence</span><span class="p">(</span><span class="nv">raw</span><span class="p">:</span> <span class="n">source</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Single</code>의 구현부를 봐보자.</p>
<p><code class="language-plaintext highlighter-rouge">SingleEvent</code>로 <code class="language-plaintext highlighter-rouge">success(value)</code>, <code class="language-plaintext highlighter-rouge">error(error)</code>가 있음을 알 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">primitiveSequenceType</code>을 확인해보면 <code class="language-plaintext highlighter-rouge">success</code>는 <code class="language-plaintext highlighter-rouge">next</code> 이벤트로 element를 방출한 뒤 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트를 방출한다.</p>
<p>(생긴게 좀 Result랑 비슷하게 생겼다….)</p>
<h2 id="completable">Completable</h2>
<p><code class="language-plaintext highlighter-rouge">Completable</code>은 <code class="language-plaintext highlighter-rouge">completed</code> 혹은 ` error(error)` 이벤트만 방출한다.</p>
<p>즉, 어떠한 값도 방출하지 않는다.</p>
<p>파일 쓰기 같은 작업이 성공적으로 완료 혹은 실패인지에만 관심 있을 경우 사용한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">typealias</span> <span class="kt">Completable</span> <span class="o">=</span> <span class="kt">PrimitiveSequence</span><span class="o"><</span><span class="kt">CompletableTrait</span><span class="p">,</span> <span class="kt">Swift</span><span class="o">.</span><span class="kt">Never</span><span class="o">></span>
<span class="kd">public</span> <span class="kd">enum</span> <span class="kt">CompletableEvent</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">error</span><span class="p">(</span><span class="kt">Swift</span><span class="o">.</span><span class="kt">Error</span><span class="p">)</span>
<span class="k">case</span> <span class="n">completed</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Completable</code>의 구현부를 봐보자.</p>
<p><code class="language-plaintext highlighter-rouge">CompletableEvent</code>로 <code class="language-plaintext highlighter-rouge">error(error)</code>, <code class="language-plaintext highlighter-rouge">completed</code>가 있음을 알 수 있다.</p>
<h2 id="maybe">Maybe</h2>
<p><code class="language-plaintext highlighter-rouge">Maybe</code>는 <code class="language-plaintext highlighter-rouge">Single</code>과 <code class="language-plaintext highlighter-rouge">Completable</code>을 섞은거와 같다.</p>
<p><code class="language-plaintext highlighter-rouge">success(value)</code>, <code class="language-plaintext highlighter-rouge">completed</code>, <code class="language-plaintext highlighter-rouge">error(error)</code>를 방출할 수 있다.</p>
<p>성공, 실패 여부와 더해서 출력된 값도 필요할 수도 있을때 사용한다.</p>
<h3 id="사용법">사용법</h3>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"Single"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">disposeBag</span> <span class="o">=</span> <span class="kt">DisposeBag</span><span class="p">()</span>
<span class="c1">// 2</span>
<span class="kd">enum</span> <span class="kt">FileReadError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">fileNotFound</span><span class="p">,</span> <span class="n">unreadable</span>
<span class="p">}</span>
<span class="c1">// 3</span>
<span class="kd">func</span> <span class="nf">loadText</span><span class="p">(</span><span class="n">from</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Single</span><span class="o"><</span><span class="kt">String</span><span class="o">></span> <span class="p">{</span>
<span class="c1">// 4</span>
<span class="k">return</span> <span class="kt">Single</span><span class="o">.</span><span class="n">create</span><span class="p">{</span> <span class="n">single</span> <span class="k">in</span>
<span class="c1">// 4 - 1</span>
<span class="k">let</span> <span class="nv">disposable</span> <span class="o">=</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="c1">// 4 - 2</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">path</span> <span class="o">=</span> <span class="kt">Bundle</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="nf">path</span><span class="p">(</span><span class="nv">forResource</span><span class="p">:</span> <span class="n">name</span><span class="p">,</span> <span class="nv">ofType</span><span class="p">:</span> <span class="s">"txt"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">single</span><span class="p">(</span><span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="kt">FileReadError</span><span class="o">.</span><span class="n">fileNotFound</span><span class="p">))</span>
<span class="k">return</span> <span class="n">disposable</span>
<span class="p">}</span>
<span class="c1">// 4 - 3</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="kt">FileManager</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">contents</span><span class="p">(</span><span class="nv">atPath</span><span class="p">:</span> <span class="n">path</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">single</span><span class="p">(</span><span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="kt">FileReadError</span><span class="o">.</span><span class="n">unreadable</span><span class="p">))</span>
<span class="k">return</span> <span class="n">disposable</span>
<span class="p">}</span>
<span class="c1">// 4 - 4</span>
<span class="nf">single</span><span class="p">(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">contents</span><span class="p">))</span>
<span class="k">return</span> <span class="n">disposable</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>1: dispose bag 생성</p>
<p>2: Error 정의</p>
<p>3: 디스크 파일로부터 텍스트 불러와서 <code class="language-plaintext highlighter-rouge">Single</code>을 반환하는 메서드</p>
<p>4: <code class="language-plaintext highlighter-rouge">Single</code>을 생성하고 반환</p>
<p> 4-1: <code class="language-plaintext highlighter-rouge">create</code>의 <code class="language-plaintext highlighter-rouge">subscribe</code> 클로저는 <code class="language-plaintext highlighter-rouge">disposable</code>을 반환하므로 <code class="language-plaintext highlighter-rouge">disposable</code> 생성</p>
<p> 4-2: 파일명 경로를 받아오고 파일이 없다면 <code class="language-plaintext highlighter-rouge">single</code>에 <code class="language-plaintext highlighter-rouge">error</code>를 추가하고 <code class="language-plaintext highlighter-rouge">disposable</code> 반환</p>
<p> 4-3: 파일로부터 데이터를 받아오고 파일을 읽을 수 없다면 <code class="language-plaintext highlighter-rouge">single</code>에 <code class="language-plaintext highlighter-rouge">error</code>를 추가하고 <code class="language-plaintext highlighter-rouge">disposable</code> 반환</p>
<p> 4-4: 원하는 동작을 수행했으니 <code class="language-plaintext highlighter-rouge">single</code>에 <code class="language-plaintext highlighter-rouge">success(value)</code>를 추가하고 <code class="language-plaintext highlighter-rouge">disposable</code> 반환</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">loadText</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="s">"Copyright"</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span>
<span class="k">switch</span> <span class="nv">$0</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="k">let</span> <span class="nv">string</span><span class="p">):</span>
<span class="nf">print</span><span class="p">(</span><span class="n">string</span><span class="p">)</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span>
<span class="nf">print</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
</code></pre></div></div>
<p>이런 식으로 사용할 수 있다.</p>
<p>볼수록 Result 같다…</p>
<hr />
<h1 id="outro">Outro</h1>
<p>Observable에 대해서 알아봤다.</p>
<p>내용이 상당히 많다.</p>
<p>이후에 더 볼 내용들이 있지만 우선은 여기서 마무리하고 이후에 다시 정리해야지…</p>lasagnaIntroRxSwift - ch.2 Observables2021-10-25T08:00:00+09:002021-10-25T08:00:00+09:00https://wonhee009.github.io/rxswift/RxSwift_ch.2_Observables<h1 id="intro">Intro</h1>
<p>여기서는 <strong>Observable</strong>에 대해서 알아보자.</p>
<p>Observable은 이벤트를 방출하는 시퀀스 정도로 생각하는 중인데 맞을런지…?</p>
<hr />
<h1 id="what-is-an-observables">What is an Observables?</h1>
<p>Observable은 Rx의 핵심이다.</p>
<p>Rx를 보다보면 <code class="language-plaintext highlighter-rouge">Observable</code>, <code class="language-plaintext highlighter-rouge">Observable sequence</code>, <code class="language-plaintext highlighter-rouge">sequence</code>가 등장하는데 이는 모두 같은 것이다.</p>
<p>Observable의 가장 중요한 점은 <strong>비동기적(asynchronous)</strong>이라는 것이다.</p>
<p>Observable은 일정 기간 동안 <strong>이벤트를 생성하고 이를 방출(emit)</strong>한다.</p>
<p>이벤트는 숫자나 사용자가 정의한 타입 인스턴스와 같은 값을 포하고, tap과 같은 제스처도 인식할 수 있다.</p>
<p>이를 가장 잘 보여주는 건 marble diagram이다.</p>
<p><a href="https://rxmarbles.com">marble diagram을 볼 수 있는 사이트</a></p>
<h2 id="lifecycle-of-an-observable">Lifecycle of an observable</h2>
<p><img src="/assets/images/rxSwift_1.png" alt="rxSwift_1" /></p>
<p>위의 marble diagram을 보면 <strong>observable</strong>이 세개의 이벤트를 방출하고 있다.</p>
<p>이 이벤트들은 <strong>next 이벤트</strong>에 의해서 방출된다.</p>
<p><br /></p>
<p><img src="/assets/images/rxSwift_2.png" alt="rxSwift_2" /></p>
<p>위의 marble diagram처럼 오른쪽의 <code class="language-plaintext highlighter-rouge">|</code>수직 막대는 이벤트 종료를 의미한다.</p>
<p><strong>observable</strong>은 3개의 tap 이벤트를 발생시킨 후 종료되었다.</p>
<p>이를 <strong>완료(completed) 이벤트</strong>라고 한다.</p>
<p><strong>observable</strong>은 완료 이벤트가 발생하면 더 이상 아무 것도 방출할 수 없다.</p>
<p><br /></p>
<p><img src="/assets/images/rxSwift_3.png" alt="rxSwift_3" /></p>
<p>빨간색 <code class="language-plaintext highlighter-rouge">X</code> 표시는 에러를 의미한다.</p>
<p><strong>Observable</strong>이 종료된 것은 위와 동일하지만 <strong>error 이벤트</strong>에 의해서 종료되었다.</p>
<ul>
<li>Observable은 어떤 element를 가지는 <strong>next 이벤트</strong>를 방출한다.</li>
<li>종료 이벤트(<strong>error 이벤트</strong> 혹은 <strong>completed 이벤트</strong>)가 발생할 때까지 위의 작업이 계속 될 수 있다.</li>
<li>Observable이 종료되면 더이상 이벤트를 생성할 수 없다.</li>
</ul>
<h3 id="event">Event</h3>
<p>이벤트는 <strong>Enum</strong>으로 표현된다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// Represents a sequence event.</span>
<span class="c1">///</span>
<span class="c1">/// Sequence grammar: </span>
<span class="c1">/// **next\* (error | completed)**</span>
<span class="kd">@frozen</span> <span class="kd">public</span> <span class="kd">enum</span> <span class="kt">Event</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="c1">/// Next element is produced.</span>
<span class="k">case</span> <span class="nf">next</span><span class="p">(</span><span class="kt">Element</span><span class="p">)</span>
<span class="c1">/// Sequence terminated with an error.</span>
<span class="k">case</span> <span class="nf">error</span><span class="p">(</span><span class="kt">Swift</span><span class="o">.</span><span class="kt">Error</span><span class="p">)</span>
<span class="c1">/// Sequence completed successfully.</span>
<span class="k">case</span> <span class="n">completed</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li><strong>next 이벤트</strong>에는 <code class="language-plaintext highlighter-rouge">Element</code> 인스턴스가 포함되어 있다.</li>
<li><strong>error 이벤트</strong>에는 Swift.Error 인스턴스가 포함되어 있다.</li>
<li><strong>error 이벤트와 completed 이벤트는</strong> 데이터가 포함되어 있지 않고 단순히 중지하는 이벤트이다.</li>
</ul>
<hr />
<h1 id="creating-observables1">Creating observables(1)</h1>
<h2 id="just">just</h2>
<p>(이번 챕터의 예제는 플레이그라운드를 사용했다.)</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"just, of, from"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">one</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">let</span> <span class="nv">two</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">three</span> <span class="o">=</span> <span class="mi">3</span>
<span class="c1">//2</span>
<span class="k">let</span> <span class="nv">observable</span><span class="p">:</span><span class="kt">Observable</span><span class="o"><</span><span class="kt">Int</span><span class="o">></span> <span class="o">=</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Int</span><span class="o">>.</span><span class="nf">just</span><span class="p">(</span><span class="n">one</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<ol>
<li>예제에서 사용할 Int 상수를 정의했다.</li>
<li><code class="language-plaintext highlighter-rouge">one</code> 상수와 <code class="language-plaintext highlighter-rouge">just</code> 메서드로 <code class="language-plaintext highlighter-rouge">Int</code> 타입의 <strong>Observable sequence</strong>를 만들었다.</li>
</ol>
<p><code class="language-plaintext highlighter-rouge">just</code>는 Observable의 타입 메서드이다.</p>
<p>단 하나의 Element를 포함하는 Observable sequence를 생성한다.</p>
<p>Rx에서는 메서드를 <code class="language-plaintext highlighter-rouge">operator</code>라고 한다. (즉, <code class="language-plaintext highlighter-rouge">just</code>도 operator이다.)</p>
<p><br /></p>
<p><img src="/assets/images/rxSwift_4.png" alt="rxSwift_4" /></p>
<p><a href="http://reactivex.io/documentation/ko/operators/just.html">출처: rx홈페이지-just</a></p>
<p>rx문서로 <code class="language-plaintext highlighter-rouge">just</code> 정의에 대해 좀 더 알아보자.</p>
<blockquote>
<p><code class="language-plaintext highlighter-rouge">just</code>는 Element를 해당 Element 타입을 방출하는 <strong>Observable로 변환</strong>하는 operator이다.</p>
</blockquote>
<p>이렇게만 보면 이해가 잘 안 간다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">just</span><span class="p">(</span><span class="n">_</span> <span class="nv">element</span><span class="p">:</span> <span class="kt">Element</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="kt">Just</span><span class="p">(</span><span class="nv">element</span><span class="p">:</span> <span class="n">element</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">just</code> operator는 위처럼 구성되어 있다.</p>
<p><code class="language-plaintext highlighter-rouge">static</code>으로 정의되어 있으므로 <strong>타입 메서드</strong>임을 알 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code>의 파라미터 타입과 return 타입을 보면 위의 정의를 이해할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o"><</span><span class="kt">Int</span><span class="o">>.</span><span class="nf">just</span><span class="p">(</span><span class="n">one</span><span class="p">)</span>
</code></pre></div></div>
<p>위의 예시에 적용해 본다면 <code class="language-plaintext highlighter-rouge">just</code> operator는 Int 값을 Int 타입의 <strong>이벤트를 방출하는 Observable로 변환</strong> 해준다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">just</code>에 nil을 전달하면 nil을 Element로 방출하는 Observable이 반환된다.</p>
<p>이는 빈 Observable을 반환하는게 아니다.</p>
<p>(마치 nil과 ““의 차이 같네…)</p>
<p>빈 Observable을 반환하고 싶다면 <code class="language-plaintext highlighter-rouge">Empty</code> operator를 사용할 수 있다.</p>
<h2 id="of">of</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"just, of, from"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">one</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">let</span> <span class="nv">two</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">three</span> <span class="o">=</span> <span class="mi">3</span>
<span class="c1">//2</span>
<span class="k">let</span> <span class="nv">observable</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="n">one</span><span class="p">,</span> <span class="n">two</span><span class="p">,</span> <span class="n">three</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">observable2</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">([</span><span class="n">one</span><span class="p">,</span> <span class="n">two</span><span class="p">,</span> <span class="n">three</span><span class="p">])</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>observable의 타입은 <code class="language-plaintext highlighter-rouge">Observable<Int></code></li>
<li>observable2의 타입은 <code class="language-plaintext highlighter-rouge">Observable<[Int]></code></li>
</ul>
<p><code class="language-plaintext highlighter-rouge">of</code> operator는 주어진 값들의 타입추론을 통해 <strong>Observable sequence</strong>를 생성한다.</p>
<p>어떤 배열을 Observable array로 만들고 싶다면 <code class="language-plaintext highlighter-rouge">of</code> operator에 배열을 넣어주면 된다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="s">"😀 of operator: Observable<Int>"</span><span class="p">)</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="n">array</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
<span class="p">})</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"😀 of operator: Observable<[Int]>"</span><span class="p">)</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">])</span><span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="n">array</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
<span class="p">})</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="n">😀</span> <span class="n">of</span> <span class="nv">operator</span><span class="p">:</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Int</span><span class="o">></span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="mi">3</span>
<span class="n">😀</span> <span class="n">of</span> <span class="nv">operator</span><span class="p">:</span> <span class="kt">Observable</span><span class="o"><</span><span class="p">[</span><span class="kt">Int</span><span class="p">]</span><span class="o">></span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</code></pre></div></div>
<p>위의 코드에서 보이듯 Int 타입의 Element를 <code class="language-plaintext highlighter-rouge">of</code> operator에 넣어주면 <code class="language-plaintext highlighter-rouge">1 2 3</code>으로 Int타입의 시퀀스가 방출된다.</p>
<p>Int 타입의 배열을 <code class="language-plaintext highlighter-rouge">of</code> operator에 넣어주면 배열 자체의 시퀀스가 방출된다.</p>
<h3 id="just-of">just? of?</h3>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">])</span><span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="n">array</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
<span class="p">})</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">just</code> operator에 Int 배열을 넣어줘도 동일한 결과를 얻을 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code>,<code class="language-plaintext highlighter-rouge">of</code> 모두 반환 타입도 <code class="language-plaintext highlighter-rouge">Observable<[Int]></code>로 동일하다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">just</code> 구현부에 가보면 파라미터에 아래와 같이 작성되어 있다.</p>
<blockquote>
<p>Single element in the resulting observable sequence.</p>
</blockquote>
<p>파라미터는 observable sequence로 만들고 싶은 <strong>single element</strong>이다.</p>
<p><br /></p>
<p>아직 이 부분은 명확하지 않은거 같다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code> 는 단일 값을 방출할때 사용하는건 이해하겠는데 단일 값을 어디까지로 봐야할까?</p>
<p>[1, 2, 3] <- 이런 배열도 하나의 배열로써 단일 값을 볼 수 있지 않을까? 라는 생각이 든다.</p>
<h2 id="from">from</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"just, of, from"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">one</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">let</span> <span class="nv">two</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">three</span> <span class="o">=</span> <span class="mi">3</span>
<span class="c1">//2</span>
<span class="k">let</span> <span class="nv">observable</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">from</span><span class="p">([</span><span class="n">one</span><span class="p">,</span> <span class="n">two</span><span class="p">,</span> <span class="n">three</span><span class="p">])</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>observable의 타입은 <code class="language-plaintext highlighter-rouge">Observable<Int></code></li>
</ul>
<p><code class="language-plaintext highlighter-rouge">from</code> operator는 배열 요소들을 하나씩 방출한다.</p>
<p><code class="language-plaintext highlighter-rouge">from</code>은 배열만 입력할 수 있다.</p>
<p><br /></p>
<p><img src="/assets/images/rxSwift_5.png" alt="rxSwift_5" /></p>
<p><a href="http://reactivex.io/documentation/operators/from.html">출처: rx홈페이지-from</a></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">from</span><span class="p">(</span><span class="n">_</span> <span class="nv">array</span><span class="p">:</span> <span class="p">[</span><span class="kt">Element</span><span class="p">],</span> <span class="nv">scheduler</span><span class="p">:</span> <span class="kt">ImmediateSchedulerType</span> <span class="o">=</span> <span class="kt">CurrentThreadScheduler</span><span class="o">.</span><span class="n">instance</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="kt">ObservableSequence</span><span class="p">(</span><span class="nv">elements</span><span class="p">:</span> <span class="n">array</span><span class="p">,</span> <span class="nv">scheduler</span><span class="p">:</span> <span class="n">scheduler</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">from</code> operator는 위와 같이 구성되어 있다.</p>
<p>파라미터 타입을 보면 배열만을 입력 받는걸 알 수 있다.</p>
<hr />
<h1 id="subscribing-to-observables">Subscribing to observables</h1>
<p>iOS 개발자라면 <code class="language-plaintext highlighter-rouge">NotificationCenter</code>에 익숙할 것이다.(observer에게 알림을 브로드캐스팅)</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">let</span> <span class="nv">observer</span> <span class="o">=</span> <span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span>
<span class="nv">forName</span><span class="p">:</span> <span class="o">.</span><span class="kt">UIKeyboardDidChangeFrame</span><span class="p">,</span>
<span class="nv">object</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span>
<span class="nv">queue</span><span class="p">:</span> <span class="kc">nil</span>
<span class="p">)</span> <span class="p">{</span> <span class="n">notification</span> <span class="k">in</span>
<span class="c1">// Handle receiving notification</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위의 코드는 클로저로 <code class="language-plaintext highlighter-rouge">UIKeyboardDidChangeFrame</code> notification observer 코드이다.</p>
<p><br /></p>
<p>RxSwift의 observable을 subscribing하는 방식은 위와 비슷하다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">addObserver()</code> 대신 <code class="language-plaintext highlighter-rouge">subscribe()</code>를 사용한다.</li>
<li><code class="language-plaintext highlighter-rouge">NotificationCenter</code>는 <code class="language-plaintext highlighter-rouge">.default</code> 싱글톤 인스턴스에서만 가능했지만, Rx Observable은 다르다.</li>
<li>핵심은 Observable은 <strong>subscriber가 있어야만 이벤트를 방출하거나 작업을 수행</strong>한다.</li>
</ul>
<p><strong>Observable은 sequence의 정의일 뿐</strong>이며 Observable이 subscriber되어야만 이후의 스텝이 진행된다.</p>
<p><br /></p>
<p>Observable의 구현은 Swift 반복문에서 <code class="language-plaintext highlighter-rouge">.next()</code>를 구현하는 것과 비슷하다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">let</span> <span class="nv">sequence</span> <span class="o">=</span> <span class="mi">0</span><span class="o">..<</span><span class="mi">3</span>
<span class="k">var</span> <span class="nv">iterator</span> <span class="o">=</span> <span class="n">sequence</span><span class="o">.</span><span class="nf">makeIterater</span><span class="p">()</span>
<span class="k">while</span> <span class="k">let</span> <span class="nv">n</span> <span class="o">=</span> <span class="n">iterator</span><span class="o">.</span><span class="nf">next</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="p">}</span>
<span class="cm">/* Prints:
0
1
2
*/</span>
</code></pre></div></div>
<p>Observable subscriber는 이보다 쉽다.</p>
<p>Observable이 <strong>방출하는 이벤트 타입에 대해 handler를 추가</strong>할 수 있다.</p>
<p>–> <code class="language-plaintext highlighter-rouge">.subscribe(onNext:_, onError:_, onCompleted:_)</code>로 이벤트 타입별로 핸들러 추가 가능하다.</p>
<p>Observable은 <code class="language-plaintext highlighter-rouge">next</code>, <code class="language-plaintext highlighter-rouge">error</code>, <code class="language-plaintext highlighter-rouge">completed</code> 이벤트를 방출한다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">next</code>: 핸들러로 방출된 Element를 보낸다.</li>
<li><code class="language-plaintext highlighter-rouge">error</code>: error 인스턴스가 포함된다.</li>
</ul>
<h2 id="subscribe">subscribe()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span> <span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="n">completed</span>
</code></pre></div></div>
<p>Observable은 각각의 Element에 대해서 <code class="language-plaintext highlighter-rouge">next</code> 이벤트를 방출한다.</p>
<p>종료 직전 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트를 방출한다.</p>
<p><br /></p>
<p>이벤트로 감싸진 Element가 아니라 직접 Element에도 접근할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span> <span class="p">{</span> <span class="n">event</span> <span class="k">in</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">element</span> <span class="o">=</span> <span class="n">event</span><span class="o">.</span><span class="n">element</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="mi">3</span>
</code></pre></div></div>
<p><br /></p>
<p>Event 구현부를 보면 아래와 같이 <code class="language-plaintext highlighter-rouge">element</code> 프로퍼티를 확인할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="k">var</span> <span class="nv">element</span><span class="p">:</span> <span class="kt">Element</span><span class="p">?</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">case</span> <span class="o">.</span><span class="nf">next</span><span class="p">(</span><span class="k">let</span> <span class="nv">value</span><span class="p">)</span> <span class="o">=</span> <span class="k">self</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">value</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">next</code> 이벤트에만 <code class="language-plaintext highlighter-rouge">element</code>가 존재하고 옵셔널이다.</p>
<h2 id="subscribeonnext">subscribe(onNext:)</h2>
<p>위에서 살펴본 <code class="language-plaintext highlighter-rouge">subscribe()</code>에 대해서 더 알아보자.</p>
<p>RxSwift에는 Observable에서 방출하는 각 이벤트 유형에 subscribe operator가 존재한다.</p>
<p><br /></p>
<p>위에서 살펴본 코드는 아래와 같이 작성할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span>
<span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">next</code> 이벤트만 처리하고 나머지(<code class="language-plaintext highlighter-rouge">error</code>, <code class="language-plaintext highlighter-rouge">completed</code> 이벤트)에 대해서는 무시한다.</p>
<p><code class="language-plaintext highlighter-rouge">onNext</code> 클로저는 <code class="language-plaintext highlighter-rouge">next</code> 이벤트의 element를 받으므로 위의 코드처럼 element를 따로 뽑지 않아도 된다.</p>
<h3 id="subscribe-subscribeonnext">subscribe()? Subscribe(onNext:)</h3>
<p>위에서 알아본 바와 두 메서드 모두 <code class="language-plaintext highlighter-rouge">next</code> 이벤트의 element를 사용할 수 있다.</p>
<p>그렇다면 둘은 어떤 차이가 있을까?</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">func</span> <span class="nf">subscribe</span><span class="p">(</span><span class="n">_</span> <span class="nv">on</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Event</span><span class="o"><</span><span class="k">Self</span><span class="o">.</span><span class="kt">Element</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="o">-></span> <span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Disposable</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">((</span><span class="k">Self</span><span class="o">.</span><span class="kt">Element</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">onError</span><span class="p">:</span> <span class="p">((</span><span class="kt">Error</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">onCompleted</span><span class="p">:</span> <span class="p">(()</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">onDisposed</span><span class="p">:</span> <span class="p">(()</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">)</span> <span class="o">-></span> <span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Disposable</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">subscribe()</code>의 클로저 입력 타입은 <code class="language-plaintext highlighter-rouge">RxSwift.Event<Self.Element></code>로 element가 이벤트로 감싸져 있다.</p>
<p>-> <code class="language-plaintext highlighter-rouge">subscribe()</code> 예제에서 <code class="language-plaintext highlighter-rouge">next(1)</code> 이런식으로 출력되는 이유이다.</p>
<p><code class="language-plaintext highlighter-rouge">subscribe(onNext:)</code>에서 <code class="language-plaintext highlighter-rouge">next</code> 이벤트 부분을 보면 <code class="language-plaintext highlighter-rouge">Self.Element</code>로 element 자체를 받고 있다.</p>
<hr />
<h1 id="creating-observables2">Creating observables(2)</h1>
<h2 id="empty">empty()</h2>
<p>element를 가지지 않는(갯수가 0인) Observable은 어떻게 만들 수 있을까.</p>
<p><code class="language-plaintext highlighter-rouge">empty</code> operator를 통해 만들 수 있고 element를 방출하지 않고 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트만 방출한다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o"><</span><span class="kt">Void</span><span class="o">>.</span><span class="nf">empty</span><span class="p">()</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="n">completed</span>
</code></pre></div></div>
<p>위의 코드에서 보듯 <code class="language-plaintext highlighter-rouge">empty</code> operator는 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트만 방출한다.</p>
<p><br /></p>
<p>empty observable은 어디에 쓰일까?</p>
<ul>
<li>의도적으로 0개의 값을 가지 observable을 반환하고 싶을 때</li>
<li>즉시 종료할 수 있는 observable을 반환하고 싶을 때</li>
</ul>
<p>사용할 수 있다고 한다.</p>
<h3 id="just-empty">just(), empty()</h3>
<p>그렇다면 <code class="language-plaintext highlighter-rouge">just</code>와 <code class="language-plaintext highlighter-rouge">empty</code> 구현부를 비교하면서 어떤 식으로 element를 반환하는지 알아보자.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">empty</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">EmptyProducer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">private</span> <span class="kd">class</span> <span class="kt">EmptyProducer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">:</span> <span class="kt">Producer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">override</span> <span class="kd">func</span> <span class="n">subscribe</span><span class="o"><</span><span class="kt">Observer</span><span class="p">:</span> <span class="kt">ObserverType</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">where</span> <span class="kt">Observer</span><span class="o">.</span><span class="kt">Element</span> <span class="o">==</span> <span class="kt">Element</span> <span class="p">{</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="n">completed</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>먼저 <code class="language-plaintext highlighter-rouge">empty</code>의 구현부이다.</p>
<p><code class="language-plaintext highlighter-rouge">empty</code> operator는 <code class="language-plaintext highlighter-rouge">EmptyProducer</code> 클래스 인스턴스를 반환한다.</p>
<p><code class="language-plaintext highlighter-rouge">EmptyProducer</code>의 <code class="language-plaintext highlighter-rouge">subscribe</code>를 보면 element 반환 없이 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트를 방출하고 종료되는걸 확인할 수 있다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">just</span><span class="p">(</span><span class="n">_</span> <span class="nv">element</span><span class="p">:</span> <span class="kt">Element</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">Just</span><span class="p">(</span><span class="nv">element</span><span class="p">:</span> <span class="n">element</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">private</span> <span class="kd">class</span> <span class="kt">Just</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">:</span> <span class="kt">Producer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="kd">private</span> <span class="k">let</span> <span class="nv">_element</span><span class="p">:</span> <span class="kt">Element</span>
<span class="nf">init</span><span class="p">(</span><span class="nv">element</span><span class="p">:</span> <span class="kt">Element</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">_element</span> <span class="o">=</span> <span class="n">element</span>
<span class="p">}</span>
<span class="k">override</span> <span class="kd">func</span> <span class="n">subscribe</span><span class="o"><</span><span class="kt">Observer</span><span class="p">:</span> <span class="kt">ObserverType</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">where</span> <span class="kt">Observer</span><span class="o">.</span><span class="kt">Element</span> <span class="o">==</span> <span class="kt">Element</span> <span class="p">{</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="nf">next</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">_element</span><span class="p">))</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="n">completed</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">just</code>의 구현부이다.</p>
<p><code class="language-plaintext highlighter-rouge">just</code> operator는 <code class="language-plaintext highlighter-rouge">Just</code> 클래스 인스턴스를 반환한다.</p>
<p><code class="language-plaintext highlighter-rouge">Just</code> 인스턴스를 생성할때 <code class="language-plaintext highlighter-rouge">just</code> operator로 입력된 값을 주입한다.</p>
<p><code class="language-plaintext highlighter-rouge">Just</code> 클래스의 <code class="language-plaintext highlighter-rouge">subscribe</code>를 보면 이벤트를 2번 방출하고 있다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">.next(self._element)</code> element 값을 주입한 <code class="language-plaintext highlighter-rouge">next</code> 이벤트를 방출한다.</li>
<li><code class="language-plaintext highlighter-rouge">.completed</code> 이벤트를 방출한다.</li>
</ul>
<p>(<code class="language-plaintext highlighter-rouge">just</code> operator는 단 하나의 element만 방출하고 종료되므로 <code class="language-plaintext highlighter-rouge">next</code> 이벤트는 한 번만 호출된다.)</p>
<h3 id="empty와-타입">empty와 타입</h3>
<p><code class="language-plaintext highlighter-rouge">empty</code> 예시 코드에 아무 element도 방출하지 않고 종료될텐데 <code class="language-plaintext highlighter-rouge">Observable<Void></code>로 왜 타입을 지정해주는지 궁금했다.</p>
<p>그래서 아래와 같이 실험을 해봤다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o"><</span><span class="kt">Void</span><span class="o">>.</span><span class="nf">empty</span><span class="p">()</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">empty</span><span class="p">()</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">completed</code>가 두 번 출력될 줄 알았는데 타입을 지정해준 Observable에서만 출력되었다.</p>
<p>책을 쭉 읽는데 <code class="language-plaintext highlighter-rouge">empty</code> operator를 사용할 때에는 Observable의 타입을 지정해줘야 한다고 한다.</p>
<p>앞에서 살펴본 <code class="language-plaintext highlighter-rouge">just</code>, <code class="language-plaintext highlighter-rouge">of</code>, <code class="language-plaintext highlighter-rouge">from</code>과 다르게 가지고 있는 element가 없어 타입 추론을 하지 못한다고 한다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">empty</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">EmptyProducer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">empty</code> 구현부를 보면서 좀 이해할 수 있었다.</p>
<p><code class="language-plaintext highlighter-rouge">empty</code>의 반환 타입을 보면 <code class="language-plaintext highlighter-rouge">Observable<Element></code>로 타입이 필요하다.</p>
<p><br /></p>
<p>흠…. 근데 어차피 아무런 element를 방출하지 않고 즉시 completed되는 Observable인데 Void 말고 다른 타입을 쓸 일이 있을까?</p>
<h2 id="never">never()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o"><</span><span class="kt">Void</span><span class="o">>.</span><span class="nf">never</span><span class="p">()</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">never</code> operator는 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트도 방출되지 않는다.</p>
<p>즉, 무한의 Observable을 반환한다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">never</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">NeverProducer</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">private</span> <span class="kd">class</span> <span class="kt">NeverProducer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">:</span> <span class="kt">Producer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">override</span> <span class="kd">func</span> <span class="n">subscribe</span><span class="o"><</span><span class="kt">Observer</span><span class="p">:</span> <span class="kt">ObserverType</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">where</span> <span class="kt">Observer</span><span class="o">.</span><span class="kt">Element</span> <span class="o">==</span> <span class="kt">Element</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">never</code>의 구현부를 확인해보자.</p>
<p><code class="language-plaintext highlighter-rouge">NeverProducer</code> 클래스 인스턴스를 반환한다.</p>
<p><code class="language-plaintext highlighter-rouge">NeverProducer</code>의 <code class="language-plaintext highlighter-rouge">subscribe</code>를 확인해보면 어떤 element도 이벤트도 방출하지 않는걸 확인할 수 있다.</p>
<h2 id="range">range()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="nf">range</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">count</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">9</span><span class="p">)</span>
<span class="nf">next</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="n">completed</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">range</code> operator는 이름 그대로이다.</p>
<p><code class="language-plaintext highlighter-rouge">start</code>로 시작 지점을 설정하고 <code class="language-plaintext highlighter-rouge">count</code>로 방출할 갯수를 지정한다.</p>
<hr />
<h1 id="disposing-and-terminating">Disposing and terminating</h1>
<p>Observable은 subscription되기 전까지 아무런 동작도 하지 않는다.</p>
<p>즉, Observable은 subscribe되어야만 동작을 한다.</p>
<p>이 동작은 <code class="language-plaintext highlighter-rouge">error</code>나 <code class="language-plaintext highlighter-rouge">completed</code>로 종료되기 전까지 반복된다.</p>
<p>그렇다면 subscribe를 취소하여 Observable의 동작을 종료할 수 있지 않을까?</p>
<h2 id="dispose">dispose()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// subscribe 구현부</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">subscribe</span><span class="p">(</span><span class="n">_</span> <span class="nv">on</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Event</span><span class="o"><</span><span class="k">Self</span><span class="o">.</span><span class="kt">Element</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="o">-></span> <span class="kt">RxSwift</span><span class="o">.</span><span class="kt">Disposable</span>
<span class="c1">// dispose() 관련 예제</span>
<span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"dispose"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">observable</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="s">"A"</span><span class="p">,</span> <span class="s">"B"</span><span class="p">,</span> <span class="s">"C"</span><span class="p">)</span>
<span class="c1">// 2</span>
<span class="k">let</span> <span class="nv">subscription</span> <span class="o">=</span> <span class="n">observable</span><span class="o">.</span><span class="nf">subscribe</span><span class="p">({</span> <span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="k">in</span>
<span class="c1">// 3</span>
<span class="nf">print</span><span class="p">(</span><span class="n">event</span><span class="p">)</span>
<span class="p">})</span>
<span class="n">subscription</span><span class="o">.</span><span class="nf">dispose</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Dispose() 관련 예제를 살펴보자.</p>
<ul>
<li>
<p>String Observable을 생성한다.</p>
</li>
<li>Observable을 subscribe한다. -> <code class="language-plaintext highlighter-rouge">subscribe()</code>는 <code class="language-plaintext highlighter-rouge">Disposable</code>을 반환한다.</li>
<li>방출된 이벤트들을 출력한다.</li>
</ul>
<p><code class="language-plaintext highlighter-rouge">dispose()</code>를 통해 subscribe를 취소할 수 있다.</p>
<p>subscribe를 취소하거나 dispose한 뒤에는 이벤트 방출이 정지된다.</p>
<p>(<code class="language-plaintext highlighter-rouge">dispose()</code>하면 completed가 방출된 후 종료된다.)</p>
<h2 id="disposebag">DisposeBag()</h2>
<p>각 subscription을 개별적으로 관리하는건 효율적이지 못해서 <code class="language-plaintext highlighter-rouge">DisposeBag</code>을 이용할 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">DisposeBag</code>은 (보통 <code class="language-plaintext highlighter-rouge">disposed(by:)</code>으로 추가된 ) disposables를 가지고 있다.</p>
<p>Dispose bag이 할당 해제되려고 할 때 각각 <code class="language-plaintext highlighter-rouge">dispose()</code>를 호출한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"DisposeBag"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">disposeBag</span> <span class="o">=</span> <span class="kt">DisposeBag</span><span class="p">()</span>
<span class="c1">// 2</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">of</span><span class="p">(</span><span class="s">"A"</span><span class="p">,</span> <span class="s">"B"</span><span class="p">,</span> <span class="s">"C"</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="c1">// 3</span>
<span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span> <span class="c1">// 4</span>
<span class="p">}</span>
</code></pre></div></div>
<p>DisposeBag() 관련 예제를 살펴보자.</p>
<p><code class="language-plaintext highlighter-rouge">dispose()</code> 예제와 크게 다르지 않다.</p>
<p><code class="language-plaintext highlighter-rouge">DisposeBag()</code> 인스턴스를 만들어주고 <code class="language-plaintext highlighter-rouge">subscribe</code>로 반환된 <code class="language-plaintext highlighter-rouge">Disposable</code>을 <code class="language-plaintext highlighter-rouge">disposed(by:)</code>로 <code class="language-plaintext highlighter-rouge">disposeBag</code>에 추가한다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">dispose bag</code>에서 subscription 추가를 잊거나 수동으로 <code class="language-plaintext highlighter-rouge">dispose()</code> 호출을 잊어버린다면 메모리 누수가 일어난다.</p>
<h2 id="disposable과-disposebag">Disposable과 DisposeBag</h2>
<p>위에서 살펴본 <code class="language-plaintext highlighter-rouge">Just</code> 구현부와 <code class="language-plaintext highlighter-rouge">DisposeBag</code> 구현부를 확인해보자.</p>
<p>(아직 완벽하게 구현부를 본건 아녀서 겉핥기식으로만 보자….)</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 예제</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
<span class="o">.</span><span class="n">subscribe</span> <span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="c1">// Just 구현부</span>
<span class="kd">final</span> <span class="kd">private</span> <span class="kd">class</span> <span class="kt">Just</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span><span class="p">:</span> <span class="kt">Producer</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">override</span> <span class="kd">func</span> <span class="n">subscribe</span><span class="o"><</span><span class="kt">Observer</span><span class="p">:</span> <span class="kt">ObserverType</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">where</span> <span class="kt">Observer</span><span class="o">.</span><span class="kt">Element</span> <span class="o">==</span> <span class="kt">Element</span> <span class="p">{</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="nf">next</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">_element</span><span class="p">))</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="n">completed</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// disposed(by:)</span>
<span class="kd">extension</span> <span class="kt">Disposable</span> <span class="p">{</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">disposed</span><span class="p">(</span><span class="n">by</span> <span class="nv">bag</span><span class="p">:</span> <span class="kt">DisposeBag</span><span class="p">)</span> <span class="p">{</span>
<span class="n">bag</span><span class="o">.</span><span class="nf">insert</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// DisposeBag</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">DisposeBag</span><span class="p">:</span> <span class="kt">DisposeBase</span> <span class="p">{</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_disposables</span> <span class="o">=</span> <span class="p">[</span><span class="kt">Disposable</span><span class="p">]()</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">insert</span><span class="p">(</span><span class="n">_</span> <span class="nv">disposable</span><span class="p">:</span> <span class="kt">Disposable</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="nf">_insert</span><span class="p">(</span><span class="n">disposable</span><span class="p">)?</span><span class="o">.</span><span class="nf">dispose</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">_insert</span><span class="p">(</span><span class="n">_</span> <span class="nv">disposable</span><span class="p">:</span> <span class="kt">Disposable</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span><span class="p">?</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">_lock</span><span class="o">.</span><span class="nf">lock</span><span class="p">();</span> <span class="k">defer</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">_lock</span><span class="o">.</span><span class="nf">unlock</span><span class="p">()</span> <span class="p">}</span>
<span class="k">if</span> <span class="k">self</span><span class="o">.</span><span class="n">_isDisposed</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">disposable</span>
<span class="p">}</span>
<span class="k">self</span><span class="o">.</span><span class="n">_disposables</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">disposable</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="c1">/// This is internal on purpose, take a look at `CompositeDisposable` instead.</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">dispose</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">oldDisposables</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="nf">_dispose</span><span class="p">()</span>
<span class="k">for</span> <span class="n">disposable</span> <span class="k">in</span> <span class="n">oldDisposables</span> <span class="p">{</span>
<span class="n">disposable</span><span class="o">.</span><span class="nf">dispose</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Just</code>의 <code class="language-plaintext highlighter-rouge">subscribe</code>를 보면 ` Disposable`을 반환하고 있다.</p>
<p>-> 예제에서 <code class="language-plaintext highlighter-rouge">.subscribe</code>에서 ` Disposable`을 얻을 수 있다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">Disposable</code>에 <code class="language-plaintext highlighter-rouge">disposed(by:)</code>가 구현되어 있다.</p>
<p>-> <code class="language-plaintext highlighter-rouge">.subscribe</code>에서 반환된 <code class="language-plaintext highlighter-rouge">Disposable</code>에 <code class="language-plaintext highlighter-rouge">disposed(by:)</code>를 호출한다.</p>
<p><code class="language-plaintext highlighter-rouge">disposed(by:)</code>를 통해 입력 받은 <code class="language-plaintext highlighter-rouge">DisposeBag</code>에 <code class="language-plaintext highlighter-rouge">Disposable</code> 자체를 넣어준다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">DisposeBag</code>에는 <code class="language-plaintext highlighter-rouge">Disposable</code> 배열 프로퍼티가 존재한다.</p>
<p>-> <code class="language-plaintext highlighter-rouge">disposed(by:)</code>는 해당 배열에 Disposable을 넣는다.</p>
<p>배열에 들어간 <code class="language-plaintext highlighter-rouge">Disposable</code>을 메모리 해제한다.</p>
<hr />
<h1 id="creating-observables3">Creating observables(3)</h1>
<h2 id="create">create()</h2>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="n">create</span> <span class="p">{</span> <span class="p">(</span><span class="n">observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">in</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span> <span class="n">element</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="mi">3</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">create</code>는 탈출 클로저로 <code class="language-plaintext highlighter-rouge">AnyObserver -> Disposable</code> 형태이다.</p>
<p><code class="language-plaintext highlighter-rouge">AnyObserver</code>는 제네릭 타입으로 Observable sequence에 값을 추가할 수 있다.</p>
<p>추가한 값은 subscriber에 방출된다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">.onNext</code>는 <code class="language-plaintext highlighter-rouge">on(.next(_:))</code>와 같은 의미이다.</p>
<p><code class="language-plaintext highlighter-rouge">onCompleted()</code>는 <code class="language-plaintext highlighter-rouge">on(.completed)</code>와 같은 의미이다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Observable</span><span class="o">.</span><span class="n">create</span> <span class="p">{</span> <span class="p">(</span><span class="n">observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">in</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onCompleted</span><span class="p">()</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span>
<span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onError</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onCompleted</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Completed"</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onDisposed</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Disposed"</span><span class="p">)</span> <span class="p">}</span>
<span class="p">)</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="kt">Completed</span>
<span class="kt">Disposed</span>
</code></pre></div></div>
<p>위의 코드를 확인해보자.</p>
<p><code class="language-plaintext highlighter-rouge">onNext</code>로 1, 2, 3을 방출했지만 2까지만 나온다.</p>
<p><code class="language-plaintext highlighter-rouge">completed</code> 이벤트가 발생하여 dispose되었기 때문에 3은 방출되지 않는다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">TestError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">error</span>
<span class="p">}</span>
<span class="kt">Observable</span><span class="o">.</span><span class="n">create</span> <span class="p">{</span> <span class="p">(</span><span class="n">observer</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span> <span class="k">in</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onError</span><span class="p">(</span><span class="kt">TestError</span><span class="o">.</span><span class="n">error</span><span class="p">)</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onCompleted</span><span class="p">()</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span>
<span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onError</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onCompleted</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Completed"</span><span class="p">)</span> <span class="p">},</span>
<span class="nv">onDisposed</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Disposed"</span><span class="p">)</span> <span class="p">}</span>
<span class="p">)</span><span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="mi">1</span>
<span class="mi">2</span>
<span class="n">error</span>
<span class="kt">Disposed</span>
</code></pre></div></div>
<p>위의 예시와 동일하게 2까지만 출력된다.</p>
<p><code class="language-plaintext highlighter-rouge">error</code> 이벤트가 발생한 후 dispose되었기 때문에 뒤의 다른 이벤트들은 방출되지 않는다.</p>
<hr />
<h1 id="traits">Traits</h1>
<p><code class="language-plaintext highlighter-rouge">Trait</code>은 일반적인 Observable보다 좁은 범위의 Observable로 선택적으로 사용할 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">Trait</code>을 사용해 가독성을 높일 수 있다.</p>
<h2 id="single">Single</h2>
<p><code class="language-plaintext highlighter-rouge">Single</code>은 <code class="language-plaintext highlighter-rouge">success(value)</code> 또는 ` error(error)` 이벤트를 방출한다.</p>
<p><code class="language-plaintext highlighter-rouge">success(value)</code>는 <code class="language-plaintext highlighter-rouge">next</code> 이벤트와 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트의 조합니다.</p>
<p>성공 혹은 실패로 확인 가능한 프로세스에 사용한다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">typealias</span> <span class="kt">Single</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="o">=</span> <span class="kt">PrimitiveSequence</span><span class="o"><</span><span class="kt">SingleTrait</span><span class="p">,</span> <span class="kt">Element</span><span class="o">></span>
<span class="kd">public</span> <span class="kd">enum</span> <span class="kt">SingleEvent</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">success</span><span class="p">(</span><span class="kt">Element</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">error</span><span class="p">(</span><span class="kt">Swift</span><span class="o">.</span><span class="kt">Error</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">PrimitiveSequenceType</span> <span class="k">where</span> <span class="kt">Trait</span> <span class="o">==</span> <span class="kt">SingleTrait</span> <span class="p">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">create</span><span class="p">(</span><span class="nv">subscribe</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kd">@escaping</span> <span class="kt">SingleObserver</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Disposable</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Single</span><span class="o"><</span><span class="kt">Element</span><span class="o">></span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">source</span> <span class="o">=</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Element</span><span class="o">>.</span><span class="n">create</span> <span class="p">{</span> <span class="n">observer</span> <span class="k">in</span>
<span class="k">return</span> <span class="n">subscribe</span> <span class="p">{</span> <span class="n">event</span> <span class="k">in</span>
<span class="k">switch</span> <span class="n">event</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="k">let</span> <span class="nv">element</span><span class="p">):</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="nf">next</span><span class="p">(</span><span class="n">element</span><span class="p">))</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="n">completed</span><span class="p">)</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span>
<span class="n">observer</span><span class="o">.</span><span class="nf">on</span><span class="p">(</span><span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="n">error</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kt">PrimitiveSequence</span><span class="p">(</span><span class="nv">raw</span><span class="p">:</span> <span class="n">source</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Single</code>의 구현부를 봐보자.</p>
<p><code class="language-plaintext highlighter-rouge">SingleEvent</code>로 <code class="language-plaintext highlighter-rouge">success(value)</code>, <code class="language-plaintext highlighter-rouge">error(error)</code>가 있음을 알 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">primitiveSequenceType</code>을 확인해보면 <code class="language-plaintext highlighter-rouge">success</code>는 <code class="language-plaintext highlighter-rouge">next</code> 이벤트로 element를 방출한 뒤 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트를 방출한다.</p>
<p>(생긴게 좀 Result랑 비슷하게 생겼다….)</p>
<h2 id="completable">Completable</h2>
<p><code class="language-plaintext highlighter-rouge">Completable</code>은 <code class="language-plaintext highlighter-rouge">completed</code> 혹은 ` error(error)` 이벤트만 방출한다.</p>
<p>즉, 어떠한 값도 방출하지 않는다.</p>
<p>파일 쓰기 같은 작업이 성공적으로 완료 혹은 실패인지에만 관심 있을 경우 사용한다.</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">typealias</span> <span class="kt">Completable</span> <span class="o">=</span> <span class="kt">PrimitiveSequence</span><span class="o"><</span><span class="kt">CompletableTrait</span><span class="p">,</span> <span class="kt">Swift</span><span class="o">.</span><span class="kt">Never</span><span class="o">></span>
<span class="kd">public</span> <span class="kd">enum</span> <span class="kt">CompletableEvent</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">error</span><span class="p">(</span><span class="kt">Swift</span><span class="o">.</span><span class="kt">Error</span><span class="p">)</span>
<span class="k">case</span> <span class="n">completed</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Completable</code>의 구현부를 봐보자.</p>
<p><code class="language-plaintext highlighter-rouge">CompletableEvent</code>로 <code class="language-plaintext highlighter-rouge">error(error)</code>, <code class="language-plaintext highlighter-rouge">completed</code>가 있음을 알 수 있다.</p>
<h2 id="maybe">Maybe</h2>
<p><code class="language-plaintext highlighter-rouge">Maybe</code>는 <code class="language-plaintext highlighter-rouge">Single</code>과 <code class="language-plaintext highlighter-rouge">Completable</code>을 섞은거와 같다.</p>
<p><code class="language-plaintext highlighter-rouge">success(value)</code>, <code class="language-plaintext highlighter-rouge">completed</code>, <code class="language-plaintext highlighter-rouge">error(error)</code>를 방출할 수 있다.</p>
<p>성공, 실패 여부와 더해서 출력된 값도 필요할 수도 있을때 사용한다.</p>
<h3 id="사용법">사용법</h3>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">example</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"Single"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">disposeBag</span> <span class="o">=</span> <span class="kt">DisposeBag</span><span class="p">()</span>
<span class="c1">// 2</span>
<span class="kd">enum</span> <span class="kt">FileReadError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">fileNotFound</span><span class="p">,</span> <span class="n">unreadable</span>
<span class="p">}</span>
<span class="c1">// 3</span>
<span class="kd">func</span> <span class="nf">loadText</span><span class="p">(</span><span class="n">from</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Single</span><span class="o"><</span><span class="kt">String</span><span class="o">></span> <span class="p">{</span>
<span class="c1">// 4</span>
<span class="k">return</span> <span class="kt">Single</span><span class="o">.</span><span class="n">create</span><span class="p">{</span> <span class="n">single</span> <span class="k">in</span>
<span class="c1">// 4 - 1</span>
<span class="k">let</span> <span class="nv">disposable</span> <span class="o">=</span> <span class="kt">Disposables</span><span class="o">.</span><span class="nf">create</span><span class="p">()</span>
<span class="c1">// 4 - 2</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">path</span> <span class="o">=</span> <span class="kt">Bundle</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="nf">path</span><span class="p">(</span><span class="nv">forResource</span><span class="p">:</span> <span class="n">name</span><span class="p">,</span> <span class="nv">ofType</span><span class="p">:</span> <span class="s">"txt"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">single</span><span class="p">(</span><span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="kt">FileReadError</span><span class="o">.</span><span class="n">fileNotFound</span><span class="p">))</span>
<span class="k">return</span> <span class="n">disposable</span>
<span class="p">}</span>
<span class="c1">// 4 - 3</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="kt">FileManager</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">contents</span><span class="p">(</span><span class="nv">atPath</span><span class="p">:</span> <span class="n">path</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">single</span><span class="p">(</span><span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="kt">FileReadError</span><span class="o">.</span><span class="n">unreadable</span><span class="p">))</span>
<span class="k">return</span> <span class="n">disposable</span>
<span class="p">}</span>
<span class="c1">// 4 - 4</span>
<span class="nf">single</span><span class="p">(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">contents</span><span class="p">))</span>
<span class="k">return</span> <span class="n">disposable</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>1: dispose bag 생성</p>
<p>2: Error 정의</p>
<p>3: 디스크 파일로부터 텍스트 불러와서 <code class="language-plaintext highlighter-rouge">Single</code>을 반환하는 메서드</p>
<p>4: <code class="language-plaintext highlighter-rouge">Single</code>을 생성하고 반환</p>
<p> 4-1: <code class="language-plaintext highlighter-rouge">create</code>의 <code class="language-plaintext highlighter-rouge">subscribe</code> 클로저는 <code class="language-plaintext highlighter-rouge">disposable</code>을 반환하므로 <code class="language-plaintext highlighter-rouge">disposable</code> 생성</p>
<p> 4-2: 파일명 경로를 받아오고 파일이 없다면 <code class="language-plaintext highlighter-rouge">single</code>에 <code class="language-plaintext highlighter-rouge">error</code>를 추가하고 <code class="language-plaintext highlighter-rouge">disposable</code> 반환</p>
<p> 4-3: 파일로부터 데이터를 받아오고 파일을 읽을 수 없다면 <code class="language-plaintext highlighter-rouge">single</code>에 <code class="language-plaintext highlighter-rouge">error</code>를 추가하고 <code class="language-plaintext highlighter-rouge">disposable</code> 반환</p>
<p> 4-4: 원하는 동작을 수행했으니 <code class="language-plaintext highlighter-rouge">single</code>에 <code class="language-plaintext highlighter-rouge">success(value)</code>를 추가하고 <code class="language-plaintext highlighter-rouge">disposable</code> 반환</p>
<p><br /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">loadText</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="s">"Copyright"</span><span class="p">)</span>
<span class="o">.</span><span class="n">subscribe</span><span class="p">{</span>
<span class="k">switch</span> <span class="nv">$0</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="k">let</span> <span class="nv">string</span><span class="p">):</span>
<span class="nf">print</span><span class="p">(</span><span class="n">string</span><span class="p">)</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span>
<span class="nf">print</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
</code></pre></div></div>
<p>이런 식으로 사용할 수 있다.</p>
<p>볼수록 Result 같다…</p>
<hr />
<h1 id="outro">Outro</h1>
<p>Observable에 대해서 알아봤다.</p>
<p>내용이 상당히 많다.</p>
<p>이후에 더 볼 내용들이 있지만 우선은 여기서 마무리하고 이후에 다시 정리해야지…</p>lasagnaIntroRxSwift - ch.1 Hello RxSwift2021-10-24T02:00:00+09:002021-10-24T02:00:00+09:00https://wonhee009.github.io/rxswift/RxSwift_ch.1_Hello_RxSwift<h1 id="intro">Intro</h1>
<p><a href="https://www.raywenderlich.com/sessions/new?return_path=%2F">Raywenderlich</a>로 RxSwift를 공부해보려 한다.</p>
<p>두근…. 두근…..</p>
<hr />
<h1 id="hello-rxswift">Hello, RxSwift!</h1>
<p>RxSwift에 대한 첫 설명은 다음과 같다.</p>
<blockquote>
<p><strong>RxSwift</strong> <em>is a library for composing asynchrnous and event-based code by using observable sequences and functional style operators, allowing for parameterized execution via schedulers</em>.</p>
</blockquote>
<p>RxSwift는 <code class="language-plaintext highlighter-rouge">observable</code>한 시퀀스와 <code class="language-plaintext highlighter-rouge">functional style</code> 연산자를 사용해 <code class="language-plaintext highlighter-rouge">asynchronous(비동기적)</code>이고 <code class="language-plaintext highlighter-rouge">event-based(이벤트 기반)</code> 코드를 작성하는 라이브러리이다.</p>
<p><code class="language-plaintext highlighter-rouge">scheduler</code>를 통해 매개변수화된 실행을 허용한다.</p>
<p><br /></p>
<p>위의 설명은 아래와 같다.</p>
<blockquote>
<p><strong>RxSwift</strong>, in its essence, simplifies developing asynchronous programs by allowing your code to react to new data and process it in a sequential, isolated manner.</p>
</blockquote>
<p>RxSwift는 코드가 <strong>새로운 데이터에 반응</strong>하고 <strong>순차적이고 독립된</strong> 방식으로 처리할 수 있도록 하여 <strong>비동기식 프로그램 개발을 단순</strong>화 했다.</p>
<p><br /></p>
<p>기존에도 비동기 코드를 지원하기 위해 제공해주던 API들이 있다.</p>
<ul>
<li>NotificationCenter</li>
<li>delegate Pattern</li>
<li>GCD</li>
<li>Closure</li>
<li>Combine</li>
</ul>
<p>보통 대부분의 클래스들은 비동기적으로 수행하고 모든 UI 요소들은 비동기적이다.</p>
<p>따라서 내가 작성한 코드가 어떤 순서로 동작하는지 가정하는건 무의미하다.</p>
<h2 id="비동기-프로그래밍-용어집">비동기 프로그래밍 용어집</h2>
<h3 id="1-state-and-sepdifically-shared-mutable-stae">1. State, and sepdifically, shared mutable stae</h3>
<p><strong>State</strong>는 정의하기 어려우므로 예를 들어보자.</p>
<ul>
<li>노트북을 사고 처음 실행하면 정상적으로 실행된다.</li>
<li>며칠 혹은 몇 주 동안 사용하면 이상하게 작동하거나 갑자기 멈추기도 한다.</li>
<li>노트북을 처음 샀을때와 기간이 지난 지금 하드웨어와 소프트웨어는 그대로 유지되지만 변한건 <strong>상태</strong>뿐이다.</li>
<li>노트북을 재시동하거나 사용할 수록 메모리 상의 데이터, 디스크에 저장된 내용을 포함한 찌꺼기들이 노트북에 남게 된다.</li>
</ul>
<p>–> 이런 것을 노트북의 <strong>state</strong>라고 할 수 있다.</p>
<p>–> 노트북을 사용하면서 데이터 교환 등이 이루어지고 남는 것들이 생기면서 상태가 변한다.</p>
<h3 id="2-명령형-프로그래밍">2. 명령형 프로그래밍</h3>
<p>명령형 프로그래밍은 명령문을 사용하여 프로그램의 상태를 변경하는 프로그래밍 패러다임이다.</p>
<ul>
<li>강아지와 놀 때 명령형 언어를 사용하는 것처럼 (ex. 누워! 죽은 척!) 명령형 코드를 사용하여 작업을 수행하는 시기와 방법을 앱에 정확하게 알려준다.</li>
<li>명령 코드는 컴퓨터가 이해하는 코드와 유사하다.</li>
<li>CPU가 하는 모든 일은 간단한 명령의 시퀀스를 따르는 것이다.</li>
</ul>
<p>문제는 인간이 복잡한 비동기식 앱을 위한 명령형 코드를 작성하는 것은 어렵다.</p>
<p>특히 공유 가능한 상태가 있을 때 더 그렇다.</p>
<p>아래의 코드를 통해 확인해보자.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidAppear</span><span class="p">(</span><span class="n">_</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">viewDidAppear</span><span class="p">(</span><span class="n">animated</span><span class="p">)</span>
<span class="nf">setupUI</span><span class="p">()</span>
<span class="nf">connectUIControls</span><span class="p">()</span>
<span class="nf">createDataSource</span><span class="p">()</span>
<span class="nf">listenForChanges</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>
<p>각각의 메서드들이 어떤 동작을 하는지 확인이 어렵다.</p>
</li>
<li>viewController를 업데이트 하는지 아닌지도 확인이 어렵다.</li>
<li>각 메서드들이 순서대로 잘 실행될지도 보증할 수 없다.</li>
<li>누군가 메서드의 순서를 바꿨다면 앱이 다르게 동작할 수 있다.</li>
</ul>
<h3 id="3-side-effects">3. Side effects</h3>
<p><strong>Side effects</strong>는 코드의 현재 scope에서 벗어나서 일어나는 모든 변화를 의미한다.</p>
<p>예를 들어, <code class="language-plaintext highlighter-rouge">2. 명령형 프로그래밍</code>의 코드 예시에서 <code class="language-plaintext highlighter-rouge">connectUIControls()</code>는 어떤 종류의 이벤트 핸들러를 일부 UI 구성 요소에 연결한다.</p>
<p>이로 인해 뷰의 상태가 변경되므로 <strong>Side effects</strong>가 발생한다.</p>
<p>앱은 <code class="language-plaintext highlighter-rouge">connectUIControls()</code>를 실행하기 전에 한 방향으로 동작하고 그 후에는 다르게 동작한다.</p>
<p><br /></p>
<p>디스크에 저장된 데이터를 수정하거나 label 텍스트를 업데이트할 때마다 <strong>Side effect</strong>가 발생한다.</p>
<p><strong>Side effects</strong>가 나쁜 것은 아니지만 통제 가능해야 한다.</p>
<p>코드 어디에서 Side effect가 일어나는지 알 수 있어야 한다.</p>
<h3 id="4-선언형-코드">4. 선언형 코드</h3>
<p>명령형 프로그래밍은 State의 변경이 자유롭다.</p>
<p>함수형 프로그래밍은 Side effect를 일으키는 코드를 최소화 하는걸 목표로 한다.</p>
<p><strong>RxSwift</strong>는 명령형 코드와 함수형 코드에서 좋은 측면을 결합했다.</p>
<p>–> 명령형 코드(State 변화가 자유로움) + 함수형 코드(Side effect 최소화 = 예측 가능한 결과)</p>
<p><br /></p>
<p>선언형 코드를 통해 동작을 정의한다.</p>
<p>RxSwift는 관련 이벤트가 있을 때마다 해당 동작을 실행하고 작업할 <strong>변경 불가능한</strong> 데이터 입력을 제공한다.</p>
<p>이렇게 하면 변경할 수 없는 데이터로 작업하고 순차적으로 실행할 수 있다.</p>
<h3 id="5-reactive-systems">5. Reactive systems</h3>
<p>반응형 시스템이란 의미는 추상적이고, 다음과 같은 특성의 대부분 또는 전부를 나타내는 iOS 앱을 다루게 된다.</p>
<ul>
<li>Responsive: 항상 UI를 최신 상태로 유지하며 가장 최근의 앱 상태를 표시한다.</li>
<li>Resilient: 각각의 행동들은 독립적으로 정의되며 에러 복구를 위해 유연하게 제공된다.</li>
<li>Elastic: 코드는 다양한 작업 부하를 처리하며 종종 lazy pull-driven 데이터 수집, 이벤트 제한 및 리소스 공유와 같은 기능을 구현한다.</li>
<li>Message driven: 구성요소는 메시지 기반 통신을 사용하여 재사용 및 고유한 기능을 개선하고, 라이프 사이클과 클래스 구현을 분리한다.</li>
</ul>
<hr />
<h1 id="foundation-of-rxswift">Foundation of RxSwift</h1>
<p>Rx 코드의 세 가지 구성 요소는 <code class="language-plaintext highlighter-rouge">Observable</code>, <code class="language-plaintext highlighter-rouge">Operator</code>, <code class="language-plaintext highlighter-rouge">Scheduler</code>이다.</p>
<h2 id="observable">Observable</h2>
<p><code class="language-plaintext highlighter-rouge">Observable<Element></code>는 Rx 코드의 기초이다.</p>
<p><code class="language-plaintext highlighter-rouge">Element</code> 형태의 데이터 snapshot을 <strong>전달</strong>할 수 있는 이벤트 시퀀스를 비동기적으로 생성하는 기능이다.</p>
<p>즉, 시간이 지남에 따라 다른 개체에서 내보내는 이벤트 혹은 값을 구독할 수 있다.</p>
<p><strong>Observable</strong> 클래스를 사용하면 하나 이상의 <code class="language-plaintext highlighter-rouge">observer</code>가 실시간으로 모든 이벤트에 반응하고 앱의 UI를 업데이트하거나 새로운 데이터를 처리하고 활용할 수 있다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">ObservableType</code>프로토콜(Observable이 준수하는)은 매우 간단하다.</p>
<p><strong>Observable</strong>은 세 가지 유형의 이벤트만 내보낼 수 있다. (<code class="language-plaintext highlighter-rouge">observer</code>는 이를 받을 수 있다.)</p>
<ul>
<li>next event
<ul>
<li>최신 데이터 값을 <strong>전달</strong>하는 이벤트이다.</li>
<li><code class="language-plaintext highlighter-rouge">observer</code>가 value를 받는 방법이다.</li>
<li>Observable은 종료 이벤트가 방출될 때까지 이런 값을 무한하게 방출할 수 있다.</li>
</ul>
</li>
<li>completed event
<ul>
<li>이벤트 시퀀스를 성공으로 종료한다.</li>
<li>Observable이 수명 주길르 성공적으로 완료했으며 추가 이벤트를 방출하지 않는다.</li>
</ul>
</li>
<li>error event
<ul>
<li>Observable은 오류와 함께 종료되고 추가 이벤트를 방출하지 않는다.</li>
</ul>
</li>
</ul>
<p>위의 Observable 이벤트는 <code class="language-plaintext highlighter-rouge">Observable</code> 혹은 <code class="language-plaintext highlighter-rouge">Observer</code>의 본질에 대해 어떤 가정도 하지 않는다.</p>
<p>따라서 delegate 프로토콜이나 클로저를 사용할 필요가 없다.</p>
<h3 id="finite-observable-sequences">Finite observable sequences</h3>
<p>일부 Observable 시퀀스는 0개, 1개 이상의 값을 방출하고 나중에는 성공적으로 종료되거나 오류와 함께 종료된다.</p>
<p>iOS 앱에서 파일 다운로드 코드를 생각해보자.</p>
<ul>
<li>먼저 다운로드를 시작하고 들어오는 데이터들을 observing하기 시작한다.</li>
<li>계속해서 파일 데이터를 받는다.</li>
<li>네트워크 연결이 끊어진다면 다운로드는 멈출 것이고 연결은 에러와 함께 일시정지 된다.</li>
<li>또는, 성공적으로 모든 파일 데이터를 다운로드 할 수 있다.</li>
</ul>
<p>이런 흐름은 위의 Observable의 생명주기와 일치한다.</p>
<p><br /></p>
<p>위의 흐름을 RxSwift 코드로 표현하면 아래와 같다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">API</span><span class="o">.</span><span class="nf">download</span><span class="p">(</span><span class="nv">file</span><span class="p">:</span> <span class="s">"http://www..."</span><span class="p">)</span>
<span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span>
<span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="n">data</span> <span class="k">in</span>
<span class="c1">// Append data to temporary file</span>
<span class="p">},</span>
<span class="nv">onError</span><span class="p">:</span> <span class="p">{</span> <span class="n">error</span> <span class="k">in</span>
<span class="c1">// Display error to user</span>
<span class="p">},</span>
<span class="nv">onCompleted</span><span class="p">:</span> <span class="p">{</span>
<span class="c1">// Use downloaded file</span>
<span class="p">}</span>
<span class="p">)</span>
</code></pre></div></div>
<ul>
<li><code class="language-plaintext highlighter-rouge">API.download(file: )</code>은 네트워크를 통해 들어오는 데이터 값을 방출하는 <code class="language-plaintext highlighter-rouge">Observable<Data></code> 인스턴스를 반환한다.</li>
<li><code class="language-plaintext highlighter-rouge">onNext</code> 클로저로 <code class="language-plaintext highlighter-rouge">next</code> 이벤트를 받을 수 있다.
<ul>
<li>위의 코드에서는 받은 데이터를 temporary file에 저장한다.</li>
</ul>
</li>
<li><code class="language-plaintext highlighter-rouge">onError</code> 클로저로 <code class="language-plaintext highlighter-rouge">error</code> 이벤트를 받을 수 있다.</li>
<li>최종적으로 <code class="language-plaintext highlighter-rouge">onCompleted</code> 클로저로 <code class="language-plaintext highlighter-rouge">completed</code> 이벤트를 받을 수 있다.</li>
</ul>
<h3 id="infinite-observable-sequences">Infinite observable sequences</h3>
<p>파일 다운로드와 같이 자연스럽게든 강제로든 종료되어야 하는 활동과 달리 단순히 무한한 시퀀스가 있다.</p>
<p>UI 이벤트는 무한히 관찰 가능한 시퀀스이다.</p>
<p><br /></p>
<p>예를 들어 앱의 기기 방향 변경에 반응하는 데 필요한 코드가 있다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">NotificationCenter</code>에서 <code class="language-plaintext highlighter-rouge">UIDeviceOrientationDidChange</code> 알림에 대한 observer를 추가한다.</li>
<li>방향 변경을 처리하기 위해 메서드 콜백을 제공해야 한다.</li>
<li>UIDevice에서 현재 방향을 가져와 최신 값에 따라 반응해야 한다.</li>
</ul>
<p><br /></p>
<p>위에서 살펴본 기기 방향 전환에는 끝이 없다.</p>
<p>즉, 디바이스가 존재하는 한 방향 전환은 연속적으로 일어난다.</p>
<p>결국 이런 시퀀스는 무한하기 때문에 항상 최초값을 가지고 있어야 한다.</p>
<p>사용자가 기기를 회전하지 않는 경우가 발생할 수 있지만 그렇다고 해서 이벤트가 종료된 것은 아니다.</p>
<p>다만, 아직 이벤트가 발생하지 않은 것일 뿐이다.</p>
<p><br /></p>
<p>위의 흐름을 RxSwift 코드로 표현하면 아래와 같다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">UIDevice</span><span class="o">.</span><span class="n">rx</span><span class="o">.</span><span class="n">orientation</span>
<span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="n">current</span> <span class="k">in</span>
<span class="k">switch</span> <span class="n">current</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">landscape</span><span class="p">:</span>
<span class="c1">// Re-arrange UI for landscape</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">portrait</span><span class="p">:</span>
<span class="c1">// Re-arrange UI for portrait</span>
<span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>
<ul>
<li><code class="language-plaintext highlighter-rouge">UIDevice.rx.orientation</code>은 <code class="language-plaintext highlighter-rouge">Observable<Orientation></code>을 생성하는 가상의 컨트롤 프로퍼티이다.</li>
<li>이를 <code class="language-plaintext highlighter-rouge">subscribe</code>함으로써 현재 디바이스 방향에 따라서 UI를 업데이트 할 수 있다.</li>
<li>해당 이벤트에서는 <code class="language-plaintext highlighter-rouge">onError</code>, <code class="language-plaintext highlighter-rouge">onCompleted</code>가 발생할 수 없으므로 생략한다.</li>
</ul>
<h2 id="operators">Operators</h2>
<p><code class="language-plaintext highlighter-rouge">ObervableType</code>과 <code class="language-plaintext highlighter-rouge">Observable</code> 클래스의 구현에는 비동기 비동기 작업 및 이벤트 조작을 추상화하는 많은 메서드가 포함되어 있고, 이는 함께 구성하면 더 복잡한 논리를 구현할 수 있도록 설계되어 있다.</p>
<p>이러한 메서드들은 매우 독립적이고 구성 가능하기 때문에 연산자(Operator)라고 부른다.</p>
<p><strong>Operator</strong>는 대부분 비동기식 입력을 받아 Side effect 없이 출력만 생성하기 때문에 퍼즐처럼 맞춰서 사용할 수 있다.</p>
<p><br /></p>
<p>예를 들어 <code class="language-plaintext highlighter-rouge">(5 + 6) * 10 - 2</code>라는 식을 생각해보자.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">*</code>, <code class="language-plaintext highlighter-rouge">()</code>, <code class="language-plaintext highlighter-rouge">+</code>, <code class="language-plaintext highlighter-rouge">-</code> 같은 연산자를 통해 데이터에 적용하고 결과를 가져와서 해결될 때까지 표현식을 계속 처리한다.</li>
</ul>
<p>이와 비슷하게 표현식이 최종값으로 도출 될 때까지 <code class="language-plaintext highlighter-rouge">Observable</code>이 방출한 값에 Rx 연산자를 적용하여 side effect를 만들 수 있다.</p>
<p><br /></p>
<p>위에서 살펴 본 방향 전환 코드에 Rx Operator를 적용시킨 코드이다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">UIDevice</span><span class="o">.</span><span class="n">rx</span><span class="o">.</span><span class="n">orientation</span>
<span class="o">.</span><span class="n">filter</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">!=</span> <span class="o">.</span><span class="n">landscape</span> <span class="p">}</span>
<span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="s">"Portrait is the best!"</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="nv">onNext</span><span class="p">:</span> <span class="p">{</span> <span class="n">string</span> <span class="k">in</span>
<span class="nf">showAlert</span><span class="p">(</span><span class="nv">text</span><span class="p">:</span> <span class="n">string</span><span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">UIDevice.rx.orientation</code>이 <code class="language-plaintext highlighter-rouge">.landscape</code> 혹은 <code class="language-plaintext highlighter-rouge">.portrait</code> 값을 생성할 때마다 RxSwift는 필터를 적용하고 방출된 데이터를 매핑한다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Observable<Orientaion></code>에서 <code class="language-plaintext highlighter-rouge">.portrait, .landscape, .portrait</code>을 방출했다. –> <code class="language-plaintext highlighter-rouge">.portrait, .landscape, .portrait</code></li>
<li><code class="language-plaintext highlighter-rouge">filter</code>에 의해서 <code class="language-plaintext highlighter-rouge">.landscape</code>가 아닌 값만 방출한다. –> <code class="language-plaintext highlighter-rouge">.portrait, .portrait</code>
<ul>
<li>만약 디바이스 방향이 <code class="language-plaintext highlighter-rouge">.landscape</code>인 경우는 <code class="language-plaintext highlighter-rouge">filter</code>에서 이벤트가 방출되지 않으므로 <code class="language-plaintext highlighter-rouge">subscribe</code>가 실행되지 않는다.</li>
</ul>
</li>
<li><code class="language-plaintext highlighter-rouge">map</code> 연산자에 의해서 <code class="language-plaintext highlighter-rouge">Orientation</code> 타입은 <code class="language-plaintext highlighter-rouge">String</code>으로 변환된다.</li>
<li><code class="language-plaintext highlighter-rouge">subscribe</code>를 통해 결과로 <code class="language-plaintext highlighter-rouge">next</code> 이벤트를 구현한다. <code class="language-plaintext highlighter-rouge">map</code>에 의해 변환된 <code class="language-plaintext highlighter-rouge">String</code> 값을 전달하고 해당 텍스트로 aleret을 화면에 출력한다.</li>
</ul>
<p>연산자는 항상 데이터를 입력으로 받아 결과를 출력하므로 쉽게 연결이 가능하다.</p>
<h2 id="schedulers">Schedulers</h2>
<p>스케줄러는 Rx에서 <code class="language-plaintext highlighter-rouge">dispatch queue</code>와 동일하지만 훨씬 강력하고 사용하기 쉽다.</p>
<p>특정 작업의 실행 컨텍스트를 정의할 수 있다.</p>
<p>RxSwift의 스케줄러는 99%의 상황에서 사용 가능하도록 사전에 정의되어 있으므로 스케줄러를 만들 필요가 거의 없다.</p>
<p>스케줄러는 초반에 보지 않아도 된다고 하니 나중에 다시 보기로 한다.</p>
<h2 id="app-architecture">App architecture</h2>
<p>RxSwift는 기존의 앱 아키텍쳐에 영향을 주지 않는다.</p>
<p>주로 이벤트나 비동기 데이터 시퀀스 등을 주로 처리하기 때문이다.</p>
<p>MVC, MVP, MVVM 등 다양한 패턴에서도 자유롭다.</p>
<p>다만 RxSwift와 MVVM은 아주 잘 어울린다.</p>
<p>ViewModel을 사용하면 <code class="language-plaintext highlighter-rouge">Observable<T></code> 속성을 노출할 수 있으며 ViewController의 UIKit에 직접 바인딩 가능하다.</p>
<p>이렇게 하면 데이터를 UI에 바인딩하고 표현하기가 매우 간단해진다.</p>
<h2 id="rxcocoa">RxCocoa</h2>
<p>RxCocoa는 UIKit 및 Cocoa 개발을 지원하는 RxSwift의 동반 라이브러리이다.</p>
<hr />
<h1 id="outro">Outro</h1>
<p>본격적으로 RxSwift에 대해 공부하기 전에 기초?를 봤다.</p>
<p>흠… 이렇게 보면 쉬운거 같은데 막상 해보면 또 다르겠지…</p>lasagnaIntroReactorKit 예시 - 12021-10-05T05:22:00+09:002021-10-05T05:22:00+09:00https://wonhee009.github.io/reactorkit/ReactorKit_%EC%98%88%EC%8B%9C_1<h2 id="intro">Intro</h2>
<p>앞에서 알아본 <code class="language-plaintext highlighter-rouge">ReactorKit</code>으로 정말 간단한 예제를 만들어보려 한다.</p>
<p><a href="https://github.com/ReactorKit/ReactorKit/tree/master/Examples/Counter">카운터 앱</a>은 해당 링크를 참고했다.</p>
<hr />
<h2 id="counter-app">Counter App</h2>
<p><img src="/assets/images/reactorKit_4.png" alt="reactorKit_4" /></p>
<p>간단하게 카운터 앱을 만들어보자.</p>
<p><br /></p>
<h3 id="명세서">명세서</h3>
<p>아래는 카운터 앱의 명세서이다.</p>
<ul>
<li>가운데에 숫자를 표시하는 <code class="language-plaintext highlighter-rouge">label</code>이 있다.</li>
<li>숫자를 표시하는 <code class="language-plaintext highlighter-rouge">label</code> 왼쪽에는 - <code class="language-plaintext highlighter-rouge">button</code>이, 오른쪽에는 + <code class="language-plaintext highlighter-rouge">button</code>이 있다.</li>
<li>-<code class="language-plaintext highlighter-rouge">button</code>을 tap하면 숫자가 1 감소한다.</li>
<li>+<code class="language-plaintext highlighter-rouge">button</code>을 tap하면 숫자가 1 증가한다.</li>
<li>숫자가 증감하는 동안 <code class="language-plaintext highlighter-rouge">label</code> 아래의 <code class="language-plaintext highlighter-rouge">indicator</code> 애니메이션이 실행된다.</li>
</ul>
<h3 id="step1">Step1</h3>
<p>화면을 구성했다.</p>
<p><img src="/assets/images/reactorKit_5.png" alt="reactorKit_5" /></p>
<h3 id="step2">Step2</h3>
<p><code class="language-plaintext highlighter-rouge">CounterViewReactor</code> 파일을 생성했다.</p>
<p>이름에서 보이듯 해당 앱의 <code class="language-plaintext highlighter-rouge">Reactor</code> 역할을 할 파일이므로 <code class="language-plaintext highlighter-rouge">Reactor</code> 프로토콜을 채택한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">CounterViewReactor</span><span class="p">:</span> <span class="kt">Reactore</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Reactor</code>는 <code class="language-plaintext highlighter-rouge">Action</code>, <code class="language-plaintext highlighter-rouge">State</code>, <code class="language-plaintext highlighter-rouge">Mutation</code>, <code class="language-plaintext highlighter-rouge">initialState</code>를 선언해줘야한다.</p>
<h4 id="action">Action</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Action</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">increase</span>
<span class="k">case</span> <span class="n">decrease</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Action</code> 사용자 인터랙션을 나타낸다.</p>
<p>카운터 앱에서는 값을 ‘증가/감소’ 시키는 인터랙션만 있으므로 <code class="language-plaintext highlighter-rouge">increase</code>, <code class="language-plaintext highlighter-rouge">decrease</code>를 선언한다.</p>
<h4 id="state">State</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">State</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Int</span>
<span class="k">var</span> <span class="nv">isLoading</span><span class="p">:</span> <span class="kt">Bool</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">State</code>는 현재 뷰의 상태를 표현한다.</p>
<p>카운터 앱에서는 숫자를 표시할 <code class="language-plaintext highlighter-rouge">value</code>와 indicatorView의 애니메이션 여부를 알려줄 <code class="language-plaintext highlighter-rouge">isLoading</code>이 필요하다.</p>
<h4 id="mutation">Mutation</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Mutation</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">increaseValue</span>
<span class="k">case</span> <span class="n">decreaseValue</span>
<span class="k">case</span> <span class="nf">setLoading</span><span class="p">(</span><span class="kt">Bool</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Mutation</code>은 ‘represent state changes’라고 한다.</p>
<p>즉, state의 변화를 표현한다.</p>
<p>+<code class="language-plaintext highlighter-rouge">button</code>을 tap하면 value <code class="language-plaintext highlighter-rouge">State</code>가 증가해야 하므로 <code class="language-plaintext highlighter-rouge">increaseValue</code>를 정의한다.</p>
<p>-<code class="language-plaintext highlighter-rouge">button</code>을 tap하면 value <code class="language-plaintext highlighter-rouge">State</code>가 감소해야 하므로 <code class="language-plaintext highlighter-rouge">decreaseValue</code>를 정의한다.</p>
<p>indicatorView 애니메이션 여부는 <code class="language-plaintext highlighter-rouge">Bool</code> 값을 통해 확인한다.</p>
<h4 id="initialstate-init">initialState, init()</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">initialState</span><span class="p">:</span> <span class="kt">State</span>
<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">initialState</span> <span class="o">=</span> <span class="kt">State</span><span class="p">(</span>
<span class="nv">value</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="nv">isLoading</span><span class="p">:</span> <span class="kc">false</span>
<span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>초기 <code class="language-plaintext highlighter-rouge">State</code>를 정의해준다.</p>
<p><code class="language-plaintext highlighter-rouge">CounterViewReactor</code>는 class이기 때문에 <code class="language-plaintext highlighter-rouge">init()</code>할 때 모든 프로퍼티가 정의되어야 한다.</p>
<p>따라서 <code class="language-plaintext highlighter-rouge">init()</code>에서 <code class="language-plaintext highlighter-rouge">initialState</code>를 정의해준다.</p>
<p>처음에는 숫자를 표시하는 <code class="language-plaintext highlighter-rouge">label</code>은 0 을 표시해야 하고, indicatorView의 애니메이션은 정지된 상태여야 한다.</p>
<h3 id="step3">Step3</h3>
<p><code class="language-plaintext highlighter-rouge">Reactor</code>는 전달받은 <code class="language-plaintext highlighter-rouge">Action</code>을 2가지 단계를 거쳐 <code class="language-plaintext highlighter-rouge">State</code>로 변환한다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">mutate()</code>: <code class="language-plaintext highlighter-rouge">Action</code> -> <code class="language-plaintext highlighter-rouge">Mutation</code></li>
<li><code class="language-plaintext highlighter-rouge">reduce()</code>: <code class="language-plaintext highlighter-rouge">Mutation</code> -> <code class="language-plaintext highlighter-rouge">State</code></li>
</ul>
<h4 id="mutate">mutate()</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">mutate</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="kt">Action</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Mutation</span><span class="o">></span> <span class="p">{</span>
<span class="k">switch</span> <span class="n">action</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">increase</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">concat</span><span class="p">([</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">(</span><span class="kt">Mutation</span><span class="o">.</span><span class="nf">setLoading</span><span class="p">(</span><span class="kc">true</span><span class="p">)),</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">(</span><span class="kt">Mutation</span><span class="o">.</span><span class="n">increaseValue</span><span class="p">)</span><span class="o">.</span><span class="nf">delay</span><span class="p">(</span><span class="o">.</span><span class="nf">milliseconds</span><span class="p">(</span><span class="mi">500</span><span class="p">),</span> <span class="nv">scheduler</span><span class="p">:</span> <span class="kt">MainScheduler</span><span class="o">.</span><span class="n">instance</span><span class="p">),</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">(</span><span class="kt">Mutation</span><span class="o">.</span><span class="nf">setLoading</span><span class="p">(</span><span class="kc">false</span><span class="p">))</span>
<span class="p">])</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">decrease</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">concat</span><span class="p">([</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">(</span><span class="kt">Mutation</span><span class="o">.</span><span class="nf">setLoading</span><span class="p">(</span><span class="kc">true</span><span class="p">)),</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">(</span><span class="kt">Mutation</span><span class="o">.</span><span class="n">decreaseValue</span><span class="p">)</span><span class="o">.</span><span class="nf">delay</span><span class="p">(</span><span class="o">.</span><span class="nf">milliseconds</span><span class="p">(</span><span class="mi">500</span><span class="p">),</span> <span class="nv">scheduler</span><span class="p">:</span> <span class="kt">MainScheduler</span><span class="o">.</span><span class="n">instance</span><span class="p">),</span>
<span class="kt">Observable</span><span class="o">.</span><span class="nf">just</span><span class="p">(</span><span class="kt">Mutation</span><span class="o">.</span><span class="nf">setLoading</span><span class="p">(</span><span class="kc">false</span><span class="p">))</span>
<span class="p">])</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>increase/decrease <code class="language-plaintext highlighter-rouge">Action</code>이 전달되면 <code class="language-plaintext highlighter-rouge">Mutation</code>으로 변환시킨다.</p>
<p>rxSwift는 나중에 다루도록 하고, 코드를 봐보자.</p>
<p>위에서 정의한 <code class="language-plaintext highlighter-rouge">enum Mutation</code>을 사용한다.</p>
<ul>
<li>
<p><code class="language-plaintext highlighter-rouge">Mutation.setLoading(true)</code>로 indicatorView의 애니메이션을 시작한다.</p>
</li>
<li>
<p><code class="language-plaintext highlighter-rouge">Mutation.increaseValue</code>로 value를 증가시키고, <code class="language-plaintext highlighter-rouge">delay(.milliseconds(500))</code>으로 지연을 준다.</p>
</li>
<li>
<p><code class="language-plaintext highlighter-rouge">Mutation.setLoading(false)</code>로 indicatorView의 애니메이션을 멈춘다.</p>
</li>
</ul>
<p>위의 과정을 담은 <code class="language-plaintext highlighter-rouge">Mutation</code>을 생성하고 반환한다.</p>
<h4 id="reduce">reduce()</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">reduce</span><span class="p">(</span><span class="nv">state</span><span class="p">:</span> <span class="kt">State</span><span class="p">,</span> <span class="nv">mutation</span><span class="p">:</span> <span class="kt">Mutation</span><span class="p">)</span> <span class="o">-></span> <span class="kt">State</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">state</span> <span class="o">=</span> <span class="n">state</span>
<span class="k">switch</span> <span class="n">mutation</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">increaseValue</span><span class="p">:</span>
<span class="n">state</span><span class="o">.</span><span class="n">value</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">decreaseValue</span><span class="p">:</span>
<span class="n">state</span><span class="o">.</span><span class="n">value</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">setLoading</span><span class="p">(</span><span class="n">isLoading</span><span class="p">):</span>
<span class="n">state</span><span class="o">.</span><span class="n">isLoading</span> <span class="o">=</span> <span class="n">isLoading</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">state</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">reduce()</code>는 이전의 <code class="language-plaintext highlighter-rouge">State</code>와 전달받은 <code class="language-plaintext highlighter-rouge">Mutation</code>으로 새로운 <code class="language-plaintext highlighter-rouge">State</code>를 생성한다.</p>
<p><code class="language-plaintext highlighter-rouge">case .increaseValue</code>를 보자.</p>
<p><code class="language-plaintext highlighter-rouge">state.value += 1</code>이 실행되는데 이는, <code class="language-plaintext highlighter-rouge">이전의 State.value += 1</code>이라고 볼 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">return state</code>에서의 state는 새로운 <code class="language-plaintext highlighter-rouge">State</code>로 볼 수 있다.</p>
<h3 id="step4">Step4</h3>
<p><code class="language-plaintext highlighter-rouge">Reactor</code> 구성을 완료했으니 <code class="language-plaintext highlighter-rouge">View</code>를 구성하자.</p>
<p>Reactor에서 View를 구성하기 위해서는 <code class="language-plaintext highlighter-rouge">View</code> 프로토콜을 채택해야 한다.</p>
<p>해당 예제는 Storyboard로 화면을 구성했으므로 <code class="language-plaintext highlighter-rouge">StoryboardView</code> 프로토콜을 채택한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">ViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="kt">StoryboardView</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</code></pre></div></div>
<p><br /></p>
<p>rxSwift를 사용하므로 <code class="language-plaintext highlighter-rouge">var disposeBag = DisposeBag()</code>을 선언해준다.</p>
<h4 id="reactor">reactor</h4>
<p><code class="language-plaintext highlighter-rouge">ViewController</code>는 <code class="language-plaintext highlighter-rouge">StoryboardView</code>를 채택함으로써 <code class="language-plaintext highlighter-rouge">reactor</code>라는 프로퍼티가 생겼다.</p>
<p><code class="language-plaintext highlighter-rouge">View</code>의 밖에서 <code class="language-plaintext highlighter-rouge">reactor</code> 프로퍼티를 지정해준다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">viewController</span><span class="o">.</span><span class="n">reactor</span> <span class="o">=</span> <span class="kt">CounterViewReactor</span><span class="p">()</span>
</code></pre></div></div>
<h4 id="bind">bind()</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">bind</span><span class="p">(</span><span class="nv">reactor</span><span class="p">:</span> <span class="kt">CounterViewReactor</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Action</span>
<span class="n">increaseButton</span><span class="o">.</span><span class="n">rx</span><span class="o">.</span><span class="n">tap</span>
<span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="kt">Reactor</span><span class="o">.</span><span class="kt">Action</span><span class="o">.</span><span class="n">increase</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">bind</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">reactor</span><span class="o">.</span><span class="n">action</span><span class="p">)</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disopseBag</span><span class="p">)</span>
<span class="n">decreaseButton</span><span class="o">.</span><span class="n">rx</span><span class="o">.</span><span class="n">tap</span>
<span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="kt">Reactor</span><span class="o">.</span><span class="kt">Action</span><span class="o">.</span><span class="n">decrease</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">bind</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">reactor</span><span class="o">.</span><span class="n">action</span><span class="p">)</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="c1">// State</span>
<span class="n">reactor</span><span class="o">.</span><span class="n">state</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">value</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">distinctUntilChanged</span><span class="p">()</span>
<span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="s">"</span><span class="se">\(</span><span class="nv">$0</span><span class="se">)</span><span class="s">"</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">bind</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">valueLabel</span><span class="o">.</span><span class="n">rx</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="n">reactor</span><span class="o">.</span><span class="n">state</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">isLoading</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">distinctUntilChanged</span><span class="p">()</span>
<span class="o">.</span><span class="nf">bind</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">activityIndicatorView</span><span class="o">.</span><span class="n">rx</span><span class="o">.</span><span class="n">isAnimating</span><span class="p">)</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">disposeBag</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">View</code>는 <code class="language-plaintext highlighter-rouge">Action</code>을 전달하기도 하고 <code class="language-plaintext highlighter-rouge">State</code>를 받기도 한다.</p>
<p>따라서 <code class="language-plaintext highlighter-rouge">bind()</code>에 <code class="language-plaintext highlighter-rouge">Action</code>과 <code class="language-plaintext highlighter-rouge">State</code> 둘 다에 대해서 구현해야 한다.</p>
<hr />
<h2 id="outro">Outro</h2>
<p>간단한 예제를 구현해 봤다.</p>
<p>역시 직접 구현해보는게 이해하는데 더 빠른거 같다.</p>
<p><br /></p>
<p>구현하면서 느낀점은</p>
<ul>
<li>
<p><code class="language-plaintext highlighter-rouge">View</code>, <code class="language-plaintext highlighter-rouge">Reactor</code>로 구분을 하니 역할 분담이 명확하게 잘 되는거 같다.</p>
</li>
<li>
<p>일정한 패턴이 있다보니 손에 익으면 작성이 좀 더 쉬워질거 같다.</p>
</li>
<li>
<p>한 화면에서 관리하는 <code class="language-plaintext highlighter-rouge">Action</code>, <code class="language-plaintext highlighter-rouge">State</code>가 많아지면 <code class="language-plaintext highlighter-rouge">bind()</code> 관리가 어려워지지 않을까?</p>
</li>
</ul>
<p>다음에는 좀 더 복잡한 예제를 구현하면서 <code class="language-plaintext highlighter-rouge">ReactorKit</code>에 대해 느낀 점을 정리해봐야겠다.</p>lasagnaIntroReactorKit - 22021-10-02T01:35:00+09:002021-10-02T01:35:00+09:00https://wonhee009.github.io/reactorkit/ReactorKit_2<h2 id="intro">Intro</h2>
<p><a href="https://wonhee009.github.io/reactorkit/ReactorKit/">이전 포스트</a>에서 ReactorKit의 컨셉을 알아봤다.</p>
<p>이번 포스트에서는 ReactorKit의 Advanced에 대해서 알아보려 한다.</p>
<hr />
<h2 id="advanced">Advanced</h2>
<h3 id="global-states">Global States</h3>
<p>Redux와 달리 ReactorKit은 전역 앱 상태를 정의하지 않는다.</p>
<p>즉, 전역 상태를 관리하기 위해 무엇이든 사용할 수 있다.</p>
<p><code class="language-plaintext highlighter-rouge">BehaviorSubject</code>, <code class="language-plaintext highlighter-rouge">PublishSubject</code>, <code class="language-plaintext highlighter-rouge">Reactor</code>를 사용할 수 있다.</p>
<p>ReactorKit은 전역 상태에서 사용되도록 강제하지 않으므로 App의 특정 기능에서만 ReactorKit을 사용할 수 있다.</p>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">Action</code> -> <code class="language-plaintext highlighter-rouge">Mutation</code> -> <code class="language-plaintext highlighter-rouge">State</code> 흐름에는 전역 상태가 없다.</p>
<p>전역 상태를 <code class="language-plaintext highlighter-rouge">Mutation</code>으로 변환하려면 <code class="language-plaintext highlighter-rouge">transform(mutation:)</code>을 사용해야 한다.</p>
<blockquote>
<p>처음에 위의 글을 봤을때 잘 이해가 가지 않았다.</p>
<p>생각해보니 ReactorKit의 큰 흐름은 <code class="language-plaintext highlighter-rouge">Action</code> -> <code class="language-plaintext highlighter-rouge">Mutation</code> -> <code class="language-plaintext highlighter-rouge">State</code>인데 이는 전역적으로 사용하고 있는 변수 등등과는 연관성이 없다.</p>
<p>따라서 전역적으로 사용하고 있는 변수 등등을 어떤 식으로 처리해 ReactorKit의 흐름으로 편입시킬 수 있는지에 대한 얘기인거 같다.</p>
</blockquote>
<p>전역적으로 사용되는 <code class="language-plaintext highlighter-rouge">currentUser</code>라는 BehaviorSubject가 있다고 가정해보자.</p>
<p><code class="language-plaintext highlighter-rouge">currentUser</code>는 현재 인증된 사용자에 대해서 저장하고 있고, 인증된 사용자가 변경될때마다 <code class="language-plaintext highlighter-rouge">Mutation.setUser(User?)</code>를 방출해야 한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">currentUser</span><span class="p">:</span> <span class="kt">BehaviorSubject</span><span class="o"><</span><span class="kt">User</span><span class="o">></span> <span class="c1">// global state</span>
<span class="kd">func</span> <span class="nf">transform</span><span class="p">(</span><span class="nv">mutation</span><span class="p">:</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Mutation</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Mutation</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">Observable</span><span class="o">.</span><span class="nf">merge</span><span class="p">(</span><span class="n">mutation</span><span class="p">,</span> <span class="n">currentUser</span><span class="o">.</span><span class="nf">map</span><span class="p">(</span><span class="kt">Mutation</span><span class="o">.</span><span class="n">setUser</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li><code class="language-plaintext highlighter-rouge">currentUser</code>는 BehaviorSubject로 전역적으로 사용되고 있다.</li>
<li><code class="language-plaintext highlighter-rouge">transform(mutation:)</code>을 통해 ReactorKit의 흐름으로 편입시킨다.</li>
</ul>
<p>위의 코드를 통해 view가 reactor에 action을 보내고 currentUser가 변경될 때마다 Mutaion이 발생한다.</p>
<h3 id="view-communication">View Communication</h3>
<p>현재 다수의 view들의 communication에 callback 클로저나 delegate 패턴이 익숙하다.</p>
<p>ReactorKit은 <code class="language-plaintext highlighter-rouge">reactive extension</code>을 사용하도록 권장한다.</p>
<p>ControlEvent의 가장 일반적인 예는 UIButton.rx.tap이다.</p>
<p>custom view를 UIButton 또는 UILabel로 처리하는 경우를 예로 들 수 있다.</p>
<p><br /></p>
<p><img src="/assets/images/reactorKit_3.png" alt="reactorKit_3" /></p>
<p>메시지를 표시하는 <code class="language-plaintext highlighter-rouge">ChatViewController</code>가 있다고 생각해보자.</p>
<p><code class="language-plaintext highlighter-rouge">ChatViewController</code>는 <code class="language-plaintext highlighter-rouge">MessageInputView</code>를 가지고 있다.</p>
<p>User가 <code class="language-plaintext highlighter-rouge">MessageInputView</code>에서 보내기 버튼을 탭하면 텍스트가 <code class="language-plaintext highlighter-rouge">ChatViewController</code>로 전송되고 <code class="language-plaintext highlighter-rouge">ChatViewController</code>가 reactor의 action에 바인딩된다.</p>
<p><br /></p>
<pre><code class="language-Swift">extension Reactive where Base: MessageInputView {
var sendButtonTap: ControlEvent<String> {
let source = base.sendButton.rx.tap.withLatestFrom(...)
return ControlEvent(events: source)
}
}
</code></pre>
<p><code class="language-plaintext highlighter-rouge">MessageInputView</code>의 reactive extension이다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">messageInputView</span><span class="o">.</span><span class="n">rx</span><span class="o">.</span><span class="n">sendButtonTap</span>
<span class="o">.</span><span class="nf">map</span><span class="p">(</span><span class="kt">Reactor</span><span class="o">.</span><span class="kt">Action</span><span class="o">.</span><span class="n">send</span><span class="p">)</span>
<span class="o">.</span><span class="nf">bind</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">reactor</span><span class="o">.</span><span class="n">action</span><span class="p">)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">ChatViewController</code>에서는 위와 같은 방식으로 extension을 사용할 수 있다.</p>
<h3 id="testing">Testing</h3>
<p>ReactorKit은 테스트를 위한 기능이 내장되어 있다.</p>
<p>해당 기능을 사용해서 view와 reactor 모두 쉽게 테스트할 수 있다.</p>
<p><br /></p>
<p>아래와 같은 것들을 테스트할 수 있다.</p>
<ul>
<li>View
<ul>
<li>Action: 주어진 유저 interaction을 적절한 action으로 reactor로 전송했는가</li>
<li>State: 따르고 있는 state에서 view의 프로퍼티가 제대로 설정되어 있는가</li>
</ul>
</li>
<li>Reactor
<ul>
<li>State: state가 적절하게 action으로 변경되었는가</li>
</ul>
</li>
</ul>
<h4 id="view-testing">View testing</h4>
<p>view는 <code class="language-plaintext highlighter-rouge">stub</code> reactor로 테스트할 수 있다.</p>
<p>reactor는 action 로그를 기록하고 state를 강제로 변경할 수 있는 <code class="language-plaintext highlighter-rouge">stub</code> 프로퍼티를 가지고 있다.</p>
<p>reactor의 <code class="language-plaintext highlighter-rouge">stub</code>이 사용가능하다면 <code class="language-plaintext highlighter-rouge">mutate()</code>와 <code class="language-plaintext highlighter-rouge">reduce()</code> 모두 실행되지 않는다.</p>
<p><code class="language-plaintext highlighter-rouge">stub</code>에는 다음과 같은 속성이 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">state</span><span class="p">:</span> <span class="kt">StateRelay</span><span class="o"><</span><span class="kt">Reactor</span><span class="o">.</span><span class="kt">State</span><span class="o">></span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">action</span><span class="p">:</span> <span class="kt">ActionSubject</span><span class="o"><</span><span class="kt">Reactor</span><span class="o">.</span><span class="kt">Action</span><span class="o">></span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">actions</span><span class="p">:</span> <span class="p">[</span><span class="kt">Reactor</span><span class="o">.</span><span class="kt">Action</span><span class="p">]</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="c1">// recorded actions</span>
</code></pre></div></div>
<h4 id="reactor-testing">Reactor testing</h4>
<p>reactor는 독립적으로 테스트할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">testIsBookmarked</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">reactor</span> <span class="o">=</span> <span class="kt">MyReactor</span><span class="p">()</span>
<span class="n">reactor</span><span class="o">.</span><span class="n">action</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="o">.</span><span class="n">toggleBookmarked</span><span class="p">)</span>
<span class="kt">XCTAssertEqual</span><span class="p">(</span><span class="n">reactor</span><span class="o">.</span><span class="n">currentState</span><span class="o">.</span><span class="n">isBookmarked</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
<span class="n">reactor</span><span class="o">.</span><span class="n">action</span><span class="o">.</span><span class="nf">onNext</span><span class="p">(</span><span class="o">.</span><span class="n">toggleBookmarked</span><span class="p">)</span>
<span class="kt">XCTAssertEqual</span><span class="p">(</span><span class="n">reactor</span><span class="o">.</span><span class="n">currentState</span><span class="o">.</span><span class="n">isBookmarked</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p><br /></p>
<p>가끔 state는 하나의 action에 의해 두 번 이상 변경된다.</p>
<p>예를 들어, <code class="language-plaintext highlighter-rouge">.refresh</code> 작업은 처음에는 <code class="language-plaintext highlighter-rouge">state.isLoading</code>을 true로 설정하고 새로 고침 후에는 false로 설정한다.</p>
<p>이런 경우 currentState로 <code class="language-plaintext highlighter-rouge">state.isLoading</code>을 테스트하기 어렵기 때문에 <code class="language-plaintext highlighter-rouge">RxTest</code> 또는 <code class="language-plaintext highlighter-rouge">RxExpect</code>를 사용해야 할 수도 있다.</p>
<h4 id="scheduling">Scheduling</h4>
<p>state 스트림을 줄이고 observing하는데 사용되는 스케줄러를 지정하려면 스케줄러 프로퍼티를 정의한다.</p>
<p>이 대기열은 직렬 대기열이다.</p>
<p>기본 스케줄러는 <code class="language-plaintext highlighter-rouge">CurrentThreadScheduler</code>이다.</p>
<hr />
<h2 id="outro">Outro</h2>
<p>ReactorKit의 Advanced에 대해서 알아봤다.</p>
<p>뭔가 알듯 말듯하다….ㅋㅋㅋㅋ</p>
<p>예시를 보면서 좀 더 알아봐야할거 같다.</p>lasagnaIntroReactorKit - 12021-09-28T22:55:00+09:002021-09-28T22:55:00+09:00https://wonhee009.github.io/reactorkit/ReactorKit_1<h2 id="intro">Intro</h2>
<p>ReactorKit에 대해 알아보려고 한다.</p>
<p><a href="https://github.com/ReactorKit/ReactorKit">ReactorKit github</a>을 기준으로 알아보고 간단한 예제를 만들어볼까 한다.</p>
<p>rxSwift에 대한 이해가 떨어지지만 rxSwift에 대한 학습은 이후로 미뤄본다….</p>
<hr />
<h2 id="basic-concept">Basic Concept</h2>
<p><img src="/assets/images/reactorKit_1.png" alt="reactorKit_1" /></p>
<p>(ReactorKit을 살펴보면서 가장 중요한 다이어그램이라고 생각한다.)</p>
<ul>
<li>user action과 view state는 observable 스트림을 통해 다른 레이어로 전달된다.</li>
<li>스트림은 <strong>단방향</strong>이다.</li>
<li>View는 action만 Reactor는 state만 방출한다.</li>
</ul>
<h3 id="view">View</h3>
<p>View는 data를 표시한다.</p>
<p>(ViewController와 cell은 View로 다뤄진다.)</p>
<p>View는 사용자 입력을 action 스트림에 바인딩하고 View state를 각 UI 구성 요소에 바인딩한다.</p>
<p>View 레이어에는 <strong>비즈니스 로직을 포함하지 않는다.</strong></p>
<p>View에는 <strong>action 스트림과 state 스트림을 어떻게 매핑할지를 정의</strong>한다.</p>
<p><br /></p>
<p>view를 정의하기 위해서 <code class="language-plaintext highlighter-rouge">View</code> protocol을 채택한다.</p>
<p>프로토콜을 채택하면 <code class="language-plaintext highlighter-rouge">reactor</code><strong>라는 프로퍼티가 자동으로 생긴다.</strong></p>
<p><code class="language-plaintext highlighter-rouge">reactor</code>는 <strong>view의 밖에서 지정</strong>된다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ProfileViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="kt">View</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">disposeBag</span> <span class="o">=</span> <span class="kt">DisposeBag</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">profileViewController</span><span class="o">.</span><span class="n">reactor</span> <span class="o">=</span> <span class="kt">ProfileViewReactor</span><span class="p">()</span>
</code></pre></div></div>
<ul>
<li>ProfileViewController가 <code class="language-plaintext highlighter-rouge">View</code> 프로토콜을 채택하고 있으므로 <code class="language-plaintext highlighter-rouge">reactor</code> 프로퍼티가 생겼다.</li>
<li><code class="language-plaintext highlighter-rouge">reactor</code>는 view의 밖에서 지정되므로 외부에서 profileViewController에 <code class="language-plaintext highlighter-rouge">reactor</code>를 지정한다.</li>
</ul>
<p><br /></p>
<p><code class="language-plaintext highlighter-rouge">reactor</code> 프로퍼티가 바뀔때 <code class="language-plaintext highlighter-rouge">bind(reactor:)</code>가 호출된다.</p>
<p><code class="language-plaintext highlighter-rouge">bind(reactor:)</code>메서드를 구현하여 action, state 스트림 바인딩을 정의한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">bind</span><span class="p">(</span><span class="nv">reactor</span><span class="p">:</span> <span class="kt">ProfileViewReactor</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// action (View -> Reactor)</span>
<span class="n">refreshButton</span><span class="o">.</span><span class="n">rx</span><span class="o">.</span><span class="n">tap</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="kt">Reactor</span><span class="o">.</span><span class="kt">Action</span><span class="o">.</span><span class="n">refresh</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">bind</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">reactor</span><span class="o">.</span><span class="n">action</span><span class="p">)</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">disposeBag</span><span class="p">)</span>
<span class="c1">// state (Reactor -> View)</span>
<span class="n">reactor</span><span class="o">.</span><span class="n">state</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">isFollowing</span> <span class="p">}</span>
<span class="o">.</span><span class="nf">bind</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">follwButton</span><span class="o">.</span><span class="n">rx</span><span class="o">.</span><span class="n">isSelected</span><span class="p">)</span>
<span class="o">.</span><span class="nf">disposed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">disposeBag</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>refreshButton이 tap되면 <code class="language-plaintext highlighter-rouge">refresh</code>라는 action으로 바인딩한다.</li>
<li>reactor의 state 중 isFollowing을 바인딩한다.</li>
</ul>
<h3 id="reactor">Reactor</h3>
<p>Reactor는 UI 독립적인 레이어로 view의 state를 관리한다.</p>
<p>Reactor의 가장 중요한 역할은 제어 흐름을 view에서 분리하는 것이다.</p>
<p><strong>모든 view에는 관련된 reactor가 있고, 모든 로직은 reactor에게 위임한다.</strong></p>
<p>reactor는 view에 의존성이 없어서 test가 쉽다.</p>
<p><br /></p>
<ul>
<li>
<p><code class="language-plaintext highlighter-rouge">Reactor</code> 프로토콜을 채택해 reactor를 정의한다.</p>
</li>
<li>
<p><code class="language-plaintext highlighter-rouge">Reactor</code> 프로토콜은 <code class="language-plaintext highlighter-rouge">Action</code>, <code class="language-plaintext highlighter-rouge">Mutation</code>, <code class="language-plaintext highlighter-rouge">State</code> 세 가지 타입이 필요하다.</p>
</li>
<li>
<p><code class="language-plaintext highlighter-rouge">Reactor</code> 프로토콜은 <code class="language-plaintext highlighter-rouge">initialState</code>라는 프로퍼티가 필요하다.</p>
</li>
</ul>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ProfileViewReactor</span><span class="p">:</span> <span class="kt">Reactor</span> <span class="p">{</span>
<span class="c1">// represent user actions</span>
<span class="kd">enum</span> <span class="kt">Action</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">refreshFollowingStatus</span><span class="p">(</span><span class="kt">Int</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">follow</span><span class="p">(</span><span class="kt">Int</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// represent state changes</span>
<span class="kd">enum</span> <span class="kt">Mutation</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">setFollowing</span><span class="p">(</span><span class="kt">Bool</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// represent the current view state</span>
<span class="kd">struct</span> <span class="kt">State</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">isFollowing</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">initialState</span><span class="p">:</span> <span class="kt">State</span> <span class="o">=</span> <span class="kt">State</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Action은 사용자 인터랙션을 나타내고 State는 view state를 나타낸다.</p>
<p>Mutation은 Action과 State 사이를 연결해준다.</p>
<p>reactor는 <code class="language-plaintext highlighter-rouge">mutation()</code>과 <code class="language-plaintext highlighter-rouge">reduce()</code> <strong>두 단계로 action 스트림을 state 스트림으로 변환</strong>한다.</p>
<p><br /></p>
<p><img src="/assets/images/reactorKit_2.png" alt="reactorKit_2" /></p>
<p>View와 Reactor에서 일어나는 과정을 좀 더 자세하게 본다면 위의 다이어그램과 같다.</p>
<p><code class="language-plaintext highlighter-rouge">Reactor</code>가 Action을 받아서 어떻게 State를 emit하는지가 핵심이다.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Reactor</code>는 Action을 받아 <code class="language-plaintext highlighter-rouge">mutate()</code>를 실행한다.</li>
<li><code class="language-plaintext highlighter-rouge">mutate()</code>는 Action으로 Mutation을 생성한다.</li>
<li><code class="language-plaintext highlighter-rouge">reduce()</code>는 Mutation으로 State를 생성한다.</li>
</ul>
<h4 id="mutate">mutate()</h4>
<p><code class="language-plaintext highlighter-rouge">mutate()</code>는 Action을 받아서 <code class="language-plaintext highlighter-rouge">Observable<Mutation></code>을 생성한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">mutate</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="kt">Action</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Mutation</span><span class="o">></span>
</code></pre></div></div>
<p><br /></p>
<p>비동기 작업이나 API 호출과 같은 side effect 작업은 해당 메서드에서 실행된다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">mutate</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="kt">Action</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Mutation</span><span class="o">></span> <span class="p">{</span>
<span class="k">switch</span> <span class="n">action</span> <span class="p">{</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">refreshFollowingStatus</span><span class="p">(</span><span class="n">userID</span><span class="p">):</span> <span class="c1">// receive an action</span>
<span class="k">return</span> <span class="kt">UserAPI</span><span class="o">.</span><span class="nf">isFollowing</span><span class="p">(</span><span class="n">userID</span><span class="p">)</span> <span class="c1">// create an API stream</span>
<span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="p">(</span><span class="nv">isFollowing</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Mutation</span> <span class="k">in</span>
<span class="k">return</span> <span class="kt">Mutation</span><span class="o">.</span><span class="nf">setFollowing</span><span class="p">(</span><span class="n">isFollowing</span><span class="p">)</span> <span class="c1">// convert to Mutation stream</span>
<span class="p">}</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">follow</span><span class="p">(</span><span class="n">userID</span><span class="p">):</span>
<span class="k">return</span> <span class="kt">UserAPI</span><span class="o">.</span><span class="nf">follow</span><span class="p">()</span>
<span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">_</span> <span class="o">-></span> <span class="kt">Mutation</span> <span class="k">in</span>
<span class="k">return</span> <span class="kt">Mutation</span><span class="o">.</span><span class="nf">setFollowing</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="reduce">reduce()</h4>
<p><code class="language-plaintext highlighter-rouge">reduce()</code>는 이전의 State와 Mutation으로 새로운 <code class="language-plaintext highlighter-rouge">State</code>를 생성한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">reduce</span><span class="p">(</span><span class="nv">state</span><span class="p">:</span> <span class="kt">State</span><span class="p">,</span> <span class="nv">mutation</span><span class="p">:</span> <span class="kt">Mutation</span><span class="p">)</span> <span class="o">-></span> <span class="kt">State</span>
</code></pre></div></div>
<p><br /></p>
<p>이 메서드는 순수 함수이다.</p>
<p>새로운 <code class="language-plaintext highlighter-rouge">State</code>를 동기식으로 반환한다.</p>
<p>비동기 작업이나 API 호출과 같은 side effect 작업은 해당 메서드에서 실행하지 않는다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">reduce</span><span class="p">(</span><span class="nv">state</span><span class="p">:</span> <span class="kt">State</span><span class="p">,</span> <span class="nv">mutation</span><span class="p">:</span> <span class="kt">Mutation</span><span class="p">)</span> <span class="o">-></span> <span class="kt">State</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">state</span> <span class="o">=</span> <span class="n">state</span> <span class="c1">// creawte a copy of the old state</span>
<span class="k">switch</span> <span class="n">mutation</span> <span class="p">{</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">setFollowing</span><span class="p">(</span><span class="n">isFollowing</span><span class="p">):</span>
<span class="n">state</span><span class="o">.</span><span class="n">isFollowing</span> <span class="o">=</span> <span class="n">isFollowing</span> <span class="c1">// manipulate the state, creating a new stae</span>
<span class="k">return</span> <span class="n">state</span> <span class="c1">// return the new state</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="transform">transform()</h4>
<p><code class="language-plaintext highlighter-rouge">transform()</code>은 각 스트림으로 변환한다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">transform</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Action</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Action</span><span class="o">></span>
<span class="kd">func</span> <span class="nf">transform</span><span class="p">(</span><span class="nv">mutation</span><span class="p">:</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Mutation</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Mutation</span><span class="o">></span>
<span class="kd">func</span> <span class="nf">transform</span><span class="p">(</span><span class="nv">state</span><span class="p">:</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">State</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">State</span><span class="o">></span>
</code></pre></div></div>
<p>위와 같은 메서드를 구현해 다른 observable 스트림으로 변환하고 결합한다.</p>
<p>예를 들어 <code class="language-plaintext highlighter-rouge">transform(mutation:)</code>은 global event 스트림을 mutation 스트림에 결합할 수 있다.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">transform</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Action</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Observable</span><span class="o"><</span><span class="kt">Action</span><span class="o">></span> <span class="p">{</span>
<span class="k">return</span> <span class="n">action</span><span class="o">.</span><span class="nf">debug</span><span class="p">(</span><span class="s">"action"</span><span class="p">)</span> <span class="c1">// Use RxSwift's debug() operator</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위의 메서드는 디버깅 목적으로 사용할 수 있다.</p>
<hr />
<h2 id="outro">Outro</h2>
<p>ReactorKit의 Basic Concept에 대해서 알아봤다.</p>
<p>대강의 흐름을 알 수 있었고, 코드를 까보고 싶어졌다….ㅋㅋㅋ</p>lasagnaIntroWebSockets with Swift - 12021-09-12T02:00:00+09:002021-09-12T02:00:00+09:00https://wonhee009.github.io/websocket/Websocket<h1 id="intro">Intro</h1>
<p><a href="https://www.raywenderlich.com/13209594-an-introduction-to-websockets#toc-anchor-013">Raywenderlich</a> 로 웹소켓을 공부해보려 한다….ㅎ</p>
<p>웹소켓에 좋은 기억은 없지만…ㅋㅋㅋ 이번에는 잘 해봐야지…</p>
<p><br /></p>
<p>웹소켓은 <strong>서버와 클라이언트 간의 양방향 통신</strong>을 허용하는 네트워크 프로토콜이다.</p>
<p>Request-Response 패턴을 사용하는 HTTP와 달리 <strong>웹소켓은 시점과 방향에 상관없이 메시지를 보낼 수 있다.</strong></p>
<p>따라서 웹소켓은 채팅과 같이 서버와 클라이언트 간에 지속적으로 대화해야하는 서비스에 자주 사용된다.</p>
<hr />
<h2 id="websocket-connect">WebSocket Connect</h2>
<p>웹소켓 연결은 handshake에서 시작한다.</p>
<p>클라이언트는 일반 HTTP request에 <code class="language-plaintext highlighter-rouge">Upgrade: WebSocket</code>, <code class="language-plaintext highlighter-rouge">Connection: Upgrade</code> 라는 특별한 header와 인증과 같은 다른 필수 요청 데이터와 함께 보낸다.</p>
<p>서버는 <code class="language-plaintext highlighter-rouge">HTTP 101 스위칭 프로토콜 상태 코드</code>를 클라이언트에 다시 보낸다.</p>
<p><code class="language-plaintext highlighter-rouge">HTTP 101 스위치 프로토콜 상태 코드</code>와 함께 <code class="language-plaintext highlighter-rouge">Upgreade: WebSocket</code>, <code class="language-plaintext highlighter-rouge">Connection: Upgrade</code> header를 같이 보낸다.</p>
<p>이런 과정을 거쳐 handshake가 완료되고 웹소켓 연결이 완료된다.</p>
<h2 id="websocket-message">WebSocket Message</h2>
<p>웹소켓 프로토콜에서 데이터는 <strong>프레임 시퀀스</strong>를 사용하여 전송된다.</p>
<p>웹소켓 프레임은 HTTP 헤더와 비교할 수 있는 몇 가지 비트와 실제 메시지로 구성되어 있다.</p>
<p>메시지가 너무 크면 여러 프레임을 사용해 보낼 수 있다.</p>
<p><br /></p>
<ul>
<li>FIN: <strong>단일 비트</strong>로 메시지의 마지막 프레임인지 뒤에 더 많은 프레임이 있는지 나타낸다.</li>
<li>opcode: <strong>4비트</strong>로 메시지의 유형과 페이로드 데이터를 처리하는 방법을 나타낸다.</li>
<li>MASK: <strong>단일 비트</strong>로 메시지가 마스킹되었는지 여부를 나타낸다. 클라이언트-서버 메시지는 항상 마스킹되어야 한다.</li>
<li>Payload length: <strong>7/23/71비트</strong>로 HTTP의 Content-Length 헤더와 마찬가지로 페이로드 데이터가 비트 단위로 얼마나 큰지 설명한다.</li>
<li>Masking key: <strong>4개의 선택적 비트</strong>로 메시지를 마스킹하는데 사용한 키가 포함된다. MASK 비트가 0이면 생략 가능하다.</li>
<li>Payload data: 나머지 메시지에는 실제 페이로드가 포함된다.</li>
</ul>
<hr />
<h1 id="outro">Outro</h1>
<p>웹소켓에 대해서 간단하게 알아봤다.</p>
<p>Raywenderlich에 코드가 다 올라와 있지만 이거에 대해서 설명하는건 의미가 없다고 생각한다.</p>
<p>직접 코드를 짜고 내가 이해한 내용을 계속 적어볼까 한다.</p>lasagnaIntro