<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>taekki.dev</title>
    <link>https://taekki-dev.tistory.com/</link>
    <description> iOS 개발자를 꿈꾸는 학생이 만드는 블로그</description>
    <language>ko</language>
    <pubDate>Wed, 8 Jul 2026 15:52:56 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>taeeekki</managingEditor>
    <image>
      <title>taekki.dev</title>
      <url>https://tistory1.daumcdn.net/tistory/4580953/attach/42b4e41141a24e388841a706157382ea</url>
      <link>https://taekki-dev.tistory.com</link>
    </image>
    <item>
      <title>[Git] CLI로 .gitignore 생성하기</title>
      <link>https://taekki-dev.tistory.com/135</link>
      <description>&lt;h1 id=&quot;user-content-문제&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0#%EB%AC%B8%EC%A0%9C&quot;&gt;문제&lt;/a&gt;&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;.gitignore 파일을 만드는 사람들의 행동이 몇 가지 존재합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.toptal.com/developers/gitignore/&quot;&gt;gitignore.io 사이트&lt;/a&gt;에 가서 키워드 입력 후 생성된 내용을 .gitignore 파일에 붙여넣는다.&lt;/li&gt;
&lt;li&gt;자주 사용하는 내용과 형식을 만들어놓고 복사/붙여넣기 한다.&lt;/li&gt;
&lt;li&gt;프로젝트 템플릿을 사용한다면 템플릿으로 프로젝트를 생성한다.(.gitignore 파일 포함)&lt;/li&gt;
&lt;li&gt;CLI로 만든다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 자주 하다보면 똑같은 gitignore 파일 만드는 게 생각보다 귀찮습니다.&lt;br /&gt;제가 그렇습니다. 오히려 Command-Line으로 만드는 게 훨씬 편하다는 생각이 듭니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://docs.gitignore.io/install/command-line&quot;&gt;Toptal 사이트&lt;/a&gt;에 가보면 Document에 방법이 정리되어 있습니다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-사용&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0#%EC%82%AC%EC%9A%A9&quot;&gt;사용&lt;/a&gt;&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;저는 여러 Shell 중에서 fish-shell을 사용하기 때문에 터미널에 다음과 같이 입력해줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;lsl&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;printf &quot;function gi\n\tcurl -sL https://www.toptal.com/developers/gitignore/api/\$argv\nend\n&quot; &amp;gt; ~/.config/fish/functions/gi.fish
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;터미널(command-line interface, shell)에서 사용할 명령어를 정의하는 과정이라고 생각하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;그리고 명령어(gi)를 키워드와 함께 입력하고, gitignore 파일에 덮어씁니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;gi xcode,swift,objective-c,macos,swiftpackagemanager,swiftpm,cocoapods,git,fastlane,homebrew,firebase &amp;gt;&amp;gt; .gitignore
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화살표(&amp;gt;&amp;gt;)는 리눅스 명령어인데 하나만 쓰는 경우도 있고, 두개 쓰는 경우도 있습니다.&lt;/li&gt;
&lt;li&gt;화살표(&amp;gt;)는 덮어쓰기, 화살표(&amp;gt;&amp;gt;)는 이어서 추가라고 생각하면 되고, 둘 다 존재하지 않는 파일에 내용을 쓰는 경우에는 파일을 새로 만들어줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;user-content-마무리&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0#%EB%A7%88%EB%AC%B4%EB%A6%AC&quot;&gt;마무리&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 본인에게 편한 방법 쓰는게 짱입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;gitignore 추가하는 작업은 일반적으로 프로젝트 개발 초기에 진행하기 때문에 Repository clone 받고 수행하면 좋을 것 같습니다.&lt;/li&gt;
&lt;li&gt;자주 쓰는 키워드도 미리 정의해두고 사용해도 괜찮을 것 같습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;awk&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;// ~/.config/fish/functions/gig.fish
function gig
 gi xcode,swift,objective-c,macos,swiftpackagemanager,swiftpm,cocoapods,git,fastlane,homebrew,firebase &amp;gt;&amp;gt; .gitignore
end
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;nginx&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;gig # 현재 경로에 iOS 프로젝트 관련 gitignore 파일 생성
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저는 gig 명령어를 입력하면 바로 gitignore 파일이 생성되도록 정의해놓고 사용하고 있습니다.&lt;/li&gt;
&lt;li&gt;위의 함수 방법은 fish shell에 해당하는 방법이므로 본인 shell(bash, zsh 등)에 맞추어 사용하는 것을 권장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;user-content-스크린샷&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0#%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7&quot;&gt;스크린샷&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;493&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWtedM/btszk5e2Ji0/2ltjwtA0ZQKzBRfVeVSj00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWtedM/btszk5e2Ji0/2ltjwtA0ZQKzBRfVeVSj00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWtedM/btszk5e2Ji0/2ltjwtA0ZQKzBRfVeVSj00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWtedM%2Fbtszk5e2Ji0%2F2ltjwtA0ZQKzBRfVeVSj00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;493&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;493&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IATlF/btsziQQQdpn/duSPlvGICq5x1eVkghksEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IATlF/btsziQQQdpn/duSPlvGICq5x1eVkghksEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IATlF/btsziQQQdpn/duSPlvGICq5x1eVkghksEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIATlF%2FbtsziQQQdpn%2FduSPlvGICq5x1eVkghksEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;790&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본은 아래 링크에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1698402854882&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;CLI로 .gitignore 파일 생성하기&quot; data-og-description=&quot;문제 해결이나 기억해야 할 것 등을 기록합니다. Contribute to taekki-labs/note development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/to2Mm/hyUkgtT5fu/lyLvweW7Hg4BKjWEbL2qgk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/taekki-labs/note/wiki/CLI%EB%A1%9C-.gitignore-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/to2Mm/hyUkgtT5fu/lyLvweW7Hg4BKjWEbL2qgk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CLI로 .gitignore 파일 생성하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제 해결이나 기억해야 할 것 등을 기록합니다. Contribute to taekki-labs/note development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>  Tip</category>
      <category>GIT</category>
      <category>IOS</category>
      <category>project</category>
      <category>swift</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/135</guid>
      <comments>https://taekki-dev.tistory.com/135#entry135comment</comments>
      <pubDate>Fri, 27 Oct 2023 19:30:43 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] 이미지 요청 취소하기</title>
      <link>https://taekki-dev.tistory.com/134</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xYlVu/btszcQJ4PoW/kyyJ9t0Osk7fkxAubVQn00/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xYlVu/btszcQJ4PoW/kyyJ9t0Osk7fkxAubVQn00/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xYlVu/btszcQJ4PoW/kyyJ9t0Osk7fkxAubVQn00/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxYlVu%2FbtszcQJ4PoW%2FkyyJ9t0Osk7fkxAubVQn00%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;645&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt; 
 &lt;h1 id=&quot;user-content-이미지-요청-취소하기&quot;&gt;&amp;nbsp;&lt;/h1&gt; 
 &lt;h2 id=&quot;user-content-이미지-또-너냐&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Cancelling-Image-Requests#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%98%90-%EB%84%88%EB%83%90&quot;&gt;이미지 또 너냐?&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;거의 모든 어플리케이션에서 이미지를 표시합니다. 사실상 이미지가 없는 앱은 없다고 볼 수 있죠. 그리고 아이콘, 로고 등을 제외한 이미지는 원격 서버에서 가져오는 경우가 대부분입니다. 이미지가 원격 서버에 있다는 의미는 어떤 의미일까요? 결국 그 역시 네트워크 요청이기 때문에 일정 시간이 소요되고 비용이 든다는 이야기입니다.&lt;/span&gt;&lt;/p&gt; 
 &lt;blockquote data-ke-style=&quot;style3&quot;&gt; 
  &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;시간과 비용이 드는 작업이기 때문에 우리는 요청 횟수를 최소화하거나, 적절한 타이밍에 요청을 해야 합니다.&lt;/span&gt;&lt;/p&gt; 
 &lt;/blockquote&gt; 
 &lt;h2 id=&quot;user-content-이미지-요청-취소cancel-작업의-필요성&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Cancelling-Image-Requests#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%9A%94%EC%B2%AD-%EC%B7%A8%EC%86%8Ccancel-%EC%9E%91%EC%97%85%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1&quot;&gt;이미지 요청 취소(Cancel) 작업의 필요성&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그리고 이미지 요청의 경우 시간이 걸리는 비동기 작업이기 때문에 애플리케이션 내에서 경쟁 조건이 발생할 수 있습니다. 어떤 요청 작업이 먼저 끝날지 예측하기도 어렵죠. 테이블 뷰나 컬렉션 뷰 처럼 많은 셀을 가질 수 있는 상황에서는 예상치 못한 문제가 발생할 수도 있습니다. 지금 셀에서 보여져야 하는 이미지가 아닌 다른 이미지가 보여질 수도 있는 것입니다. 좀 더 이야기해보면 화면 밖으로 벗어난 셀에서 진행된 요청 작업이 이제 막 끝나서 화면에 보여지고 있는 셀에 이미지를 전달해버리는 상황이 생기는 것입니다.&lt;/span&gt;&lt;/p&gt; 
 &lt;h2 id=&quot;user-content-이미지-요청-취소cancel-작업&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Cancelling-Image-Requests#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%9A%94%EC%B2%AD-%EC%B7%A8%EC%86%8Ccancel-%EC%9E%91%EC%97%85&quot;&gt;이미지 요청 취소(Cancel) 작업&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그러면 어떻게 취소 작업을 수행할 수 있을까요?&lt;/span&gt;&lt;/p&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;가령 URLSession을 이용하는 상황이라고 생각해보면 URLSessionDataTask를 반환하는 dataTask 메서드를 사용해서 처리를 해볼 수 있어요.&lt;/span&gt;&lt;/p&gt; 
 &lt;div&gt; 
  &lt;pre class=&quot;less&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;open func dataTask(with url: URL, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -&amp;gt; Void) -&amp;gt; URLSessionDataTask&lt;/code&gt;&lt;/pre&gt; 
  &lt;div&gt;
   &amp;nbsp;
  &lt;/div&gt; 
 &lt;/div&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;task를 변수에 임시 저장해서 사용할 건데, 이 task에 대해서 다음과 같은 작업들을 수행할 수 있습니다:&lt;/span&gt;&lt;/p&gt; 
 &lt;blockquote data-ke-style=&quot;style3&quot;&gt;
  &lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;resume() : 작업 시작&lt;/span&gt;
  &lt;br&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;cancel() : 작업 취소&lt;/span&gt;
  &lt;br&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;suspend() : 작업 일시중지&lt;/span&gt;
 &lt;/blockquote&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 작업 중에서 cancel() 메서드를 사용하면 됩니다.&lt;/span&gt;&lt;/p&gt; 
 &lt;br&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예를 들어서 Image 요청을 하고 가져오는 서비스 객체가 있다고 해볼게요.&lt;/span&gt;&lt;/p&gt; 
 &lt;div&gt; 
  &lt;pre class=&quot;swift&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;final class ImageService {
  
  func image(for url: URL, completion: @escaping((UIImage?) -&amp;gt; Void)) -&amp;gt; URLSessionDataTask {
    let dataTask = URLSession.shared.dataTask(with: url) { data, _, _ in
      var image: UIImage?
      
      
      defer {
        DispatchQueue.main.async {
          completion(image)
        }
      }
      
      if let data {
        image = UIImage(data: data)
      }
    }
    
    dataTask.resume()
    return dataTask
  }
}&lt;/code&gt;&lt;/pre&gt; 
  &lt;div&gt;
   &amp;nbsp;
  &lt;/div&gt; 
 &lt;/div&gt; 
 &lt;div&gt; 
  &lt;pre class=&quot;verilog&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;// cell
var task: URLSessionDownloadTask?

let service = ImageService()

task = service.image(url: imageURL) {
  // work
}

// cell reuse or collection view end displaying
task.cancel()&lt;/code&gt;&lt;/pre&gt; 
  &lt;div&gt;
   &amp;nbsp;
  &lt;/div&gt; 
 &lt;/div&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그리고 사용하는 쪽에서는 대략 위와 같이 해줄 수 있겠죠. image를 요청하는 작업 자체를 task에 담아두고, 취소해야 하는 상황에서는 cancel 메서드를 호출해주면 됩니다.&lt;/span&gt;&lt;/p&gt; 
 &lt;h2 id=&quot;user-content-코드를-개선해볼까요&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Cancelling-Image-Requests#%EC%BD%94%EB%93%9C%EB%A5%BC-%EA%B0%9C%EC%84%A0%ED%95%B4%EB%B3%BC%EA%B9%8C%EC%9A%94&quot;&gt;코드를 개선해볼까요?&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;저는 지금부터 Cancellable이란 프로토콜을 만들 것입니다. 이 프로토콜은 취소 가능한 타입을 나타냅니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위의 프로토콜을 만들어서 사용하는 이유에는 여러가지가 있을 수 있지만 저는 아래와 같이 설명하겠습니다:&lt;/span&gt;&lt;/p&gt; 
 &lt;blockquote data-ke-style=&quot;style3&quot;&gt;
  1. 사용하는 곳에서 구체적인 타입을 알지 못하게 합니다. 이는 의도와 다르게 다른 작업들을 수행할 수 있게 합니다. 우리는 취소 작업만 수행하게 하고 싶습니다.
  &lt;br&gt;2. 당장은 이미지를 가져오는 작업만 다루고 있지만, 앱 내에서는 정말 다양한 비동기 작업을 수행합니다. 일괄적으로 다양한 작업을 취소하고 싶을 수도 있습니다.
 &lt;/blockquote&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이름은 Combine의 프로토콜과 중복되니, 당장은 다른 이름을 사용하겠습니다.&lt;/span&gt;&lt;/p&gt; 
 &lt;div&gt; 
  &lt;pre class=&quot;swift&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;protocol WorkCancellable {
  func cancel()
}&lt;/code&gt;&lt;/pre&gt; 
  &lt;div&gt;
   &amp;nbsp;
  &lt;/div&gt; 
  &lt;div&gt;
   &lt;span style=&quot;font-family: 'Nanum Gothic'; letter-spacing: 0px;&quot;&gt;WorkCancellable이란 프로토콜을 정의했습니다. 그리고 이 프로토콜은 취소를 하는&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; letter-spacing: 0px;&quot;&gt;cancel()&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; letter-spacing: 0px;&quot;&gt;메서드를 가지고 있습니다.&lt;/span&gt;
  &lt;/div&gt; 
  &lt;div&gt;
   &amp;nbsp;
  &lt;/div&gt; 
 &lt;/div&gt; 
 &lt;div&gt; 
  &lt;pre class=&quot;swift&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;extension URLSessionTask: WorkCancellable {}&lt;/code&gt;&lt;/pre&gt; 
  &lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그리고 URLSessionTask에 WorkCancellable을 채택하겠습니다. (URLSessionDataTask는 URLSessionTask를 상속하고 있습니다.) &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;URLSessionTask는 이미 내부적으로 cancel()메서드를 들고 있기 때문에 자동으로 프로토콜을 준수하게 됩니다.&lt;/span&gt;&lt;/p&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt; 
 &lt;div&gt; 
  &lt;pre class=&quot;swift&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;final class ImageService {
  
  func image(for url: URL, completion: @escaping((UIImage?) -&amp;gt; Void)) -&amp;gt; WorkCancellable {
    let dataTask = URLSession.shared.dataTask(with: url) { data, _, _ in
      var image: UIImage?
      
      
      defer {
        DispatchQueue.main.async {
          completion(image)
        }
      }
      
      if let data {
        image = UIImage(data: data)
      }
    }
    
    dataTask.resume()
    return dataTask
  }
}&lt;/code&gt;&lt;/pre&gt; 
  &lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt; 
  &lt;div&gt;
   &lt;span style=&quot;font-family: 'Nanum Gothic'; letter-spacing: 0px;&quot;&gt;기존에는 URLSessionDataTask를 반환하고 있었는데 이것을 WorkCancellable로 수정했습니다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; letter-spacing: 0px;&quot;&gt;클라이언트(사용하는 쪽)에서는 이미지 요청하는 작업을 수행할 때 WorkCancellable 타입에 담아두었다가 작업을 취소하고 싶을 때 cancel()메서드를 호출할 수 있습니다.&lt;/span&gt;
  &lt;/div&gt; 
 &lt;/div&gt; 
 &lt;h2 id=&quot;user-content-마무리&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Cancelling-Image-Requests#%EB%A7%88%EB%AC%B4%EB%A6%AC&quot;&gt;마무리&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;생각을 해보면 작업을 취소한다는 것은 넓게 생각해보면 불필요한 리소스를 줄이고, 앱의 성능을 개선할 수 있도록 해주는 중요한 작업인 것 같습니다. 특히 비동기 작업과 매우 밀접한 관련이 있죠. 수많은 오픈소스, 그리고 당장 Combine과 같은 프레임워크만 하더라도 취소 가능한 타입을 정의해두고 사용합니다. 한 번은 고민해보고 넘어갈 중요한 지점이라고 생각이 드네요.&lt;/span&gt;&lt;/p&gt; 
&lt;/div&gt;</description>
      <category>  iOS &amp;amp; Swift</category>
      <category>Cancellable</category>
      <category>Fetch Image</category>
      <category>image</category>
      <category>IOS</category>
      <category>swift</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/134</guid>
      <comments>https://taekki-dev.tistory.com/134#entry134comment</comments>
      <pubDate>Thu, 26 Oct 2023 20:48:02 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] commit hitch 제거하기</title>
      <link>https://taekki-dev.tistory.com/133</link>
      <description>&lt;h1 id=&quot;user-content-문제&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0#%EB%AC%B8%EC%A0%9C&quot;&gt;문제&lt;/a&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://apps.apple.com/kr/app/%EB%B9%84%EB%A7%88%EC%9D%B4%ED%94%8C%EB%9E%9C-%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%8A%A4%ED%86%A0%EC%96%B4/id1627682568&quot;&gt;비마이플랜&lt;/a&gt;&amp;nbsp;1.4.3 버전의 메인 화면에서 스크롤 시 끊김 현상이 발생합니다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 코드에 문제일 것이다.&lt;br /&gt;2. 렌더링 문제일 것이다.&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;iOS 어플리케이션의 UI 렌더링 과정에 대해서 알아보기로 했습니다. Render Loop, Hitch 등등의 키워드가 나오더군요. 키워드에 대해서 알아보던 중 관련 있는 것 같은 WWDC 세션이 몇 개 나왔습니다. 우선 영상부터 보기로 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;다음 영상을 찾아보면 좋을 것 같습니다:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.apple.com/videos/play/tech-talks/10855/&quot;&gt;Explore UI animation hitches and the render loop&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.apple.com/videos/play/tech-talks/10857/&quot;&gt;Demystify and eliminate hitches in the render phase&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.apple.com/videos/play/tech-talks/10856/&quot;&gt;Find and fix hitches in the commit phase&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;영상을 찾아보면 알겠지만 우선&amp;nbsp;Render Loop,&amp;nbsp;Commit Phase,&amp;nbsp;hitch&amp;nbsp;등의 개념에 대해서 이해해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-hitch&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0#hitch&quot;&gt;hitch&lt;/a&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;hitch라는 용어에 대해서 우선 알아봅시다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2fYIn/btszfL80K5D/g09TISE6e1szKflS1AzWn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2fYIn/btszfL80K5D/g09TISE6e1szKflS1AzWn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2fYIn/btszfL80K5D/g09TISE6e1szKflS1AzWn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2fYIn%2FbtszfL80K5D%2Fg09TISE6e1szKflS1AzWn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;800&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://post.naver.com/viewer/postView.nhn?volumeNo=8726906&amp;amp;memberNo=5039741&quot;&gt;hitch라는 용어&lt;/a&gt;는&amp;nbsp;줄을 고리 모양으로 만들어 홀치는 동작을 의미하는 것으로 시작된 용어라고 합니다. 보통 끊을 묶거나 매듭을 지을 때 마지막에 힘을 꽉 주게 되는데 이러한 모습을 바탕으로&amp;nbsp;갑자기 움직이다,&amp;nbsp;홱 움직이다라는 의미인 jerk와 비슷한 의미로 쓰이기 시작합니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;WWDC 세션을 보면 hitch라는 용어에 대한 설명을 다음과 같이 하고 있습니다:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;759&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfwZGM/btszb7yBAzC/bdVyBmtCZJwrpfgjAnq7j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfwZGM/btszb7yBAzC/bdVyBmtCZJwrpfgjAnq7j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfwZGM/btszb7yBAzC/bdVyBmtCZJwrpfgjAnq7j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfwZGM%2Fbtszb7yBAzC%2FbdVyBmtCZJwrpfgjAnq7j1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;759&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;759&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;hitch라는 것은 프레임(frame)이 예상된 시간보다 지연되게 나타나는 시간을 의미하는 거죠.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;프레임이 예상된 시간에 나타나지 않는다는 것은 어떤 것을 의미하는 것일까요? 아이폰의 주사율은 60Hz입니다. Hz(Refresh Rate, 주사율)은 1초에 화면을 몇 번 갱신할 수 있는지에 대한 수치입니다. 우리의 휴대폰은 시간의 흐름에 따라서 보여주어야 할 화면의 순간, 즉 프레임을 이런 저런 처리를 통해 준비합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예를 들어서, 플립북을 떠올려볼까요?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIH9gb/btszejLNwEJ/frzKO1Kz9cAhGDuUJQALpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIH9gb/btszejLNwEJ/frzKO1Kz9cAhGDuUJQALpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIH9gb/btszejLNwEJ/frzKO1Kz9cAhGDuUJQALpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIH9gb%2FbtszejLNwEJ%2FfrzKO1Kz9cAhGDuUJQALpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;225&quot; height=&quot;225&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;애니메이션처럼 보이게 하려면:&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;움직임이 이어지는 그림을 수십, 수백장 준비한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;순서에 맞춰 그림을 쌓는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;한 장씩 빠르게 넘긴다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위와 같은 과정으로 정적인 그림이 움직이는 애니메이션처럼 보이게 되는 것이죠. 휴대폰도 마찬가지입니다. 한 장의 그림이 하나의 프레임과 대응되는 거에요. 아이폰의 주사율이 60Hz라고 했으니까 1초에 60장씩 그림(프레임)을 빠르게 보여주게 되는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그런데 만약에 순서가 정해져 있는 그림(프레임)에서 중간에 몇 장씩 준비가 늦어지거나 빠져서 표시를 못하게 되면, 매우 부자연스러운 애니메이션이 보여지겠죠. 실제로 iOS에서는&amp;nbsp;&lt;b&gt;프레임을 준비하는 단계&lt;/b&gt;가 별도로 존재하는데&amp;nbsp;&lt;b&gt;어떤 이유&lt;/b&gt;로 인해서 프레임 준비가 제 때 되지 않으면 화면에 표시할 수 없어서 부자연스러운 애니메이션(끊김 현상)이 발생하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;user-content-render-loop-프레임-준비되는-단계-뷰가-그려지는-단계&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0#render-loop-%ED%94%84%EB%A0%88%EC%9E%84-%EC%A4%80%EB%B9%84%EB%90%98%EB%8A%94-%EB%8B%A8%EA%B3%84-%EB%B7%B0%EA%B0%80-%EA%B7%B8%EB%A0%A4%EC%A7%80%EB%8A%94-%EB%8B%A8%EA%B3%84&quot;&gt;Render Loop: 프레임 준비되는 단계, 뷰가 그려지는 단계&lt;/a&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위에서 이야기한 프레임을 준비하는 단계를&amp;nbsp;Render Loop라고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1OWa8/btszcGAIREm/GN0Be939TneadAn3XUeVLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1OWa8/btszcGAIREm/GN0Be939TneadAn3XUeVLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1OWa8/btszcGAIREm/GN0Be939TneadAn3XUeVLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1OWa8%2FbtszcGAIREm%2FGN0Be939TneadAn3XUeVLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;586&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;앱을 사용하다보면 정말 다양한 이벤트가 발생합니다. 이벤트 중에는 사용자와 상호작용하는 이벤트가 있죠. 그 중에는 터치 이벤트도 있습니다. 가령 버튼을 눌러서 화면에 있는 뷰를 움직이는 상황이라고 생각해봅시다. 그 순간에 터치 이벤트가 앱으로 전달되고, 다음 UI에 대한 변경 사항이 적용됩니다. 어떻게 움직여야 할 지, 최종적으로는 뷰가 어떤 위치에 있어야 할지 등에 정보가 정해지겠죠? 이러한 정보를 바탕으로 프레임을 완성시키기 위한 작업을 하기 위해 그런 내용들이 운영체제로 전달됩니다. 이러한 과정들이 앱을 사용하는 동안 연속적으로 진행됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qEGxM/btszbKwSktW/KYmXANKtxwoG4GkoK5pDx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qEGxM/btszbKwSktW/KYmXANKtxwoG4GkoK5pDx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qEGxM/btszbKwSktW/KYmXANKtxwoG4GkoK5pDx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqEGxM%2FbtszbKwSktW%2FKYmXANKtxwoG4GkoK5pDx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;545&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;545&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;단계는 더 세부적으로 나누어지는데요.(내부의 세계는 어렵다 정말 )&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;크게 3가지의 단계가 병렬적으로 수행됩니다:&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;App 단계 : 이벤트가 처리되고 UI가 변경되는 단계&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Render 단계 : UI가 실제로 렌더링되는 단계&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Display 단계 : 프레임이 화면에 디스플레이되는 단계&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;모든 단계가 전부 중요하지만 개인적으로 Render 단계가 가장 중요하다고 생각합니다. 뷰의 계층 구조, 레이아웃 등등의 여러가지 정보를 조합해서 프레임(그림)을 만들어내는 단계라고 생각하면 될 것 같아요. 이 역할을 담당하는 친구가 GPU 입니다. 레이어 트리, 렌더 서버 등등 여러 용어가 또 나오지만 그 부분에 대해서는 좀 더 찾아보면 좋겠고, 결론은 실질적인 그림이 만들어지는 단계라는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;각 단계에는 16.67ms(&lt;b&gt;1초에 60프레임이 준비되고 그려져야 하니까 1초를 60으로 나눈 수치&lt;/b&gt;)의 제한시간이 있어요. 제한시간을 지키지 못하면 그 프레임은 화면에 보여지지 못하는 것이죠. (마치 과제를 마감 기한 안에 하지 못하는 느낌이랄까.. 교수님 죄송해요..) 그래서 제 시간 안에 작업을 완수하는 것이 매우 중요하겠죠?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;생각을 조금만 해보면 '무거운 작업(비용이 많이 드는 작업)이나 불필요한 작업은 줄이거나 하지 않는 것이 좋다'는 결론을 도출해 낼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;user-content-해결-과정&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0#%ED%95%B4%EA%B2%B0-%EA%B3%BC%EC%A0%95&quot;&gt;해결 과정&lt;/a&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위에서 몇 가지 개념을 살펴보면서 '무거운 작업(비용이 많이 드는 작업)이나 불필요한 작업은 줄이거나 하지 않는 것이 좋다'라는 결론을 도출했어요. 너무 당연한 말인데 사실 개발자로써 개발하는 그 순간에는 잘 인지하지 못하고 또 신경을 쓰기란 참 어렵죠.. 여하튼 그럼 어떻게 문제를 해결할 수 있을까요?&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스크롤 시 끊김 현상이 발생한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이것이 hitch라는 것을 알게되었다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Render Loop의 렌더링 단계에서 문제가 생긴 것 같다. (가정)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;무거운 작업이나 불필요한 작업때문에 렌더링 시간이 많이 걸리는 것 같다. (가정)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이러한 과정으로 생각이 전개되었고, instruments를 가지고 가정했던 상황이 정말 맞는지 확인해보기로 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;user-content-instruments&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0#instruments&quot;&gt;instruments&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Xcode 12부터는 instruments에서 Animation Hitches라는 템플릿을 제공합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mCkQu/btszfKCghJ4/tTpj4zJkllIODbCDMmo6Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mCkQu/btszfKCghJ4/tTpj4zJkllIODbCDMmo6Dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mCkQu/btszfKCghJ4/tTpj4zJkllIODbCDMmo6Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmCkQu%2FbtszfKCghJ4%2FtTpj4zJkllIODbCDMmo6Dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;771&quot; height=&quot;820&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위의 템플릿을 이용해서 메인화면의 스크롤 동작만 측정해보았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;user-content-before&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0#before&quot;&gt;before&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgRpdA/btszbtIO72D/lSaRfFlCki2UuDpwYrRNx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgRpdA/btszbtIO72D/lSaRfFlCki2UuDpwYrRNx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgRpdA/btszbtIO72D/lSaRfFlCki2UuDpwYrRNx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgRpdA%2FbtszbtIO72D%2FlSaRfFlCki2UuDpwYrRNx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;706&quot; height=&quot;482&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;482&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;발생하고 있는 hitch의 개수가 16개 정도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YmKxw/btszcGHteFE/9LVipCMFmz7svB3Hbl1bXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YmKxw/btszcGHteFE/9LVipCMFmz7svB3Hbl1bXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YmKxw/btszcGHteFE/9LVipCMFmz7svB3Hbl1bXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYmKxw%2FbtszcGHteFE%2F9LVipCMFmz7svB3Hbl1bXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;192&quot; height=&quot;73&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;139&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nA0c8/btszdo7z8U0/eTKCkcMOkAKth9yfkgE8a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nA0c8/btszdo7z8U0/eTKCkcMOkAKth9yfkgE8a0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nA0c8/btszdo7z8U0/eTKCkcMOkAKth9yfkgE8a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnA0c8%2Fbtszdo7z8U0%2FeTKCkcMOkAKth9yfkgE8a0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;75&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;139&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Main Thread 쪽에서 어떤 일이 일어나고 있는지 살펴보니, 아무래도 MainCVC(컬렉션 뷰 셀)에서 이미지를 설정해주는 곳에 문제가 있는 것 같습니다. 작업 시간을 생각보다 엄청 잡아먹고 있네요. 실제로 셀에서 이미지를 설정해주는 코드를 주석 처리하고 측정을 다시 해보니 hitch가 거의 발생하지 않다시피 줄어드는 것을 확인할 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그럼 이제는 이미지 설정 부분만 개선을 해주면 문제는 해결이 되는 거겠죠?&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;user-content-이미지-설정&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%84%A4%EC%A0%95&quot;&gt;이미지 설정&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;365&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcf1Dk/btszgkJX5Vg/uodypSbsHqvHY8JO4oha9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcf1Dk/btszgkJX5Vg/uodypSbsHqvHY8JO4oha9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcf1Dk/btszgkJX5Vg/uodypSbsHqvHY8JO4oha9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdcf1Dk%2FbtszgkJX5Vg%2FuodypSbsHqvHY8JO4oha9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;259&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;365&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;앱 내에서는 이미지 캐싱 및 다운로드에 Kingfisher 라이브러리를 사용하고 있고, 이를 한 번 래핑해서 이미지를 설정해주고 있었습니다. 분명 이전에 캐시 정책도 설정해주고 메모리 개선까지 시켜줬는데 왜 그런 걸까요? 한 가지를 놓치고 생각하고 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤 할 때마다 수많은 이미지 다운로드 요청이 발생하고 있었습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;디스플레이 되는 셀에 한해서 이미지 다운로드 작업을 수행하고, 그 외의 셀에 대해서는 다운로드를 취소하는 작업도 필요했습니다. 또한 같은 요청에 대해서는 중복으로 작업을 수행할 필요도 없었죠. Kingfisher를 사용하고 있는 상황이어서 우선은 제공되는 메서드나 프로퍼티를 이용해서 해결하고자 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;swift&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;// Cell

var task: Kingfisher.DownloadTask?

...

func configureFor(plan: PlanViewModel) {
  guard let url = URL.decodeURL(urlString: plan.imageURLString) else {
    #if DEBUG
      print(&quot;invalid url&quot;)
    #endif
    return
  }

  let processor = DownsamplingImageProcessor(size: .init(width: 500, height: 500))
  task = KingfisherManager.shared.retrieveImage(with: url, options: [.processor(processor)]) { [weak self] result in
    switch result {
    case let .success(r):
      self?.imageView.image = r.image
    case let .failure(error):
      print(&quot;error: \(error) or cancelled&quot;)
    }
  }

  planId = plan.planId
  isScrap = plan.isScraped

  homePlanPropertiesView.configureFor(plan: plan)
}

// CollectionView DataSource
func collectionView(
  _ collectionView: UICollectionView,
  didEndDisplaying _: UICollectionViewCell,
  forItemAt indexPath: IndexPath
) {
  switch cells[indexPath.section] {
  case .homePlanCell:
    let cell = collectionView.dequeueCell(HomePlanCell.self, for: indexPath)
    cell.task?.cancel()
  default:
    break
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Cell안에 작업을 수동으로 취소할 수 있도록 Task에 대한 프로퍼티를 선언해주었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;셀을 Configure하는 메서드 내에서 이미지를 다운로드하는 작업을 프로퍼티에 담아주었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Display가 끝난 셀에 대해서는 다운로드가 진행 중인 작업이 있을 때 취소하도록 했습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;전체 코드를 첨부할 수는 없지만 위와 같은 방법을 이용해서 문제를 해결하려고 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;user-content-결론&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0#%EA%B2%B0%EB%A1%A0&quot;&gt;결론&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤을 정말 빠르게 하는 상황에서 모든 셀에 대해 이미지 다운로드 작업을 수행하게 된다면 많은 부하를 줄 수 밖에 없고, 렌더링 성능이 저하될 수 있습니다. 그렇기에 보여지지 않는 셀에 대해서는 다운로드 작업을 취소하는 것이 필요했던 것이죠. 당장은 이미지 처리에서 문제를 발견할 수 있었지만, 이외에도 렌더링 성능에 영향을 줄 수 있는 케이스는 무수히 많을 수 있기에 앞으로도 비슷한 문제가 발생하면 우선 instruments로 확인하는 습관을 들이면 좋을 것 같습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;user-content-after&quot; style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0#after&quot;&gt;after&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KMJu4/btszfvd91Ii/J3NaEWeylKOksZKpOkaoqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KMJu4/btszfvd91Ii/J3NaEWeylKOksZKpOkaoqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KMJu4/btszfvd91Ii/J3NaEWeylKOksZKpOkaoqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKMJu4%2Fbtszfvd91Ii%2FJ3NaEWeylKOksZKpOkaoqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;304&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;563&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;유의미하게 hitch의 개수가 줄어든 것을 확인할 수 있습니다.&amp;nbsp;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://apps.apple.com/kr/app/%EB%B9%84%EB%A7%88%EC%9D%B4%ED%94%8C%EB%9E%9C-%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%8A%A4%ED%86%A0%EC%96%B4/id1627682568&quot;&gt;비마이플랜&lt;/a&gt;&amp;nbsp;1.4.4 버전을 다운받아 메인화면을 스크롤해보면 부드럽게 스크롤되는 것을 확인하실 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;## 링크&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1698312008312&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Commit Hitch 제거하기&quot; data-og-description=&quot;Contribute to taekki-labs/note development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lwpBq/hyUkfBqIae/A0NZ1KKvTSXiPmXLCni5Ak/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/taekki-labs/note/wiki/Commit-Hitch-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lwpBq/hyUkfBqIae/A0NZ1KKvTSXiPmXLCni5Ak/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Commit Hitch 제거하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to taekki-labs/note development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>  iOS &amp;amp; Swift</category>
      <category>Hitch</category>
      <category>IOS</category>
      <category>rendering</category>
      <category>scroll</category>
      <category>swift</category>
      <category>Trouble Shooting</category>
      <category>UI</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/133</guid>
      <comments>https://taekki-dev.tistory.com/133#entry133comment</comments>
      <pubDate>Thu, 26 Oct 2023 18:20:33 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] 반나절동안 메모리 사용량 91% 감소시키기</title>
      <link>https://taekki-dev.tistory.com/132</link>
      <description>&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;들어가며&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O4r3w/btstnvYNp9p/WAsTptZDJ0gRj5zakwcpZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O4r3w/btstnvYNp9p/WAsTptZDJ0gRj5zakwcpZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O4r3w/btstnvYNp9p/WAsTptZDJ0gRj5zakwcpZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO4r3w%2FbtstnvYNp9p%2FWAsTptZDJ0gRj5zakwcpZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1349&quot; height=&quot;309&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/%EB%B9%84%EB%A7%88%EC%9D%B4%ED%94%8C%EB%9E%9C-%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%8A%A4%ED%86%A0%EC%96%B4/id1627682568&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://apps.apple.com/kr/app/%EB%B9%84%EB%A7%88%EC%9D%B4%ED%94%8C%EB%9E%9C-%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%8A%A4%ED%86%A0%EC%96%B4/id1627682568&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;(  앱 스토어 링크)&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694097166619&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;&amp;lrm;비마이플랜 - 여행 코스 스토어&quot; data-og-description=&quot;&amp;lrm;여행 코스 스토어, 비마이플랜 현지인 크리에이터들이 만든 다양한 컨셉의 여행 코스 콘텐츠로 여행 계획 시간을 확 줄여보세요! 크리에이터가 엄선한 장소들을 효율적인 루트로 구성하여 일&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app/%EB%B9%84%EB%A7%88%EC%9D%B4%ED%94%8C%EB%9E%9C-%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%8A%A4%ED%86%A0%EC%96%B4/id1627682568&quot; data-og-url=&quot;https://apps.apple.com/kr/app/%EB%B9%84%EB%A7%88%EC%9D%B4%ED%94%8C%EB%9E%9C-%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%8A%A4%ED%86%A0%EC%96%B4/id1627682568&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/NX9Xg/hyTSoyfhgU/asTonT40PhfDDlwrINkKJ0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bDurY7/hyTSqixJ7V/dJBOWaPyvfe9ZdOxpD4s3K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/%EB%B9%84%EB%A7%88%EC%9D%B4%ED%94%8C%EB%9E%9C-%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%8A%A4%ED%86%A0%EC%96%B4/id1627682568&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app/%EB%B9%84%EB%A7%88%EC%9D%B4%ED%94%8C%EB%9E%9C-%EC%97%AC%ED%96%89-%EC%BD%94%EC%8A%A4-%EC%8A%A4%ED%86%A0%EC%96%B4/id1627682568&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/NX9Xg/hyTSoyfhgU/asTonT40PhfDDlwrINkKJ0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bDurY7/hyTSqixJ7V/dJBOWaPyvfe9ZdOxpD4s3K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lrm;비마이플랜 - 여행 코스 스토어&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lrm;여행 코스 스토어, 비마이플랜 현지인 크리에이터들이 만든 다양한 컨셉의 여행 코스 콘텐츠로 여행 계획 시간을 확 줄여보세요! 크리에이터가 엄선한 장소들을 효율적인 루트로 구성하여 일&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;나는 현재 여행 코스 스토어&amp;nbsp;비마이플랜의 유지, 보수, 신규 기능 개발을 맡고 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그동안 팀에 합류하고 크고 작은 5-6차례의 업데이트를 해왔는데, 짧은 호흡으로 제한된 시간 동안의 개발을 하다보니 놓치고 있는 부분들이 있었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;특히 이번 스프린트 동안 개발을 하다가 크게 문제가 있음을 확인했다. 개발을 하다가 디버깅을 하다보니 메모리 사용량이 끝도 없이 올라가고 있던 것이다. 화면 전환을 좀 많이 하다보니 메모리 사용량이 높아지고 결국에는 앱이 종료되는 상황을 겪었다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sx3Mn/btstmsutcGd/jRZSkNNi36Ze08KKJ5ZeF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sx3Mn/btstmsutcGd/jRZSkNNi36Ze08KKJ5ZeF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sx3Mn/btstmsutcGd/jRZSkNNi36Ze08KKJ5ZeF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsx3Mn%2FbtstmsutcGd%2FjRZSkNNi36Ze08KKJ5ZeF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;251&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;메모리 사용량이 1.93GB..?&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;922&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSx3r6/btstq7C4TJN/JGO8XSFVgFHa5kzk4D5RbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSx3r6/btstq7C4TJN/JGO8XSFVgFHa5kzk4D5RbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSx3r6/btstq7C4TJN/JGO8XSFVgFHa5kzk4D5RbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSx3r6%2Fbtstq7C4TJN%2FJGO8XSFVgFHa5kzk4D5RbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;922&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;922&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;뭐가 문제지.. 그냥 못 본 척 할까?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;는 무슨 .. ㅎㅎ 왜 문제가 생기는지 생각해보기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;생각의 시작&lt;/span&gt;&lt;/h1&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;메모리 누수 no, 메모리 낭비 yes&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;첫 번째는 Memory Leak이다. 저 정도의 메모리 사용량이면 200% 메모리 누수에서 문제가 시작되고 있다고 생각했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;(누구나 그렇게 생각할거다 )&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그럼 과연 어디서 메모리 누수가 발생하고 있는지가 핵심이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 순간부터는 정말 막막하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;회사에서 운영하는 서비스보다는 앱 사이즈가 작기에 괜찮아보이지만 그래도 막막하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;모든 파일과 화면을 다 살펴봐야 하니까..&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;여기서부터 시작하자. 어차피 돌아갈 곳은 없다.&lt;/span&gt;&lt;/h3&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;접근 1) Instruments 사용&lt;/span&gt;&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Xcode에서는 좋은 툴인 Instruments를 제공한다. 100퍼센트 원하는 결과만을 제공하지는 않지만, 메모리 누수를 간편하게 확인해볼 수 있는 도구이므로 여기서부터 접근을 시작한다. (사용법은 다른 블로그를 참고하세요..!)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;사실 나는 큰 기대를 안고 Instruments 켰다. 여기서 모든 누수를 잡아내기를..&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BB6Yb/btstrRtfA3G/P0zEEvswzVqcRKs0Gn8k71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BB6Yb/btstrRtfA3G/P0zEEvswzVqcRKs0Gn8k71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BB6Yb/btstrRtfA3G/P0zEEvswzVqcRKs0Gn8k71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBB6Yb%2FbtstrRtfA3G%2FP0zEEvswzVqcRKs0Gn8k71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1639&quot; height=&quot;714&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;오, 처음부터 메모리 누수 경고가 나오고 있었다. Log를 살펴보니까 저 부분은 Firebase SDK를 사용하면서 그 내부의 문제였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n6Eg8/btsts1WEXFY/Qpuktko2MINvXBBa2F86n0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n6Eg8/btsts1WEXFY/Qpuktko2MINvXBBa2F86n0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n6Eg8/btsts1WEXFY/Qpuktko2MINvXBBa2F86n0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn6Eg8%2Fbtsts1WEXFY%2FQpuktko2MINvXBBa2F86n0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1107&quot; height=&quot;221&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;FirebaseSDK 릴리즈 노트에 가보면 Memory Leak을 수정했다고 되어있다. SDK 버전을 올리면 되는 것이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;저것이 근본적인 문제가 아니었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;실제로 Instruments를 켜고 계속해서 디버깅을 진행하고 확인을 하면 더 이상 경고 표시는 확인할 수 없었는데 여전히 메모리 사용량이 너무 높았다. 이전이랑 차이가 없었다. 그래서 처음부터 다시 시작하기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;접근 2) Memory Object Graph와 deinitializer 확인&lt;/span&gt;&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그렇다. 이게 어찌보면 정석과도 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;처음부터 시뮬레이션을 해보기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;일단 첫 화면부터 시작을 했다. 기기로 테스트를 진행하면서&amp;nbsp;Main(홈 화면)에서&amp;nbsp;NewPlanPreview(코스 미리보기 화면)을 몇 번 번갈아서 왔다 갔다 해보았다. 시간이 지나도 메모리 사용량이 줄지 않고 계속해서 늘고 있는 것이었다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;처음 드는 생각은 올바르게 뷰 컨트롤러가 deinit 되는지 먼저 살펴봐야겠다는 생각이 든다.&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;NewPlanPreviewViewController로 가서 deinit 메서드에 로그를 찍어보았는데 Main 화면으로 돌아올 때 네비게이션 스택에서 pop은 되지만 deinit 메서드는 호출되지 않고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;실제로 더 확실한 방법으로 메모리 그래프를 확인해보면 스택에 VC가 불필요하게 7개나 쌓여있는 것을 확인할 수 있었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;A &amp;harr; B를 왔다갔다 하는 것을 7번 정도 하니까 7개 쌓여있는 것을 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNa9p7/btstpDCvAUj/grPVFjAR7KseXmarWzpzGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNa9p7/btstpDCvAUj/grPVFjAR7KseXmarWzpzGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNa9p7/btstpDCvAUj/grPVFjAR7KseXmarWzpzGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNa9p7%2FbtstpDCvAUj%2FgrPVFjAR7KseXmarWzpzGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;954&quot; height=&quot;441&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;왜 deinit이 되지 않을까?&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;원인 가정 1)&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;처음 생각했던 지점은 A(MainVC) 과 B(NewPlanPreviewVC)가 서로 강하게 참조하고 있어서가 아닐까? 라는 부분이었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그러나 서로 참조하고 있는 형태나 코드는 없었기에 이 부분은 원인이 아니었다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;원인 가정 2)&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;B(NewPlanPreviewVC)가 메모리에 계속 살아있기에, 1번의 원인이 아니라면 내부에서 순환 참조가 발생할 가능성이 클 것이라는 합리적인 의심을 해볼 수 있었다. 코드를 천천히 살펴보았다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;swift&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;final class NewPlanPreviewVC: UIViewController {

...

@IBOutlet var topNavibar: NewPlanPreviewNaviBar!

...

topNavibar.backButton.press {
  self.postObserverAction(.navigationPop)
}

...

}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;코드를 엄청 간소화했다. 상황을 간단하게 요약하면 중간에 버튼(backButton)이 액션 핸들러 클로저 부분에서 self를 강하게 참조하고 있다. 이렇게 뷰 컨트롤러와 버튼이 서로 강하게 참조하고 있기 때문에, 뷰 컨트롤러가 메모리에서 정상적으로 해제되지 않는 문제가 생기고 있었던 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문제의 원인 자체는 간단해보이지만, 과정은 결코 쉽지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHRDSO/btstsOiJy0e/mTk28ftIPqsKA1P1lNRGK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHRDSO/btstsOiJy0e/mTk28ftIPqsKA1P1lNRGK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHRDSO/btstsOiJy0e/mTk28ftIPqsKA1P1lNRGK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHRDSO%2FbtstsOiJy0e%2FmTk28ftIPqsKA1P1lNRGK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;210&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;코드가 약 1,000줄 가까이 되기 때문에 하나하나 문제를 찾아가는 것은 머리 아픈 일이다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;여기서 ViewController의 코드를 좀 분리시키고 싶다는 생각이 강하게 들었다..ㅎ&lt;/span&gt;&lt;/h4&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1+1 덤으로 문제 발견&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;또 어디 누수난 곳은 없나하고 메모리 그래프를 쭉 훑어보던 중에 이상한 부분을 발견했다. 프로젝트 내에는 인스턴스 생성을 담당하는 객체인 ModuleFactory가 있다. 이 객체는 여러 개의 인스턴스를 생성할 필요가 없는 객체이다. 그렇기에 싱글톤(- 적절한 선택이 아닐 수도 있으나 -)으로 만들거나 하는 것이 맞다. 프로젝트 기존에 코드가 짜여있던 것도 그러한 형태로 쓰여왔었다. 나도 그동안 싱글톤인줄 알았는데 그게 아니었다. 객체가 여러 개 생기고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;swift&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;// before
static func resolve() -&amp;gt; ModuleFactory {
  ModuleFactory()
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기존에는 resolve메서드를 호출해서 ModuleFactory인스턴스를 생성하고 이 인스턴스가 가지고 있는 인스턴스 생성 메서드를 이용해서 뷰 컨트롤러를 만들고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그러나 생각해보면 ModuleFactory인스턴스 1개만 있어도 된다. 인스턴스를 계속해서 생성하는 것은 불필요하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;뷰 컨트롤러마다 해당 인스턴스를 넘겨주고 있는 형태도 아니고, ModuleFactory는 특정 공간 한 곳에서만 사용되고 있기도 했다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;pre class=&quot;ceylon&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;&lt;code&gt;// after
static let shared = ModuleFactory()&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그렇기에 타입 저장 프로퍼티로 인스턴스를 만들어서 싱글톤 형태로 하나의 객체만 사용할 수 있도록 변경해주었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이렇게 전체 파일 곳곳에 강한 참조로 엮어있는 부분을 약한 참조로 수정해주었더니 문제가 해결된 듯 하였으나&amp;hellip; 메모리 사용량이 여전히 많았다. (얼추 줄기는 했지만 &amp;hellip;)&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;대체 무엇이 문제일까.. 무엇이 나를 이토록 힘들게 하는가..&lt;/span&gt;&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHjpzz/btstqX1AbEE/rfRjw3bqq7XGSDtWQ1K6Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHjpzz/btstqX1AbEE/rfRjw3bqq7XGSDtWQ1K6Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHjpzz/btstqX1AbEE/rfRjw3bqq7XGSDtWQ1K6Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHjpzz%2FbtstqX1AbEE%2FrfRjw3bqq7XGSDtWQ1K6Jk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;168&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;생각을 해보자..&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;비마이플랜이라는 서비스는 이미지가 정말 많이 쓰이는 서비스이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;곰곰히 생각을 해보았다. 캐싱이 문제일까..?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이미지 캐싱을 위해서 이미 서비스 내에서는 Kingfisher라는 라이브러리를 쓰고 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;근데 고민해보면, 캐싱은 결국에 높은 코스트의 이미지 요청을 줄이기 위해 하고 있는 것이고&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;근본적으로는 이미지 사이즈(용량)을 줄이는 것이 크게 중요하다는 생각이 들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기존에 사용하고 있는 이미지의 사이즈를 찍어보면&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1920 * 1080 이정도 사이즈의 고화질의 이미지를 사용하고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RcxCP/btstkVcSJPp/aQozAJazVkOKhOOBgc8fk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RcxCP/btstkVcSJPp/aQozAJazVkOKhOOBgc8fk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RcxCP/btstkVcSJPp/aQozAJazVkOKhOOBgc8fk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRcxCP%2FbtstkVcSJPp%2FaQozAJazVkOKhOOBgc8fk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;34&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;생각보다 이미지가 잡아먹는 메모리가 크다는 것을 알고 있는가?&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;약, 이미지 해상도 * 4byte라고 보면 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예를 들면, 1920 * 1080 * 4 = 8,294,400 = 약 8mb&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이미지도 데이터다.&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이미지도 특정 포맷으로 인코딩된 데이터의 일종인데, 이를 휴대폰에서 보여주기 위해서는 디코딩이라는 과정을 거치게되고 디코딩 후에는 이미지 버퍼라는 특정 메모리에 이미지 데이터가 저장되게 된다. 이것이 렌더링되어서 화면에 보여지게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이미지 사이즈가 크면 결국에 포함되는 데이터 정보도 많아지게 되는 것이고 차지하는 메모리의 크기도 증가하게 될 것이다.&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;화면에 보여질 수 있는 공간은 한정적인데 굳이 고화질 사진을 해상도 그대로 가지고 있을 필요가 없었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;사이즈 자체를 줄여줘서 메모리에 로드되는 크기를 줄이는 것이 중요해보였다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;리사이징 / 다운샘플링 하는 코드는 많이 나와있기 때문에 참고해서 구현을 했다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;결론..&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;후 힘들다.. 글이 생각보다 길어졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문제를 해결 전에는&lt;/span&gt;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문제 있는 녀석 x 문제 있는 녀석의 콜라보로 무지막지한 메모리 사용량을 보이다가 앱이 종료되는 문제가 발생했는데&lt;/span&gt;&lt;/h3&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이렇게 위의 과정을 거쳐서 메모리 누수를 잡고, 이미지 리사이징까지 해주니까 메모리 사용량을 현저히 줄일 수 있었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;앱 사용 시 평균 100 - 200 mb 메모리 사용량이 유지되는 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;끝까지와서 생각해보면 엄청 큰 원인/문제는 아니었지만, 사소하게 놓칠 수 있는 부분들이 큰 이슈를 만들 수 있다는 것에 다시 한 번 꼼꼼히 코드를 짜야겠다라는 생각이 들었다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;마지막, 스크린 샷&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;1176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqxZpr/btstq041MJf/batAevEU5qD55vjuL5KGuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqxZpr/btstq041MJf/batAevEU5qD55vjuL5KGuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqxZpr/btstq041MJf/batAevEU5qD55vjuL5KGuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqxZpr%2Fbtstq041MJf%2FbatAevEU5qD55vjuL5KGuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;1176&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;1176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;메모리도 줄줄 새고, 내 눈물도 줄줄 새는 상황 + 이미지도 엄청 큰 거 쓰는 상황;;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;1136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zXHCb/btstktVaMY1/L6xlAgDFKDKtuYcBZKi0W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zXHCb/btstktVaMY1/L6xlAgDFKDKtuYcBZKi0W1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zXHCb/btstktVaMY1/L6xlAgDFKDKtuYcBZKi0W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzXHCb%2FbtstktVaMY1%2FL6xlAgDFKDKtuYcBZKi0W1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;1136&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;1136&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그냥 다 틀어막아버린 경우 -&amp;gt; 용감했다.&lt;/span&gt;&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;결론: 기존 대비 메모리 사용량 최대 91% 감소  &lt;/span&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;여기까지 읽어주셔서 감사합니다 :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;레이아웃을 다듬고 싶지만,, 힘에 부쳐서 정돈된 글을 읽고 싶으시다면 아래 링크를 참고해주세요 ㅎㅎ&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그리고 잘못된 내용이나 피드백 있으면 언제든지 환영입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://github.com/Taehyeon-Kim/Today-I-Learned/issues/104&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/Taehyeon-Kim/Today-I-Learned/issues/104&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694097107869&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;[Memory] 반나절동안 메모리 사용량 91% 감소시키기 &amp;middot; Issue #104 &amp;middot; Taehyeon-Kim/Today-I-Learned&quot; data-og-description=&quot;들어가며 나는 현재 여행 코스 스토어 비마이플랜의 유지, 보수, 신규 기능 개발을 맡고 있다. . 그동안 팀에 합류하고 크고 작은 5-6차례의 업데이트를 해왔는데, 짧은 호흡으로 제한된 시간 동&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Taehyeon-Kim/Today-I-Learned/issues/104&quot; data-og-url=&quot;https://github.com/Taehyeon-Kim/Today-I-Learned/issues/104&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/urc6h/hyTPF2IgHB/GcLvABKZKr8FUfw03xMRX1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Taehyeon-Kim/Today-I-Learned/issues/104&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Taehyeon-Kim/Today-I-Learned/issues/104&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/urc6h/hyTPF2IgHB/GcLvABKZKr8FUfw03xMRX1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Memory] 반나절동안 메모리 사용량 91% 감소시키기 &amp;middot; Issue #104 &amp;middot; Taehyeon-Kim/Today-I-Learned&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;들어가며 나는 현재 여행 코스 스토어 비마이플랜의 유지, 보수, 신규 기능 개발을 맡고 있다. . 그동안 팀에 합류하고 크고 작은 5-6차례의 업데이트를 해왔는데, 짧은 호흡으로 제한된 시간 동&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>  iOS &amp;amp; Swift</category>
      <category>bemyplan</category>
      <category>Instruments</category>
      <category>IOS</category>
      <category>iOS 문제 해결</category>
      <category>Memory Graph</category>
      <category>memory leak</category>
      <category>swift</category>
      <category>메모리 개선</category>
      <category>메모리릭</category>
      <category>비마이플랜</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/132</guid>
      <comments>https://taekki-dev.tistory.com/132#entry132comment</comments>
      <pubDate>Thu, 7 Sep 2023 23:32:10 +0900</pubDate>
    </item>
    <item>
      <title>[Note] #1</title>
      <link>https://taekki-dev.tistory.com/131</link>
      <description>&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;&lt;b&gt;[ 기록 ]&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;&quot;&gt;&lt;b&gt;개인적으로 학습하면서 기록한 것이라 틀린 내용이 있을 수 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;▪️ Hash table 복습&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 해시 테이블, 버킷, 슬롯, 해시 함수, 해싱, 해시 충돌, 해시 충돌 해결&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;▪️&amp;nbsp;&lt;/span&gt;비트와 바이트&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 비트란 컴퓨터가 데이터를 처리하기 위한 데이터의 최소 단위&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 컴퓨터는 2진수(0과 1)로 동작&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 바이트(byte)란 비트(bit)가 8개 모여서 구성, 일반적으로 한 문자를 표현할 수 있는 최소 단위&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;&lt;b&gt;▪️ &lt;/b&gt;바이트 저장 순서&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;컴퓨터는 데이터를 메모리에 저장할 때 바이트(byte) 단위로 나누어 저장한다. 컴퓨터가 저장하는 데이터는 대개 32bit, 64bit로 구성된다. 연속되는 바이트를 순서대로 저장해야 하는데, 이것을 바이트 저장 순서라고 한다. 바이트가 저장되는 순서에 따라 다음 두 가지 방식으로 나눈다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;1. 빅 엔디안(Big Endian)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;2. 리틀 엔디안(Little Endian)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- Mac OS X on x86, x64는 리틀 엔디안&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;▪️ 빅 엔디안(big endian)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;빅 엔디안 방식은 낮은 주소에 데이터의 높은 바이트(MSB, Most Significant Bit)부터 저장하는 방식이다. 메모리에 저장된 순서 그대로 읽을 수 있고, 이해하기 쉽다. (대부분의 RISC CPU 계열에서 해당 방식 사용)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오전 12.14.19.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pBUnu/btskAIzwxdu/3FXOQjoZKPe1TEBlEKk1IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pBUnu/btskAIzwxdu/3FXOQjoZKPe1TEBlEKk1IK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pBUnu/btskAIzwxdu/3FXOQjoZKPe1TEBlEKk1IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpBUnu%2FbtskAIzwxdu%2F3FXOQjoZKPe1TEBlEKk1IK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1364&quot; height=&quot;262&quot; data-filename=&quot;스크린샷 2023-06-20 오전 12.14.19.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 낮은 주소에 높은 비트(MSB)부터 저장&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- MSB는 가장 왼쪽에 있는 비트를 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 네트워크를 통해 데이터를 전송할 때에는 빅 엔디안 방식이 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;▪️ 리틀 엔디안(little endian)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 낮은 주소에 데이터의 낮은 비트(LSB)부터 저장&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&amp;nbsp;&lt;/span&gt;LSB(최하위 비트)라고 함은 가장 오른쪽에 있는 비트를 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&amp;nbsp;&lt;/span&gt;LSB는 항상 0번 비트이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&amp;nbsp;&lt;/span&gt;인텔 기반의 윈도우는 리틀 엔디안 방식을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;&lt;b&gt;▪️&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;x86, x64&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- x86 : 32비트 &amp;rarr; 메모리를 4GB까지만 사용 가능&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- x64 : 64비트 &amp;rarr; 운영체제가 사용하는 기본 데이터 처리 단위가 64비트(8바이트)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 명칭의 의미는 칩셋의 구분기호를 뜻한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 실질적인 차이는 램(RAM)의 인식 차이이다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;▪️운영체제와 포인터의 관계&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 포인터란 변수의 주소를 담는 변수이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 포인터 변수의 크기는 운영체제가 데이터를 처리하는 방식에 따라 영향을 받는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 32비트 운영체제 &lt;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: start;&quot;&gt;&amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 32비트(4바이트) &lt;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: start;&quot;&gt;&amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 2^32의 데이터 표현 범위를 갖는다. (&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;4096MB &amp;rarr; 약 4GB에 해당 &amp;rarr; 4GB의 메모리에 접근이 가능하다.)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 64비트 운영체제 &amp;rarr; 64비트(8바이트) &amp;rarr; 32비트 운영체제보다 2^32배 데이터의 표현이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 메모리 한 칸은 1바이트(8비트)의 크기를 갖는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 32비트 운영체제에서는 4바이트 길이의 주소를 갖는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 집 평수는 1평, 이 집을 가리키는 주소를 32자리로 표현한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 즉 32비트 운영체제는 2^32(약 43억)개의 주소를 가리킬 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 이 말은 다시 바꿔서 1바이트 크기의 메모리를 43억 개까지 인식할 수 있다는 말과 동일하다. (약 43억 Byte = 약 4GB이다.)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 주소의 범위가 다음과 같이 달라진다. == 운영체제 비트에 따라 포인터의 크기가 달라진다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 32bit에서는 0x00000000 ~ 0xFFFFFFFF&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 64bit에서는 0x0000000000000000 ~ 0xFFFFFFFFFFFFFFFF&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;▪️ &lt;b&gt;TCP/IP 송수신 원리&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 소켓 역시 파일이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- Process(사용자 프로그램)과 TCP/IP 프로토콜이 통신하는 창구를 소켓 인터페이스라고 부른다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 소켓과 TCP는 각각 buffer를 가지고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 송신 측에서 큰 크기의 파일을 전송(send)할 때에는 작은 크기(ex. 64KB)로 나누어 전송한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- TCP로 전달될 때에는 buffer에 똑같이 copy를 하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 순차적으로 Segment &amp;rarr; Packet &amp;rarr; Frame으로 Wrapping 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 그러고 나서 전송이 이루어지는데 수신 측에서는 역순으로 흐름이 진행된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 수신 측의 tcp버퍼에 segment가 쌓이고 난 뒤에는 송신 측에게 ACK를 전달하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- ACK에는 다음 데이터의 요청과 함께 window size를 함께 담는다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- window size는 수신 측의 tcp buffer size를 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 남은 tcp buffer size보다 다음 segment size(or Maximum Segment Size) 보다 커야 데이터 전송이 원활할 것이다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 그렇지 않으면 더 이상 보낼 수 있는 공간이 없으니 Wait가 일어나고 처리지연이 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;- 수신 측의 Read(Receive) 속도가 네트워크 속도보다 빨라야 처리지연 문제를 해소할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;[ 생각 ]&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Sans Light';&quot;&gt;- 기록을 안 하다 보니까 이곳저곳에 지식, 생각이 흩어져버리는 느낌이 든다. 알고 있는 내용도 막상 말하려고 하고, 적으려고 하면 쉽게 되지 않더라. 그래서 다시 찾은 방법이 기록이다.&amp;nbsp; 가볍게 하루하루 기록하면서 다시 시작하자. 아직 글의 양식을 생각해보지도, 어떤 내용을 기록할지도 전혀 정해지고 다듬어지지 않았지만 그냥 일단 시작해보는거다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span&gt;- 가볍게 이런저런 cs 공부함 &lt;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: justify;&quot;&gt;&amp;rarr; 최근에 공부하면서 느낀 거지만 진짜 파편화된 지식이 너무 많고, 오래전에 공부했던 내용들이라 하나도 기억이 안난다. (차근차근 복습하자)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span&gt;- 부스트캠프 1차 테스트 봄 &lt;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: justify;&quot;&gt;&amp;rarr; 올해 몇 번의 코딩테스트를 보면서 항상 든 생각은 구현 연습을 열심히 해야겠다는 생각이다. 근데 그게 말처럼 쉽지 않다. 알고리즘 문제 푸는게 한동안은 재미있다가 요새 너무 하기 싫다! ... 마음을 좀 다잡을 필요가 있다. 열심히 해야겠다는 생각이 들면서도 마음이 싱숭생숭하는 시기다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #333333; text-align: justify;&quot;&gt;- 어쨌든 다시 열심히 살고 공부하기로 마음 먹었으니 뭐가 됐든 한 번 해보련다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>  Note</category>
      <category>CS</category>
      <category>TIL</category>
      <category>개발</category>
      <category>공부</category>
      <category>기록</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/131</guid>
      <comments>https://taekki-dev.tistory.com/131#entry131comment</comments>
      <pubDate>Tue, 20 Jun 2023 00:43:47 +0900</pubDate>
    </item>
    <item>
      <title>슬라이딩 윈도우 알고리즘(Sliding window algorithm)</title>
      <link>https://taekki-dev.tistory.com/130</link>
      <description>&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;인사말&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;안녕하세요 태끼입니다 올해의 첫 포스팅이네요. 작년은 유독 기억에 남는 해가 아닐까 싶습니다. 생각해보면 참 열심히 살았고 그에 따라 얻는 것도 많았지만, 체력적/심적으로 많이 지치기도 했던 한 해였던 것 같아요. 작년에 대한 회고는 스스로 차분히 잘 해봤는데요. 작년을 거름 삼아 올해를 더 잘 보내는 데에 집중해보도록 하겠습니다 :)&lt;br&gt;&lt;br&gt;올해에 저에게는 몇 가지 목표가 있는데요. 그중 한 가지는 코딩테스트(여러 가지 형태가 될 것 같아요!)에 응시해서 한 번이라도 통과하는 것입니다. 코딩테스트마다 조건이나 기준이 다를 테지만 공부한 만큼 성과를 이뤄내고 싶고 동기부여를 받고 싶어요. 여느 해보다는 좀 더 코딩테스트 준비에 힘쓸 예정이에요. 알고리즘 이론이나 새롭게 알게된 문법이나 스킬 등등도 종종 포스팅해보겠습니다! &lt;br&gt; &lt;br&gt; &lt;br&gt;서론이 너무 길었네요! ㅋㅋ 오늘은 슬라이딩 윈도우 알고리즘에 대해서 알아본 내용을 정리하고 기록해 놓으려고 합니다. 문제를 풀다 보니까 많이는 아니더라도 필요한 순간이 가끔 있어서 정리해보면 좋겠다 싶었습니다. 편한 말투로 적도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot; style=&quot;text-align: justify;&quot;&gt; &lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;슬라이딩 윈도우 알고리즘(Sliding window algorithm)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;Sliding window algorithm(슬라이딩 윈도우 알고리즘)은 윈도우라고 불리우는 특정 구간을 이동시키면서, 특정 구간 내에 있는 데이터를 이용해서 문제를 풀이하는 알고리즘을 말한다.&lt;br&gt; &lt;br&gt;&lt;b&gt;가장 기본적인 상황을 같이 한번 살펴보자.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
 &lt;li&gt;N개의 원소를 가지는 배열&lt;/li&gt;
 &lt;li&gt;W의 크기를 가지는 윈도우(= 구간)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;이렇게 2가지의 재료가 있다고 가정해볼 것이다.&lt;br&gt;여기서 중요한 포인트는 N, W 모두 고정된 크기라는 점, 좌 · 우 한 방향으로 이동한다는 점이다.&lt;br&gt; &lt;br&gt;&lt;b&gt;예시로, 9개의 원소를 가지는 아래의 배열에서 K = 3 구간에서 나올 수 있는 최대 합을 구해보자.&lt;/b&gt;&lt;br&gt;[ 7 ] [ 4 ] [ 3 ] [ 2 ] [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ]&lt;br&gt; &lt;br&gt;[ 7 ] [ 4 ] [ 3 ] [ 2 ] [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ] sum = 14&lt;br&gt;^^^^^^^^^^&lt;br&gt;[ 7 ] [ 4 ] [ 3 ] [ 2 ] [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ] sum = 13&lt;br&gt; ^^^^^^^^^^&lt;br&gt;[ 7 ] [ 4 ] [ 3 ] [ 2 ] [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ] sum = 10&lt;br&gt; ^^^^^^^^^^&lt;br&gt;[ 7 ] [ 4 ] [ 3 ] [ 2 ] [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ] sum = 13&lt;br&gt; ^^^^^^^^^^&lt;br&gt;[ 7 ] [ 4 ] [ 3 ] [ 2 ] [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ] &lt;b&gt;sum = 19&lt;/b&gt; &lt;br&gt; ^^^^^^^^^^&lt;br&gt;[ 7 ] [ 4 ] [ 3 ] [ 2 ] [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ] sum = 15&lt;br&gt; ^^^^^^^^^^ &lt;br&gt;[ 7 ] [ 4 ] [ 3 ] [ 2 ] [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ] sum = 11&lt;br&gt; ^^^^^^^^^^&lt;br&gt; &lt;br&gt;사실 풀이 자체가 어려운 문제는 아니다. 1칸씩 옆으로 움직이면서 3개의 범위 안에 있는 숫자를 더해주고 최종적으로 최대 합을 구하면 되는 문제이기 때문이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;let array = [7, 4, 3, 2, 5, 6, 8, 1, 2]
let k = 3 // 3인 구간

var sum = 0
for i in 0..&amp;lt;array.count {
	let prefixSum = array[i..&amp;lt;i+k].reduce(0, +)  // &amp;lt;- 이 부분에 대한 효율성이 요점이다.
	sum = max(sum, prefixSum)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;기본적인 방법으로 구간합을 구하게 되면, K개의 숫자의 합을 구해야 하니까 시간 복잡도 O(K)가 도출된다. 그렇게 되면 전체 시간 복잡도는 O(N * K) (N은 배열의 크기)의 시간이 될 것이다. 그러나 이런 식으로 계산을 했을 때 시간 초과가 발생하는 상황이 생긴다. 이를 좀 더 효율적으로 개선할 수는 없을까?&lt;br&gt; &lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;let array = [7, 4, 3, 2, 5, 6, 8, 1, 2]
let k = 3 // 3인 구간

var prefixSum = array[0..&amp;lt;3].reduce(0,+) // 최초의 구간 합 -&amp;gt; 이 때는 어쩔 수 없이 계산을 해주어야 함

var sum = 0
for i in k..&amp;lt;array.count {
	prefixSum += array[i] - array[i - k] // 효율성을 개선할 수 있다.
	sum = max(sum, prefixSum)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;교집합을 제외한 나머지 값에만 변화를 주는 방식으로 코드를 개선해볼 수 있다.&lt;br&gt; &lt;br&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;[ 7 ]&lt;/b&gt;&lt;/span&gt;&lt;b&gt; [ 4 ] [ 3 ]&lt;/b&gt; [ 2 ] [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ] sum = 14 // 1)&lt;br&gt;^^^^^^^^^^ &lt;br&gt;[ 7 ] &lt;b&gt;[ 4 ] [ 3 ] &lt;/b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;[ 2 ]&lt;/b&gt;&lt;/span&gt; [ 5 ] [ 6 ] [ 8 ] [ 1 ] [ 2 ] sum = 13 // 2) &lt;br&gt; ^^^^^^^^^^&lt;br&gt;여기서 4, 3은 교집합이기 때문에 1)의 구간 합(14)에서 1) 구간의 첫번째 값만 빼주고, 2) 구간의 마지막 값만 더해주면 된다.&lt;br&gt; &lt;br&gt;이렇게 되면 숫자 하나씩만 더하고 빼주는 상황이 성립되기 때문에 O(1)의 시간 복잡도를 가진다. 기존 전체 시간 복잡도 O(N * K)가 O(N)으로 개선되는 것이다. 차이가 안 나는 거 아닌가라는 생각이 들 수 있는데 K가 N과 같은 상황이 되면 O(N^2)과 O(N)으로 엄청난 차이가 발생한다.&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;메모&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;적절한 상황에서 이 알고리즘을 사용하게 되면 효율성 측면에서 장점을 기대할 수 있을 것이다.&lt;br&gt;다음과 같은 유형에서 사용한다고 한다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;유형&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
 &lt;li&gt;구간 합&lt;/li&gt;
 &lt;li&gt;Anagram&lt;/li&gt;
 &lt;li&gt;구간 최소값, 최대값&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;투 포인터 알고리즘(Two pointers algorithm)과도 연계해서 많이 쓰이는 것 같은데 해당 알고리즘은 다음 시간에 다뤄보도록 하자. 이론 자체는 어렵지 않은데 적절한 상황에서 문제에 응용하는 것이 어려운 것 같다. 해당 알고리즘이 활용되는 문제를 몇 개 풀어보면서 체화하는 것이 중요할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;이 정도 풀어보면 감이 좀 잡힐 것 같다. 1번은 위에서 살펴본 개념을 그대로 적용하는 예제이고, 4번이 개념을 응용하는 문제이다.&lt;br&gt; &lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
 &lt;li&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12847&quot; target=&quot;_self&quot;&gt;&lt;span&gt;백준 - 꿀 아르바이트&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/21921&quot; target=&quot;_self&quot;&gt;&lt;span&gt;백준 - 블로그&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href=&quot;https://leetcode.com/problems/longest-substring-without-repeating-characters/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;LeetCode - 중복 없는 긴 문자열&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17676&quot; target=&quot;_self&quot;&gt;&lt;span&gt;프로그래머스 - 추석 트래픽&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;혹시 틀린 내용이나 궁금한 점이 있으면 댓글 남겨주세요!&lt;br&gt;그리고 문제 풀이는 다음 레포지터리에서 진행하고 있습니다. 공부 잘하고 있나 궁금하시면 방문해주세요 ^__^&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Taehyeon-Kim/SwiftAlgorithm&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://github.com/Taehyeon-Kim/SwiftAlgorithm&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub - Taehyeon-Kim/SwiftAlgorithm: 나 이제 진짜 Swift로 알고리즘 공부한다?&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;나 이제 진짜 Swift로 알고리즘 공부한다? Contribute to Taehyeon-Kim/SwiftAlgorithm development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Taehyeon-Kim/SwiftAlgorithm&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WLZ4H/hyRgihqCqu/apoiPZMmKzy5ZN6Tc4QJGk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot; data-og-url=&quot;https://github.com/Taehyeon-Kim/SwiftAlgorithm&quot;&gt;
 &lt;a href=&quot;https://github.com/Taehyeon-Kim/SwiftAlgorithm&quot; target=&quot;_blank&quot; data-source-url=&quot;https://github.com/Taehyeon-Kim/SwiftAlgorithm&quot;&gt;
  &lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WLZ4H/hyRgihqCqu/apoiPZMmKzy5ZN6Tc4QJGk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600')&quot;&gt; 
  &lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;GitHub - Taehyeon-Kim/SwiftAlgorithm: 나 이제 진짜 Swift로 알고리즘 공부한다?&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;나 이제 진짜 Swift로 알고리즘 공부한다? Contribute to Taehyeon-Kim/SwiftAlgorithm development by creating an account on GitHub.&lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
  &lt;/div&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;</description>
      <category>✨ Problem Solving</category>
      <category>Algorithm</category>
      <category>PS</category>
      <category>Sliding window algorithm</category>
      <category>swift</category>
      <category>swift algorithm</category>
      <category>Swift PS</category>
      <category>슬라이딩 윈도우 알고리즘</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/130</guid>
      <comments>https://taekki-dev.tistory.com/130#entry130comment</comments>
      <pubDate>Fri, 13 Jan 2023 20:37:30 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Stored Property vs Computed Property vs Method</title>
      <link>https://taekki-dev.tistory.com/129</link>
      <description>&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Stored Property vs Computed Property vs Method&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최근에 기초가 부족하다는 것을 느껴 틈틈히 평소에 궁금했던 것이나 문법들을 다시 보고 있는데 생각보다 재밌다.. (자, 일단 자신을 속여..!!) &lt;/b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;연산 프로퍼티를 언제 쓰는 것이 좋을지 고민을 하다가 괜찮다고 느낀 관점의 글이 있어서 메모해놓는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;저장 프로퍼티(Stored Property)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;- 값의 변화가 없고, 정기적으로 접근(Read)해서 쓰려고 할 때&lt;/li&gt;
&lt;li&gt;- 만들어놓고 거의 읽지 않게되는 저장 프로퍼티인 경우 lazy 키워드를 사용해서 성능을 개선해볼 수 있을 것 같다.&lt;/li&gt;
&lt;li&gt;- 마찬가지로 Property Observer를 통해서도 성능을 개선해볼 수 있지 않을까? (- 요 부분은 살짝 와닿지 않는다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;연산 프로퍼티(Computed Property)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;- 다른 프로퍼티와 종속성이 있을 때 즉, 다른 값에 의해서 영향을 받는 경우&lt;/li&gt;
&lt;li&gt;- 값을 계산해야할 때&lt;/li&gt;
&lt;li&gt;- 꼭 연산이 필요하지 않다면 연산 프로퍼티를 쓰지 않는 것이 나아보인다. (불필요한 연산은 줄이는 게 좋을 듯)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;메서드(Method)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;- 많은 글들을 보니 O(1)의 시간복잡도가 드는 연산이면 연산 프로퍼티가 적절하고, 더 무거운 작업(즉, O(N)이상의 시간복잡도가 드는 연산인 경우)에는 메서드의 사용이 적절하다고 이야기하고 있다.&lt;/li&gt;
&lt;li&gt;- 매 연산/동작/작업마다 다른 결과를 반환하는 경우라면 메서드가 더 적합해보인다. (이게 가장 와닿음)&lt;/li&gt;
&lt;li&gt;- 다른 프로퍼티와의 종속 관계를 유지하고 있는 상황에서 단순히 값을 반환하는 경우라면 연산 프로퍼티가 더 적절한 것 같다. Enum에서 Case에 따른 결과 반환?이 가장 이해하기 쉬운 예시인 것 같다. (마지막 블로그에서 정말 와닿게 잘 설명해주고 있다.)&lt;/li&gt;
&lt;li&gt;- 그리고 Error가 발생할 수 있는 상황의 경우 메서드가 연산 프로퍼티보다 적절하다고 생각한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;보완할 내용이 있으면 업데이트 해보겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;틀린 내용이나 추가할 내용이 있으면 댓글 남겨주세요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;레퍼런스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://www.avanderlee.com/swift/computed-property/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.avanderlee.com/swift/computed-property/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://medium.com/swift-india/functions-vs-computed-property-what-to-use-64bbe2df3916&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/swift-india/functions-vs-computed-property-what-to-use-64bbe2df3916&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://daheenallwhite.github.io/swift/2019/05/09/Computed-Property-vs-Method-What-To-Choose/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://daheenallwhite.github.io/swift/2019/05/09/Computed-Property-vs-Method-What-To-Choose/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://velog.io/@wonhee010/Computed-Property%EC%97%B0%EC%82%B0-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-vs-Method&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@wonhee010/Computed-Property%EC%97%B0%EC%82%B0-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-vs-Method&lt;/a&gt;&lt;/p&gt;</description>
      <category>  iOS &amp;amp; Swift</category>
      <category>IOS</category>
      <category>Property</category>
      <category>swift</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/129</guid>
      <comments>https://taekki-dev.tistory.com/129#entry129comment</comments>
      <pubDate>Fri, 4 Nov 2022 16:03:32 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] 네트워크 통신 톺아보기를 시작하기 전에(0)</title>
      <link>https://taekki-dev.tistory.com/128</link>
      <description>&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요즘 공부의 어느 정도 시간은 네트워크 통신 부분에 대한 학습, 네트워크 레이어 추상화에 대한 것을 연구해보고 있다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;그리고 나만의 네트워크 라이브러리 선택 기준도 고민해보는 중&lt;/b&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;네트워크 통신은 정말 요청(Request)과 응답(Response)이 다라는 것을 요즘 더 강하게 느낀다.&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;스스로 정리가 얼추 되면, 네트워크 통신에 대해서 내 나름대로 정리해보려고 한다. 자원(리소스)이 있는 서버 쪽에 요청할 요청서를 만들고, 가져온 자원을 앱의 상황에 맞게 적절히 해독하고 가공해서 쓰는 것 이것이 다라고 생각한다. 요청을 통해 자원을 생성하는 경우도 있지만 대부분의 경우 데이터를 받아쓰는 클라이언트의 입장에서는 요청이 특히나 중요하다고 본다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;요청서에 필요한 정보는 크게 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URL(Path, Query ...) (필수)&lt;/li&gt;
&lt;li&gt;HTTPMethod (필수)&lt;/li&gt;
&lt;li&gt;HTTPHeader (반 필수)&lt;/li&gt;
&lt;li&gt;HTTPParameter (선택)&lt;/li&gt;
&lt;li&gt;기타 정보 .. (timeout, caching policy, network connection ...)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;다음의 정보를 잘 조합시켜서 요청서(요청정보 == URLRequest)를 만드는 것이 네트워크 통신에 있어 가장 중요한 작업 중 하나가 아닐까 싶다. 이것만 잘 이해한다면 통신은 80~90% 끝이라고 본다. (아니라고 생각하면 어쩔 수 없다. .)&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;Builder 패턴으로 네트워크 레이어를 추상화 해봄&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hmlongco/RequestBuilder&quot;&gt;https://github.com/hmlongco/RequestBuilder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;위의 레포지터리를 공부하면서 Rx 형태로 변화시켜보려고 함.&lt;/li&gt;
&lt;li&gt;BaseSessionManager.swift 파일 부분에서 nil 값이 생겨서 정상적으로 결과를 반환하지 못했음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLDB로 이슈를 조금 빠른 속도로 찾아볼 수 있었음. (print 찍었으면 날 밤 샜을 듯)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Builder 패턴을 이용하면 SwiftUI 처럼 선언형으로 코드 조각을 구성해볼 수 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Request에 필요한 내용들이 어느 정도 정해져있는 네트워크 레이어의 경우 해당 패턴으로 구성하면 적절하게 잘 쓸 수 있을 것 같아서 공부해보고 싶었음.&lt;/li&gt;
&lt;li&gt;지금은 아직 완벽히 구현은 하지 않은 상태라 애매한데, 필요한 파일만 남기면 괜찮게 이용해볼 수 있을 것 같음.&lt;/li&gt;
&lt;li&gt;Alamofire, Moya 정말 좋은 라이브러리들인데, 지금 드는 생각은 생각보다 정말 일부 기능만 쓰고 있다는 생각이 들었음. 사용하지 않는 기능에 대한 코드는 불필요하게 낭비되는 경우도 많다고 봄. (1) 개발 기간이 어느 정도 확보되어있거나 (2) 혼자 개발하거나 한다면 URLSession으로 필요한 기능만 추상화해서 사용하는 것도 나쁘지 않다고 생각함. 물론 숙련도가 어느 정도 있거나 한다면 Alamofire, Moya를 쓰는 것이 효율성 측면에서는 좋아보임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;지금은 data 메서드의 return 타입이 RxSwift의 Single Traits이기 때문에 사용 예시가 아래와 같음. Combine과도 섞어볼 수 있고 다양한 형태로 가공할 수 있을 것 같음&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;ruby&quot; data-ke-language=&quot;ruby&quot;&gt;&lt;code&gt;let base = URL(string: SeSACAPI.baseURL)
let sessionManager = BaseSessionManager(base: base, session: URLSession.shared)

sessionManager.request()
    .add(path: SeSACAPI.profile.path)
    .add(headers: SeSACAPI.profile.headers)
    .method(.get)
    .data(type: UserResponse.self, decoder: JSONDecoder())
    .subscribe { response in
        print(response)
        print(&quot;  :: 프로필 불러오기 성공 :: \(response.user)&quot;)
    } onFailure: { error in
        print(&quot;  :: 프로필 불러오기 실패 :: \(error.localizedDescription)&quot;)
    }.disposed(by: disposeBag)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;오늘은 여기까지&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;좀만 더 정리되면 기존에 많이 볼 수 있는 글처럼 단순히 네트워크 라이브러리 사용법에 대한 내용이 아닌 내가 이해한대로 네트워크 통신을 몇 편(미정)의 글로 정리해보고자 한다. (할 수 있겠지 ...? ^^)&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;아우.. 근데 글 이제 어떻게 써야할지를 모르겠네.. 일단 적고보자..!! 간단하게 메모할 수 있는 카테고리를 하나 파야겠다.&lt;/p&gt;</description>
      <category>  iOS &amp;amp; Swift</category>
      <category>IOS</category>
      <category>network</category>
      <category>swift</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/128</guid>
      <comments>https://taekki-dev.tistory.com/128#entry128comment</comments>
      <pubDate>Fri, 4 Nov 2022 00:51:51 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 221103</title>
      <link>https://taekki-dev.tistory.com/127</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 내가 보기 위해 메모하는 키워드(너무 자세하게 적으려고 부담갖지 말기)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;- 이미 알고 있는 내용이더라도 적고 다시 생각하고 의문점 갖기&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Alamofire 중복 코드 개선하기&lt;/h2&gt;
&lt;div&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;import Foundation

import Alamofire

final class Network {
    
    static let shared = Network()
    private init() {}
}

extension Network {
    func request&amp;lt;T: Decodable&amp;gt;(
        _ type: T.Type = T.self,
        url: URL,
        method: HTTPMethod = .get,
        parameters: [String: String]? = nil,
        headers: HTTPHeaders,
        completion: @escaping (Result&amp;lt;T, Error&amp;gt;) -&amp;gt; Void
    ) {
        AF.request(url, method: .get, parameters: parameters, headers: headers)
            .responseDecodable(of: T.self) { response in
                switch response.result {
                case .success(let data):
                    completion(.success(data))
                case .failure:
                    guard let statusCode = response.response?.statusCode else { return }
                    guard let error = SeSACError(rawValue: statusCode) else { return }
                    
                    completion(.failure(error))
                }
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Generic 메서드의 인자로 들어오는 값을 프로토콜로 묶어주면 어떨까?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://taekki-dev.tistory.com/23&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://taekki-dev.tistory.com/23&lt;/a&gt; (URLRequestConvertible 프로토콜로 개선해볼 수 있음, 옛날에 쓴 글이 있었다..)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Generic Type&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Placeholder로의 역할&lt;/li&gt;
&lt;li&gt;모든 타입이 들어올 수 있기 때문에 에러를 방지하기 위해 제약을 걸어주는 것이 필요함&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Meta Type&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입의 타입&lt;/li&gt;
&lt;li&gt;타입 자체를 가리켜야 하는 경우가 있기 때문에 존재&lt;/li&gt;
&lt;li&gt;타입이란 메타타입의 인스턴스&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Escaping Closure/Auto Closure&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://yagom.net/forums/topic/escaping%EA%B3%BC-autoclosure/&quot;&gt;https://yagom.net/forums/topic/escaping%EA%B3%BC-autoclosure/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Result Type&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 통신 수행의 결과는 성공 또는 실패 중 하나이다. 그렇기 때문에 생겨난 타입이라고 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;@frozen public enum Result&amp;lt;Success, Failure&amp;gt; where Failure : Error {

    /// A success, storing a `Success` value.
    case success(Success)

    /// A failure, storing a `Failure` value.
    case failure(Failure)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Frozen&quot; data-hovercard-type=&quot;user&quot; data-hovercard-url=&quot;/users/Frozen/hovercard&quot; data-octo-click=&quot;hovercard-link-click&quot; data-octo-dimensions=&quot;link_type:self&quot;&gt;@Frozen&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 케이스가 더이상 추가되지 않을 것을 컴파일러에게 알려서 성능 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Error&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Error는 Sendable을 채택하는 프로토콜 타입이다.&lt;/li&gt;
&lt;li&gt;일반적으로 통신시에 발생할 수 있는 에러의 종류는 다양하기 때문에, Error를 채택하여 Custom한 Error타입을 정해주는 것이 좋다.&lt;/li&gt;
&lt;li&gt;열거형과 엮어서 사용해볼 수 있다.&lt;/li&gt;
&lt;li&gt;범용적으로 발생할 수 있는 에러도 추가해볼 수 있다. (Offline 상태)&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;enum SeSACError: Int, Error {
    case invalidAuthorization = 401
    case takenEmail = 406
    case emptyParameters = 501
}

extension SeSACError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .invalidAuthorization:
            return &quot;토큰이 만료되었습니다. 다시 로그인 해주세요.&quot;
        case .takenEmail:
            return &quot;이미 가입된 회원입니다. 로그인 해주세요.&quot;
        case .emptyParameters:
            return &quot;필요한 정보가 없습니다.&quot;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;guard let error = SeSACError(rawValue: statusCode) else { return }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 캐시&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shorturl.at/ryY05&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;캐시 종류&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;속도와 용량 측면에서 차이가 있기 때문에 적절한 사용 필요&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시 메모리(메모리 캐시)&lt;/li&gt;
&lt;li&gt;디스크 캐시&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. RxSwift&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Single Traits : 개념 자체는 어렵지 않다. 그런데 구조적인 측면에서 활용하는게 감이 잘 안와서 좀 헤맸다.&lt;/li&gt;
&lt;li&gt;subscribe, bind, drive&lt;/li&gt;
&lt;li&gt;Driver vs Signal&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. Copy on Write (CoW)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 값의 수정 또는 삭제 등 변경이 발생했을 때 메모리 주소가 달라진다.&lt;/li&gt;
&lt;li&gt;Collection 타입(Array, Dictionary, Set)에 대해서 성능 최적화 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// 포인터 주소를 알아보자
func address(_ value: UnsafeRawPointer) {
    let address = String(format: &quot;%p&quot;, Int(bitPattern: value))   // pointer 추적
    print(address)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 서브스크립트(Subscript)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 멤버 엘리먼트에 접근할 수 있도록 도와주는 문법&lt;/li&gt;
&lt;li&gt;컬렉션에 기본 구현이 되어 있음&lt;/li&gt;
&lt;li&gt;String에서 쓰면 유용할 것으로 생각함(특히 코테)&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;extension String {
    
    // &quot;abcde&quot; &amp;gt;&amp;gt;&amp;gt; [1] &amp;gt;&amp;gt;&amp;gt; b
    subscript(idx: Int) -&amp;gt; String? {
        guard (0..&amp;lt;count).contains(idx) else {
            return nil // early exit
        }
        
        let result = index(startIndex, offsetBy: idx)
        return String(self[result])
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://gist.github.com/brennanMKE/634eda16f6a00a2ae8cf127b061504d3&quot;&gt;https://gist.github.com/brennanMKE/634eda16f6a00a2ae8cf127b061504d3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. ETC&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ARC&lt;/li&gt;
&lt;li&gt;final, AnyObject&lt;/li&gt;
&lt;li&gt;접근제어&lt;/li&gt;
&lt;li&gt;강제해제 연산자 지양&lt;/li&gt;
&lt;li&gt;안쓰는 코드, 파일 제거(print, 주석, storyboard 등)&lt;/li&gt;
&lt;li&gt;Appearance
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호출한 시점부터 모든 곳에 적용되기 때문에, AppDelegate or SceneDelegate에서 호출해주는 것이 적절할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Available(*, unavailable)&lt;/li&gt;
&lt;li&gt;DTO
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 데이터와 화면 데이터를 분리해볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  SeSAC</category>
      <category>IOS</category>
      <category>swift</category>
      <category>TIL</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/127</guid>
      <comments>https://taekki-dev.tistory.com/127#entry127comment</comments>
      <pubDate>Thu, 3 Nov 2022 12:53:31 +0900</pubDate>
    </item>
    <item>
      <title>[RxSwift] Disposable과 DisposeBag</title>
      <link>https://taekki-dev.tistory.com/126</link>
      <description>&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;기록의 소중함을 알면서도 무언가를 꾸준히 기록한다는 것은 참 어려운 것 같습니다. 이런저런 생각을 많이 했지만 그냥 핑계대지말고, 몇 자라도 남겨놓는 것이 중요하다는 결론을 내렸습니다. 깊이 있는 글은 달에 1~2번 정도 적는 것으로 하고 우선은 꾸준히 메모(ex. 키워드, 요약 등)를 하는 식으로 가보려고 해요. 깃허브에 기록하는 것이 편해서 보통 메모나 기록을 깃허브에 하는 편이지만, 앞으로 블로그에도 동시에 꾸준히 포스팅할 생각입니다. 파이팅!!&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;Disposable&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;RxSwift &amp;gt; Disposable&lt;/blockquote&gt;
&lt;div&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;//  Disposable.swift

/// Represents a disposable resource.
public protocol Disposable {
    /// Dispose resource.
    func dispose()
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'처리할 수 있는'의 사전적 의미를 지닙니다.&lt;/li&gt;
&lt;li&gt;protocol 타입입니다.&lt;/li&gt;
&lt;li&gt;dispose() 메서드가 선언되어 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;//  ObservableType+Extensions.swift

public func subscribe( 
    onNext: ((Element) -&amp;gt; Void)? = nil,
    onError: ((Swift.Error) -&amp;gt; Void)? = nil,
    onCompleted: (() -&amp;gt; Void)? = nil,
    onDisposed: (() -&amp;gt; Void)? = nil
) -&amp;gt; Disposable { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Observable은 subscribe(구독)이 가능합니다.&lt;/li&gt;
&lt;li&gt;모든 subscription 객체는 Disposable 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Disposable 하다는 것의 핵심은 dispose를 수행할 수 있다는 것입니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;  핵심은 Disposable한 객체는 dispose를 수행함으로써 '리소스 정리'와 '구독 취소'를 할 수 있다는 것입니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;DisposeBag&lt;/h2&gt;
&lt;div&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;// 1. 옵저버블(이벤트 방출)
let observable = Observable.just(&quot;RxSwift&quot;)

// 2. 구독자(구독하는 시점부터 옵저버블이 방출하는 요소를 받아볼 수 있음)
let subsriber1 = observable
    .subscribe(onNext: { element in
        print(element)
    })

let subsriber2 = observable
    .subscribe(onNext: { element in
        print(element)
    })

// 3. 구독 취소
subsriber1.dispose()
subsriber2.dispose()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;우리는 시퀀스를 각각 dispose 메서드를 통해 구독 취소 해줄 수 있습니다. 그러나 따로따로 구독 취소를 하는 것은 번거롭기도 하고, 시퀀스가 여러 개가 있을 때는 구독 취소 하는 것을 놓칠 수도 있죠. 그렇기에 RxSwift에서 좀 더 편한 방법을 제공합니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;DisposeBag&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;바로 Disposable한 것을 담을 수 있는 가방(Bag)을 제공하는데요. 우리가 시퀀스를 일일이 구독 취소 하는 것을 놓칠 수 있으니까 '그냥 DisposeBag에 모아서 담아놔, 그러면 우리가 알아서 dispose 해줄게!' 이런 의미입니다.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;let disposeBag = DisposeBag()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음과 같이 DisposeBag 객체를 전역 변수로 만들어놓고 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;관례적으로 disposeBag 변수명을 쓰는 것 같아요.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;// 1. 옵저버블(이벤트 방출)
let observable = Observable.just(&quot;RxSwift&quot;)

// 2. 구독자(구독하는 시점부터 옵저버블이 방출하는 요소를 받아볼 수 있음)
let subsriber1 = observable
    .subscribe(onNext: { element in
        print(element)
    }).dispose(by: disposeBag)

let subsriber2 = observable
    .subscribe(onNext: { element in
        print(element)
    }).dispose(by: disposeBag)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 시퀀스의 말미를 보면 disposeBag에 담아놓은 것이 보이시나요.&lt;/li&gt;
&lt;li&gt;subscription한 객체들을 이렇게 disposeBag에 담아놓겠다는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;메모리 해제&lt;/h2&gt;
&lt;div&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;let disposeBag = DisposeBag()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이후에 뷰 컨트롤러가 deinit 될 때 disposeBag 객체가 메모리에서 해제되면서 그 안에서 관리되고 있던 Disposable한 요소들 역시 해제가 되게 됩니다.&lt;/li&gt;
&lt;li&gt;뷰 컨트롤러가 할 일 끝났으니 disposeBag보고 떠나라고 하는거죠. 그럼 disposeBag가 데리고 있던 자식들도 같이 떠날 수밖에 없겠죠...?&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;var disposeBag: DisposeBag?
...
disposeBag = nil&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nil을 넣어줘서 해제하는 것도 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;var disposeBag = DisposeBag()
...
self.disposeBag = DisposeBag()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;subscribe 중이던 Disposables를 초기화해주고 싶을 때 새로운 DisposeBag 인스턴스를 넣어줄 수 있습니다. 이렇게 하면 기존의 리소스를 한 번에 정리해줄 수 있게 됩니다. disposeBag이 해제되는 것과 같은 원리입니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  iOS &amp;amp; Swift</category>
      <category>Disposable</category>
      <category>DisposeBag</category>
      <category>IOS</category>
      <category>rxswift</category>
      <category>swift</category>
      <author>taeeekki</author>
      <guid isPermaLink="true">https://taekki-dev.tistory.com/126</guid>
      <comments>https://taekki-dev.tistory.com/126#entry126comment</comments>
      <pubDate>Wed, 26 Oct 2022 04:23:41 +0900</pubDate>
    </item>
  </channel>
</rss>