<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Blitz.dev</title>
    <link>https://linux-studying.tistory.com/</link>
    <description>iOS, Swift 관련 포스팅을 주로 작성합니다.</description>
    <language>ko</language>
    <pubDate>Fri, 26 Jun 2026 11:54:25 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>heoblitz</managingEditor>
    <image>
      <title>Blitz.dev</title>
      <url>https://tistory1.daumcdn.net/tistory/2945515/attach/a69cb5e06b21400da842fe8875855951</url>
      <link>https://linux-studying.tistory.com</link>
    </image>
    <item>
      <title>정적 분석으로 iOS 앱 구조 파악하기</title>
      <link>https://linux-studying.tistory.com/40</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;robin-glauser-zP7X_B86xOg-unsplash.jpg&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kNebL/btrSIrY3DGZ/ZNackBTZt7EnIYC4EsIWT0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kNebL/btrSIrY3DGZ/ZNackBTZt7EnIYC4EsIWT0/img.jpg&quot; data-alt=&quot;Photo by Robin Glauser on Unsplash&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kNebL/btrSIrY3DGZ/ZNackBTZt7EnIYC4EsIWT0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkNebL%2FbtrSIrY3DGZ%2FZNackBTZt7EnIYC4EsIWT0%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;437&quot; height=&quot;291&quot; data-filename=&quot;robin-glauser-zP7X_B86xOg-unsplash.jpg&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Photo by Robin Glauser on Unsplash&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ 본 포스팅은 언패킹(리버스 엔지니어링)에 대한 내용으로 &lt;a href=&quot;https://glaw.scourt.go.kr/wsjo/lawod/sjo192.do?contId=2135829&amp;amp;jomunNo=101&amp;amp;jomunGajiNo=4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;관련 법률&lt;/a&gt;에 유의하시기 바랍니다. ]&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;iPA&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS App-Store Package 의 줄임말으로 iOS 및 ipad 어플리케이션 아카이브 파일입니다. 모든 앱은 IPA 형태로 앱스토어에 배포가 됩니다. 개발한 코드와 함께 번들, 사이닝, 리소스 파일등이 모두 포함됩니다.&amp;nbsp;&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;766&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2GIUU/btrSKveVQ6Y/Wv2nGkqsDUfa3vJElff3Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2GIUU/btrSKveVQ6Y/Wv2nGkqsDUfa3vJElff3Kk/img.png&quot; data-alt=&quot;https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2GIUU/btrSKveVQ6Y/Wv2nGkqsDUfa3vJElff3Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2GIUU%2FbtrSKveVQ6Y%2FWv2nGkqsDUfa3vJElff3Kk%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;422&quot; height=&quot;459&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html&lt;/figcaption&gt;
&lt;/figure&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;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;iPA 다운 받기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱스토어에 올라가 있는 앱의 iPA 를 얻기 위해 &lt;a href=&quot;https://github.com/majd/ipatool&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ipatool&lt;/a&gt; 을 사용해보겠습니다. ( 다른 방법으로&amp;nbsp;&lt;a href=&quot;https://www.3u.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;3utools&lt;/a&gt;, Xcode 아카이브, 탈옥된 iOS 기기를 통해 얻을 수 있습니다. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1670022599402&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ipatool 설치
brew tap majd/repo
brew install ipatool

// ipatool 로그인
ipatool auth login --email test@apple.com --password testPassword&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1670023104206&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 앱스토어에서 키워드로 검색
ipatool search kickstarter

// 번들 ID 로 IPA 다운 받기
ipatool download -b com.kickstarter.kickstarter&lt;/code&gt;&lt;/pre&gt;
&lt;p 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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정적 분석&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Apache License 2.0 로 공개되어 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/kickstarter/ios-oss&quot;&gt;kickstarter-iOS&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;앱을 분석해보겠습니다. 다운 받은 ipa 확장자를 zip 으로 변경하고 압축을 풉니다.&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;630&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfUuXj/btrSIrldvvI/ipRuptCmjg9RhUQRqnRfi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfUuXj/btrSIrldvvI/ipRuptCmjg9RhUQRqnRfi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfUuXj/btrSIrldvvI/ipRuptCmjg9RhUQRqnRfi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfUuXj%2FbtrSIrldvvI%2FipRuptCmjg9RhUQRqnRfi1%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;420&quot; height=&quot;312&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;312&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;com.kickstarter -&amp;gt; Payload 폴더에 들어간 후, Kickstarter.app 에서 패키지 내용 보기를 누릅니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mLBSi/btrSJmRfXgf/KeneC6qqjNpIZa6W8imnZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mLBSi/btrSJmRfXgf/KeneC6qqjNpIZa6W8imnZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mLBSi/btrSJmRfXgf/KeneC6qqjNpIZa6W8imnZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmLBSi%2FbtrSJmRfXgf%2FKeneC6qqjNpIZa6W8imnZ1%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;428&quot; height=&quot;317&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;1034&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dV40DN/btrSL0fnIDt/kCGy8UBHbHSPlKppPIQkZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dV40DN/btrSL0fnIDt/kCGy8UBHbHSPlKppPIQkZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dV40DN/btrSL0fnIDt/kCGy8UBHbHSPlKppPIQkZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdV40DN%2FbtrSL0fnIDt%2FkCGy8UBHbHSPlKppPIQkZ1%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;635&quot; height=&quot;460&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;1034&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;iPA 구조&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. ( root ) /&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 번들 폴더입니다. 실행 파일과 함께 앱 아이콘과 컴파일된 스토리 보드 파일이 있습니다. 또한 별도로 분리해서 사용하는 &lt;a href=&quot;https://developer.apple.com/documentation/foundation/bundle&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;번들&lt;/a&gt; 파일들도 볼 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;2. _CodeSignature&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 서명과 관련된 폴더입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Frameworks&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lf5MM/btrTMZ0kNus/U0KSl19izwkVM0F7XXcniK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lf5MM/btrTMZ0kNus/U0KSl19izwkVM0F7XXcniK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lf5MM/btrTMZ0kNus/U0KSl19izwkVM0F7XXcniK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLf5MM%2FbtrTMZ0kNus%2FU0KSl19izwkVM0F7XXcniK%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;506&quot; height=&quot;221&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;612&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;Embed 되는 Dynamic Framework 가 모여있는 폴더입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( * Static Framework 의 경우 Linking 되어 실행 파일에 포함됩니다. )&amp;nbsp;&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;1380&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t6d4r/btrTKa97sTF/znRdWxI1F8OZJfKlw2D1fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t6d4r/btrTKa97sTF/znRdWxI1F8OZJfKlw2D1fk/img.png&quot; data-alt=&quot;Frameworks 폴더에서 보이는 KsApi 는 Dynamic Framework 임을 확인할 수 있습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t6d4r/btrTKa97sTF/znRdWxI1F8OZJfKlw2D1fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft6d4r%2FbtrTKa97sTF%2FznRdWxI1F8OZJfKlw2D1fk%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;506&quot; height=&quot;206&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Frameworks 폴더에서 보이는 KsApi 는 Dynamic Framework 임을 확인할 수 있습니다.&lt;/figcaption&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;&amp;nbsp;&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;Mach-O&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u3qp6/btrTMfI5e91/jirWvkeot4CcbHB3nZPls0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u3qp6/btrTMfI5e91/jirWvkeot4CcbHB3nZPls0/img.png&quot; data-alt=&quot;https://github.com/aidansteele/osx-abi-macho-file-format-reference/blob/master/Mach-O_File_Format.pdf&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u3qp6/btrTMfI5e91/jirWvkeot4CcbHB3nZPls0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu3qp6%2FbtrTMfI5e91%2FjirWvkeot4CcbHB3nZPls0%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;477&quot; height=&quot;480&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/aidansteele/osx-abi-macho-file-format-reference/blob/master/Mach-O_File_Format.pdf&lt;/figcaption&gt;
&lt;/figure&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;282&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWaFt8/btrTIsXRqq6/e74atq6u29wNSRqnA9aUGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWaFt8/btrTIsXRqq6/e74atq6u29wNSRqnA9aUGk/img.png&quot; data-alt=&quot;iPA &amp;amp;gt; Payload 폴더에 있는 앱 실행파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWaFt8/btrTIsXRqq6/e74atq6u29wNSRqnA9aUGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWaFt8%2FbtrTIsXRqq6%2Fe74atq6u29wNSRqnA9aUGk%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;232&quot; height=&quot;183&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;iPA &amp;gt; Payload 폴더에 있는 앱 실행파일&lt;/figcaption&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;Mach Object file format 의 줄임말으로서 Mach 커널을 사용하는 OS 의 실행 파일, 오브젝트 파일, 공유 라이브러리에 사용되는 파일 포맷입니다.&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;내부 구조는 크게 Header, Load commands, Data 로 구분되어 있습니다. 각 섹션에 어떤 정보가 있는지 파악해보겠습니다.&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Mach-O Header&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;file 은 해당 바이너리가 어떤 정보를 가지고 있는지를 보여주는 명령어입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1671112778771&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;file Kickstarter&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lwHjF/btrULOcsvwf/R2BHN4brKvkIxAiKeAIdzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lwHjF/btrULOcsvwf/R2BHN4brKvkIxAiKeAIdzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lwHjF/btrULOcsvwf/R2BHN4brKvkIxAiKeAIdzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlwHjF%2FbtrULOcsvwf%2FR2BHN4brKvkIxAiKeAIdzK%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;1292&quot; height=&quot;84&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arm64 아키텍처를 지원하는 실행파일임을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Load commands&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;otool 은 오브젝트 파일과 라이브러리를 분석할 수 있는 툴입니다. 아래와 같은 명령어를 통해 앱에서 어떤 정적/동적 라이브러리를 사용했는지 유추할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1672110430553&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;otool -L Kickstarter&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2022-12-27_12-16-58.png.png&quot; data-origin-width=&quot;2482&quot; data-origin-height=&quot;1300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YNN5P/btrULOwKdJv/1pcg89CVSnPyIH3wBSDTfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YNN5P/btrULOwKdJv/1pcg89CVSnPyIH3wBSDTfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YNN5P/btrULOwKdJv/1pcg89CVSnPyIH3wBSDTfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYNN5P%2FbtrULOwKdJv%2F1pcg89CVSnPyIH3wBSDTfK%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;2482&quot; height=&quot;1300&quot; data-filename=&quot;2022-12-27_12-16-58.png.png&quot; data-origin-width=&quot;2482&quot; data-origin-height=&quot;1300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( @rpath 가 없더라도 dynamic 프레임워크일 수 있습니다. 시스템 프레임워크의 경우에는 절대 경로를 사용합니다. )&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2922&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQRG6W/btrUCi7qyGf/Ce6wuJBudGmWjj23Nkt3LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQRG6W/btrUCi7qyGf/Ce6wuJBudGmWjj23Nkt3LK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQRG6W/btrUCi7qyGf/Ce6wuJBudGmWjj23Nkt3LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQRG6W%2FbtrUCi7qyGf%2FCe6wuJBudGmWjj23Nkt3LK%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;2922&quot; height=&quot;162&quot; data-origin-width=&quot;2922&quot; data-origin-height=&quot;162&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Data&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일된 바이너리의 로직을 분석하기 위해 Disassembler 툴을 사용할 수 있습니다. ( Hopper, IDA Pro, Ghidra.. )&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;위와 같은 분석을 방지하기 위해 LINE 에서는 ORK 라는 난독화 컴파일러를 만들어서 사용하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://engineering.linecorp.com/ko/blog/code-obfuscation-compiler-tool-ork-1/&quot;&gt;https://engineering.linecorp.com/ko/blog/code-obfuscation-compiler-tool-ork-1/&lt;/a&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-filename=&quot;Untitled (3).png&quot; data-origin-width=&quot;3444&quot; data-origin-height=&quot;1796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O8MOO/btrUGGUfjby/O6uJwhMhuMXN9pUD5E05wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O8MOO/btrUGGUfjby/O6uJwhMhuMXN9pUD5E05wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O8MOO/btrUGGUfjby/O6uJwhMhuMXN9pUD5E05wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO8MOO%2FbtrUGGUfjby%2FO6uJwhMhuMXN9pUD5E05wk%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;3444&quot; height=&quot;1796&quot; data-filename=&quot;Untitled (3).png&quot; data-origin-width=&quot;3444&quot; data-origin-height=&quot;1796&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;Disassembler 툴을 사용하면 코드가 공개되지 않은 바이너리 파일의 동작을 분석할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( UIKit 버그 파악하기 관련 게시글 )&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://pspdfkit.com/blog/2021/reverse-engineering-uikit/&quot;&gt;https://pspdfkit.com/blog/2021/reverse-engineering-uikit/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://medium.com/bumble-tech/using-hopper-to-investigate-an-ios-bug-66d373e6336d&quot;&gt;https://medium.com/bumble-tech/using-hopper-to-investigate-an-ios-bug-66d373e6336d&lt;/a&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;br /&gt;&lt;br /&gt;참고:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ss64.com/osx/lipo.html&quot;&gt;https://ss64.com/osx/lipo.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://minsone.github.io/ios/mac/ios-framework-part-1-static-framework-dynamic-framework&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://minsone.github.io/ios/mac/ios-framework-part-1-static-framework-dynamic-framework&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레츠스위프트 2022, iOS 개발에서 알아두면 좋은 것들&lt;span style=&quot;background-color: #0a0a0a; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS Dev/iOS</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/40</guid>
      <comments>https://linux-studying.tistory.com/40#entry40comment</comments>
      <pubDate>Sat, 3 Dec 2022 05:43:21 +0900</pubDate>
    </item>
    <item>
      <title>코딩테스트를 위한 Swift 필수 자료구조 구현하기</title>
      <link>https://linux-studying.tistory.com/38</link>
      <description>&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-filename=&quot;thisisengineering-raeng-LxVzgYjkHp4-unsplash.jpg&quot; data-origin-width=&quot;5239&quot; data-origin-height=&quot;7855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8gkwi/btrqirFHUsy/exe0aKw2zfyFapz4ekH6V1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8gkwi/btrqirFHUsy/exe0aKw2zfyFapz4ekH6V1/img.jpg&quot; data-alt=&quot;Photo by ThisisEngineering on Unsplash&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8gkwi/btrqirFHUsy/exe0aKw2zfyFapz4ekH6V1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8gkwi%2FbtrqirFHUsy%2Fexe0aKw2zfyFapz4ekH6V1%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;255&quot; height=&quot;382&quot; data-filename=&quot;thisisengineering-raeng-LxVzgYjkHp4-unsplash.jpg&quot; data-origin-width=&quot;5239&quot; data-origin-height=&quot;7855&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Photo by ThisisEngineering on Unsplash&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩테스트는 취업에 있어 꼭 필요한 과정 중 하나입니다. 저도 익숙하게 사용했던 Python 언어를 활용하여 코딩 테스트를 준비했었는데요. 하지만 최근에 점차 많은 회사들이 포지션에 맞게 코딩테스트 언어 제한을 두었습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-01-11 오전 11.36.59.png&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQckv/btrqmqGGWFZ/2dux5joXhBnVewE1MIElkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQckv/btrqmqGGWFZ/2dux5joXhBnVewE1MIElkk/img.png&quot; data-alt=&quot;2021 네이버웹툰 개발 챌린지 ( iOS 포지션은 Swift를 사용해야 한다 )&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQckv/btrqmqGGWFZ/2dux5joXhBnVewE1MIElkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQckv%2FbtrqmqGGWFZ%2F2dux5joXhBnVewE1MIElkk%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;475&quot; height=&quot;207&quot; data-filename=&quot;스크린샷 2022-01-11 오전 11.36.59.png&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2021 네이버웹툰 개발 챌린지 ( iOS 포지션은 Swift를 사용해야 한다 )&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift는 Type-Safe 언어이고 문법도 간결해서 iOS 개발할 때에 무척 편리하게 사용하고 있습니다. 하지만 Python과 달리 별도의 Built-in 자료 구조가 없어서 코딩테스트에 필요한 부분은 직접 구현해야 합니다.&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;이번 게시글에서는 Swift를 통해 필요한 자료구조를 직접 구현하는 방법에 대해 기술하고자 합니다.&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Queue&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queue 는 FIFO 방식으로 동작하는 자료구조입니다. 프린터, 작업 대기열 같은 문제에 활용되고 더 나아가 SPFA 같은 최단 거리 알고리즘에 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Double Array를 활용한 Queue&lt;/h4&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/bfa27742308fba59a44a6d0a7cf25d4e.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift Reversed 메서드의 시간 복잡도는 O(1) 이기 때문에 Double Array를 사용하면 쉽게 Queue를 구현할 수 있습니다. 일반적인 상황에서 주로 사용합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Cursor를 활용한 Queue&lt;/h4&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/e1aa47885402763621a0d8b2df1b94e0.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Double Array를 활용한 Queue는 극단적인 테스트 케이스에서 시간초과가 발생하게 됩니다. ( 상당히 많은 1회 추가, 삭제를 반복하는 경우 Array 를 계속 재할당하는 오버헤드가 발생함 )&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;이런 경우에는 Cursor를 활용해서 Queue를 구현할 수 있습니다.&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Heap ( Priority Queue )&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙(우선순위 큐)는 완전 이진트리를 기반으로 최소(혹은 최대) 값을 O(logN)으로 구할 수 있는 자료구조입니다. 최소, 최대 값을 지속적으로 추적해야 하는 문제나 최단거리를 구하는 다익스트라 알고리즘에 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/169af8eec48c8181d58cbdb586a5001d.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 편의를 위해 트리가 아닌 배열을 사용하고 첫번째 index 는 사용하지 않습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Heap 구현이 꼭 필요하지 않은 경우&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Heap에 대한 원리는 파악하고 있어야 하지만 실제 코딩테스트 환경에서 구현하는데 큰 부담이 있습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 최대, 최소 값을 계속 추척해야하는 문제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 테스트 케이스 범위와 실행 시간이 여유롭다면 min, max 메서드로 코드를 작성해봅니다. 시간 복잡도에 큰 손해가 있지만 우선 동작에 초점을 맞춥니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 시간 초과가 발생하거나 시험 여유 시간이 남는다면 다시 Heap으로 리팩토링합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 최단거리를 구하는 문제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 다익스트라 알고리즘 대신 SPFA 알고리즘을 사용합니다. SPFA는 Queue를 사용하기 때문에 구현에 큰 어려움이 없고 일반적인 상황에서 O(V+E)으로 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 다만 테스트 케이스가 까다롭다면 O(V*E)으로 동작하기 때문에 Heap을 사용하는 다익스트라 알고리즘으로 구현해야 합니다.&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;&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;&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;&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;* leetcode를 통해 동작을 확인했지만 틀린부분이 있을 수 있습니다. 잘못된 부분은 말씀해주시면 감사하겠습니다.&lt;/p&gt;</description>
      <category>PS/Algorithm</category>
      <category>codingtest</category>
      <category>SWIFT</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/38</guid>
      <comments>https://linux-studying.tistory.com/38#entry38comment</comments>
      <pubDate>Tue, 11 Jan 2022 11:31:02 +0900</pubDate>
    </item>
    <item>
      <title>8개월간 같은 iOS 앱을 리팩토링하며 배운점</title>
      <link>https://linux-studying.tistory.com/37</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3014&quot; data-origin-height=&quot;2688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Va3zd/btrkQkEs6e0/mnX3nrgRtNTexODcLSDTa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Va3zd/btrkQkEs6e0/mnX3nrgRtNTexODcLSDTa0/img.png&quot; data-alt=&quot; https://github.com/ModuTeam/LinkMoa_iOS &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Va3zd/btrkQkEs6e0/mnX3nrgRtNTexODcLSDTa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVa3zd%2FbtrkQkEs6e0%2FmnX3nrgRtNTexODcLSDTa0%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;329&quot; height=&quot;294&quot; data-origin-width=&quot;3014&quot; data-origin-height=&quot;2688&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; https://github.com/ModuTeam/LinkMoa_iOS &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&quot;링크모아&quot; 는 사용자의 북마크를 쉽게 관리하고 공유할 수 있는 앱 서비스입니다. 20년 12월 기획을 시작으로 디자이너 1명, 백엔드 개발자 2명, iOS 개발자 2명이 협업하며 21년 3월에 개발을 완료했습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;iOS 개발자와 처음으로 협업을 하며 이론적으로만 알고있던 git-flow 를 적용하고 merge 시에는 코드 리뷰를 꼭 거치도록 했습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;잠깐이지만 앱 스토어 생산성 부문에서 160위 내에 들었고 동아리 데모데이 대상을 받는 등, 저에게 있어 무척 뜻 깊은 프로젝트입니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;데모데이 이후에 프로젝트가 마무리될거라고 생각했지만 같이 협업을 했던 iOS 개발자분이 한 가지 제안을 주셨습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&quot;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;현업에서 사용하는 기술들을 적용해보고 지속적으로 같이 리팩토링 해봐요&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;.&lt;/span&gt;&quot;&lt;br&gt;&amp;nbsp;&lt;br&gt;매번 기능 개발만 진행하고 프로젝트가 종료되어서 유지보수와 좋은 코드에 대한 아쉬움이 컸었는데요. 팀원분이 주신 제안에 깊히 공감하게 되어 개발 완료 이후에 리팩토링을 진행하게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RxSwift 공부하고 적용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyK8Ea/btrkOljkypY/ZhwKTmFyetyIkviPjxMI71/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyK8Ea/btrkOljkypY/ZhwKTmFyetyIkviPjxMI71/img.jpg&quot; data-alt=&quot; ReactiveX/RxSwift &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyK8Ea/btrkOljkypY/ZhwKTmFyetyIkviPjxMI71/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyK8Ea%2FbtrkOljkypY%2FZhwKTmFyetyIkviPjxMI71%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;290&quot; height=&quot;145&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; ReactiveX/RxSwift &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;RxSwift 는 마이크로소프트에서 만든 ReactiveX 를 Swift 로 구현한 프레임워크입니다.&lt;br&gt;비동기 코드를 &quot;함수형과 선언형&amp;nbsp;프로그래밍&quot; 통해 간단히 작성할 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;MVVM 코드 패턴과 같이 사용하면 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;데이터 바인딩&lt;/span&gt;을 간결하게 할 수 있다는 장점이 있습니다.&lt;br&gt;여러 MVVM 패턴의 장단점을 정리한 내용을 아래 포스팅으로 남겼습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;iOS MVVM 패턴 구현 정리: &lt;/span&gt;&lt;a href=&quot;https://linux-studying.tistory.com/28&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://linux-studying.tistory.com/28&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/8a44988a1e42555aa392016c2094d39a.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;또한 복잡한 비동기 코드를 작업할 때 위와 같이 클로저가 중첩되며 가독성이 무척 떨어지는 문제점이 있었는데요. RxSwift 를 사용하면 flatMap 을 통해 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;여러 비동기 코드를 체이닝&lt;/span&gt;으로 연결하여 작업할 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/50bd1dd998f7447bb7b0706e2269763b.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;위처럼 RxSwift를 사용하여 비동기 클로저가 중첩하여 발생하는 코드 Depth 를 상당히 줄일 수 있습니다. 그 외에도 RxDataSource, RxGesture, RxMoya... 등등 여러 서포트 프레임워크를 통해 가독성 좋은 코드를 작성할 수 있었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Tuist 와 앱 모듈화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyU0zN/btrkHELaax7/5s8ngRpoMYoBlTGCSJ0Ke1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyU0zN/btrkHELaax7/5s8ngRpoMYoBlTGCSJ0Ke1/img.gif&quot; data-alt=&quot; Tuist &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyU0zN/btrkHELaax7/5s8ngRpoMYoBlTGCSJ0Ke1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cyU0zN/btrkHELaax7/5s8ngRpoMYoBlTGCSJ0Ke1/img.gif&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;412&quot; height=&quot;137&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; Tuist &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;Tuist 는 Xcode 프로젝트 파일을 Swift 로 관리할 수 있도록 도와주는 프레임워크입니다.&lt;br&gt;git binary merge 설정을 해도 발생하는 프로젝트 충돌을 해결하기 위해 도입했습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2kEkM/btrlw2Dwuj0/JmhCAN9K989jwwZIbwFW4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2kEkM/btrlw2Dwuj0/JmhCAN9K989jwwZIbwFW4k/img.png&quot; data-alt=&quot; https://engineering.linecorp.com/ko/blog/improving-build-performance-line-ios-bazel/ &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2kEkM/btrlw2Dwuj0/JmhCAN9K989jwwZIbwFW4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2kEkM%2Fbtrlw2Dwuj0%2FJmhCAN9K989jwwZIbwFW4k%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;638&quot; height=&quot;215&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; https://engineering.linecorp.com/ko/blog/improving-build-performance-line-ios-bazel/ &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;다만 Tuist 적용을 위해 예시 프로젝트를 살펴보니 상당히 많은 앱들이 모듈로 구성되어 있었습니다. 전에 Line 기술 블로그를 보며 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&quot;앱이 수백 개의 모듈로 구성되어 있다.&quot;&lt;/span&gt; 라는 의미를 정확하게 이해하지 못했는데요. Tuist 의 방향성에 맞게 링크모아에도 앱 모듈화가 필요하다는 판단을 하여 학습하게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;lt;참고한 자료 및 프로젝트&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;민소네님, Let us go 2019 프레임워크 주도 개발:&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=YiEpuZQPuko&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;https://www.youtube.com/watch?v=YiEpuZQPuko&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Soojin Ro님, BookStore-iOS: &lt;/span&gt;&lt;a href=&quot;https://github.com/nsoojin/BookStore-iOS&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;https://github.com/nsoojin/BookStore-iOS&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;라인 개발자가 말하는 대규모 iOS 앱 개발 썰:&lt;/span&gt; &lt;a href=&quot;https://youtu.be/wZh8WXPAIeg?t=583&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://youtu.be/wZh8WXPAIeg?t=583&lt;/span&gt;&lt;/a&gt;&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Kanghoon님, Xcode 프로젝트 관리를 위한 Tuist 알아보기:&lt;/span&gt; &lt;a href=&quot;https://okanghoon.medium.com/xcode-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-tuist-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-6a92950780be&quot; target=&quot;_blank&quot; title=&quot;https://okanghoon.medium.com/xcode...&quot;&gt;&lt;span&gt;https://okanghoon.medium.com/xcode...&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;앱 모듈화의 장점&lt;/span&gt;은 다음과 같습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;1. 빌드 속도를 줄일 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( * 클린 빌드는 예외, 코드가 변경된 모듈만 빌드하기 때문에 빌드 속도 개선이 가능합니다. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;2. 큰 프로젝트에서 많은 개발자와 협업을 진행할 수 있는 환경입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( * 기능들을 모듈로 나누고 각각 Git 서브 모듈로 구성하면 한 앱을 여러개의 프로젝트로 작업할 수 있습니다. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;3. 각 기능별 의존성을 명확하게 지정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( * 기본 키워드인 Internal 는 다른 모듈에서 접근이 불가하기 때문에 접근하려면 명시적으로 Open 또는 Public 을 사용해야 합니다. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;4. 기능 간 결합도를 낮추어서 유연한 서비스를 제작할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( * 모듈을 통해 쉽게 다른 프레임워크로 교체할 수 있고 또한 필요한 모듈은 새로운 프로젝트에 다시 재사용할 수 있습니다.&amp;nbsp; )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbjH1l/btrlEUEYpEM/AQl0c94QXaMlm8Ibp5K2TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbjH1l/btrlEUEYpEM/AQl0c94QXaMlm8Ibp5K2TK/img.png&quot; data-alt=&quot; LinkMoa Dependency Graph &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbjH1l/btrlEUEYpEM/AQl0c94QXaMlm8Ibp5K2TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbjH1l%2FbtrlEUEYpEM%2FAQl0c94QXaMlm8Ibp5K2TK%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;488&quot; height=&quot;348&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; LinkMoa Dependency Graph &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;lt;리팩토링 후 링크모아 구조&amp;gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
 &lt;li&gt;LinkMoa: iOS 앱&lt;/li&gt;
 &lt;li&gt;LinkMoaShareExtension: Share Extension ( 공유하기 기능 )&amp;nbsp;&lt;/li&gt;
 &lt;li&gt;LinkMoaWidget: 위젯&lt;/li&gt;
 &lt;li&gt;LinkMoaBottomSheet: Custom Bottom Sheet 프레임워크&lt;/li&gt;
 &lt;li&gt;LinkMoaCore: 네트워크 및 저장소 프레임워크&lt;/li&gt;
 &lt;li&gt;LinkMoaKit: Assets, Fonts 및 Helper Class 프레임워크&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;링크모아에서는 Tuist 를 통해 프로젝트 머지 충돌을 해결할 수 있었고 앱 모듈화를 통해 각 코드에 대한 의존성을 명확하게 구분할 수 있었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트 가능한 코드로 리팩토링하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;앱 구조 리팩토링 이후에 프로젝트 코드를 검증하기 위해 테스트 코드를 작성을 시작했습니다. 다만 테스트를 작성하면서 &lt;a href=&quot;https://tech.buzzvil.com/handbook/test-principles/&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;FIRST 원칙&lt;/span&gt;&lt;/a&gt;이 전혀 지켜지지 않고 있다는 생각이 들었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;네트워크는 실제 서버 API에 호출하여 매우 느리고 반복하기에는 상당히 불안정했습니다. 또한 UserDefault는 실제 앱에서 사용하는 저장소에 테스트 코드가 접근할 수 있었고 이는 전혀 고립되지 않은 테스트 환경이였습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;따라서 먼저 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Testable 한 코드&lt;/span&gt;를 파악하는 능력을 기르고 리팩토링을 진행하고자 하였습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQntYW/btrlCY26pqO/UE5kXpWOml1LBoajAlTgiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQntYW/btrlCY26pqO/UE5kXpWOml1LBoajAlTgiK/img.png&quot; data-alt=&quot; WWDC17 Engineering for Testability &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQntYW/btrlCY26pqO/UE5kXpWOml1LBoajAlTgiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQntYW%2FbtrlCY26pqO%2FUE5kXpWOml1LBoajAlTgiK%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;599&quot; height=&quot;333&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; WWDC17 Engineering for Testability &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;WWDC 에서 학습한 테스트 가능한 코드의 특성은 다음과 같습니다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;1. 모든 입력 값들은 제어 가능해야 합니다.&lt;br&gt;2. 생성되는 출력을 검사할 수 있는 방법을 제공해야 합니다.&lt;br&gt;3. 코드의 동작의 영향을 미칠 수 있는 내부 상태에 의존하지 않습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d00qvA/btrlEJcyGgP/CJRLAiO0ksK0w1kWePIdqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d00qvA/btrlEJcyGgP/CJRLAiO0ksK0w1kWePIdqk/img.png&quot; data-alt=&quot; WWDC17 Engineering for Testability &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d00qvA/btrlEJcyGgP/CJRLAiO0ksK0w1kWePIdqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd00qvA%2FbtrlEJcyGgP%2FCJRLAiO0ksK0w1kWePIdqk%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;789&quot; height=&quot;378&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; WWDC17 Engineering for Testability &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;위 코드는 싱글톤을 사용하므로 전역 시스템 상태에 의존하게 됩니다. 또한 open 을 통해 실제로 원하는 동작이 수행됐는지 유닛 테스트를 통해 확인하기 어렵습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&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;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baRhIR/btrlC7r1AUq/Y0uCNpMi4o1OckI91jNws0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baRhIR/btrlC7r1AUq/Y0uCNpMi4o1OckI91jNws0/img.png&quot; data-alt=&quot; WWDC17 Engineering for Testability &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baRhIR/btrlC7r1AUq/Y0uCNpMi4o1OckI91jNws0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaRhIR%2FbtrlC7r1AUq%2FY0uCNpMi4o1OckI91jNws0%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;780&quot; height=&quot;439&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; WWDC17 Engineering for Testability &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이를 프로토콜과 매개변수화를 통해 testable 하게 코드를 리팩토링할 수 있습니다. 필요한 의존성을 모두 외부에서 주입하도록 합니다. 그리고 필요한 동작을 정의한 Protocol 을 가르키게 합니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;위와 같이 변경하면 해당 Protocol 를 준수한 어떠한 객체도 주입이 가능하게 됩니다. 이때 Protocol 을 준수한 Mock 을 주입하면 행위와 상태 검증을 수행할 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;883&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TiWhy/btrlEmIGzbe/5q8G58ONDQ341e5IZIa080/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TiWhy/btrlEmIGzbe/5q8G58ONDQ341e5IZIa080/img.png&quot; data-alt=&quot; WWDC17 Engineering for Testability &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TiWhy/btrlEmIGzbe/5q8G58ONDQ341e5IZIa080/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTiWhy%2FbtrlEmIGzbe%2F5q8G58ONDQ341e5IZIa080%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;780&quot; height=&quot;864&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;883&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; WWDC17 Engineering for Testability &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;링크모아에서도 내부에 의존성을 가지고 있는 경우는 의존성 주입을 통해 외부에서 전달받도록 변경했습니다. 또한 모의 객체가 필요한 부분은 Protocol 를 구성하여 테스트에 필요한 Mock 객체를 만들었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트 코드 작성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;유닛 테스트 작성시에 유의했던 부분은 &quot;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;개발자의 관점에 따라 테스트 입력 값이 변경될 수 있다.&lt;/span&gt;&quot; 였습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;sungdoo 님, [서평] 지식 제로부터 배우는 소프트웨어 테스트:&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;https://www.sungdoo.dev/post/review/book/software-test-starting-from-zero&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://www.sungdoo.dev/post/review/book/...&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/02a5a0e94e33f69d70e1f8f8f4f54e94.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;위와 같이 양의 정수만을 문자열로 변경하는 프로그램이 있을 때, 음수가 들어가는 경우에 대한 테스트가 꼭 필요합니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;하지만 실제 프로젝트의 함수는 더 복잡하고 많은 일을 수행합니다. 따라서 테스트 코드 작성시에는 더 작은 기능들로 분리하여 함수의 역할을 명확하게 하거나 테스트시에 어떤 값이 들어갈 수 있는지 한번 더 고민하였습니다. 또한 테스트 코드 가독성을 위해 Given, When, Then 을 구분지어 사용하는 GWT 방식을 사용했습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0vtwR/btrlEJcSlWN/uUp7BprFoxO6Y3D844YRRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0vtwR/btrlEJcSlWN/uUp7BprFoxO6Y3D844YRRk/img.png&quot; data-alt=&quot; Int 값을 한글로 변경하는 메서드, 10개의 테스트 케이스로 검증 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0vtwR/btrlEJcSlWN/uUp7BprFoxO6Y3D844YRRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0vtwR%2FbtrlEJcSlWN%2FuUp7BprFoxO6Y3D844YRRk%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;433&quot; height=&quot;307&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; Int 값을 한글로 변경하는 메서드, 10개의 테스트 케이스로 검증 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b61oYW/btrlEJjDIIK/apUTBVv3Syu0kQD6Rk4bY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b61oYW/btrlEJjDIIK/apUTBVv3Syu0kQD6Rk4bY0/img.png&quot; data-alt=&quot; 폴더를 추가하는 API 메서드, 명세서 Code 에 해당하는 모든 케이스를 검증 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b61oYW/btrlEJjDIIK/apUTBVv3Syu0kQD6Rk4bY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb61oYW%2FbtrlEJjDIIK%2FapUTBVv3Syu0kQD6Rk4bY0%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;433&quot; height=&quot;364&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 폴더를 추가하는 API 메서드, 명세서 Code 에 해당하는 모든 케이스를 검증 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv7VF3/btrlDxEfnGB/guxTN6kQtcktQFDhrH2ORK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv7VF3/btrlDxEfnGB/guxTN6kQtcktQFDhrH2ORK/img.png&quot; data-alt=&quot; LinkMoa Kit, Core, BottomSheet &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv7VF3/btrlDxEfnGB/guxTN6kQtcktQFDhrH2ORK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv7VF3%2FbtrlDxEfnGB%2FguxTN6kQtcktQFDhrH2ORK%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;678&quot; height=&quot;142&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; LinkMoa Kit, Core, BottomSheet &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btkBZK/btrlDXilRUk/f1SI7aKL4hbwxVxdFodzk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btkBZK/btrlDXilRUk/f1SI7aKL4hbwxVxdFodzk0/img.png&quot; data-alt=&quot; 174 개의 테스트 메서드 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btkBZK/btrlDXilRUk/f1SI7aKL4hbwxVxdFodzk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtkBZK%2FbtrlDXilRUk%2Ff1SI7aKL4hbwxVxdFodzk0%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;674&quot; height=&quot;508&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 174 개의 테스트 메서드 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;heoblitz, iOS URLSession Test Double 만들기:&lt;/span&gt; &lt;a href=&quot;https://linux-studying.tistory.com/31&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://linux-studying.tistory.com/31&lt;/span&gt;&lt;/a&gt;&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;heoblitz, iOS 뷰 컨트롤러 간단하게 테스트 코드 연습하기:&lt;/span&gt; &lt;a href=&quot;https://linux-studying.tistory.com/33&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://linux-studying.tistory.com/33&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;그 결과 Kit, Core, BottomSheet 각 프레임워크에 대한 테스트 코드 작성을 하였고 평균 테스트 커버리지는 70%, 테스트 메서드 개수는 174개로 구성되었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;중복되는 코드를 줄이고 재사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;Apple 에서는 Swift 를 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;프로토콜 지향 언어&lt;/span&gt;라 말합니다. &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;POP&lt;/span&gt; 라 불리는 프로토콜 중심 프로그래밍을 통해 &lt;u&gt;수평 확장&lt;/u&gt;이 가능하고 또한 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;OOP&lt;/span&gt; 를 통한 &lt;u&gt;수직 확장&lt;/u&gt;을 통해 여러 중복 코드를 제거하고 재사용할 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Protocol&amp;nbsp;&lt;/h3&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/64a044aa63fcf92b7b0ad8df38b136da.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;Protocol 과 extension 을 조합하여 만들고 특정 객체에서 채택하면 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #24292f;&quot;&gt;cellIdentifier를 일일히 지정하지 않아도 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/37b4fbdc5870ee68ffe54b11e4e601c2.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시도 BackgroundBlur 를 채택하면 필요한 메서드를 간단하게 확장하여 사용할 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Inheritance&amp;nbsp;&lt;/h3&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/57b0903d090bec3d3ccb97b3a2f8068d.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 처럼 프로젝트에서 반복되어 사용되는 코드가 있다고 가정해보겠습니다. 이때 LinkMoaNavigationController 를 상속하여 사용하면 코드 중복을 상당히 줄일 수 있습니다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/59a296e45739cbe7005f9e8e45a42883.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 UI 가 동일하지만 다른 동작이 필요할 때 상속(Inheritance) 과 재정의(Override) 를 통해 자식 객체에서 필요한 기능을 구현할 수 있습니다. 상속을 활용하면 AddLinkVC 내부에 분기처리를 제거할 수 있으므로 &lt;a href=&quot;https://ko.wikipedia.org/wiki/SOLID_(%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5_%EC%84%A4%EA%B3%84)&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;SOLID&lt;/span&gt;&lt;/a&gt; 의 개방-폐쇄 원칙을 준수할 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;더 나아가야 할 방향&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UI 컴포넌트 재사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 프론트엔드 분야에서 재사용 가능한 컴포넌트로 작업하는 것은 무척 중요합니다. 반복되는 UI를 컴포넌트화하고 이를 재사용하여 중복코드를 줄일 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;iOS 앱인 링크모아에서도 TableView 혹은 CollectionView 에서 사용하는 Cell 이나 Supplementary View 는 XIB 로 구현하여 다른 화면에서 재사용하고 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;하지만 동일한 Style 의 버튼이나 Label, 특정 View 들은 Storyboard 에서 각각 구현되어 있는 경우가 많습니다. 혹은 커스텀 클래스로 구현되어 있는 경우에도 @IBDesignable 어노테이션이 없어서 Storyboard 에서 바로 확인할 수 없습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;개발 당시에는 UI 구조화에 대한 이해도가 높은 편이 아니였고 기능 개발에만 집중했기 때문에 뷰 재사용이 미숙했다고 생각됩니다. 가능하면 리팩토링과 더불어 다음 프로젝트에서는 UI 컴포넌트 재사용에 대해서 고민해보고 작업을 할 계획입니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Storyboard 일 필요가 없다면 XIB 로 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크모아는 Storyboard 가 많아 질수록 느려지는 문제를 해결하기 위해 Storyboard 하나당 한개의 Initial ViewController 가 할당되어 있습니다. 개발 당시에는 가장 좋은 방식이라고 생각했었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;하지만 Storyboard 는 여러개의 뷰 컨트롤러의 전체적인 흐름을 파악하는데 장점이 있지만 링크모아와 같이 한개의 뷰 컨트롤러만 할당한다면 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;XIB 가 더 적합&lt;/u&gt;&lt;/span&gt;하다고 생각합니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그 이유는 Storyboard 는 파라미터가 필요한 ViewController를 초기화할 때 더 복잡한 방식을 거치기 때문입니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/b10878631e798985642e3519b89cc7d4.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;위처럼 스토리보드를 생성하고 다시 instantiateInitialViewController 를 통해 다시 ViewController를 생성해야 합니다. 따라서 UIKit 을 사용한 새로운 프로젝트를 진행할 때에는 XIB 로 제작해야겠다는 생각을 했습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS Dev/iOS</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/37</guid>
      <comments>https://linux-studying.tistory.com/37#entry37comment</comments>
      <pubDate>Sat, 20 Nov 2021 12:20:54 +0900</pubDate>
    </item>
    <item>
      <title>iOS Dependency Injection 개념 파악하기</title>
      <link>https://linux-studying.tistory.com/36</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3712&quot; data-origin-height=&quot;5568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rXrN3/btrhJr1oW87/O0hXBX0BPcZJINRqujIkT1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rXrN3/btrhJr1oW87/O0hXBX0BPcZJINRqujIkT1/img.jpg&quot; data-alt=&quot;Photo by&amp;amp;amp;nbsp;Bozhin Karaivanov on Unsplash&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rXrN3/btrhJr1oW87/O0hXBX0BPcZJINRqujIkT1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrXrN3%2FbtrhJr1oW87%2FO0hXBX0BPcZJINRqujIkT1%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;308&quot; height=&quot;462&quot; data-origin-width=&quot;3712&quot; data-origin-height=&quot;5568&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Photo by&amp;amp;nbsp;Bozhin Karaivanov on Unsplash&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;의존성 주입이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;iOS 테스트 코드를 공부하며 상당히 많이 접했던 단어입니다. &quot;의존성 주입&quot; 은 무엇일까요?&lt;br /&gt;이론적인 개념은 다음과 같습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;```&lt;br /&gt;소프트웨어 엔지니어링에서 &lt;b&gt;의존성 주입&lt;/b&gt;(dependency injection)은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;하나의 객체가 다른 객체의 의존성을 제공하는 테크닉이다.&lt;/span&gt; 클라이언트가 어떤 서비스를 사용할 것인지 지정하는 대신, 클라이언트에게 무슨 서비스를 사용할 것인지를 말해주는 것이다. &quot;주입&quot;은 의존성(서비스)을 사용하려는 객체(클라이언트)로 전달하는 것을 의미한다. &lt;br /&gt;```&lt;br /&gt;출처: &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9D%98%EC%A1%B4%EC%84%B1_%EC%A3%BC%EC%9E%85&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://ko.wikipedia.org/wiki/%EC%9D%98%EC%A1%B4%EC%84%B1_%EC%A3%BC%EC%9E%85&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;대부분의 프로그램은 객체들의 조합인 Composition 으로 이루어집니다. 의존성 주입은 객체가 서로 의존 관계에 있을 때 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;&quot;직접 생성하는&quot;&lt;/span&gt; 것이 아니라 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;&quot;제공 받는&quot;&lt;/span&gt; 형태를 말합니다. 이를 예시 코드로 살펴보겠습니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&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;script src=&quot;https://gist.github.com/heoblitz/eced917ab9154b08fb82859ce83a6656.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;MVC 패턴에서 주로 ViewController에서 Network 관련 객체를 생성하고, 이를 토대로 로직을 구성하게 됩니다. 이를 Dependency Injection ( 의존성 주입 ) 으로 바꾸면 다음과 같습니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/769051b610b409441e449b0210eefc06.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;NetworkProtocol 를 구현하고 NetworkProvider 에서는 해당 프로토콜을 채택합니다.&lt;br /&gt;ViewController 에서는 이니셜라이저를 통해 필요한 의존성을 주입 받게 됩니다.&lt;br /&gt;&lt;br /&gt;ViewController 에서는 더 이상 NetworkProvider 가 아닌 NetworkProtocol 를 가르키게 됩니다.&lt;br /&gt;따라서 NetworkProtocol 를 채택한 어떤 객체도 주입 가능하게 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;이러한 프로토콜을 통한 간접화는 의존성을 쉽게 치환할 수 있습니다.&lt;/u&gt;&lt;br /&gt;&lt;u&gt;또한 해당 프로토콜을 준수한 Mock 객체를 만들어서 주입하면 &lt;/u&gt;&lt;u&gt;쉽게 테스트 환경을 만들 수 있습니다.&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;의존성 주입은 좋은 방식인가? 장점은 무엇일까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Uber 에서 만든 Needle 프레임워크는 예시와 함께 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;의존성 주입의 장점&lt;/span&gt;에 대해서 설명합니다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;사진을 검색하는 앱을 만든다고 가정해보겠습니다. PhotosViewController는 PhotosService를 통해 서버에서 사진을 가져와서 보여주게 됩니다. 
이때 PhotosService는 내부에서 생성하도록 구현되어 있습니다.

위와 같은 강한 커플링 구조는 다음과 같은 이슈를 만들 수 있습니다. 


1. PhotosService의 코드를 수정하려면 PhotosViewController 또한 코드를 수정해야 합니다. 
( 두개의 클래스에서는 괜찮아 보이지만 실제 수백개의 클래스가 있는 현업 앱에서는 앱 반복을 늦추게 됩니다. ) 
* 필자는 개발 주기로 이해했습니다. 

2. PhotosService를 교체하려면 PhotosViewController 또한 코드를 수정해야 합니다.
( 성능을 개선한 PhotosServiceV2 를 사용하려 할 때 PhotosViewController 코드를 일일히 파악하고 수정해야 합니다. ) 

3. PhotosService의 호출 없이 PhotosViewController 유닛 테스트가 불가합니다. 
( 내부에서 의존성을 생성하므로 테스트시에 주입이 불가합니다. ) 

4. PhotosViewController 와 PhotosService 를 동시에 개발할 수 없습니다. 
( 작은 규모의 앱에서는 큰 문제가 아닌 것 처럼 보일 수 있지만 실제 팀에서는 엔지니어가 끊임없이 막힐 수 있습니다. ) 

* 의역이 있을 수 있습니다. 자세한 내용은 링크를 참고해주시면 감사하겠습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/uber/needle/blob/master/WHY_DI.md&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://github.com/uber/needle/blob/master/WHY_DI.md&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성 주입은 testable 한 코드와도 무척 연관이 깊습니다. 어떤 객체가 다른 객체와 연관이 있을 때 &lt;br /&gt;이를 여러가지 방법(이니셜라이저, 프로퍼티, 메서드)를 통해 외부에서 주입할 수 있는 방식을 말합니다.&lt;br /&gt;&lt;br /&gt;의존성 주입은 위와 같이 협업에서 발생할 수 있는 이슈나 유닛 테스트시에 발생하는 문제를 해결합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;의존성 주입의 단점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;의존성 주입을 했을 때에는 결합도(coupling) 가 적다고 표현할 수 있습니다.&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;300&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTbh9v/btrjJk6Ea2p/ldHv11zrGcunVw7zJ3kFx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTbh9v/btrjJk6Ea2p/ldHv11zrGcunVw7zJ3kFx1/img.png&quot; data-alt=&quot;https://ko.wikipedia.org/wiki/%EA%B2%B0%ED%95%A9%EB%8F%84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTbh9v/btrjJk6Ea2p/ldHv11zrGcunVw7zJ3kFx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTbh9v%2FbtrjJk6Ea2p%2FldHv11zrGcunVw7zJ3kFx1%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;385&quot; height=&quot;289&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://ko.wikipedia.org/wiki/%EA%B2%B0%ED%95%A9%EB%8F%84&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이때 낮은 결합도를 가진 구조는 구조화가 잘 되어있다고 표현할 수 있으며 좋은 설계로 표현될 수 있습니다. 이와 반대로 높은 결합도를 가진 구조는 &lt;u&gt;높은 가독성과 유지보수성&lt;/u&gt;을 가진다고 할 수 있습니다.&lt;br /&gt;&lt;br /&gt;따라서 의존성 주입 패턴은 생성자가 다른 코드에 위치하기 때문에 상대적으로 &lt;u&gt;낮은 가독성&lt;/u&gt;을 가질 수 있다는 것이 단점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;* 유지보수성의 경우에는 팀원이 많을 수록 늘어난다고 생각합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;의존성은 어디에서 주입되는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;의존성 주입은 의존 관계에 있는 객체를 직접 생성하는 것이 아닌 제공받는것이라고 개념을 정리했습니다.&lt;br /&gt;그러면 iOS 앱에서는 의존성을 어디에서 주입해야할까요? &lt;u&gt;크게 2가지 방식이 있습니다.&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1.&amp;nbsp;&amp;nbsp;DI Container&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성을 관리해주는 별도의 컨테이너를 사용하는 방식을 말합니다. 대표적으로 &lt;a href=&quot;https://github.com/Swinject/Swinject&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Swinject&lt;/span&gt;&lt;/a&gt; 라는 프레임워크가 있습니다.&lt;br /&gt;DI Container는 등록하는 register 와 의존성을 풀어내는 resolve 를 지원합니다. iOS 에서는 앱 시작점&amp;nbsp;&amp;nbsp;(AppDelegate)에서 필요한 의존성들을 register 하고 필요한 부분에서 resolve 하여 사용합니다.&lt;br /&gt;&lt;br /&gt;주로 DI Container 는 싱글톤으로 구현되는 경우가 많기 때문에 어떤 지점에서도 접근이 가능합니다. 따라서 필요한 의존성을 DI Container 를 통해 &lt;u&gt;직접 생성&lt;/u&gt;하거나 이전 뷰 컨트롤러에서 resolve 하여 주입하는 방식을 사용합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. Pure DI&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5fFYn/btrjFApclIm/gIztkZQsN5H5fA1AKhrgaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5fFYn/btrjFApclIm/gIztkZQsN5H5fA1AKhrgaK/img.png&quot; data-alt=&quot;https://freecontent.manning.com/dependency-injection-in-net-2nd-edition-understanding-the-composition-root/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5fFYn/btrjFApclIm/gIztkZQsN5H5fA1AKhrgaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5fFYn%2FbtrjFApclIm%2FgIztkZQsN5H5fA1AKhrgaK%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;432&quot; height=&quot;211&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://freecontent.manning.com/dependency-injection-in-net-2nd-edition-understanding-the-composition-root/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #222426;&quot;&gt;DI Container 를 사용하지 않고 모든 의존성을 Compostion Root 에서 관리하는 방식을 말합니다. iOS 에서 &lt;/span&gt;&lt;span style=&quot;color: #222426;&quot;&gt;Compostion Root 는 AppDelegate 에 해당됩니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #222426;&quot;&gt;Compostion Root&lt;/span&gt;&lt;span style=&quot;color: #222426;&quot;&gt; 에서는&lt;/span&gt; 의존성 트리를 구성하고 Factory 형식으로 하위 객체에 필요한 의존성을 모두 제공하게 됩니다. 클로저로 이어지는 이니셜라이저 Depth 를 줄이기 위하여 &lt;a href=&quot;https://github.com/devxoul/Pure&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Pure&lt;/span&gt;&lt;/a&gt; 라는 프레임워크를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스팅을 작성하며 의존성 주입은 iOS 뿐만 아니라 소프트웨어 공학에서 널리 사용되는 개념임을 알게 되었습니다. 따라서 개발자에 따라서 이해도나 관점이 다르기 때문에 더 많은 레퍼런스를 찾아보는 것이 좋겠다는 생각을 했습니다.&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;br /&gt;&lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;IoC? DIP? IoC Container? DI? DI Framework? 도대체 그게 뭔데?&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;여러분이 자바 혹은 C#을 사용한다면 제목과 같은 용어들을 봐왔을겁니다. 하지만 보면 볼수록 매우 혼란스럽게 느껴질 것 입니다. 이렇게 복잡하게 느껴지는 이유는 이러한 용어들이 긴 시간에&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@wickedev/IoC-DIP-IoC-Container-DI-DI-Framework-%EB%8F%84%EB%8C%80%EC%B2%B4-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KZjJ0/hyMcpFeUO9/1fzaC2rfk2uu2IPlK9ohu0/img.png?width=691&amp;amp;height=355&amp;amp;face=0_0_691_355,https://scrap.kakaocdn.net/dn/bbqxnL/hyMcf3H8oS/rsO7i3xvE7UrdiPurD8AtK/img.png?width=691&amp;amp;height=355&amp;amp;face=0_0_691_355&quot; data-og-url=&quot;https://velog.io/@wickedev/IoC-DIP-IoC-Container-DI-DI-Framework-도대체-그게-뭔데&quot;&gt;&lt;a href=&quot;https://velog.io/@wickedev/IoC-DIP-IoC-Container-DI-DI-Framework-도대체-그게-뭔데&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@wickedev/IoC-DIP-IoC-Container-DI-DI-Framework-%EB%8F%84%EB%8C%80%EC%B2%B4-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KZjJ0/hyMcpFeUO9/1fzaC2rfk2uu2IPlK9ohu0/img.png?width=691&amp;amp;height=355&amp;amp;face=0_0_691_355,https://scrap.kakaocdn.net/dn/bbqxnL/hyMcf3H8oS/rsO7i3xvE7UrdiPurD8AtK/img.png?width=691&amp;amp;height=355&amp;amp;face=0_0_691_355');&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;IoC? DIP? IoC Container? DI? DI Framework? 도대체 그게 뭔데?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;여러분이 자바 혹은 C#을 사용한다면 제목과 같은 용어들을 봐왔을겁니다. 하지만 보면 볼수록 매우 혼란스럽게 느껴질 것 입니다. 이렇게 복잡하게 느껴지는 이유는 이러한 용어들이 긴 시간에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&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 Dev/iOS</category>
      <category>Dependency Injection</category>
      <category>DI</category>
      <category>IOS</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/36</guid>
      <comments>https://linux-studying.tistory.com/36#entry36comment</comments>
      <pubDate>Wed, 3 Nov 2021 11:10:16 +0900</pubDate>
    </item>
    <item>
      <title>초보 개발자가 오픈소스에 기여하는 방법</title>
      <link>https://linux-studying.tistory.com/35</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;5184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxPFy0/btrhshqrtSL/HzVIUIWYkPcxKPCi1T32NK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxPFy0/btrhshqrtSL/HzVIUIWYkPcxKPCi1T32NK/img.jpg&quot; data-alt=&quot; Photo by Stephen Picilaidis on Unsplash &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxPFy0/btrhshqrtSL/HzVIUIWYkPcxKPCi1T32NK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxPFy0%2FbtrhshqrtSL%2FHzVIUIWYkPcxKPCi1T32NK%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;270&quot; height=&quot;405&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;5184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; Photo by Stephen Picilaidis on Unsplash &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;필자는 깃허브에 있는 오픈소스 프로젝트를 자주 들여다 봅니다.&lt;br&gt;뛰어난 개발자분들이 작성한 코드를 보고 새로운 언어 기능이나 구조를 보며 배우는 것이 무척 많습니다.&lt;br&gt;&lt;br&gt;그러던 중 기여할 수 있는 부분이 생기면 여러번 고민해보고 PR 을 보내기도 합니다.&lt;br&gt;처음에는 확신이 없었고 민폐가 아닌가? 하는 생각이 들었지만&lt;br&gt;에러를 해결하고 나서 많은 분들이 좋은 피드백을 주셨던 경험이 있습니다.&lt;br&gt;&lt;br&gt;그 후 개선할 부분을 발견하면 이슈를 꼼꼼히 확인하고 PR 을 올리는 습관을 가질 수 있었습니다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Swift 첫 오픈 소스 기여 후기 ( Open Source PR )&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;오픈소스라 하면 거대한 프로젝트이고, 뛰어난 개발자분들이 기여하기 때문에, 입문 개발자가 쉽게 접근하기 어렵다고 생각했습니다. 하지만 베타 Xcode 에서 컴파일 에러를 해결하여, 입문 개발&quot; data-og-host=&quot;linux-studying.tistory.com&quot; data-og-source-url=&quot;https://linux-studying.tistory.com/14&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxOHuW/hyLVWJaty9/UpymF4rqQhLWGuzxmVIdc0/img.png?width=395&amp;amp;height=195&amp;amp;face=231_77_254_102,https://scrap.kakaocdn.net/dn/bDUsEo/hyLVSfHoRz/FZZ2cKC7kDCBiHKZVYiXHk/img.png?width=395&amp;amp;height=195&amp;amp;face=231_77_254_102,https://scrap.kakaocdn.net/dn/zybwG/hyLVRVoFqo/xjpAujMi04EpMtM4Zo6ee1/img.png?width=1502&amp;amp;height=1528&amp;amp;face=0_0_1502_1528&quot; data-og-url=&quot;https://linux-studying.tistory.com/14&quot;&gt;
 &lt;a href=&quot;https://linux-studying.tistory.com/14&quot; target=&quot;_blank&quot; data-source-url=&quot;https://linux-studying.tistory.com/14&quot;&gt;
  &lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxOHuW/hyLVWJaty9/UpymF4rqQhLWGuzxmVIdc0/img.png?width=395&amp;amp;height=195&amp;amp;face=231_77_254_102,https://scrap.kakaocdn.net/dn/bDUsEo/hyLVSfHoRz/FZZ2cKC7kDCBiHKZVYiXHk/img.png?width=395&amp;amp;height=195&amp;amp;face=231_77_254_102,https://scrap.kakaocdn.net/dn/zybwG/hyLVRVoFqo/xjpAujMi04EpMtM4Zo6ee1/img.png?width=1502&amp;amp;height=1528&amp;amp;face=0_0_1502_1528')&quot;&gt; 
  &lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;Swift 첫 오픈 소스 기여 후기 ( Open Source PR )&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;오픈소스라 하면 거대한 프로젝트이고, 뛰어난 개발자분들이 기여하기 때문에, 입문 개발자가 쉽게 접근하기 어렵다고 생각했습니다. 하지만 베타 Xcode 에서 컴파일 에러를 해결하여, 입문 개발&lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;linux-studying.tistory.com&lt;/p&gt;
  &lt;/div&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;이번 포스팅에서는 초보 개발자가 관심이 있는 프로젝트에 기여하고 싶을 때&lt;br&gt;어떻게 시작할 수 있는지에 대해서 다루어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;br&gt;* Github 를 기준으로 작성하였습니다. 필자도 많이 미숙하지만 관련된 내용을 정리하면 좋겠다는 생각에 포스팅을 작성하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;목차&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 - Pull Request 에 대해서 이해하기&lt;br&gt;2 - 버그 및 수정사항 발견하기&lt;br&gt;3 - 마무리&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Pull Request 에 대해서 이해하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;오픈소스는 다양한 사람들이 협업하기 때문에 Git 에 대한 이해는 필수적이라고 생각합니다.&lt;br&gt;혹시 Git 의 개념이 아직 어색하다면 다음과 같은 강의들을 추천드립니다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;누구나 쉽게 이해할 수 있는 Git 입문~버전 관리를 완벽하게 이용해보자~ | Backlog&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;누구나 쉽게 알 수 있는 Git에 입문하신 것을 환영합니다. Git을 사용해 버전 관리를 할 수 있도록 함께 공부해봅시다!&quot; data-og-host=&quot;backlog.com&quot; data-og-source-url=&quot;https://backlog.com/git-tutorial/kr/intro/intro1_1.html&quot; data-og-url=&quot;https://backlog.com/git-guide/kr/intro/intro1_1.html&quot;&gt;
 &lt;a href=&quot;https://backlog.com/git-guide/kr/intro/intro1_1.html&quot; target=&quot;_blank&quot; data-source-url=&quot;https://backlog.com/git-tutorial/kr/intro/intro1_1.html&quot;&gt;
  &lt;div class=&quot;og-image&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;누구나 쉽게 이해할 수 있는 Git 입문~버전 관리를 완벽하게 이용해보자~ | Backlog&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;누구나 쉽게 알 수 있는 Git에 입문하신 것을 환영합니다. Git을 사용해 버전 관리를 할 수 있도록 함께 공부해봅시다!&lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;backlog.com&lt;/p&gt;
  &lt;/div&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;git을 구경합시다! - GIT1&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;수업소개 여기서는 git의 핵심목적을 소개하고, 앞으로 어떤 마음으로 공부하실지를 권유해드립니다. 강의&quot; data-og-host=&quot;opentutorials.org&quot; data-og-source-url=&quot;https://opentutorials.org/module/3733/22434&quot; data-og-url=&quot;https://opentutorials.org/module/3733/22434&quot;&gt;
 &lt;a href=&quot;https://opentutorials.org/module/3733/22434&quot; target=&quot;_blank&quot; data-source-url=&quot;https://opentutorials.org/module/3733/22434&quot;&gt;
  &lt;div class=&quot;og-image&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;git을 구경합시다! - GIT1&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;수업소개 여기서는 git의 핵심목적을 소개하고, 앞으로 어떤 마음으로 공부하실지를 권유해드립니다. 강의&lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;opentutorials.org&lt;/p&gt;
  &lt;/div&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;pull request 는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&quot;제가 작업한 코드를 확인하고 브랜치를 병합해주세요.&quot;&lt;/span&gt; 라는 일련의 과정을 말합니다.&lt;br&gt;권한이 있는 저장소에서 작업할 경우에는 직접 브랜치를 만들고 내부에서 pull request 가 가능합니다.&lt;br&gt;&lt;br&gt;하지만 오픈 소스의 경우 main contributors 를 제외한 개발자들은 push 권한이 없기 때문에 다음과 같은 과정을 거쳐야 합니다.&lt;br&gt;&lt;br&gt;&lt;br&gt;&amp;lt; Pull Request 과정 요약 &amp;gt;&lt;br&gt;&lt;br&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;A&lt;/span&gt;: 해당 오픈소스 저장소&lt;br&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;B&lt;/span&gt;: fork 한 저장소&lt;br&gt;&lt;br&gt;`&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;A&lt;/span&gt; 저장소를 fork` -&amp;gt; `&lt;span style=&quot;background-color: #99cefa;&quot;&gt;B&lt;/span&gt; 에서 commit` -&amp;gt; `&lt;span style=&quot;background-color: #99cefa;&quot;&gt;B&lt;/span&gt; 에서 push` -&amp;gt; `&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;A&lt;/span&gt; 에게 pull request 요청`&lt;br&gt;&lt;br&gt;&lt;br&gt;&amp;lt; Github Pull Request 과정 &amp;gt;&lt;br&gt;&lt;br&gt;1 - 해당 오픈소스 저장소를 fork 합니다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bP1BDd/btrhgj4E47Z/YZPOKV3eJtkhmDAJY7nG21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bP1BDd/btrhgj4E47Z/YZPOKV3eJtkhmDAJY7nG21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bP1BDd/btrhgj4E47Z/YZPOKV3eJtkhmDAJY7nG21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbP1BDd%2Fbtrhgj4E47Z%2FYZPOKV3eJtkhmDAJY7nG21%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;545&quot; height=&quot;220&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;2 - fork 한 저장소에서 작업하고 commit 을 push 합니다.&lt;br&gt;3 - New pull request 를 생성합니다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNI575/btrhdBLunEv/qKtUgx3CK4UPdyIVIyHQx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNI575/btrhdBLunEv/qKtUgx3CK4UPdyIVIyHQx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNI575/btrhdBLunEv/qKtUgx3CK4UPdyIVIyHQx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNI575%2FbtrhdBLunEv%2FqKtUgx3CK4UPdyIVIyHQx1%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;544&quot; height=&quot;179&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPI17m/btrhn2graNo/jxR0SAdca0fOnBOqul0B1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPI17m/btrhn2graNo/jxR0SAdca0fOnBOqul0B1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPI17m/btrhn2graNo/jxR0SAdca0fOnBOqul0B1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPI17m%2Fbtrhn2graNo%2FjxR0SAdca0fOnBOqul0B1k%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;543&quot; height=&quot;53&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;4 - 해당 저장소와 브랜치를 설정하고 양식에 맞게 작성하면 됩니다.&lt;br&gt;( 각 프로젝트에 맞는 contributing guide 를 참고하는 것이 가장 좋습니다. )&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpkFBe/btrhjr8R5iA/h1j8Mmjoi1DnkhVMSlHsqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpkFBe/btrhjr8R5iA/h1j8Mmjoi1DnkhVMSlHsqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpkFBe/btrhjr8R5iA/h1j8Mmjoi1DnkhVMSlHsqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpkFBe%2Fbtrhjr8R5iA%2Fh1j8Mmjoi1DnkhVMSlHsqK%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;539&quot; height=&quot;426&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 버그 및 수정사항 발견하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&lt;b&gt;&amp;lt; 오픈소스 사용중에 문제가 발생하는 경우 &amp;gt;&lt;/b&gt;&lt;br&gt;&lt;br&gt;컴파일 도중에 에러가 발생하거나 혹은 오동작이 발생하는 버그를 발견했을 때&lt;br&gt;로그와 실행 환경을 파악하여 어떤 것이 문제인지 살펴봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* Github issue 를 확인하여 같은 문제가 발생한 유저가 있는지, 이에 대한 문제 해결이 진행되는지 확인해보면 더 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;오픈소스 자체의 문제라고 생각되면 Issue 를 등록하거나 수정한 코드로 해결된다면 PR 을 요청해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* Issue, pull request 에 대한 규칙이 각 오픈소스에 있는 경우가 많습니다. 꼭 확인 후 진행하는 것을 추천드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;901&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2YrEf/btrivPN1Hdd/0xN8uzZbxfIb5bk1otZaqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2YrEf/btrivPN1Hdd/0xN8uzZbxfIb5bk1otZaqK/img.png&quot; data-alt=&quot; 최신 버전 IDE 에서 발생한 컴파일 에러를 해결하고 PR &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2YrEf/btrivPN1Hdd/0xN8uzZbxfIb5bk1otZaqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2YrEf%2FbtrivPN1Hdd%2F0xN8uzZbxfIb5bk1otZaqK%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;513&quot; height=&quot;533&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;901&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 최신 버전 IDE 에서 발생한 컴파일 에러를 해결하고 PR &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&lt;b&gt;&amp;lt; Github Issue 확인하기 &amp;gt;&lt;/b&gt;&lt;br&gt;&lt;br&gt;Github Issue 는 프로젝트에 대한 기능, 버그, 제안등의 여러가지 이슈를 등록할 수 있는 기능입니다.&lt;br&gt;해당 이슈에 대한 수정이나 개선이 가능하다면 pull request 에 다음과 같이 Issue 번호를 붙여서 작성할 수 있습니다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biOcNb/btrhsgkWCqJ/se6m58yC4ny6NJhJran1t0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biOcNb/btrhsgkWCqJ/se6m58yC4ny6NJhJran1t0/img.png&quot; data-alt=&quot; 프로젝트 방향성에 맞게 기능을 구현하고 PR &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biOcNb/btrhsgkWCqJ/se6m58yC4ny6NJhJran1t0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiOcNb%2FbtrhsgkWCqJ%2Fse6m58yC4ny6NJhJran1t0%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;513&quot; height=&quot;332&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 프로젝트 방향성에 맞게 기능을 구현하고 PR &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;&amp;lt; README 에 있는 오타, 코드 컨벤션 수정하기 &amp;gt;&lt;/b&gt;&lt;br&gt;&lt;br&gt;코드에 변수 명이나 주석 철자에 오타가 있거나 README 에 있는 문서가 잘못 링크가 되어있는 경우에도&lt;br&gt;간단하게 수정해서 PR 할 수 있습니다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;731&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGXzIu/btrhtYjZZzI/TY2eCk73mQrxY5Xi5uMg3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGXzIu/btrhtYjZZzI/TY2eCk73mQrxY5Xi5uMg3k/img.png&quot; data-alt=&quot; 코드 컨벤션이 잘못된 경우 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGXzIu/btrhtYjZZzI/TY2eCk73mQrxY5Xi5uMg3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGXzIu%2FbtrhtYjZZzI%2FTY2eCk73mQrxY5Xi5uMg3k%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;517&quot; height=&quot;412&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;731&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 코드 컨벤션이 잘못된 경우 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 마무리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;오픈소스의 이슈를 해결하는 것은 무척이나 까다롭고 어려운 작업이라고 생각합니다.&lt;br&gt;특히 기능을 개선하거나 새롭게 만들 때에는 상황에 따라 테스트 코드 작업도 필요한 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* 일정 규모 이상의 오픈소스는 CI/CD 환경이 구축되어 PR 과정에 테스트를 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;다만 꼭 기여를 하지 않더라도 오픈소스를 통해 다양한 사람들의 코드를 살펴볼 수 있고 점차 코드를 이해하는 능력이 성장하는 것을 느낍니다. 이러한 과정은 본인만의 오픈소스 라이브러리를 만들 수 있는 토대가 될 수 있다고 생각합니다.&lt;br&gt;&lt;br&gt;마지막으로 Github Issue 통한 프로그래밍 학습법을 소개하고 포스팅을 마치겠습니다. 읽어주셔서 감사합니다!&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;How To Rapidly Improve At Any Programming Language&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Last year, I submitted a PR to the Clojure web request routing library, Compojure. Even though I was doing Clojure full time at work, there was still a lot for me to learn and always will be. Opening a PR for an open source library I was familiar with and &quot; data-og-host=&quot;www.cbui.dev&quot; data-og-source-url=&quot;https://www.cbui.dev/how-to-rapidly-improve-at-any-programming-language/&quot; data-og-url=&quot;https://www.cbui.dev/how-to-rapidly-improve-at-any-programming-language/&quot;&gt;
 &lt;a href=&quot;https://www.cbui.dev/how-to-rapidly-improve-at-any-programming-language/&quot; target=&quot;_blank&quot; data-source-url=&quot;https://www.cbui.dev/how-to-rapidly-improve-at-any-programming-language/&quot;&gt;
  &lt;div class=&quot;og-image&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;How To Rapidly Improve At Any Programming Language&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;Last year, I submitted a PR to the Clojure web request routing library, Compojure. Even though I was doing Clojure full time at work, there was still a lot for me to learn and always will be. Opening a PR for an open source library I was familiar with and &lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;www.cbui.dev&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>Other</category>
      <category>Git</category>
      <category>github</category>
      <category>오픈소스</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/35</guid>
      <comments>https://linux-studying.tistory.com/35#entry35comment</comments>
      <pubDate>Mon, 11 Oct 2021 14:11:24 +0900</pubDate>
    </item>
    <item>
      <title>Swift Reference Count 고찰 ( with side table )</title>
      <link>https://linux-studying.tistory.com/34</link>
      <description>&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-filename=&quot;alexandre-lecocq-v-8o8vGMg3s-unsplash.jpg&quot; data-origin-width=&quot;2585&quot; data-origin-height=&quot;2585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB7rbB/btrhcGyJgUX/e0fuLqq6SMk4BhCRW1efU1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB7rbB/btrhcGyJgUX/e0fuLqq6SMk4BhCRW1efU1/img.jpg&quot; data-alt=&quot;Photo by Alexandre Lecocq on Unsplash&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB7rbB/btrhcGyJgUX/e0fuLqq6SMk4BhCRW1efU1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB7rbB%2FbtrhcGyJgUX%2Fe0fuLqq6SMk4BhCRW1efU1%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;328&quot; height=&quot;328&quot; data-filename=&quot;alexandre-lecocq-v-8o8vGMg3s-unsplash.jpg&quot; data-origin-width=&quot;2585&quot; data-origin-height=&quot;2585&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Photo by Alexandre Lecocq on Unsplash&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift ARC 의 기본적인 개념은 아래 포스팅을 참고해주시면 감사하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1633691072413&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Swift ARC ( Automatic Reference Counting ) 정리&quot; data-og-description=&quot;추상적으로 &amp;quot;메모리를 관리해주는 것&amp;quot; 으로 이해하고 있었지만, JAVA 의 GC ( 가비지 컬렉터 ) 와의 차이점을 명확하게 설명하게 어려워서 포스팅을 작성했습니다. 프로그래밍 언어의 메모리 관리 &quot; data-og-host=&quot;linux-studying.tistory.com&quot; data-og-source-url=&quot;https://linux-studying.tistory.com/18&quot; data-og-url=&quot;https://linux-studying.tistory.com/18&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gq4h5/hyLTzHQBla/HlQIkMJKDVSvR5Y9LaUDdK/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/RZ2Qk/hyLTFgZtF7/u46OmI1cYyTYIi77dyk9E1/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533&quot;&gt;&lt;a href=&quot;https://linux-studying.tistory.com/18&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://linux-studying.tistory.com/18&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gq4h5/hyLTzHQBla/HlQIkMJKDVSvR5Y9LaUDdK/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/RZ2Qk/hyLTFgZtF7/u46OmI1cYyTYIi77dyk9E1/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533');&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;Swift ARC ( Automatic Reference Counting ) 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;추상적으로 &quot;메모리를 관리해주는 것&quot; 으로 이해하고 있었지만, JAVA 의 GC ( 가비지 컬렉터 ) 와의 차이점을 명확하게 설명하게 어려워서 포스팅을 작성했습니다. 프로그래밍 언어의 메모리 관리&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;linux-studying.tistory.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자는 iOS 를 처음 공부하며 클로저 캡처리스트를 작성할 때 u&lt;span style=&quot;color: #000000;&quot;&gt;nowned&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;를 남발했던 기억이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 앱 크래시를 유발할 수 있다는 조언을 듣고 강한 순환 참조가 발생할 수 있는 상황에서는 weak 키워드를 사용했습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;weak 와 u&lt;span style=&quot;color: #000000;&quot;&gt;nowned&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 사용하면 레퍼런스 카운트가 올라가지 않고 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;self 가 옵셔널이라는 차이가 있다는 &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;단편적인 이해를 하고 넘어갔었지만, &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번 포스팅에서는 조금 더 자세하게 ARC 동작에 대해서 다루겠습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Swift Reference Count&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/ff3a45c497c0440260759b7d2fd43b28.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift &quot;RefCount.h&quot; 헤더 파일의 주석을 참고하면 레퍼런스 카운트는 다음과 같이 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;3개&lt;/span&gt;로 나누어 집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Strong RC&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 오브젝트를 참조할 때 기본적으로 사용되는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Strong 카운터 가 0이 되면 오브젝트가 deinited 됩니다.&lt;/span&gt; ( 메모리에서 해제를 의미하지는 않습니다. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- String 카운터가 0일 때 unowned 레퍼런스를 읽으면 error, weak 레퍼런스는 nil 이 됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Unowned RC&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 오브젝트를 미소유(unonwned) 로 참조할 때 해당됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 참조될 때 Unowned RC 카운트가 올라가고 해당 카운트는 오브젝트가 deinited 될 때 카운트가 내려갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #ffc9af;&quot;&gt;Unowned 카운터 가 0이 되면 오브젝트가 메모리에서 해제(free) 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Weak RC&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 오브젝트를 약하게(weak) 참조할 때 해당됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 참조될 때 Weak RC 카운트가 올라가고 해당 카운트는 오브젝트가 해제(free) 될 때 카운트가 내려갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Weak 카운터 가 0이 되면, 해당 오브젝트의 Side Table 또한 해제됩니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* 코드 주석에 맞게 &lt;b&gt;오브젝트&lt;/b&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2021-10-11 오전 10.15.50.png&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by0Owx/btrhqflYqNj/NqGQweNgkzwm0fQWvkkYk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by0Owx/btrhqflYqNj/NqGQweNgkzwm0fQWvkkYk0/img.png&quot; data-alt=&quot;https://www.vadimbulavin.com/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by0Owx/btrhqflYqNj/NqGQweNgkzwm0fQWvkkYk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby0Owx%2FbtrhqflYqNj%2FNqGQweNgkzwm0fQWvkkYk0%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;383&quot; height=&quot;418&quot; data-filename=&quot;스크린샷 2021-10-11 오전 10.15.50.png&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.vadimbulavin.com/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What is Side Table?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 테이블은 Swift 4.0 버전부터 새롭게 도입된 기능입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 방식은 Strong 과 Weak 두 가지의 참조 카운트를 가지고 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Strong 카운터가 0이 되면 deinit 되지만, 메모리에서 바로 free 되지는 않았습니다.&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;background-color: #f6e199;&quot;&gt;좀비 오브젝트&lt;/span&gt;라고 불렀고, 그 후 weak 참조로 다시 접근하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;이 방식은 매우 간단한 설계였지만 사용하지않는 오브젝트가 메모리에 계속 남는 것은 비효율적인 방식입니다. 따라서 Side Table 이라는 별도의 관리 개체를 만들게 되었습니다.&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;&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;1539&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPUaIj/btrhgjXwYlv/JLYAMxsa8XbGb3HWpeIJx1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPUaIj/btrhgjXwYlv/JLYAMxsa8XbGb3HWpeIJx1/img.jpg&quot; data-alt=&quot;https://maximeremenko.com/swift-arc-weak-references&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPUaIj/btrhgjXwYlv/JLYAMxsa8XbGb3HWpeIJx1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPUaIj%2FbtrhgjXwYlv%2FJLYAMxsa8XbGb3HWpeIJx1%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;594&quot; height=&quot;368&quot; data-origin-width=&quot;1539&quot; data-origin-height=&quot;954&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://maximeremenko.com/swift-arc-weak-references&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림처럼 Strong 과 Unowned 는 오브젝트를 직접 가르키고, weak 는 side table 를 가르키게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;1. Strong 혹은 Unowned 카운트가 오버플로우 될 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Weak 참조를 사용할 때&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;이러한 side table 덕분에 Strong 카운트가 0가 되면 deinit 에서 메모리가 해제되는 과정을 거치게됩니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Weak vs Unowned?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mike Ash 개발자에 따르면 side table 이 필요한 오브젝트는 &lt;span style=&quot;background-color: #f6e199;&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;size14&quot;&gt;* 구글링을 통해 weak unowned 키워드에 따른 유의미한 성능 차이는 찾지 못했습니다. 다만 별도의 오브젝트를 사용하므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;할당/해제에 발생하는 오버헤드가 있는 것은 분명한 사실입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 오브젝트의 생명주기를 확실하게 파악한 상태이면 Unowned 키워드를 사용하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외의 경우라면 안전한 코딩을 위해 weak 키워드를 사용하는 것이 좋겠다는 생각이 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* unowned 키워드는 RxSwift, Alamofire 같은 유명한 라이브러리에서도 사용된&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;코드를 발견할 수 있었습니다. 상황에 따라 적절하게 사용가능할 것으로 보입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;참고:&lt;/p&gt;
&lt;figure id=&quot;og_1633916832406&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;mikeash.com: Friday Q&amp;amp;A 2017-09-22: Swift 4 Weak References&quot; data-og-description=&quot;Friday Q&amp;amp;A 2017-09-22: Swift 4 Weak References This article is also available in Chinese (translation by Xiang Deng). Soon after Swift was initially open sourced, I wrote an article about how weak references are implemented. Time moves on and things change&quot; data-og-host=&quot;mikeash.com&quot; data-og-source-url=&quot;https://mikeash.com/pyblog//friday-qa-2017-09-22-swift-4-weak-references.html&quot; data-og-url=&quot;https://mikeash.com/pyblog//friday-qa-2017-09-22-swift-4-weak-references.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://mikeash.com/pyblog//friday-qa-2017-09-22-swift-4-weak-references.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mikeash.com/pyblog//friday-qa-2017-09-22-swift-4-weak-references.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;mikeash.com: Friday Q&amp;amp;A 2017-09-22: Swift 4 Weak References&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Friday Q&amp;amp;A 2017-09-22: Swift 4 Weak References This article is also available in Chinese (translation by Xiang Deng). Soon after Swift was initially open sourced, I wrote an article about how weak references are implemented. Time moves on and things change&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mikeash.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1633916845923&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Discover Side Tables - Weak Reference Management Concept in Swift&quot; data-og-description=&quot;Swift 5 and iOS development blog: free step-by-step tutorials authored by Maxim Eremenko since 2019.&quot; data-og-host=&quot;maximeremenko.com&quot; data-og-source-url=&quot;https://maximeremenko.com/swift-arc-weak-references&quot; data-og-url=&quot;https://maximeremenko.com/swift-arc-weak-references&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bzGnkT/hyLUqSvAA3/Pugu5KM4j3kja65IIYpcO1/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/bd42ok/hyLUum4kPc/9If3ClkOQMZPKwttKql730/img.jpg?width=1920&amp;amp;height=1280&amp;amp;face=0_0_1920_1280,https://scrap.kakaocdn.net/dn/BMJOd/hyLUrjAvkv/QTe4oIstypHmKOhYWGpmwK/img.jpg?width=1920&amp;amp;height=1280&amp;amp;face=0_0_1920_1280&quot;&gt;&lt;a href=&quot;https://maximeremenko.com/swift-arc-weak-references&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://maximeremenko.com/swift-arc-weak-references&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bzGnkT/hyLUqSvAA3/Pugu5KM4j3kja65IIYpcO1/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/bd42ok/hyLUum4kPc/9If3ClkOQMZPKwttKql730/img.jpg?width=1920&amp;amp;height=1280&amp;amp;face=0_0_1920_1280,https://scrap.kakaocdn.net/dn/BMJOd/hyLUrjAvkv/QTe4oIstypHmKOhYWGpmwK/img.jpg?width=1920&amp;amp;height=1280&amp;amp;face=0_0_1920_1280');&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;Discover Side Tables - Weak Reference Management Concept in Swift&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Swift 5 and iOS development blog: free step-by-step tutorials authored by Maxim Eremenko since 2019.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;maximeremenko.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>iOS Dev/Swift</category>
      <category>ARC</category>
      <category>SWIFT</category>
      <category>weak</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/34</guid>
      <comments>https://linux-studying.tistory.com/34#entry34comment</comments>
      <pubDate>Fri, 8 Oct 2021 18:32:39 +0900</pubDate>
    </item>
    <item>
      <title>iOS 뷰 컨트롤러 간단하게 테스트 코드 연습하기 ( with Unit Test )</title>
      <link>https://linux-studying.tistory.com/33</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;UIKit 환경에서 뷰 컨트롤러는 3가지 방식으로 구성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(&lt;b&gt; Xib, Storyboard, Code Base &lt;/b&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;뷰 컨트롤러 인스턴스 가져오기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Xib, Code Base&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 별도의 파라미터가 없는 경우 바로 인스턴스화 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/ba908e424478d37352865d95ce96b933.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Storyboard&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 먼저 UIStoryboard 인스턴스에 접근하고, 해당 메서드로 생성해야 합니다. &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uistoryboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/b8503261e230439acf782aec5638490c.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;뷰 컨트롤러 view 로드하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 가져온 뷰 컨트롤러는 따로 view 를 로드하는 과정을 거쳐야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XIB 와 Storyboard 기반의 vc 는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;loadViewIfNeeded()&lt;/span&gt; 를 통해 Outlet 들을 구성하고 연결할 수 있게 됩니다.&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;Code Base 로 구성된 뷰 컨트롤러도 해당 메서드를 호출하면 viewDidLoad 도 이어서 실행되기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;loadViewIfNeeded()&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;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/7870e8c164812f569ceb18bb72f85569.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 다음과 같은 코드도 loadViewIfNeeded() 와 같은 동작을 합니다.&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;뷰 컨트롤러 view 테스트 하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IBAction 으로 연결된 버튼을 누르면, textLabel 이 변경되는 간단한 앱을 테스트 해보겠습니다.&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;&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;600&quot; data-origin-height=&quot;1065&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pKjPZ/btrf0suYvE1/9ENzvQ4UUYz4CqmxiInqJ1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pKjPZ/btrf0suYvE1/9ENzvQ4UUYz4CqmxiInqJ1/img.gif&quot; data-alt=&quot;button 을 누르면 text 가 바뀌는 앱&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pKjPZ/btrf0suYvE1/9ENzvQ4UUYz4CqmxiInqJ1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/pKjPZ/btrf0suYvE1/9ENzvQ4UUYz4CqmxiInqJ1/img.gif&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;280&quot; height=&quot;497&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1065&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;button 을 누르면 text 가 바뀌는 앱&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/0a6f429aa37073db7f8b5cd7b7034ab3.js&quot;&gt;&lt;/script&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;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/394794f66aa2a36944b0161423745939.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* 1: @testable 어노테이션과 함께 import 하면 private 를 제외한 모든 요소에 접근할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* 2: sut 는 system under test 의 약어로서 테스트할 대상을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* 3: 유닛 테스트는 각 메서드 마다 서로 다른 인스턴스를 생성하여 테스트합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;즉 필요한 객체를 생성하고 해제하지 않는다면, 해당 테스트가 전부 끝날 때 까지 sut 가 메모리에 남게됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;따라서 tearDown 에서 레퍼런스 카운트를 줄이는 과정이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; ( 순서 setUp -&amp;gt; 테스트 메서드 -&amp;gt; tearDown )&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;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/febbebdb197bd828640cf3df27c27ae7.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. loadViewIfNeeded 를 통해 Outlet 과 Action 을 구성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. UIButton 의 sendActions 메서드를 통해 터치 이벤트를 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. XCTAssertEqual 로 값이 맞게 들어갔는지 검증합니다.&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;네비게이션 컨트롤러 push 테스트 하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 버튼을 누르면 NavigationVC 에서 push 되는 상황을 테스트 해보겠습니다.&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;&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;600&quot; data-origin-height=&quot;1065&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rKpQf/btrf5h01ap7/qe6cCPzcVow2S4BZc4sAEk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rKpQf/btrf5h01ap7/qe6cCPzcVow2S4BZc4sAEk/img.gif&quot; data-alt=&quot;버튼을 누르면 push 되는 앱&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rKpQf/btrf5h01ap7/qe6cCPzcVow2S4BZc4sAEk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/rKpQf/btrf5h01ap7/qe6cCPzcVow2S4BZc4sAEk/img.gif&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;280&quot; height=&quot;497&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1065&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;버튼을 누르면 push 되는 앱&lt;/figcaption&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/962bfd474849ba0a9364d5122bebeb4f.js&quot;&gt;&lt;/script&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;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/65a09d63ea39317c76d08f0dbc70cd28.js&quot;&gt;&lt;/script&gt;
&lt;p 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;size14&quot;&gt;* 1: push 와 같은 동작은 유닛 테스트 환경에서 동작되지 않을 수 있습니다. 따라서 런 루프에 있는 이벤트를&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;처리하기 위하여 다음과 같은 코드로 런 루프를 실행시킵니다. 이는 다른 UIKit 컴포넌트 테스트에서도 동일하게&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* 2: navigation controller 는 viewControllers 를 통해 현재 가지고 있는 뷰 컨트롤러들에 접근할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;따라서 count 를 통해 push 된 VC 가 있는지 파악할 수 있습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;연습을 마무리하며 &quot;UITest VS Unit Test?&quot;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View 와 관련된 요소는 UITest 를 통해서도 테스트할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 뷰 컨트롤러를 Unit Test 로 테스트하게 되면 다음과 같은 장점이 있습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. 테스트할 때 컨트롤러에 다른 의존성을 주입할 수 있습니다.&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;실제 앱의 경우 서로 다른 의존성들이 컨트롤러에 존재하게 됩니다. ( MVVM 의 경우 ViewModel )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Mock 으로 구현된 객체를 주입하면 반복 가능하고 환경에 영향을 받지 않는 테스트가 가능해집니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2. Unit Test는 UITest 보다 속도가 빠릅니다.&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;/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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;3. 앱 시작점 부터 탐색할 필요가 없이 바로 테스트가 가능합니다.&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;UITest 는 앱 시작점 부터 테스트를 시작하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 필요한 테스트 영역까지 탐색해서 들어가야 한다는 것을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 달리 Unit Test 는 필요한 객체를 생성하고 바로 테스트 할 수 있습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;언제 UITest 를 사용하면 좋을까?&lt;/h2&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;background-color: #ffc9af;&quot;&gt;UITest 는 End To End 테스트에 적합합니다.&lt;/span&gt; 사용자가 직접 사용하는 것 처럼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GUI 환경에서 시나리오, 기능 테스트를 진행하는 것을 의미합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2021-09-27 오후 6.42.30.png&quot; data-origin-width=&quot;2394&quot; data-origin-height=&quot;1164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RFBDl/btrf3sogxcd/UyS0lC0oXN4LDMrg0RbRF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RFBDl/btrf3sogxcd/UyS0lC0oXN4LDMrg0RbRF0/img.png&quot; data-alt=&quot;Apple WWDC 18,&amp;amp;nbsp;Testing Tips and Tricks&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RFBDl/btrf3sogxcd/UyS0lC0oXN4LDMrg0RbRF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRFBDl%2Fbtrf3sogxcd%2FUyS0lC0oXN4LDMrg0RbRF0%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;626&quot; height=&quot;305&quot; data-filename=&quot;스크린샷 2021-09-27 오후 6.42.30.png&quot; data-origin-width=&quot;2394&quot; data-origin-height=&quot;1164&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Apple WWDC 18,&amp;nbsp;Testing Tips and Tricks&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플 WWDC 세션 자료를 참고하면 UITest 가 테스트 피라미드에 가장 높은 곳에 위치한 것을 볼 수 있습니다. 이는 Unit 테스트에 비해서 개수가 적고 변경되기 쉬우며, 통합 테스트에 가장 적합하다는 것을 말합니다.&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;UITest 는 다른 테스트 러너에서 실행되어서 의존성을 주입하기 어렵다는 단점이 있지만 테스트시에 앱 시작점에서 BaseURL 를 변경하고 그에 맞게 테스트 서버를 준비하면 요청을 Stub 할 수 있게 됩니다.&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;&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;/p&gt;
&lt;figure id=&quot;og_1632736195505&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;GitHub - nsoojin/BookStore-iOS:  Sample iOS App  - A collection of examples and patterns for Unit Testing, UI Testing, handli&quot; data-og-description=&quot; Sample iOS App - A collection of examples and patterns for Unit Testing, UI Testing, handling Result/Optionals, writing documentation, and more. Details in README. - GitHub - nsoojin/BookStore-i...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nsoojin/BookStore-iOS&quot; data-og-url=&quot;https://github.com/nsoojin/BookStore-iOS&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bszimM/hyLKiglW8S/NvqQdnPkmKEMwHUiD4pBO1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nsoojin/BookStore-iOS&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nsoojin/BookStore-iOS&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bszimM/hyLKiglW8S/NvqQdnPkmKEMwHUiD4pBO1/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;GitHub - nsoojin/BookStore-iOS:  Sample iOS App - A collection of examples and patterns for Unit Testing, UI Testing, handli&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt; Sample iOS App - A collection of examples and patterns for Unit Testing, UI Testing, handling Result/Optionals, writing documentation, and more. Details in README. - GitHub - nsoojin/BookStore-i...&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;figure id=&quot;og_1632736171488&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;book&quot; data-og-title=&quot;iOS Unit Testing by Example&quot; data-og-description=&quot;Fearlessly change the design of your iOS code with solid unit tests. Use Xcode's built-in test framework XCTest and Swift to get rapid feedback on all your code - including legacy code. Learn the tricks and techniques of testing all iOS code, especially vi&quot; data-og-host=&quot;books.google.co.jp&quot; data-og-source-url=&quot;https://books.google.co.kr/books/about/IOS_Unit_Testing_by_Example.html?id=cA4CEAAAQBAJ&amp;amp;source=kp_cover&amp;amp;redir_esc=y&quot; data-og-url=&quot;https://books.google.com/books/about/iOS_Unit_Testing_by_Example.html?hl=ko&amp;amp;id=cA4CEAAAQBAJ&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/boKM3a/hyLLLBc5pE/bVYekQZfkaCotr3HT5lSp0/img.jpg?width=128&amp;amp;height=154&amp;amp;face=0_0_128_154&quot;&gt;&lt;a href=&quot;https://books.google.co.kr/books/about/IOS_Unit_Testing_by_Example.html?id=cA4CEAAAQBAJ&amp;amp;source=kp_cover&amp;amp;redir_esc=y&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://books.google.co.kr/books/about/IOS_Unit_Testing_by_Example.html?id=cA4CEAAAQBAJ&amp;amp;source=kp_cover&amp;amp;redir_esc=y&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/boKM3a/hyLLLBc5pE/bVYekQZfkaCotr3HT5lSp0/img.jpg?width=128&amp;amp;height=154&amp;amp;face=0_0_128_154');&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;iOS Unit Testing by Example&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Fearlessly change the design of your iOS code with solid unit tests. Use Xcode's built-in test framework XCTest and Swift to get rapid feedback on all your code - including legacy code. Learn the tricks and techniques of testing all iOS code, especially vi&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;books.google.co.jp&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 Dev/Testing</category>
      <category>IOS</category>
      <category>Test Code</category>
      <category>XCTest</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/33</guid>
      <comments>https://linux-studying.tistory.com/33#entry33comment</comments>
      <pubDate>Sun, 26 Sep 2021 23:03:20 +0900</pubDate>
    </item>
    <item>
      <title>iOS JSON 을 Decoding 하는 여러 가지 방식들</title>
      <link>https://linux-studying.tistory.com/32</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;JSON 는 자바스크립트에서 객체를 표현하는 방식으로 REST API 에서 널리 사용되는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS 에서는 Codable 을 통해 쉽게 JSON 을 디코딩할 수 있는데요.&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;Codable 과 Dictionary 로 디코딩 하는 방법을 살피고 예외적인 상황까지 이번 포스팅에서 다루고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Codable&lt;/h2&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;background-color: #ffc9af;&quot;&gt;Codable 은 Decodable &amp;amp; Encodable 프로토콜을 모두 준수하는 타입입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Encode 할 필요가 없다면 Decodable 만 채택해도 네트워킹 처리에는 문제가 없습니다.&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;Decodable 는 프로토콜이기 때문에 Class, Struct, Enum 모두 채택할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/030f6641dcf5e2dbb2ac0d000a822aeb.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 CodingKeys 를 통해 Key 값을 변경할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;2904058&quot; data-ke-size=&quot;size23&quot;&gt;Encode and Decode Manually&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 같이 JSON 요청과 모델의 구조가 일치하는 경우에는 직관적으로 Decoding 을 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 JSON 과 Model 의 구조가 다른 경우에는 커스텀하게 Decode 을 진행해야 합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/0bcfbb97a306582598b9d36bae3c477d.js&quot;&gt;&lt;/script&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;background-color: #f6e199;&quot;&gt;기존 구조와 달리 small 과 large 프로퍼티가 모두 TestResponse 안으로 들어가도록 변경되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 CodingKeys 안에는 JSON 과 같은 형태인 image case 와&amp;nbsp;ImageKeys 를 추가합니다.&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;그 후 코드 가독성을 위해 Extension 으로 Decodable 를 채택하고, 이니셜 라이저를 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 가장 상위 Codingkeys 를 통해 직접 Decode 를 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 nestedContainer 를 통해 ImageKeys 에 접근하고 하위 프로퍼티들을 Decode 해주면 끝입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;jsonserialization 를 통한 디코딩&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codable 이 존재하지 않을 때 iOS 5 버전부터 지원한 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴 값이 딕셔너리 [String: Any] 값으로 각각 타입 캐스팅을 통해 모델을 구성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/f8d8b750483bf834c75d988c641035ca.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key 값으로 직접 접근할 수 있다는 장점이 있지만, 많은 타입 캐스팅 구문을 사용해야하기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레거시를 지원해야 하는 경우가 아니라면 Codable 를 사용하는 것이 좋을 것 같습니다.&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;&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;&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;참고 문서:&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1629962176660&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;Apple Developer Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types&quot; data-og-url=&quot;https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1629964606814&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;Apple Developer Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/foundation/jsonserialization&quot; data-og-url=&quot;https://developer.apple.com/documentation/foundation/jsonserialization&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundation/jsonserialization&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/foundation/jsonserialization&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.apple.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 Dev/Swift</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/32</guid>
      <comments>https://linux-studying.tistory.com/32#entry32comment</comments>
      <pubDate>Thu, 26 Aug 2021 16:03:46 +0900</pubDate>
    </item>
    <item>
      <title>iOS URLSession Test Double 만들기</title>
      <link>https://linux-studying.tistory.com/31</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;iOS 에서 네트워크를 테스트하는 방법은 크게 두가지가 있습니다.&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;color: #333333;&quot;&gt;&lt;u&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;1. 실제 서버(혹은 테스트 서버) 에 직접 요청하기&lt;/span&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;u&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;2. Stub 을 통해 실제 네트워크에 요청하지 않고 테스트하기&lt;/span&gt;&lt;/u&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;color: #333333;&quot;&gt;네트워크 요청은 느리고, 실패 가능성이 있으며 불확실성이 높은 요소입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이런 경우에는 일정한 요청을 return 하는 Stub 을 제작하는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;2번&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 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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;156&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nOynV/btrcStPBbIr/8TQ7r8WREbWa6ZKpPPa7c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nOynV/btrcStPBbIr/8TQ7r8WREbWa6ZKpPPa7c1/img.png&quot; data-alt=&quot;출처:&amp;amp;amp;nbsp;https://developer.apple.com/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nOynV/btrcStPBbIr/8TQ7r8WREbWa6ZKpPPa7c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnOynV%2FbtrcStPBbIr%2F8TQ7r8WREbWa6ZKpPPa7c1%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;156&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처:&amp;nbsp;https://developer.apple.com/&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;URLSessionDataTask vs URLSession 어떤 것을 Stubbing 할까?&lt;/span&gt;&lt;/h2&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: #333333;&quot;&gt;테스트 코드 학습을 위해 가장 처음 접했던 포스팅은&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;배달의 민족 기술블로그 조원님의 &quot;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://techblog.woowahan.com/2704/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;iOS Networking and Testing&lt;/a&gt;&quot; 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;( Moya 기반의 test code 코드 작성법도 기술되어 있으니, 꼭 읽어보시면 좋습니다. )&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;color: #333333;&quot;&gt;위에서는 MockURLSession 과 MockURLSessionDataTask 를 통해 DataTask 를 Mocking 하여&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;dataTask 메서드를 통해 특정한 응답을 갖는 stub 으로 변경하는 방식입니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;608&quot; data-filename=&quot;스크린샷 2021-09-20 오후 11.51.48.png&quot; width=&quot;471&quot; height=&quot;252&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3f9k3/btrfuOq93ak/5xC4lMnmFfhvmintO1XQ31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3f9k3/btrfuOq93ak/5xC4lMnmFfhvmintO1XQ31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3f9k3/btrfuOq93ak/5xC4lMnmFfhvmintO1XQ31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3f9k3%2FbtrfuOq93ak%2F5xC4lMnmFfhvmintO1XQ31%2Fimg.png&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;608&quot; data-filename=&quot;스크린샷 2021-09-20 오후 11.51.48.png&quot; width=&quot;471&quot; height=&quot;252&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;span style=&quot;color: #333333;&quot;&gt;하지만 iOS 13 에서는 URLSessionDataTask 이니셜라이저가 deprecated 되어 컴파일 경고가&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;발생하게 되었고, 추후에 나올 iOS 버전에서는 해당 방법을 적용하기 어렵다고 판단했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;URLSession Stubbing&amp;nbsp;하기&lt;/span&gt;&lt;/h2&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: #333333;&quot;&gt;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://developer.apple.com/videos/play/wwdc2018/417/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC 18&lt;/a&gt; 에 소개되었던 방식입니다. URLSession 를 상속받는 MockURLSession 을 구현합니다.&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-origin-width=&quot;2718&quot; data-origin-height=&quot;1374&quot; data-filename=&quot;스크린샷 2021-09-20 오후 11.57.18.png&quot; width=&quot;744&quot; height=&quot;376&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csaOKG/btrfpC6yglJ/hRFwfgksuzhkJHd7wrVctk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csaOKG/btrfpC6yglJ/hRFwfgksuzhkJHd7wrVctk/img.png&quot; data-alt=&quot;WWDC 18, Testing Tips &amp;amp;amp;amp; Tricks&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csaOKG/btrfpC6yglJ/hRFwfgksuzhkJHd7wrVctk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsaOKG%2FbtrfpC6yglJ%2FhRFwfgksuzhkJHd7wrVctk%2Fimg.png&quot; data-origin-width=&quot;2718&quot; data-origin-height=&quot;1374&quot; data-filename=&quot;스크린샷 2021-09-20 오후 11.57.18.png&quot; width=&quot;744&quot; height=&quot;376&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;WWDC 18, Testing Tips &amp;amp; Tricks&lt;/figcaption&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;span style=&quot;color: #333333;&quot;&gt;이때 오버라이드해야 하는 메서드는 총 4개입니다.&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;color: #333333;&quot;&gt;1. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;canInit&lt;/span&gt; : 요청이 처리할 수 있는지 등록된 프로토콜을 확인하는 메서드 입니다. 구현에서는 true 로 고정합니다.&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;color: #333333;&quot;&gt;2. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;canonicalRequest&lt;/span&gt; : 요청을 대체할 수 있는 메서드입니다. 구현에서는 파라미터에 주어진 request 를 그대로 리턴합니다.&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;color: #333333;&quot;&gt;3. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;startLoading&lt;/span&gt; : 요청을 처리하는 메서드 입니다. 해당 메서드에서 URLProtocolClient 에 있는 메서드를 호출하여 Stub 처리를 할 수 있습니다.&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;color: #333333;&quot;&gt;4. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;stopLoading&lt;/span&gt; : 해당 메서드가 호출되면 요청 작업을 취소해야 합니다. Mock 에서는 해당 메서드는 비워둡니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/b4f638ca96572d0e2aad4480a3d6243b.js&quot;&gt;&lt;/script&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: #333333;&quot;&gt;WWDC 예제와는 조금 다르게 클로저가 아닌, Dictionary 로 Stub 을 등록하기 쉽게 구현하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;클래스 프로퍼티인 success/failureMock 를 각 유닛 테스트에 맞게 지정해주면 간단하게 이용할 수 있습니다.&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;color: #333333;&quot;&gt;startLoading 메서드 에서는 해당 Stub 이 존재하면 성공/실패 요청을 보내게 되고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Dictionary 에 없는 값이 요청되면 MockSessionError 가 발생하게 됩니다.&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Test Code 작성하기&lt;/span&gt;&lt;/h2&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: #333333;&quot;&gt;구현한 Mock Protocol 을 사용하려면 Network 을 수행하는 클래스에서 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;의존성 주입&lt;/span&gt;으로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;URLSession 을 넣어주어야 합니다. ( 방법에는 이니셜라이저, 프로퍼티, 팩토리 방식이 있습니다. )&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;각자의 NerworkService ( or Provider ) 는 이미 구현되어 있다고 가정해보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/17fb337ec44465f6c25a9945365a986f.js&quot;&gt;&lt;/script&gt;
&lt;p 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: #333333;&quot;&gt;URLSessionConfiguration.ephemeral ( 로컬에 캐싱하지 않는 옵션 ) 을 통해 config 을 생성하고,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;protocolClasses 를 아까 구현한 MockProtocol 로 지정해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그 다음 urlSession 을 생성하고, 각자 의존성 주입 방식에 맞게 테스트 하려는 네트워크 객체에 주입합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/heoblitz/05cf2112935f9b063ec9e592aa7c38d1.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;* Behaviour Driven Development 기반으로 테스트 코드를 작성하였습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가장 핵심은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&quot;MockURLProtocol.successMock[urlString] = Data(responseString.utf8&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;)&quot;&lt;/span&gt; 코드를 통해&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;네트워크 요청을 간편하게 Stub 할 수 있습니다. 그 후에는 각 프로젝트 상황에 맞게 Data 를 검증하면 됩니다.&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;&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;&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&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;color: #333333;&quot;&gt;Mock 과 Stub은 Test Double 에서 나온 용어로서, 상태 검증과 행위 검증에 따라 구분한다는 것을 알게되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다만 Apple 에서는 MockURLProtocol 로 네이밍을 했지만 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;개인적으로는 Stubbing 에 더 가깝다는 생각을 했습니다.&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 data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;학습을 위해 다른 게시글과 자료들을 찾아보아도 Stub 과 Mock 을 혼용하는 예제들을 많이 보게 되었고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;명확하게 구분하기 어려운 개념이 아닐까 단편적으로 이해한 상태입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&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;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;* &lt;/span&gt;&lt;/b&gt;&lt;u&gt;&lt;span style=&quot;color: #333333;&quot;&gt;현업에서는 상태와 행위 검증이 동시에 이루어지는 경우가 많기 때문에 Mock 으로 많이 사용한다는 말씀을 들었습니다.&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;</description>
      <category>iOS Dev/Testing</category>
      <category>Bdd</category>
      <category>IOS</category>
      <category>network</category>
      <category>SWIFT</category>
      <category>unit test</category>
      <category>URLSession</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/31</guid>
      <comments>https://linux-studying.tistory.com/31#entry31comment</comments>
      <pubDate>Tue, 17 Aug 2021 21:38:27 +0900</pubDate>
    </item>
    <item>
      <title>iOS 에서 Void Observable 테스트 코드 작성하기</title>
      <link>https://linux-studying.tistory.com/30</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RxSwift 에서 다음 이벤트로 값을 전달할 필요가 없는 경우에는 Void Observable 를 사용합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;256&quot; data-filename=&quot;스크린샷 2021-07-13 오후 7.53.50.png&quot; width=&quot;608&quot; height=&quot;113&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NmS5s/btq9zGdK867/kPvhzhtsex2gJq1kytyMKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NmS5s/btq9zGdK867/kPvhzhtsex2gJq1kytyMKK/img.png&quot; data-alt=&quot;viewModel 에서 화면 전환에 사용 중인 Relay&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NmS5s/btq9zGdK867/kPvhzhtsex2gJq1kytyMKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNmS5s%2Fbtq9zGdK867%2FkPvhzhtsex2gJq1kytyMKK%2Fimg.png&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;256&quot; data-filename=&quot;스크린샷 2021-07-13 오후 7.53.50.png&quot; width=&quot;608&quot; height=&quot;113&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;viewModel 에서 화면 전환에 사용 중인 Relay&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 테스트 코드를 작성하기 위해서는 RxTest 에 있는 TestScheduler 를 이용하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상 시간의 이벤트들을 방출하는 옵저버블을 만들고, XCTAssertEqual 을 통해 events 를 비교하게 됩니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;1228&quot; data-filename=&quot;스크린샷 2021-07-13 오후 7.58.17.png&quot; width=&quot;479&quot; height=&quot;526&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QLcxY/btq9xLUoLed/bzzzfFaAWlnYzIa6GGykfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QLcxY/btq9xLUoLed/bzzzfFaAWlnYzIa6GGykfK/img.png&quot; data-alt=&quot;Bool 옵저버블을 비교하는 Test Code&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QLcxY/btq9xLUoLed/bzzzfFaAWlnYzIa6GGykfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQLcxY%2Fbtq9xLUoLed%2FbzzzfFaAWlnYzIa6GGykfK%2Fimg.png&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;1228&quot; data-filename=&quot;스크린샷 2021-07-13 오후 7.58.17.png&quot; width=&quot;479&quot; height=&quot;526&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Bool 옵저버블을 비교하는 Test Code&lt;/figcaption&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;Bool Observable 은 문제 없이 테스트 가능하지만 여기서 Void Observable 에서 문제점이 발생하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.next 로 들어가는 요소는 Equatable 을 준수해야 하기 때문입니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2012&quot; data-origin-height=&quot;168&quot; data-filename=&quot;스크린샷 2021-07-13 오후 8.01.23.png&quot; width=&quot;717&quot; height=&quot;60&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xn5YC/btq9ynFLDnI/uoVmJ7eLwv9veEeslsWqnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xn5YC/btq9ynFLDnI/uoVmJ7eLwv9veEeslsWqnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xn5YC/btq9ynFLDnI/uoVmJ7eLwv9veEeslsWqnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxn5YC%2Fbtq9ynFLDnI%2FuoVmJ7eLwv9veEeslsWqnk%2Fimg.png&quot; data-origin-width=&quot;2012&quot; data-origin-height=&quot;168&quot; data-filename=&quot;스크린샷 2021-07-13 오후 8.01.23.png&quot; width=&quot;717&quot; height=&quot;60&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RxSwift 저장소에서 해당 문제가 논의되며 &lt;a href=&quot;https://github.com/ReactiveX/RxSwift/pull/1747&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PR&lt;/a&gt;이 올라왔었지만, 현재는 메인 저장소에 반영되지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하는 방식은 크게 2가지가 있습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 별도의 테스트 객체를 선언하고, Event Time 으로 Equtable 을 준수한다.&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;2. map 을 통해 Boolean 이벤트로 변경하여 테스트한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;924&quot; data-filename=&quot;스크린샷 2021-08-05 오후 4.00.55.png&quot; width=&quot;584&quot; height=&quot;442&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEqOuS/btrbqvBhlUc/nbsgJx0mSjfkrxqzM8OXE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEqOuS/btrbqvBhlUc/nbsgJx0mSjfkrxqzM8OXE1/img.png&quot; data-alt=&quot;map 으로 Bool 이벤트로 변경하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEqOuS/btrbqvBhlUc/nbsgJx0mSjfkrxqzM8OXE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEqOuS%2FbtrbqvBhlUc%2FnbsgJx0mSjfkrxqzM8OXE1%2Fimg.png&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;924&quot; data-filename=&quot;스크린샷 2021-08-05 오후 4.00.55.png&quot; width=&quot;584&quot; height=&quot;442&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;map 으로 Bool 이벤트로 변경하기&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 코드를 작성하면 Void 옵저버블을 쉽게 테스트할 수 있습니다.&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS Dev/Testing</category>
      <category>RxSwift</category>
      <category>SWIFT</category>
      <category>TestCode</category>
      <category>xcode</category>
      <author>heoblitz</author>
      <guid isPermaLink="true">https://linux-studying.tistory.com/30</guid>
      <comments>https://linux-studying.tistory.com/30#entry30comment</comments>
      <pubDate>Tue, 13 Jul 2021 09:55:25 +0900</pubDate>
    </item>
  </channel>
</rss>