<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>동동이</title>
    <link>https://dongminyoon.tistory.com/</link>
    <description>IT 관련 개인 공부를 정리하는 블로그입니다 ☺️</description>
    <language>ko</language>
    <pubDate>Thu, 7 May 2026 02:04:46 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>윤동민</managingEditor>
    <image>
      <title>동동이</title>
      <url>https://tistory1.daumcdn.net/tistory/4113113/attach/36979b96467f4fd3b8073de5faf621f3</url>
      <link>https://dongminyoon.tistory.com</link>
    </image>
    <item>
      <title>[Swift] Macro (1)</title>
      <link>https://dongminyoon.tistory.com/85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;이전에 &lt;i&gt;&lt;b&gt;Macros&lt;/b&gt;&lt;/i&gt;라는 Swift 언어의 기능이 나왔다고 알고있었는데, 느낌만 알고있다 이번에 &lt;i&gt;&lt;b&gt;SwiftUI + TCA&lt;/b&gt;&lt;/i&gt; 스터디를 진행하다보니 &lt;i&gt;&lt;b&gt;TCA&lt;/b&gt;&lt;/i&gt;에서 &lt;i&gt;&lt;b&gt;Macros&lt;/b&gt;&lt;/i&gt;를 활용하는 경우가 보여서 어떤 기능인지 자세히 알아보려고합니다  &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;span style=&quot;color: #409d00; font-family: 'Noto Serif KR';&quot;&gt;만든 이유&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;우선 &lt;i&gt;&lt;b&gt;Macros&lt;/b&gt;&lt;/i&gt;의 개발자문서 설명을 보면 '컴파일 타임에 코드를 발생시킨다'고 되어있는데요. 뭐 여기 본문만 보면은 어떤 느낌인지 감이 잘안오는데, 한번 알아보면서 살살 잡아가보자구요&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Use macros to generate code at compile time&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;좀더 자세한 설명을 보면 개발자들이 코드의 반복을 피하기위해 우리의 코드를 컴파일 타임에 전환해준다고하는데요. 그림을 보면 우리가 작성한 코드가 'Expanded code source'로 확장되는 모습을 볼수있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아마도 우리가 메소드를 재사용하는것처럼 반복되는 코드들을 피하기위해 Swift에서 제공해주는 새로운 기능인 것 같습니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 3.50.57.png&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHW3Ay/btsJDhq3dYh/ofm1LqTQJfmQVNzQ18snIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHW3Ay/btsJDhq3dYh/ofm1LqTQJfmQVNzQ18snIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHW3Ay/btsJDhq3dYh/ofm1LqTQJfmQVNzQ18snIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHW3Ay%2FbtsJDhq3dYh%2Fofm1LqTQJfmQVNzQ18snIk%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;1068&quot; height=&quot;404&quot; data-filename=&quot;스크린샷 2024-09-16 오후 3.50.57.png&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;0&quot;&gt;Macros&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;의 기능을 이용하면 컴파일러를 수정하지 않고도 Swift Package에 개발자가 원하는 기능을 배포할 수 있게 해주는데 오픈소스인 Swift에 기능을 추가하는 방법보다 좀 더 간편하게 개발자가 기능을 추가할 수 있게 지원하기위해 도입된 기능인 것 같습니다  &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;span style=&quot;color: #409d00; font-family: 'Noto Serif KR';&quot;&gt;디자인 목표&lt;/span&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; 기능을 사용할 때, &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;를 사용하고 있다는게 명확해야합니다. &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;에는 크게 2가지 Freestanding &amp;amp; Attached 두가지가 존재합니다. 각각 아래의 특징을 가지고 있기 때문에, &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;를 사용하고있구나 알수있습니다.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;i&gt;&lt;b&gt;Freestanding&lt;/b&gt;&lt;/i&gt;은 항상 #을 prefix로 붙이고 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;i&gt;&lt;b&gt;Attached&lt;/b&gt;&lt;/i&gt;는 항상 @을 prefix로 붙이고 있다&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;에 넘긴 코드와 &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;에서 넘어온 코드 둘다 완전하고 실패를 확인할 수 있어야한다.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;뭔가 잘못된 인자를 넘기거나 잘못된 인자가 넘어오는 경우 컴파일러 에러로 이를 올바르게 쓰고 있는지 확인이 가능해야한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt; 전개가 예상가능한 방법으로 프로그램에 포함되어야한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;는 마법이 되면안된다. 어디로 전개되거나 어떻게 실행되는지 알수있어야하고 이를 실행되는 곳에서 디버깅이 가능하고 펼쳐볼수도 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이제부터는 각각 어떤 매크로의 기능들이 있고 어떤 역할들을 수행하는지 알아보겠습니다&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;span style=&quot;color: #409d00; font-family: 'Noto Serif KR';&quot;&gt;Macro Role&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이제부터는 각각 어떤 매크로의 기능들이 있고 어떤 역할들을 수행하는지 알아보겠습니다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;우선 &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;는 크게 2가지(Freestanding &amp;amp; Attached)가 존재하는데 각각에 어떤 속성들이 있고 어떤 역할을 하는지 알아보겠습니다 :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;@freestanding(expression)&lt;/span&gt; : 실행해서 어떤 결과를 생성하는 것에 대한것을 담당&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 그게 뭔데,,,, 어떻게 사용하는건데 아래 예시와 함께 살펴보겠습니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 3.58.35.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nH3JB/btsJDTiGtsb/OHaoU1nK6t4X0jlezJGyl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nH3JB/btsJDTiGtsb/OHaoU1nK6t4X0jlezJGyl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nH3JB/btsJDTiGtsb/OHaoU1nK6t4X0jlezJGyl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnH3JB%2FbtsJDTiGtsb%2FOHaoU1nK6t4X0jlezJGyl0%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;1272&quot; height=&quot;362&quot; data-filename=&quot;스크린샷 2024-09-16 오후 3.58.35.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이런 상황에서 이런 것을 &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;로 선언해서 만들어주면 좀 더 균형잡힌 코드로 표현하는 것이 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.01.32.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/REIP5/btsJDcJ8CC3/oUd56DLQAOzmwDfyoItoC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/REIP5/btsJDcJ8CC3/oUd56DLQAOzmwDfyoItoC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/REIP5/btsJDcJ8CC3/oUd56DLQAOzmwDfyoItoC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FREIP5%2FbtsJDcJ8CC3%2FoUd56DLQAOzmwDfyoItoC1%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;1270&quot; height=&quot;280&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.01.32.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;물론 위의 코드처럼 이렇게 사용하는데 내부에는 unwrap Macro를 구현하고 있어야합니다. 물론 사용하는 부분에서 실제 &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;가 어떻게 구현되어있는지 이처럼 볼수도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;근데 만약 guard let을 이용해서 동일하게 unwrapping을 해야하는 코드가 많다면 직접 코드를 작성하는게 번거로울 수 있겠죠..? &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이때 이렇게 &lt;span style=&quot;color: #006dd7;&quot;&gt;@freestanding(expression)&lt;/span&gt;을 이용하면 좀 더 보일러플레이트 코드들을 줄이는 방향으로 사용이 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.02.11.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7UpYc/btsJDOIw8LL/ySs0uAgPdKqfmSWKsMxikK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7UpYc/btsJDOIw8LL/ySs0uAgPdKqfmSWKsMxikK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7UpYc/btsJDOIw8LL/ySs0uAgPdKqfmSWKsMxikK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7UpYc%2FbtsJDOIw8LL%2FySs0uAgPdKqfmSWKsMxikK%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;1272&quot; height=&quot;366&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.02.11.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;366&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;@freestanding(declaration)&lt;/span&gt; : 하나 이상의 선언을 생성&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이건 또 무슨 소리야&amp;hellip;..? 또 예시를 통해 알아보겠습니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;예로 2차원 배열이 있는데 인덱스를 1차원 배열의 인덱스로 변경해서 산출하길 원하는 경우가 있다고라고 생각해보면 아래와 같은 코드가 나와야합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.05.21.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MHMpz/btsJDSqvrc7/WjZq1moKJDl2KinJLp6Chk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MHMpz/btsJDSqvrc7/WjZq1moKJDl2KinJLp6Chk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MHMpz/btsJDSqvrc7/WjZq1moKJDl2KinJLp6Chk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMHMpz%2FbtsJDSqvrc7%2FWjZq1moKJDl2KinJLp6Chk%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;1274&quot; height=&quot;806&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.05.21.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이게 코드를 이해하기보다는 &lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;1&quot;&gt;(1,0) &amp;rarr; 4&lt;/span&gt;, &lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;3&quot;&gt;(0,1) &amp;rarr; 1&lt;/span&gt; 요런식으로 아래의 &amp;lsquo;2차원 배열의 인덱스가 1차원 배열의 인덱스로 바뀐다&amp;rsquo;라고만 이해하고보면 될 것 같습니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.06.12.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd9p92/btsJDNQovqq/AMxig4qZ7Yjn6qRsgUl6a1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd9p92/btsJDNQovqq/AMxig4qZ7Yjn6qRsgUl6a1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd9p92/btsJDNQovqq/AMxig4qZ7Yjn6qRsgUl6a1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd9p92%2FbtsJDNQovqq%2FAMxig4qZ7Yjn6qRsgUl6a1%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;1272&quot; height=&quot;820&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.06.12.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;근데 만약 여기서 3차원 배열에서도 동일한 구현이 필요하다고하면 또 한번 매우 비슷한 선언이 아래와 같이 만들어지게 됩니다.&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-filename=&quot;스크린샷 2024-09-16 오후 4.06.38.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWK2SC/btsJDiwKZxW/vzyoR2MlWsuGmRAhwTbnK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWK2SC/btsJDiwKZxW/vzyoR2MlWsuGmRAhwTbnK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWK2SC/btsJDiwKZxW/vzyoR2MlWsuGmRAhwTbnK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWK2SC%2FbtsJDiwKZxW%2FvzyoR2MlWsuGmRAhwTbnK1%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;1272&quot; height=&quot;840&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.06.38.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그리고 여기서 만약 4, 5, 6&amp;hellip;N차까지 필요하게되면 거의 비슷한 구현들이 계속 선언되게 되겠죠.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;바로 이때, 활용할 수 있는것이 &lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;1&quot;&gt;@freestanding(declaration)&lt;/span&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.12.32.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A8fMe/btsJEQet7GW/N2Wpa5nY2EfWrWNAp6g8vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A8fMe/btsJEQet7GW/N2Wpa5nY2EfWrWNAp6g8vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A8fMe/btsJEQet7GW/N2Wpa5nY2EfWrWNAp6g8vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA8fMe%2FbtsJEQet7GW%2FN2Wpa5nY2EfWrWNAp6g8vk%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;1268&quot; height=&quot;336&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.12.32.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 선언하고 사용할 수 있는데, 이때보면 &lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;1&quot;&gt;@freestanding(expression)&lt;/span&gt;과는 다르게 return 타입을 지정하지 않습니다. 바로 이 &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;3&quot;&gt;Macro&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;는 선언을 추가하는 역할을 하기 때문에 필요하지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;0&quot;&gt;@attached(peer)&lt;/span&gt;&lt;span style=&quot;color: #37352f;&quot; data-token-index=&quot;1&quot;&gt; : 어떤 선언에든 첨부되어서 변수, 함수, 타입, 연산자 선언까지 아우르며 옆에 새로운 선언을 삽입&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이번에도 그냥 보고 무슨 역할을 하는지 알아보기는 힘드니 예시로 보겠습니다 !&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서는 만약 &lt;span style=&quot;color: #333333;&quot;&gt;async로 작성한 메소드가 있는데, 이를 예전 버전을 지원하기 위해 completion&lt;/span&gt;을 넘기는 API가 필요한 경우 아래와 같이 새롭게 작성하는건 큰 문제가 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.15.46.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cl8hGV/btsJDzSj94y/CTXbDVqtRIPXTPx8wmpQ40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cl8hGV/btsJDzSj94y/CTXbDVqtRIPXTPx8wmpQ40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cl8hGV/btsJDzSj94y/CTXbDVqtRIPXTPx8wmpQ40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcl8hGV%2FbtsJDzSj94y%2FCTXbDVqtRIPXTPx8wmpQ40%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;1270&quot; height=&quot;398&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.15.46.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;하지만 이런 경우가 많아서 이에 대해 새롭게 API를 작성하다보면 이 과정이 번거롭게 느껴집니다. 이때 활용할 수 있는것이 &lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;1&quot;&gt;@attached(peer)&lt;/span&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.16.19.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BUEVc/btsJDccjjMK/o0ybMQ4WJ8K4QXtyzkCcZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BUEVc/btsJDccjjMK/o0ybMQ4WJ8K4QXtyzkCcZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BUEVc/btsJDccjjMK/o0ybMQ4WJ8K4QXtyzkCcZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBUEVc%2FbtsJDccjjMK%2Fo0ybMQ4WJ8K4QXtyzkCcZ1%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;1274&quot; height=&quot;234&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.16.19.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 선언한 것을 기존의 메소드에 선언해준다면,,,?!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.16.36.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2I5Qg/btsJDsMGkgs/mVMcEbpFwKfqhh9kNIRK8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2I5Qg/btsJDsMGkgs/mVMcEbpFwKfqhh9kNIRK8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2I5Qg/btsJDsMGkgs/mVMcEbpFwKfqhh9kNIRK8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2I5Qg%2FbtsJDsMGkgs%2FmVMcEbpFwKfqhh9kNIRK8K%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;1274&quot; height=&quot;426&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.16.36.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;바로 completionHandler 기반의 원본과 동일한 코드를 생성하고 확장되고 추가로 문서 주석까지 달아주게됩니다  &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.17.30.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btWiv2/btsJERjXNFM/6I3k9SzDkWRzrnMqFTm980/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btWiv2/btsJERjXNFM/6I3k9SzDkWRzrnMqFTm980/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btWiv2/btsJERjXNFM/6I3k9SzDkWRzrnMqFTm980/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtWiv2%2FbtsJERjXNFM%2F6I3k9SzDkWRzrnMqFTm980%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;1276&quot; height=&quot;536&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.17.30.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;536&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;0&quot;&gt;@attached(accessor)&lt;/span&gt; : 변수에 추가될수있고 예로 &lt;span style=&quot;color: #333333;&quot;&gt;get, set, willSet, didSet&lt;/span&gt; 이런것들을 삽입&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;예로 Person이라는 구조체에 Dictionary가 있고 이에 대해 각 Key 값으로 접근하는 name, height, birthDate들을 정의했습니다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;근데 이렇게 매번 새로운 Key 값에 대해 새롭게 getter, setter를 정의하는게 조금 번거롭습니다,,,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.18.53.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIGhGt/btsJD6B6m5V/PriYJnXVNlgdVXxKEmFJt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIGhGt/btsJD6B6m5V/PriYJnXVNlgdVXxKEmFJt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIGhGt/btsJD6B6m5V/PriYJnXVNlgdVXxKEmFJt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIGhGt%2FbtsJD6B6m5V%2FPriYJnXVNlgdVXxKEmFJt1%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;1268&quot; height=&quot;870&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.18.53.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Property Wrapper&lt;/span&gt;를 사용하면 어떨까하지만 &lt;span style=&quot;color: #006dd7;&quot;&gt;Property Wrapper&lt;/span&gt;&lt;/span&gt;는 각각의 변수에 대해 다른 Stored Property에 접근하기 때문에 여기서는 부적절합니다,,,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;이때, &lt;span style=&quot;color: #006dd7;&quot;&gt;@attached(accessor)&lt;/span&gt;를 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.22.28.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HMOun/btsJCYrKpp6/HIqkEwit45r4pYoTgb1wX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HMOun/btsJCYrKpp6/HIqkEwit45r4pYoTgb1wX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HMOun/btsJCYrKpp6/HIqkEwit45r4pYoTgb1wX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHMOun%2FbtsJCYrKpp6%2FHIqkEwit45r4pYoTgb1wX1%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;1276&quot; height=&quot;242&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.22.28.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;이렇게 선언된 &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;를 사용하기위해 각 변수앞에 @DictionaryStorage를 붙여주면됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.22.55.png&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XCXi9/btsJDccjrz1/j7zw0RyCdHknl9I0NcNArk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XCXi9/btsJDccjrz1/j7zw0RyCdHknl9I0NcNArk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XCXi9/btsJDccjrz1/j7zw0RyCdHknl9I0NcNArk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXCXi9%2FbtsJDccjrz1%2Fj7zw0RyCdHknl9I0NcNArk%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;1266&quot; height=&quot;496&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.22.55.png&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 붙여주면 &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;가 아래처럼 accessor를 생성해서 사용할수 있게 됩니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.28.24.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwA68c/btsJDzrimIo/JjCKd6m1d6BJOSKpkKsBu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwA68c/btsJDzrimIo/JjCKd6m1d6BJOSKpkKsBu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwA68c/btsJDzrimIo/JjCKd6m1d6BJOSKpkKsBu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwA68c%2FbtsJDzrimIo%2FJjCKd6m1d6BJOSKpkKsBu0%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;1274&quot; height=&quot;692&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.28.24.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;692&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;font-family: 'Noto Serif KR';&quot;&gt;근데, 여기서도 아직 많진 않지만 보일러 플레이트 코드가 존재합니다. 바로 @DictionaryStorage를 반복적으로 작성 해주어야 한다는 점입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이런 점을 확장하기위해서 아래의 &lt;span style=&quot;color: #006dd7;&quot;&gt;@attached(memberAttribute)&lt;/span&gt;를 사용할 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;0&quot;&gt;@attached(memberAttribute)&lt;/span&gt; : Type / Extension의 선언에 속성을 삽입&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;이번에는 새로운 &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;를 생성하는 대신에 앞에서 보았던 선언에 추가로 &lt;span style=&quot;color: #006dd7;&quot;&gt;@attached(memberAttribute)&lt;/span&gt; 속성을 추가해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;i&gt;&lt;b&gt;하나의 매크로가 여러가지 속성을 가질수 있는거야..?&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;에는 2가지 속성이 있다고 했는데 freestanding은 조합이 불가능하고 attached는 조합이 가능합니다. attached 속성을 여러가지를 부여하면 Swift에서 부여한 속성을 맥락에 맞게 확장시켜서 컴파일합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프로퍼티에 첨부하면 &lt;span style=&quot;color: #006dd7;&quot;&gt;@attached(accessor)&lt;/span&gt; 역할로 확장 타입에 첨부하면 &lt;span style=&quot;color: #006dd7;&quot;&gt;@attached(memberAttribute)&lt;/span&gt; 역할로 확장 이를 메소드에 첨부하면 컴파일러 에러 발생 - DictionaryStorage는 메소드에 첨부할 수 있는 역할이 X&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.26.02.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/osYNS/btsJDtEN2zO/Uf69OiuKUP3F5Sgtk0v8o0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/osYNS/btsJDtEN2zO/Uf69OiuKUP3F5Sgtk0v8o0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/osYNS/btsJDtEN2zO/Uf69OiuKUP3F5Sgtk0v8o0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FosYNS%2FbtsJDtEN2zO%2FUf69OiuKUP3F5Sgtk0v8o0%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;1274&quot; height=&quot;272&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.26.02.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이를 이전에 모든 프로퍼티에 일일이 적용했던 것과 다르게 유형의 앞에 선언해주게되면 자동으로 전체 유형에 첨부가 가능합니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.26.17.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUlRy1/btsJEiClOUf/yKTKsC6e6al7FcDSK4WHu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUlRy1/btsJEiClOUf/yKTKsC6e6al7FcDSK4WHu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUlRy1/btsJEiClOUf/yKTKsC6e6al7FcDSK4WHu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUlRy1%2FbtsJEiClOUf%2FyKTKsC6e6al7FcDSK4WHu0%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;1270&quot; height=&quot;494&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.26.17.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;실제로는 이렇게 추가가 되게 됩니다 그리고 실제 구현체에는 특정 멤버(initializer, 이미 속성을 가진 타입등)를 건너뛸수 있는 로직이 있을 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.27.53.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ec1vSS/btsJDhLnM8C/ZpXrQ4VZULi6qkR9X1ZwTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ec1vSS/btsJDhLnM8C/ZpXrQ4VZULi6qkR9X1ZwTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ec1vSS/btsJDhLnM8C/ZpXrQ4VZULi6qkR9X1ZwTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fec1vSS%2FbtsJDhLnM8C%2FZpXrQ4VZULi6qkR9X1ZwTk%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;1270&quot; height=&quot;436&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.27.53.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;436&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;@attached(member)&lt;/span&gt; : Type / Extension 내에 새로운 선언(메소드, 프로퍼티, 이니셜라이저)을 삽입&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;아직도 이전 예시에서는 보일러 플레이트 코드들이 남아있습니다. 바로 이니셜라이저와 저장프로퍼티입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;이들은 DictionaryRepresentable 프로토콜에서 요구하는 구현체입니다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.29.56.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bChMSj/btsJFrZzZg7/wnD8b5nTwJgiSTroDqaklK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bChMSj/btsJFrZzZg7/wnD8b5nTwJgiSTroDqaklK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bChMSj/btsJFrZzZg7/wnD8b5nTwJgiSTroDqaklK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbChMSj%2FbtsJFrZzZg7%2FwnD8b5nTwJgiSTroDqaklK%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;1272&quot; height=&quot;484&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.29.56.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;이때 &lt;span style=&quot;color: #006dd7;&quot;&gt;@attached(member)&lt;/span&gt; 속성을 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.30.29.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ab3PA/btsJEF5aIpE/lv3eHsklhIxGw8EoszJ921/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ab3PA/btsJEF5aIpE/lv3eHsklhIxGw8EoszJ921/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ab3PA/btsJEF5aIpE/lv3eHsklhIxGw8EoszJ921/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAb3PA%2FbtsJEF5aIpE%2Flv3eHsklhIxGw8EoszJ921%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;1268&quot; height=&quot;316&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.30.29.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이제 이렇게 추가해주게되면 해당 DictionaryStorage를 이용하는 것만으로 이니셜라이저와 저장 프로퍼티를 자동으로 추가해주게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;물론 실제 &lt;i&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/i&gt;의 구현부에는 이니셜라이저와 저장 프로퍼티를 추가해주는 코드가 작성되어 있을겁니다 !&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.30.58.png&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M5njM/btsJEThNk96/zkskOPIq2ilTCltefhKeO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M5njM/btsJEThNk96/zkskOPIq2ilTCltefhKeO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M5njM/btsJEThNk96/zkskOPIq2ilTCltefhKeO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM5njM%2FbtsJEThNk96%2FzkskOPIq2ilTCltefhKeO1%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;1264&quot; height=&quot;390&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.30.58.png&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;390&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;0&quot;&gt;@attached(conformance)&lt;/span&gt; : Type / Extension에 프로토콜 conformance를 채택&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아직도 여전이 보일러 플레이트 코드가 남아있는데요. 바로 &lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;1&quot;&gt;DictionaryRepresentable&lt;/span&gt; 프로토콜 채택입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.31.39.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GCXCH/btsJEUViL3V/yfc6CforKAZ3GmIgW8STdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GCXCH/btsJEUViL3V/yfc6CforKAZ3GmIgW8STdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GCXCH/btsJEUViL3V/yfc6CforKAZ3GmIgW8STdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGCXCH%2FbtsJEUViL3V%2Fyfc6CforKAZ3GmIgW8STdK%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;1268&quot; height=&quot;390&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.31.39.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이때는&lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;1&quot;&gt; @attached(conformance)&lt;/span&gt; 속성을 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.32.13.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T8aUH/btsJDVndVjn/tid2SsZrlWlqccaW9NP2kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T8aUH/btsJDVndVjn/tid2SsZrlWlqccaW9NP2kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T8aUH/btsJDVndVjn/tid2SsZrlWlqccaW9NP2kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT8aUH%2FbtsJDVndVjn%2Ftid2SsZrlWlqccaW9NP2kk%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;1270&quot; height=&quot;348&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.32.13.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 추가하고 구현해주는 것만으로 이제 수작업으로 &lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;1&quot;&gt;DictionaryRepresentable&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;font-family: 'Noto Serif KR';&quot;&gt;이것도 물론 구현부에는 해당 프로토콜을 채택하는 부분이 구현되어 있겠죠 ?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.32.48.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n6e1M/btsJDfmx3nA/HbYdVmWq5zSKqHKe7uedH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n6e1M/btsJDfmx3nA/HbYdVmWq5zSKqHKe7uedH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n6e1M/btsJDfmx3nA/HbYdVmWq5zSKqHKe7uedH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn6e1M%2FbtsJDfmx3nA%2FHbYdVmWq5zSKqHKe7uedH0%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;1262&quot; height=&quot;304&quot; data-filename=&quot;스크린샷 2024-09-16 오후 4.32.48.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;304&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;font-family: 'Noto Serif KR';&quot;&gt;우선 오늘은 Macro가 왜 나왔고 어떤 디자인 목표를 가지고 있고 어떤역할을하고 기능들이 있는지만 가볍게 먼저 알아보았습니다 :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;다음 포스팅에서는 이를 어떻게 구현하는지 좀 더 자세한 부분들을 알아보도록 하겠습니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;감사합니다  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #409d00;&quot;&gt;레퍼런스&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #409d00;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1726472208920&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;Expand on Swift macros - WWDC23 - Videos - Apple Developer&quot; data-og-description=&quot;Discover how Swift macros can help you reduce boilerplate in your codebase and adopt complex features more easily. Learn how macros can...&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/videos/play/wwdc2023/10167/&quot; data-og-url=&quot;https://developer.apple.com/videos/play/wwdc2023/10167/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cvdD14/hyW2Uoc94r/VCIWw8V0SqWtP2V2yR68Jk/img.jpg?width=500&amp;amp;height=282&amp;amp;face=0_0_500_282&quot;&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2023/10167/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/videos/play/wwdc2023/10167/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cvdD14/hyW2Uoc94r/VCIWw8V0SqWtP2V2yR68Jk/img.jpg?width=500&amp;amp;height=282&amp;amp;face=0_0_500_282');&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;Expand on Swift macros - WWDC23 - Videos - Apple Developer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Discover how Swift macros can help you reduce boilerplate in your codebase and adopt complex features more easily. Learn how macros can...&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>SWIFT</category>
      <category>IOS</category>
      <category>iOS Dev</category>
      <category>macro</category>
      <category>swift macro</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/85</guid>
      <comments>https://dongminyoon.tistory.com/85#entry85comment</comments>
      <pubDate>Mon, 16 Sep 2024 16:35:38 +0900</pubDate>
    </item>
    <item>
      <title>[2023년 회고] 3년차 iOS 개발자</title>
      <link>https://dongminyoon.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2022년도 회고를 하고 넘어갔어야 하는데, 하지못했고 2023년은 여러가지로 변하고 또 신년에는 마음가짐을 새롭게하고 정비하자는 의미에서 올해를 돌아보고 2024년을 계획해보려고 3년차 개발자의 회고를 작성해보려고 합니다  &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회사원으로서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년 1월 25일부로 네이버제트에 입사해서 근무를 한지 만으로 3년이 흐르게 되었다. 그래도 나름 한 회사에 오래 있었고 그동안 많은 변화들이 있었는데 2023년은 가장 많은 변화가 있었던 해였던 것 같다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;조직개편&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 가장 큰 변화는 아무래도 조직개편이었던 것 같다. 처음에 조직개편을 한다는 소문이 있을 때, iOS 개발자임은 변화가 없으니 나에게는 크게 변화가 없을 것 같다고 느꼈다. 하지만 조직개편이 1년에 2번정도 있었는데 생각과는 달랐다. 나의 업무에도 많은 변화가 생겼고 그에 따라 생각도 많아졌었던 것 같다. 특히 큰 변화는 기능조직 &amp;rarr; 목적조직인데 이 변화가 크게 다가왔었던 것 같다.&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;그럼에도 내가 배정된 목적조직은 기존에도 같이 업무를 많이 진행하던 멤버들이 작은 팀 단위로 묶이게 되어서 원활하게 업무가 진행되고 많은 피쳐들을 진행할 수 있었던 것 같다. 확실히 목적 단위로 묶이다보니 기능 단위와는 또 다른 장단이 있는 것 같았지만 여러 직군들이 한 목적에 대해 같이 얼라인 되어서 일을 진행하다보니 피쳐에 대한 오너십도 생기고 일의 진행이 수월했다(무엇보다 커뮤니케이션 비용이 적게듦). 물론 개발자로서 아쉬운 점들도 있었지만 좋은 경험이고 조직이라는 의미에 대해 생각해볼 수 있는 시간이었다.&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어떤 동료가 되고 싶은지&lt;/b&gt;&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;우선 가장 중요하다고 생각했던 부분은 &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;다음으로는 iOS 팀원으로서도 소홀하지 않도록 애쓰려고 했다. 같은 코드베이스 내에서 작업하는 것은 이전과 다름없었다. 그렇기 때문에, 비록 내가 맡지 않은 도메인이더라도 다른 팀원들의 PR 확인에 더더욱 시간을 쏟았던 것 같고 시간이 났을 때, 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;PR 확인 같은 경우는 시간을 많이 할애했다. 하지만 힘든 부분도 있었다. 프로젝트가 커지고 특히 기획을 모르는 상황에서 코드만으로 파악하기는 힘들었다. 그리고 많은 PR 들이 리뷰어로 지정되다보니 아침 시간은 거의 PR 보는 시간으로 사용했는데 파악이 쉽지 않으니 더 많은 시간들을 사용했던 것 같다. 뭔가 계속 이렇게 진행되다보니 기계적으로 PR을 읽게되고 양이 많다보니 조금씩 소홀해지는 부분들이 생겼다. 이 부분이 개인적으로 조금은 아쉬웠다. 내년에는 좀 더 좋은 방향을 찾아서 개선해보고 싶다는 생각도 들었다.&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 프로젝트적으로 도움이 되는 부분들을 진행하겠다고 했다. 특히, 모듈화가 일부분은 되어있지만 아직 완전 LOW한 부분들에 대해서는 안되어 있는 부분도 있어서 호기롭게 팀에 선언하고 Network 로직들과 모델을 우선 분리하겠다고 구조를 설계해서 팀에 공유하고 진행했다. 아무래도 목적 조직에 있다보니 iOS 프로젝트적으로는 조금 소홀해질 수 있다고 생각이 들었고 이런 부분들을 진행해보고 싶어서 노력했던 것 같다. 물론,, 너무 작업이 크기도하고 피쳐도 진행하면서 개발하다보니 아직도 진행중이지만 2024년엔 꼭 완료해보고 싶다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발자로서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사원으로서의 회고도 중요하지만 나 개인적으로 개발자라는 부제(?)를 달고 회사에서 일하고 있기 때문에, 중요하다고 생각한다. 어느덧 2024년 1월 25일부로 만 3년이라는 기간동안 iOS 개발자로서 일을 했고 올해로 4년차 개발자가 되었다. 그러면서 여러 생각들과 고민들이 스쳐지나갔다. 여기에 대해서도 회고해보려고한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개인공부&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 공부 부분에서는 개인적으로 아쉬움이 들었던 한 해였다. WWDC에서 새롭게 나오는 기술들 및 새롭게 나오는 필수적인 기능(Concurrency, UICollectionViewCompositionalLayout, DiffableDataSource등)들은 개인적으로 공부해서 체득했다. 다만, 2023년 동안 SwiftUI를 일부만 공부하고 적용하지 못한게 너무 아쉬웠다. 책을 읽으면서 공부했는데 아직 반정도 진도를 나가고 나머지는 남아있는 상태이다. 지금 UI는 조금씩 찾아가면서 개발할 수 있는 상태인데, 올해는 꼭 다시 공부해서 회사 프로젝트에서 작은 부분들 먼저 하나씩 적용해 볼 생각이다. 평소에는 회사 사용하던 스택 외에 새로운 아키텍쳐, 모듈화 이런 부분들은 먼저 공부해서 가져와서 공유하고 발표해서 적용하는 편이었는데 올해는 작년에 비해 여러가지로 미비했던 것 같다. 기존에 장점이라고 생각했던 부분이 이번년도에는 약간 미비했던 것 같아서 2024년부터는 이런 나의 장점들을 더욱더 잘 살려서 업무적으로 외에도 인사이트를 더 줄 수 있는 팀원이 되고싶다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;커리어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 커리어 부분에서도 이제 4년차에 접어든만큼 여러 생각을 하게 된 해였던 것 같다. 특히, 회사의 조직개편이 진행되면서 중간에 텀이 있었는데 그 기간이 가장 컸던 것 같다.&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 개발자의 커리어를 이어가면서 주요 도메인으로 가져가고 싶었던 라이브 개발 팀에 들어갔다 만족스러웠다. 라이브쪽 개발은 재밌기도하면서 네이티브 개발자로서는 평소 경험하기 힘든 도메인이라 평소에도 iOS 개발자의 커리어에서 라이브쪽 도메인을 이어가고 싶다고 생각했었다. 다만, 조직개편이 진행되는 와중에 몇몇 조직은 일이 없어서 뜨는 기간이 있었다. 우리조직도 포함이었고 나는 그 기간동안 우리 코드 베이스에서 그동안 진행하고 싶었던 네트워크 모듈화를 진행했었다. 코드에서 상당히 LOW한 레벨이라 한번에 적용하기는 쉽지 않았고 모든 팀원들에게 설계를 공유하고 피드백을 받아 수정하는 기간들도 있었다. 그래서 우선 일부분에 적용하면서 변경하는 중이다. 무엇보다 필요한 일이라 생각해서 의미가 있다고 생각했다.&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;lsquo;앞으로 하고싶은 개발은 무엇인지&amp;rsquo;, &amp;lsquo;현재에 만족하는지&amp;rsquo;들을 생각하게 되었다. 일단 결론적으로는 라이브 조직이 잘되어서 앞으로도 라이브 개발을 쭉 진행하기는 할 것 같다. 물론 아쉬운 부분도 있긴 하지만 라이브 개발이 더욱 바빠질 것 같아서 당분간은 집중해보자고 생각을 했다. 다만, 어떤 개발을 하고싶은가에 대해서는 현재 우리 앱의 라이브는 유니티와 엮여 있는 부분이 조금은 많아서 그 나름에 경험할 수 있는 부분들은 있지만 다음은 네이티브 Only의 라이브 서비스를 개발해보고 싶다는 생각이 들긴했다ㅎ&amp;hellip;&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;앞으로 현재 회사에서 해볼 라이브 기획들은 더 나올것 같고 그에 따라 개발할 양도 많을 것 같아서 올해는 조금 더 현재 라이브 개발에 집중을 해볼 생각이다. 그리고 4년차 개발자가 된만큼 이전보다는 어떤 태도를 가지고 개발해야하는지 퍼포먼스나 여러가지 부분으로 더 성숙해져야 할 것 같고 커리어를 쭉 이어나가기 위해서는 그동안 개발들을 정리하는 부분도 꼭 필요할 것 같다는 생각이 들었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인간으로서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년은 개인적으로도 많은 변화가 있었고 불안정했던 시기였던 것 같다. 또한 30(만 28세)이 되었고 그에 따라 개인적으로 느낀 것들도 있어서 정리해두려고 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장단점&lt;/b&gt;&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;J의 성향이 있기 때문에 아무래도 체계화하거나 정리하는 것을 조금은 잘하는 편이라 생각한다. 특히, 회사에도 처음 입사하고 따로 정리되어 있지 않던 히스토리나 여러가지들을 위키에 문서로 정리하고 공유하는 것들을 많이했었고 PR을 작성할 때도 적용되었던 것 같다. (코드도 대칭맞추고 심미성(?)에 초점을 두는 것 같기도하다) 하지만 올해는 목적 조직으로 들어가다보니 iOS 팀적으로 PR 외에는 조금은 장점이 발휘되지 못하고 미비했던 것 같다. 이런 부분들은 2024년에는 다시 좀 더 신경쓰고 집중해야겠다고 생각했다.&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;단점으로는 T성향이 조금 강했다. (MBTI 맹신자 같기도) 보통 더 친하면 친할수록 또는 일할때 짙은 것 같은데, 요거를 조금 학습해서 F력을 넣어보려고한다,, 아무래도 대화를 하다보면 해결점을 찾으려고 하다보니 그런 것 같은데 적절한 성향이 있으면 좋을 것 같다고 생각이 들었다. 그래도 강하게 의견을 주장하는 편은 아니라 주변 사람들이 느낄 부분은 없었을 수도 있지만 개인적으로 생각할 때, 좀 더 감정을 이해하는 방향으로 생각하려고 애써보려고 한다.&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;lsquo;이거해봐&amp;rsquo;, &amp;lsquo;이거 한번 들어봐&amp;rsquo;라고 들으면 내가 정말 필요하다고 느끼지 않으면 잘 해보지 않았다. 물론 정말 필요할 것 같은 것들은 잘했는데 정말 가까운 사람과는 그런 행동 하나하나가 말을 듣고 있다고 느끼게 하는 것 같아서 올해 말부터 노력하고 있는데 내년에는 좀 더 노력해 볼 생각이다 ㅎㅎ..&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;취미&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래도 액티브한 운동을 좋아하는 편인데 올해는 기존에 했던 운동들 외에도 배드민턴 &amp;amp; 스노우보드까지 또 취미가 생겨버렸다&amp;hellip; 이런것들을 하고나면 개운하고 기분이 좋아서 너무 좋다.&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;지금도 헬스는 꾸준히 하고있고 이제 버릇이 되어버려서 특별하지 않은 이상 일주일에 4회 이상 하지않으면 찌뿌둥해서 버릇처럼 간다. 물론 다른 액티브한 운동들에 비해 재미는 없는 편이지만 몸이 건강해지는 것 같아서 좋다. 그리고 축구는 어릴때부터 꾸준히 이어오고 있고 그에 이어서 이제는 종종 오전 배드민턴 그리고 스키장은 매년 꾸준히 조금씩 가고 있었는데 이제는 스키장에 대한 로망을 실현해보고 싶다. 어릴때부터 내 장비로 시즌권을 끊어서 다니는 로망이 있었는데 올해는 우선 더 재미를 느낀 것 같아서 돌아오는 시즌부터는 정말 본격적으로 타보지 않을까 생각중이다.&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;h3 data-ke-size=&quot;size23&quot;&gt;총평&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 첫 입사를하고 2021, 2022년과는 또 다르게 다른 관점으로 한해를 바라볼 수 있었던 한해였던 것 같다. 2021, 2022는 개인의 성장에 집중되어있었고 여러가지를 시도하는 느낌이었다면 올해는 년차도 조금은 올라온만큼 회사에 대해 그리고 그 회사에서 속해있는 나, 그리고 앞으로의 나에 대해 많이 돌아보고 생각할 수 있는 한 해였다.&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;전체적으로 생각해봤을 때, 인간으로서 그리고 회사원으로서는 정말 많이 성장하고 생각할 수 있었던 한 해였던 것 같다. 다만, 개발자로서는 올해는 개인적으로 생각하기에는 약간은 아쉬운 해였다고 생각한다. 여러가지 상황과 맞물리면서 개발자로서는 조금 아쉬움이 남는 한해였지만 또 아쉬운만큼 새로운 2024년에는 돌아보고 더더욱 발전할 수 있을거라고 생각한다. 올해는 3가지 측면으로서의 2023년의 나를 회고해보았는데 내년에는 아쉬웠던 부분은 발전시키고 좋았던 부분들은 더더욱 키워나가면서 더 좋은 2024년을 보냈으면 좋겠다.&lt;/p&gt;</description>
      <category>회고</category>
      <category>3년차 개발자 회고</category>
      <category>IOS</category>
      <category>iOS Dev</category>
      <category>회고</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/84</guid>
      <comments>https://dongminyoon.tistory.com/84#entry84comment</comments>
      <pubDate>Sun, 11 Feb 2024 21:02:42 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] UICollectionViewCompositionalLayout (1)</title>
      <link>https://dongminyoon.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이번 글에서는 &lt;b&gt;UICollectionView&lt;/b&gt;의 Layout을 지정하는 새로운 방법을 알아보려고합니다&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot; data-token-index=&quot;0&quot;&gt;UICollectionViewCompositionalLayout&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 애플에서는 해당 레이아웃에 대해 유연하고 적응력 있게 우리가 기존에 사용하던 CollectionView의 레이아웃을 구성할 수 있다고 설명하고 있습니다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;A layout object that lets you combine items in highly adaptive and flexible visual arrangements.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UICollectionViewCompositionalLayout&lt;/b&gt;은 한개 혹은 여러개의 Seciton을 가지는데, Section은 안에 여러개의 그룹으로 레이아웃이 나뉘어져 있습니다. 하나의 섹션은 여러개의 그룹 그리고 그룹은 개별 아이템들로 구성되어있습니다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-19 오후 4.48.41.png&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cr4uQG/btsAx05wiU4/xGG5KKOUkY9KGZnHZzvoa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cr4uQG/btsAx05wiU4/xGG5KKOUkY9KGZnHZzvoa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cr4uQG/btsAx05wiU4/xGG5KKOUkY9KGZnHZzvoa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcr4uQG%2FbtsAx05wiU4%2FxGG5KKOUkY9KGZnHZzvoa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;436&quot; data-filename=&quot;스크린샷 2023-11-19 오후 4.48.41.png&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림을 보시면 &lt;span style=&quot;color: #333333;&quot;&gt;Section &amp;rarr; Group &amp;rarr; Item&lt;/span&gt;으로 이루어져서 하나의 Layout을 구성하는 것을 알 수 있습니다. 그렇다면 간단하게 코드로는 어떻게 구현되어 있는지 보고 각각의 구성요소에 대해 알아보겠습니다 &lt;span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1700380193219&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func createBasicListLayout() -&amp;gt; UICollectionViewLayout { 
    let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),                                  
                                         heightDimension: .fractionalHeight(1.0))    
    let item = NSCollectionLayoutItem(layoutSize: itemSize)  
  
    let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),                                          
                                          heightDimension: .absolute(44))    
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,                                                   
                                                   subitems: [item])  
  
    let section = NSCollectionLayoutSection(group: group)    

    let layout = UICollectionViewCompositionalLayout(section: section)    
    return layout
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 &lt;b&gt;UICollectionViewCompositionalLayout&lt;/b&gt;의 기본적인 구성요소들을 코드로 작성하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명했듯이 Section -&amp;gt; Group -&amp;gt; Item의 구성으로 이루어져서 하나의 레이아웃이 구성되는 것을 알 수 있습니다.&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;NSCollectionLayoutSize&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UICollectionViewCompositionalLayout&lt;/b&gt;에서 각 아이템 or 그룹의 사이즈를 지정할 때, 사용할 수 있는 타입이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 타입의 생성자에서 &lt;b&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;1&quot;&gt;NSCollectionLayoutDimesion&lt;/span&gt;&lt;/b&gt;을 받고 있는데 width, height의 쌍을 이루어 크기를 지정하는 타입입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1700380735274&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@available(iOS 13.0, *)
open class NSCollectionLayoutSize : NSObject, NSCopying {
    public convenience init(
        widthDimension width: NSCollectionLayoutDimension, 
        heightDimension height: NSCollectionLayoutDimension
    )
}&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;NSCollectionLayoutDimension&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NSCollectionLayoutSize&lt;/b&gt;를 구성하고 있는 이 타입은 무엇일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플에서는 CollectionView의 높이 or 넓이를 보여주는 개별적인 값이라고 설명하고있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;An individual dimension representing an item&amp;rsquo;s width or height in a collection view.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이애 대해 높이 or 높이에 대해 각각을 정의할 수 있게 총 3가지의 방법을 사용해서 나타낼 수 있게 제공하고있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Absoulte&lt;/b&gt; : 말그대로 절댓값으로 아래 예시에서는 44px에 해당하는 만큼의 width, height를 지정하게 된다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1700381051538&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let absoluteSize = NSCollectionLayoutSize(widthDimension: .absolute(44),
                                         heightDimension: .absolute(44))&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Estimated&lt;/b&gt; : 기존에 &lt;span style=&quot;color: #333333;&quot;&gt;UITableView, UICollectionView&lt;/span&gt;에서 활용하던 estimated와 같은 개념입니다. 실제로 데이터 로드 후, 값이 변하는 경우나 유동적으로 사용해야 할 때 대략의 값을 넘겨주면 런타임 중에 실제 width, height가 계산된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1700381146232&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let estimatedSize = NSCollectionLayoutSize(widthDimension: .estimated(200),
                                          heightDimension: .estimated(100))&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Fractional&lt;/b&gt; : 이 값으로 설정하면 비율에 따라 유동적으로 변하는 경우에 유용하게 사용할 수 있습니다. 아래 예시와 같게 설정하면 Group의 넓이의 20%에 해당하게 width, height를 설정하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1700381213242&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 넓이의 20%에 해당하게 width, height 설정
let fractionalSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2),
                                           heightDimension: .fractionalWidth(0.2))

// 높이의 20%에 해당하게 width, height 설정
let fractionalSize = NSCollectionLayoutSize(widthDimension: .fractionalHeight(0.2),
                                           heightDimension: .fractionalHeight(0.2))&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;NSCollectionLayoutItem&lt;/b&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;b&gt;NSCollectionLayoutDimension&lt;/b&gt;, &lt;b&gt;NSCollectionLayoutSize&lt;/b&gt;&lt;/span&gt; 이 요소들을 사용해서 Layout을 이루는 가장 기본 구성단위인 Item에 대해서 알아보겠습니다.&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;Layout의 기본 단위인 Item은 일반적으로 실제 보이는 화면에서 렌더링되는 Cell이라고 보아도 될 것 같습니다. 물론 Item은 헤더, 푸터로 제공되는 supplementary views일수도 있습니다 !&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;Item의 크기를 잡는 방법은 위의 요소에서 설명했든 LayoutSize를 이용해서 컨테이너 뷰와 상대적으로 잡거나, 절대적인 크기 또는 런타임 중에 유동적으로 변화할 수 있게 추정해서 사이즈를 잡을 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1700381695883&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@available(iOS 13.0, *)
@MainActor open class NSCollectionLayoutItem : NSObject, NSCopying {
    public convenience init(layoutSize: NSCollectionLayoutSize)
}&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;NSCollectionLayoutGroup&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 타입은 Item들의 집합으로 Item들이 어떻게 화면에 배열되고 어떤 관계를 가지는지를 결정하는 요소입니다. 자체로는 콘텐츠를 렌더링하지 않지만 내부 Item들이 실제 UI에 렌더링이 되고 어떻게 보일지를 결정하는 요소입니다. 그러나 Group 역시 &lt;b&gt;NSCollectionLayoutItem&lt;/b&gt;을 상속받고 있어서 Item처럼 사용될 수 있습니다.&lt;/span&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;이를 활용해서 다른 Group 안의 요소로 넣어서 Group을 중첩해서 아이템처럼 사용하는 것이 가능합니다. 그렇기 때문에 이를 활용하면 더욱 다양한 Layout을 쉽게 표현하는 것이 가능해집니다.&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-filename=&quot;스크린샷 2023-11-19 오후 5.19.03.png&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dulAmL/btsABBjaIq0/ifsWKyTXG5Aj0MVoxs62hK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dulAmL/btsABBjaIq0/ifsWKyTXG5Aj0MVoxs62hK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dulAmL/btsABBjaIq0/ifsWKyTXG5Aj0MVoxs62hK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdulAmL%2FbtsABBjaIq0%2FifsWKyTXG5Aj0MVoxs62hK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;422&quot; data-filename=&quot;스크린샷 2023-11-19 오후 5.19.03.png&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;564&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: #006dd7;&quot;&gt;&lt;b&gt;NSCollectionLayoutSection&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NSCollectionLayoutGroup&lt;/b&gt;보다 한단계 큰 개념으로 그룹들을 묶는 집합의 역할을 합니다. 여기서 각각의 섹션들은 같은 레이아웃을 가질수도 있고 다른 레이아웃을 가질수도 있는데, 이는 내부 요소의 Group 객체들이 결정합니다.&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;Section에 Header, Footer들을 넣을 수 있습니다. 그리고.. 가장 놀랬던 점은 기존에는 vertical axis를 가진 CollectionView에 내부의 horizonl axis를 가진 스크롤이 필요할 경우 CollectionView를 중첩해서 넣어야했지만, 여기서는 이럴 필요가 없어졌습니다  &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;b&gt;orthogonalScrollingBehavior&lt;/b&gt;&lt;/span&gt; 프로퍼티를 설정함으로 이를 가능하게 합니다. 만약 Section에서 메인 axis와 다른 값을 가져야할 경우 이 값을 수정하면 됩니다&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;심지어 이 값을 활용하면 그동안 inset을 가진 CollectionView의 페이징 시, center 배치를 위해 직접 offset을 계산해서 넣어주어야 하는 과정이 필요했는데 이것도 설정값으로만 가능해졌습니다&amp;hellip; (애플이 개발자들에게 생각을 하지못하게 하는건가.. 바보가 되어가는 느낌)&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: #006dd7;&quot;&gt;&lt;b&gt;NSCollectionLayoutAnchor&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 타입은 Supplymentary 아이템이 CollectionView 안에서 어떻게 위치하는지를 정의하는 객체입니다. 크게 두 가지를 이용해서 어디에 위치할지를 정해줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Edges&lt;/b&gt; : Supplymentary 아이템이 모서리 중, 어디쪽에 붙을지를 정의할 수 있습니다. 총 4가지의 정보가 있고 2가지의 인접한 엣지를 이용해서 총 8가지의 위치를 정의할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-19 오후 5.24.06.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c29GPh/btsAy8ofcJY/IZWSM5ImMj8COd3g15HRzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c29GPh/btsAy8ofcJY/IZWSM5ImMj8COd3g15HRzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c29GPh/btsAy8ofcJY/IZWSM5ImMj8COd3g15HRzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc29GPh%2FbtsAy8ofcJY%2FIZWSM5ImMj8COd3g15HRzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;285&quot; data-filename=&quot;스크린샷 2023-11-19 오후 5.24.06.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Offset&lt;/b&gt; : 정의했던 Edge로부터 어느정도 떨어지는지를 정의할 수 있습니다. 여기서도 정의할 수 있는 2가지 방법이 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Absolute&lt;/b&gt; : 픽셀값으로 지정하는 방법입니다. 흔히 사용하던 절대적으로 값을 지정하는 방법입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fractional&lt;/b&gt; : 상대적인 값으로 지정하는 방법입니다. 만약 x = 0.3으로 지정한다면 width의 30% 정도의 위치만큼 떨어진다는 뜻입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1700382497111&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(44),
                                     heightDimension: .absolute(44))
    
// Fractional
let badgeAnchor = NSCollectionLayoutAnchor(edges: [.top, .trailing],
                                           fractionalOffset: CGPoint(x: 0.3, y: -0.3))
// Absolute
let badgeAnchor = NSCollectionLayoutAnchor(edges: [.top, .trailing],
                                           absoluteOffset: CGPoint(x: 30, y: -30)
    
let badgeSize = NSCollectionLayoutSize(widthDimension: .absolute(20),
                                      heightDimension: .absolute(20))
    
let badge = NSCollectionLayoutSupplementaryItem(layoutSize: badgeSize,
                                               elementKind: &quot;badge&quot;,
                                               containerAnchor: badgeAnchor)
    
let item = NSCollectionLayoutItem(layoutSize: itemSize,
                                  supplementaryItems: [badge])&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;NSCollectionLayoutSupplementaryItem&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CollectionView 아이템들의 주변에 시각적인 효과를 만들어줄 수 있는 객체이다. 아래 그림에서 우측 상단의 아이콘 같은 요소를 표현할 때 사용할 수 있습니다. (근데, 개인적으로는 Cell 안에 아이콘을 넣지 해당 객체를 이용할 것 같진 않습니다.. )&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-19 오후 5.33.10.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;922&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MLA0t/btsAuJKiCHf/gLoWTPmIR9gdlEj1ozeNT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MLA0t/btsAuJKiCHf/gLoWTPmIR9gdlEj1ozeNT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MLA0t/btsAuJKiCHf/gLoWTPmIR9gdlEj1ozeNT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMLA0t%2FbtsAuJKiCHf%2FgLoWTPmIR9gdlEj1ozeNT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;342&quot; data-filename=&quot;스크린샷 2023-11-19 오후 5.33.10.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;922&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: #006dd7;&quot;&gt;&lt;b&gt;NSCollectionLayoutBoundarySupplementaryItem&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;CollectionView의 Seciton의 Header, Footer로 활용할 수 있는 객체이다. 이 객체 역시 &lt;b&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;1&quot;&gt;NSCollectionLayoutSupplementaryItem&lt;/span&gt;&lt;/b&gt;을 상속받아서 사용하고 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1700382860303&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let section = NSCollectionLayoutSection(group: group)
let headerFooterSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: .estimated(44))

let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerFooterSize,
                                                                elementKind: ElementKind.sectionHeader,
                                                                alignment: .top)
let sectionFooter = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerFooterSize,
                                                                elementKind: ElementKind.sectionFooter,
                                                                alignment: .bottom)
section.boundarySupplementaryItems = [sectionHeader, sectionFooter]&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;일단 오늘은 간단히 UICollectionViewCompositionalLayout의 기본적인 요소에 대해 알아보았습니다 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에는 이를 이용해 간단한 레이아웃들을 구성해보고 좀 더 사용해보면서 알아가보도록하겠습니다&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/83</guid>
      <comments>https://dongminyoon.tistory.com/83#entry83comment</comments>
      <pubDate>Sun, 19 Nov 2023 17:35:40 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] Dynamic Framework &amp;amp; Static Framework</title>
      <link>https://dongminyoon.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 회사 프로젝트의 크기가 커지다보니 각 모듈간의 의존도도 커지고 빌드 시간도 오래 걸리게 되면서, Framework 단위로 분리해서 관리하는 방법에 관심을 가지게 되었습니다.&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;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt; &amp;amp; &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&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;span style=&quot;color: #006dd7;&quot;&gt;Dynamic Framework&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 4.55.33.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RfHb7/btr7funrSjQ/UQ4Tu7gk3KV3gfTPXtZon1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RfHb7/btr7funrSjQ/UQ4Tu7gk3KV3gfTPXtZon1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RfHb7/btr7funrSjQ/UQ4Tu7gk3KV3gfTPXtZon1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRfHb7%2Fbtr7funrSjQ%2FUQ4Tu7gk3KV3gfTPXtZon1%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;1276&quot; height=&quot;758&quot; data-filename=&quot;스크린샷 2023-04-01 오후 4.55.33.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;우선 &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;는 Xcode에서 Framework를 생성하게 되면 기본적으로 &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;로 생성됩니다. &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;는 그림에서처럼 &lt;b&gt;Static Linker&lt;/b&gt;를 통해 &lt;b&gt;Dynamic Library Reference&lt;/b&gt;가 &lt;b&gt;Application Code&lt;/b&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;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;를 프로젝트에 추가하고 빌드하게 되면 &lt;span data-token-index=&quot;3&quot;&gt;해당 프레임워크의 &lt;/span&gt;&lt;b&gt;&lt;span data-token-index=&quot;4&quot;&gt;excutable binary&lt;/span&gt;&lt;/b&gt;&lt;span data-token-index=&quot;5&quot;&gt; 파일이 앱의 &lt;/span&gt;&lt;b&gt;&lt;span data-token-index=&quot;6&quot;&gt;excutable binary&lt;/span&gt;&lt;/b&gt;&lt;span data-token-index=&quot;7&quot;&gt;와는 별도로 생성&lt;/span&gt;됩니다. 그리고 앱에서는 해당 모듈을 사용할 때, 참조만 가지고 있다가 사용하게 되는 원리입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;어려우니까 한번 직접 &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;를 직접 만들어서 사용하면서 알아볼게요~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Framework를 선택해서 하나 생성해주세요~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.08.07.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;2024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TPJet/btr7ghg4kQR/QLC7QSpBJpPO4hPzbPuKy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TPJet/btr7ghg4kQR/QLC7QSpBJpPO4hPzbPuKy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TPJet/btr7ghg4kQR/QLC7QSpBJpPO4hPzbPuKy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTPJet%2Fbtr7ghg4kQR%2FQLC7QSpBJpPO4hPzbPuKy1%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;3024&quot; height=&quot;2024&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.08.07.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;2024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고는 테스트를 위해 Framework 내부에 요런 클래스를 하나 구현했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.10.27.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;2024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfx4w8/btr7s4N24UB/tfyC43kQgUTwJwd2oIzswK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfx4w8/btr7s4N24UB/tfyC43kQgUTwJwd2oIzswK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfx4w8/btr7s4N24UB/tfyC43kQgUTwJwd2oIzswK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfx4w8%2Fbtr7s4N24UB%2FtfyC43kQgUTwJwd2oIzswK%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;3024&quot; height=&quot;2024&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.10.27.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;2024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 생성했던 Framework 테스트를 위해 앱 프로젝트에 추가해주겠습니다. 앱의 프로젝트에 &lt;span style=&quot;color: #333333; background-color: #dddddd;&quot; data-token-index=&quot;1&quot;&gt;Add Files to &amp;ldquo;App Name&amp;rdquo;&lt;/span&gt;을 클릭해서 아까 만들어주었던 Framework의 프로젝트 파일을 추가해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.12.41.png&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;2234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGd7je/btr7iL2HvCC/7JLspBKfTzKHTTELoN22a1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGd7je/btr7iL2HvCC/7JLspBKfTzKHTTELoN22a1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGd7je/btr7iL2HvCC/7JLspBKfTzKHTTELoN22a1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGd7je%2Fbtr7iL2HvCC%2F7JLspBKfTzKHTTELoN22a1%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;3456&quot; height=&quot;2234&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.12.41.png&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;2234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.14.13.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dukYpj/btr7s32FPot/yd33jNK2gF2BJKS9rssVz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dukYpj/btr7s32FPot/yd33jNK2gF2BJKS9rssVz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dukYpj/btr7s32FPot/yd33jNK2gF2BJKS9rssVz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdukYpj%2Fbtr7s32FPot%2Fyd33jNK2gF2BJKS9rssVz0%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;3680&quot; height=&quot;2382&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.14.13.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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: #dddddd;&quot;&gt;General&lt;/span&gt; &amp;rarr; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Frameworks, Libraries, and Embedded Content&lt;/span&gt; &amp;rarr; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;+&lt;/span&gt;를 눌러 Framework를 추가해주세요. 추가하게 되면 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Embeded &amp;amp; Sign&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Do Not Embed&lt;/span&gt;의 메뉴가 선택가능한데 우선 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Embeded &amp;amp; Sign&lt;/span&gt; 메뉴를 선택한 상태로 나두시면 됩니다 ! (이 부분은 뒤에서 알아볼게요  )&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.15.50.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C02Q1/btr7iNsHmJw/Kkx0TsZKYMb74IMlS4j1S0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C02Q1/btr7iNsHmJw/Kkx0TsZKYMb74IMlS4j1S0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C02Q1/btr7iNsHmJw/Kkx0TsZKYMb74IMlS4j1S0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC02Q1%2Fbtr7iNsHmJw%2FKkx0TsZKYMb74IMlS4j1S0%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;3680&quot; height=&quot;2382&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.15.50.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 앱에서 해당 Framework를 사용해서 빌드를 해볼게요. 해당 Framework를 import하고 내부의 TestHelper를 접근하면 잘 접근되고 빌드까지 성공하는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.27.29.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ra4bJ/btr7n1xprnH/t6k4KDO7oD6yKnmKjjBSDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ra4bJ/btr7n1xprnH/t6k4KDO7oD6yKnmKjjBSDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ra4bJ/btr7n1xprnH/t6k4KDO7oD6yKnmKjjBSDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRa4bJ%2Fbtr7n1xprnH%2Ft6k4KDO7oD6yKnmKjjBSDK%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;3680&quot; height=&quot;2382&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.27.29.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 가볍게 &lt;span style=&quot;color: #333333;&quot;&gt;&lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;를 사용하는 방법을 알아봤습니다. 다음으로는 &lt;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;를 사용하는 법에 대해 간단히 알아보고 살짝 딥하게 비교하면서 다뤄볼게요&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;span style=&quot;color: #006dd7;&quot;&gt;Static Framework&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.30.17.png&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIQq5u/btr7gifYB62/djwgbmsMkk7wuC58CcNYg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIQq5u/btr7gifYB62/djwgbmsMkk7wuC58CcNYg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIQq5u/btr7gifYB62/djwgbmsMkk7wuC58CcNYg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIQq5u%2Fbtr7gifYB62%2FdjwgbmsMkk7wuC58CcNYg1%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;1338&quot; height=&quot;864&quot; data-filename=&quot;스크린샷 2023-04-01 오후 5.30.17.png&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;는 &lt;b&gt;Static Linker&lt;/b&gt;를 통해 레퍼런스가 아니고 &lt;b&gt;Static libraries&lt;/b&gt;가 복사되어서 &lt;b&gt;Application Code&lt;/b&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;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;는 앱의 &lt;b&gt;excutable binary&lt;/b&gt;와는 별도로 생성되고 레퍼런스만 참조한다고했었죠? 반대로 &lt;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;에서는 앱의 &lt;b&gt;excutable binary&lt;/b&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;Framework가 컴파일 타임에 포함되기 때문에, 빌드속도는 늘어나지만 런타임 중에 레퍼런스를 찾는 작업이 없기 때문에 앱 실행속도는 더 빠르게 됩니다.&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;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;도 만들어보겠습니다. 아까 만들었던 &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;를 바꾸어볼게요. 만들었던 Framework의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Build Settings&lt;/span&gt; &amp;rarr; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Mach-O Type&lt;/span&gt;을 들어가주세요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.02.43.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kTFYv/btr7gRI84DD/jv2neB4ASkblC2r86BVfm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kTFYv/btr7gRI84DD/jv2neB4ASkblC2r86BVfm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kTFYv/btr7gRI84DD/jv2neB4ASkblC2r86BVfm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkTFYv%2Fbtr7gRI84DD%2Fjv2neB4ASkblC2r86BVfm1%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;3680&quot; height=&quot;2382&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.02.43.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;기본적으로 생성할 때, &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;이기 때문에 &lt;b&gt;Dynamic Library&lt;/b&gt;로 선택되어있을겁니다. 요거를 &lt;b&gt;Static Library&lt;/b&gt;로 변경해주세요 !&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.04.46.png&quot; data-origin-width=&quot;2902&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ArTQ5/btr7gPR8wDQ/Z3uqV9dkplg4ayRzb1d4q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ArTQ5/btr7gPR8wDQ/Z3uqV9dkplg4ayRzb1d4q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ArTQ5/btr7gPR8wDQ/Z3uqV9dkplg4ayRzb1d4q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FArTQ5%2Fbtr7gPR8wDQ%2FZ3uqV9dkplg4ayRzb1d4q0%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;2902&quot; height=&quot;554&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.04.46.png&quot; data-origin-width=&quot;2902&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그리고 앱의 프로젝트에서 만들었던 Framework를 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Embeded &amp;amp; Sign&lt;/span&gt;으로 선택했었죠? 이거를 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Do Not Embed&lt;/span&gt;로 변경해주세요 !&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.06.32.png&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;2234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dleMiS/btr7eVMKGtq/k2jOj51ryxYbmdGAoESWY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dleMiS/btr7eVMKGtq/k2jOj51ryxYbmdGAoESWY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dleMiS/btr7eVMKGtq/k2jOj51ryxYbmdGAoESWY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdleMiS%2Fbtr7eVMKGtq%2Fk2jOj51ryxYbmdGAoESWY1%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;3456&quot; height=&quot;2234&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.06.32.png&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;2234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;그렇다면 왜 &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt; &amp;amp; &lt;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt; 각각 Embed 방식을 다르게 가져가는걸까요? 요걸 이제 살짝 딥하게 알아볼게요&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;span style=&quot;color: #006dd7;&quot;&gt;Dynamic Framework &amp;amp; Static Framework&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 아까 &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;를 Embed하는 방식은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Embeded &amp;amp; Sign&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;빌드를 하고 생성된 Products에 우클릭을 하고 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Show in Finder&lt;/span&gt;를 선택해주세요 ~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.15.22.png&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;2234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E8SxD/btr7gjspjZp/9ogKK2489RnJtwE7KSdzd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E8SxD/btr7gjspjZp/9ogKK2489RnJtwE7KSdzd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E8SxD/btr7gjspjZp/9ogKK2489RnJtwE7KSdzd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE8SxD%2Fbtr7gjspjZp%2F9ogKK2489RnJtwE7KSdzd0%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;3456&quot; height=&quot;2234&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.15.22.png&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;2234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Finder에서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;패키지 내용 보기&lt;/span&gt;를 클릭해서 들어가면 앱의 &lt;b&gt;&lt;span data-token-index=&quot;1&quot;&gt;excutable binary&lt;/span&gt;&lt;/b&gt; 파일을 볼 수 있습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.17.21.png&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;860&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fd8Km/btr7g8dbxKM/GI86Ao1CuHkhvmdhVPtEZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fd8Km/btr7g8dbxKM/GI86Ao1CuHkhvmdhVPtEZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fd8Km/btr7g8dbxKM/GI86Ao1CuHkhvmdhVPtEZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFd8Km%2Fbtr7g8dbxKM%2FGI86Ao1CuHkhvmdhVPtEZK%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;1838&quot; height=&quot;860&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.17.21.png&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;860&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.20.24.png&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjsDYH/btr7fZnzCkz/uITiy65FvGzhEjBYTn8dK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjsDYH/btr7fZnzCkz/uITiy65FvGzhEjBYTn8dK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjsDYH/btr7fZnzCkz/uITiy65FvGzhEjBYTn8dK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjsDYH%2Fbtr7fZnzCkz%2FuITiy65FvGzhEjBYTn8dK0%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;1834&quot; height=&quot;868&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.20.24.png&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 이제 중요한건 아까 &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;는 Framework의 &lt;b&gt;excutable binary&lt;/b&gt; 파일이 따로 생성되고 앱에서는 레퍼런스를 얻어서 사용한다고 했었죠. 그렇다면 한번 Framework의 &lt;b&gt;excutable binary&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;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;패키지 내용 보기&lt;/span&gt;를 통해 들어간 곳에 Frameworks 폴더를 타고 가다보면 우리가 만든 Framework의 &lt;b&gt;excutable binary&lt;/b&gt; 파일을 쉽게 확인할 수 있습니다. 와우,, 이래서 &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;는 별도의 &lt;b&gt;excutable binary&lt;/b&gt;가 생성되고 이를 참조해서 사용한다는 말이 이해가 조금은 되죠?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.24.33.png&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;1096&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE82ja/btr7d7TQDPK/yhKMJlnq8nog3W1KOQ3740/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE82ja/btr7d7TQDPK/yhKMJlnq8nog3W1KOQ3740/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE82ja/btr7d7TQDPK/yhKMJlnq8nog3W1KOQ3740/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE82ja%2Fbtr7d7TQDPK%2FyhKMJlnq8nog3W1KOQ3740%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;2064&quot; height=&quot;1096&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.24.33.png&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;1096&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자.. 근데 만약 여기서 Embed 방식을 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Do Not Embed&lt;/span&gt;로 변경하고 빌드하면 어떻게 될까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 빌드해서 패키지 내용을 확인해보면 Framework의 &lt;b&gt;excutable binary&lt;/b&gt; 파일이 없어진 것을 확인할 수 있습니다  &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.27.55.png&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;1096&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vEYYf/btr7s3uQlvg/3a2CMUIDfYofKUV30ArjoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vEYYf/btr7s3uQlvg/3a2CMUIDfYofKUV30ArjoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vEYYf/btr7s3uQlvg/3a2CMUIDfYofKUV30ArjoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvEYYf%2Fbtr7s3uQlvg%2F3a2CMUIDfYofKUV30ArjoK%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;2064&quot; height=&quot;1096&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.27.55.png&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;1096&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;의 경우에는 런타임 중에 Framework의 레퍼런스로 찾는다고 했는데, &lt;b&gt;excutable binary&lt;/b&gt;가 없어졌으니.. 빌드는 가능하지만 실행을 시켜보면 런타임 중에 링크를 찾기 위해 Frameworks를 찾는데 없기 때문에 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;dyld: Library not loaded&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;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;를 사용하는 경우에는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Embeded &amp;amp; Sign&lt;/span&gt; 옵션을 선택해서 Framework의 별도 &lt;b&gt;excutable binary&lt;/b&gt; 파일이 포함될 수 있게 설정해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;⭐️ TIP &lt;i&gt;&lt;b&gt;⭐️&lt;/b&gt;&lt;/i&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;의 Linker가 Framework의 레퍼런스를 찾는 위치는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;@rpath(Runpath Search Path)&lt;/span&gt;로 프로젝트의 설정에서 확인가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.41.31.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b15HkA/btr7hQ4sh50/rP2EMUWGKsoemEEGrH4BVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b15HkA/btr7hQ4sh50/rP2EMUWGKsoemEEGrH4BVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b15HkA/btr7hQ4sh50/rP2EMUWGKsoemEEGrH4BVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb15HkA%2Fbtr7hQ4sh50%2FrP2EMUWGKsoemEEGrH4BVk%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;3680&quot; height=&quot;2382&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.41.31.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;2382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 프로젝트에서 사용하는 &lt;b&gt;shared library&lt;/b&gt;의 경우는 터미널의&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;$otool -L TestProject&lt;/span&gt; 명령어를 통해 확인할 수 있습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 7.03.44.png&quot; data-origin-width=&quot;2394&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdtDcW/btr7fmDmyFa/ykEiK0LgybHBKNthwUUBn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdtDcW/btr7fmDmyFa/ykEiK0LgybHBKNthwUUBn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdtDcW/btr7fmDmyFa/ykEiK0LgybHBKNthwUUBn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdtDcW%2Fbtr7fmDmyFa%2FykEiK0LgybHBKNthwUUBn1%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;2394&quot; height=&quot;754&quot; data-filename=&quot;스크린샷 2023-04-01 오후 7.03.44.png&quot; data-origin-width=&quot;2394&quot; data-origin-height=&quot;754&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;의 경우를 알아보겠습니다. &lt;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;의 경우는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Do Not Embed&lt;/span&gt;로 설정을 했었죠?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이 경우에는 앱의 &lt;b&gt;excutable binary&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;다시 &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&gt;로 설정했던 부분을 &lt;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;로 변경해주고 빌드를 해주세요. 그리고 이번에는 터미널에서 한번 &lt;b&gt;excutable binary&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;터미널 환경에서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;패키지 내용 보기&lt;/span&gt;의 위치까지 디렉토리를 우선 이동해주세요~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.52.13.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;1304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceI04l/btr7iNlVogH/L2a9ciue7KgZtFxRQLZqLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceI04l/btr7iNlVogH/L2a9ciue7KgZtFxRQLZqLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceI04l/btr7iNlVogH/L2a9ciue7KgZtFxRQLZqLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceI04l%2Fbtr7iNlVogH%2FL2a9ciue7KgZtFxRQLZqLK%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;3680&quot; height=&quot;1304&quot; data-filename=&quot;스크린샷 2023-04-01 오후 6.52.13.png&quot; data-origin-width=&quot;3680&quot; data-origin-height=&quot;1304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;$nm -debug-syms TestProject | grep &amp;ldquo;\\.o&amp;rdquo;&lt;/span&gt; 명령어를 입력해주세요. 이 명령어를 통해서 &lt;b&gt;excutable binary&lt;/b&gt; 파일에 포함되어 있는 symbol중 object 파일만을 확인할 수 있습니다.&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;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;에서는 해당 앱 프로젝트의 파일 외에도 Framework의 TestHelper 파일이 포함된 것을 확인할 수 있죠?!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-01 오후 9.38.48.png&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XNHDm/btr7fsiQBLS/Kg4oSkKoJstYuGeivbRaN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XNHDm/btr7fsiQBLS/Kg4oSkKoJstYuGeivbRaN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XNHDm/btr7fsiQBLS/Kg4oSkKoJstYuGeivbRaN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXNHDm%2Fbtr7fsiQBLS%2FKg4oSkKoJstYuGeivbRaN1%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;1958&quot; height=&quot;680&quot; data-filename=&quot;스크린샷 2023-04-01 오후 9.38.48.png&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;$nm&lt;/span&gt; 명령어를 사용하면 라이브러리, 컴파일된 오브젝트 모듈, 공유 오브젝트 파일, 실행파일등의 바이너리 파일을 검사해서 그 파일의 저장된 내용 및 메타 정보를 확인가능합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 반대로 &lt;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;의 Embed 방식을 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Embeded &amp;amp; Sign&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;해당 패키지 내용 안에 Framework의 &lt;b&gt;excutable binary&lt;/b&gt; 파일이 별도로 생성되지만 이미 앱의 &lt;b&gt;excutable binary&lt;/b&gt; 파일 안에 포함되어 있으므로 중복으로 포함되는거라 결국 필요없게 됩니다. 그렇기 때문에, &lt;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt;의 경우에는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Do Not Embed&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;i&gt;&lt;b&gt;Static Framework&lt;/b&gt;&lt;/i&gt; &amp;amp; &lt;i&gt;&lt;b&gt;Dynamic Framework&lt;/b&gt;&lt;/i&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;&lt;span style=&quot;color: #333333;&quot;&gt; 오늘 내용도 도움이 되었으면 좋겠네요 감사합니다  &lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>Dynamic Framework</category>
      <category>IOS</category>
      <category>iOS Dev</category>
      <category>Static Framework</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/82</guid>
      <comments>https://dongminyoon.tistory.com/82#entry82comment</comments>
      <pubDate>Sat, 1 Apr 2023 21:40:18 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] RunLoop란</title>
      <link>https://dongminyoon.tistory.com/81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 기능 개발중에 Timer를 받는 &lt;b&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;1&quot;&gt;RunLoop&lt;/span&gt;&lt;/b&gt;로 인해 이슈가 있었던 경험이 있어서 다시금 &lt;b&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;3&quot;&gt;RunLoop&lt;/span&gt;&lt;/b&gt;를 좀 더 자세히 알아보고자 블로그를 써보려고 합니다 &lt;span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;RunLoop란?&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RunLoop&lt;/b&gt;란 쓰레드와 관련되어있는 인프라의 일부인데, 입력 이벤트(키보드, 터치)들을 처리하기 위한 루프입니다. 그렇다면 &lt;b&gt;RunLoop&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;쓰레드가 필요할 때는 일을하게 하고 필요가 없을 때는 쉬게하기 위한 목적이라고 합니다! 아무래도 리소스를 효율적으로 사용하기 위한 기능(?)인 것 같습니다. 해당 &lt;b&gt;RunLoop&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;단, 주의할 점은 &lt;b&gt;RunLoop&lt;/b&gt;는 자동으로 관리되는 것이 아니고 우리가 개발할 때, 적절한 &lt;b&gt;RunLoop&lt;/b&gt; 내에서 쓰레드가 이벤트를 받을 수 있게 설계해야한다고 합니다   그리고 &lt;b&gt;RunLoop&lt;/b&gt;는 쓰레드와 관련이 있는 친구인 것 같은데, 각각의 쓰레드 내에서 &lt;b&gt;RunLoop&lt;/b&gt;를 가지고 있기 때문에 생성해 줄 필요는 없는 것 같습니다. (RunLoop.current를 이용해서 현재 쓰레드의 &lt;b&gt;RunLoop&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;뭐,, 자동은 아니니 관리도 해야하고 쓰레드에서 각각의 &lt;b&gt;RunLoop&lt;/b&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;span style=&quot;color: #006dd7;&quot;&gt;RunLoop 원리&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RunLoop&lt;/b&gt;는 크게 2가지의 이벤트를 받는다고 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Input Sources&lt;/b&gt; : 다른 쓰레드 또는 다른 Application으로부터 온 비동기 이벤트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;0&quot;&gt;Timer Sources&lt;/span&gt;&lt;/b&gt; : 예약된 시간 또는 일정 간격으로부터 발생해서 오는 동기 이벤트&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-01 오후 7.33.02.png&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbqZZe/btr1udjlOFs/7FmLmuy0nmSPgB6ybFrWtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbqZZe/btr1udjlOFs/7FmLmuy0nmSPgB6ybFrWtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbqZZe/btr1udjlOFs/7FmLmuy0nmSPgB6ybFrWtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbqZZe%2Fbtr1udjlOFs%2F7FmLmuy0nmSPgB6ybFrWtK%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;1010&quot; height=&quot;532&quot; data-filename=&quot;스크린샷 2023-03-01 오후 7.33.02.png&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RunLoop&lt;/b&gt;는 이름처럼 그림에서 노란색 루프동안 도착한 이벤트들(&lt;b&gt;Input Sources&lt;/b&gt;, &lt;b&gt;Timer Sources&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;lsquo;&lt;b&gt;RunLoop&lt;/b&gt;가 자동으로 관리되는 것이 아니고 우리가 개발할 때, 적절한 이벤트를 받아서 처리할 수 있게 설계해야한다고 했는데 그걸 어떻게 하는데&amp;rsquo;겠죠. 우선 그 방법은 다시 살펴보고 이 말 뜻을 먼저 이해해봅시다 !&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;RunLoop&lt;/b&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;RunLoop&lt;/b&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;span style=&quot;color: #006dd7;&quot;&gt;RunLoop 실행하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 혹시 globalQueue에서 Timer를 돌려보신 분 있으신가요.. 저는 소켓관련 기능을 개발하다 소켓이벤트만 받는 Queue에서 Timer를 돌려야하는 필요가 있어서 돌렸었는데 정상 동작하지 않았던 경험이 있습니다  &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;RunLoop&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;우선 애플 공식문서를 보면 &lt;b&gt;RunLoop&lt;/b&gt;를 실행시키기 위해 총 4가지의 메소드를 제공해주고 있는 것 같습니다. 이 중에서 자주 사용하는 것들만 우선 알아볼게요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-01 오후 7.34.18.png&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPl1al/btr1fOkIFu7/mT6kxlhS6J5tPwc8D9qTKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPl1al/btr1fOkIFu7/mT6kxlhS6J5tPwc8D9qTKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPl1al/btr1fOkIFu7/mT6kxlhS6J5tPwc8D9qTKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPl1al%2Fbtr1fOkIFu7%2FmT6kxlhS6J5tPwc8D9qTKk%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;1420&quot; height=&quot;564&quot; data-filename=&quot;스크린샷 2023-03-01 오후 7.34.18.png&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;run()&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;우선 해당 메소드를 실행시키게되면 영구적인 루프에 Receiver를 넣습니다. 그리고 들어오는 모든 Input Sources들을 처리합니다. 그러나 run()을 실행했을 때, 들어오는 이벤트가 없으면 그 즉시 종료됩니다.&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;/span&gt;그리고 주의할 점이 run()을 실행시키는 시점에 등록되어있는 Receiver들의 이벤트만 처리할 수 있습니다 !&lt;/p&gt;
&lt;pre id=&quot;code_1677666960745&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DisaptchQueue.global().async {
    Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
        print(&quot;TIK TOK&quot;)
    }
    
    let currentLoop = RunLoop.current
    currentLoop.run()
    // TIK TOK (1초마다 무한 반복)

    Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
        print(&quot;TIK TOK2&quot;)
    }
    // TIK TOK2는 Receiver로 등록되어있지 않아 수행 X
}&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;2. &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;run(until: Date)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RunLoop&lt;/b&gt;를 지정된 시간까지 실행하고 그 동안 들어오는 &lt;b&gt;Input Sources&lt;/b&gt;들을 처리합니다. 이전 메소드와는 다른 점이 시간을 지정해서 그 동안의 이벤트만 처리한다는 것 같네요 !&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메소드도 역시 실행할 당시 등록되어있는 Receiver가 없으면 즉시 종료됩니다. 그리고 그 시점에 등록되어있는 Receiver들의 이벤트만 처리가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1677667089151&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DispatchQueue.global().async {
    Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
        print(&quot;TIK TOK&quot;)
    }
    
    let currentLoop = RunLoop.current
    currentLoop.run(until: Date().addingTimeInterval(5.5))
    // TIK TOK 5번 반복 후, RunLoop 종료
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 기본적으로는 위와 같은 방법으로 사용이 가능한데요. 좀 다른 글을 보다보니 이런 방법으로 필요한 경우에 &lt;b&gt;RunLoop&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;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;isRunning&lt;/span&gt;&lt;/b&gt;이라는 변수를 만들어서 특정 필요한 시점까지 돌리고 false로 바꾸어서 &lt;b&gt;RunLoop&lt;/b&gt;를 종료시킬 수 있는 방법입니다. 앗 그리고 while에서 반복되어서 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;run(_:)&lt;/span&gt; &lt;/b&gt;메소드가 중복실행되는거 아닌가 걱정하실 수 있는데요. 0.5초뒤에 다음 구문으로 넘어가기 때문에 괜찮습니다  &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;다만, 해당 현재 코드는 sync하게 작동하고 있어서 while문 뒤의 구문은 실행되지 않습니다. 그리고 만약 globalQueue처럼 concurrent가 아닌 serial인 경우는 Queue 내에서 다음 작업이 실행되지 않을 수 있기 때문에, 주의해야되겠습니다. 그래서 저는 좋은 코드인지는 약간 의문이 드네요   (저는 실제 Socket Queue에서 Timer를 돌려야했을 때, 이런 방법은 불안전해보여서 Timer는 메인쓰레드에서 돌려주었습니다 ㅎㅎ..)&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;오늘은 RunLoop에 대해서 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하면서 대부분을 메인 쓰레드에서 실행하기 때문에, RunLoop를 직접 관리하는 일은 많지 않았는데요. 그래도 역시 알아두면 유용하고 필수인 지식같습니다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 부분이나 궁금한 내용있으면 댓글남겨주세요  &amp;zwj;♂️&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고문서&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple 공식 문서&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1677667390739&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;Run Loops&quot; data-og-description=&quot;Run Loops Run loops are part of the fundamental infrastructure associated with threads. A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread bus&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html&quot; data-og-url=&quot;https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dNEGuQ/hyRNLvD8cc/wp0aOIT5wsSOrPQGZb4We0/img.jpg?width=484&amp;amp;height=253&amp;amp;face=0_0_484_253,https://scrap.kakaocdn.net/dn/bHZ9Ka/hyRMoIQH3Y/LSknW5oom2E8Xel6rqiHzK/img.jpg?width=465&amp;amp;height=225&amp;amp;face=0_0_465_225&quot;&gt;&lt;a href=&quot;https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dNEGuQ/hyRNLvD8cc/wp0aOIT5wsSOrPQGZb4We0/img.jpg?width=484&amp;amp;height=253&amp;amp;face=0_0_484_253,https://scrap.kakaocdn.net/dn/bHZ9Ka/hyRMoIQH3Y/LSknW5oom2E8Xel6rqiHzK/img.jpg?width=465&amp;amp;height=225&amp;amp;face=0_0_465_225');&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;Run Loops&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Run Loops Run loops are part of the fundamental infrastructure associated with threads. A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread bus&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple 공식 문서&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1677667419922&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;RunLoop | Apple Developer Documentation&quot; data-og-description=&quot;The programmatic interface to objects that manage input sources.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/foundation/runloop&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/foundation/runloop&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/LVYQd/hyRMrFzxen/p7mskp8FJaIoGuMXKKwctK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/AK7ij/hyRNJYTbxr/0v2oAwJodKB5ztpxAeh0Fk/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundation/runloop&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/foundation/runloop&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/LVYQd/hyRMrFzxen/p7mskp8FJaIoGuMXKKwctK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/AK7ij/hyRNJYTbxr/0v2oAwJodKB5ztpxAeh0Fk/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512');&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;RunLoop | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The programmatic interface to objects that manage input sources.&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</category>
      <category>IOS</category>
      <category>iOS Dev</category>
      <category>RunLoop</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/81</guid>
      <comments>https://dongminyoon.tistory.com/81#entry81comment</comments>
      <pubDate>Wed, 1 Mar 2023 19:43:52 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Codable (2)</title>
      <link>https://dongminyoon.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;저번 &lt;a href=&quot;https://dongminyoon.tistory.com/77?category=419822&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Codable (1) &lt;/a&gt; 글에 이어서 이번에는 좀 더 심화된 사용법에 대해 알아보려고 합니다.&lt;/p&gt;
&lt;figure id=&quot;og_1662979023191&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] Codable (1)&quot; data-og-description=&quot;블로그에 너무 소홀했는데, 오랜만에 정신을 차리고 다시 포스팅을 진행해보려고 합니다   오늘은 그동안 스터디를 진행하면서 이 부분은 꼭 따로 공부해서 써봐야지 했던 부분인 Codable에 관&quot; data-og-host=&quot;dongminyoon.tistory.com&quot; data-og-source-url=&quot;https://dongminyoon.tistory.com/77?category=419822&quot; data-og-url=&quot;https://dongminyoon.tistory.com/77&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/U809V/hyPLfr8m3D/kKK2w3FeJxDKnheDE2NpZ1/img.png?width=800&amp;amp;height=449&amp;amp;face=0_0_800_449,https://scrap.kakaocdn.net/dn/87pC7/hyPLoJlTU3/0soUCRGBaen5tbaVr62ay0/img.png?width=800&amp;amp;height=449&amp;amp;face=0_0_800_449&quot;&gt;&lt;a href=&quot;https://dongminyoon.tistory.com/77?category=419822&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dongminyoon.tistory.com/77?category=419822&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/U809V/hyPLfr8m3D/kKK2w3FeJxDKnheDE2NpZ1/img.png?width=800&amp;amp;height=449&amp;amp;face=0_0_800_449,https://scrap.kakaocdn.net/dn/87pC7/hyPLoJlTU3/0soUCRGBaen5tbaVr62ay0/img.png?width=800&amp;amp;height=449&amp;amp;face=0_0_800_449');&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] Codable (1)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;블로그에 너무 소홀했는데, 오랜만에 정신을 차리고 다시 포스팅을 진행해보려고 합니다   오늘은 그동안 스터디를 진행하면서 이 부분은 꼭 따로 공부해서 써봐야지 했던 부분인 Codable에 관&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dongminyoon.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소에는 이렇게까지 사용할 수 없을 수 있는데, 분명 알아두면 언젠가는 꼭 쓸일이 있을 것 같아요  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Container란?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Container&lt;/b&gt;&lt;/i&gt;는 우리가 디코딩 &amp;amp; 인코딩을 하기 위한 Context라고 생각하면 될 것 같습니다. 각 기능을 위한 데이터의 구조(?) 정도로 저는 크게 이해했는데, 여기서 저희가 사용할 수 있게 크게 3가지 흐름으로 구분됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-12 오후 8.07.17.png&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chVJEt/btrLZzn1Lb8/zznBkVEgbsx6TnaaLVKtS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chVJEt/btrLZzn1Lb8/zznBkVEgbsx6TnaaLVKtS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chVJEt/btrLZzn1Lb8/zznBkVEgbsx6TnaaLVKtS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchVJEt%2FbtrLZzn1Lb8%2FzznBkVEgbsx6TnaaLVKtS0%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;1114&quot; height=&quot;440&quot; data-filename=&quot;스크린샷 2022-09-12 오후 8.07.17.png&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Keyed Container&lt;/b&gt; : CondingKey을 Key로 사용하는 Dictionary 같이 사용할 수 있는 Container&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Unkeyed Container &lt;/b&gt;: 여러 값들을 가지지만 Key가 없는 Container 대표적으로 Array가 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Single Value Container &lt;/b&gt;: 하나의 primitive type(Int, Bool, String)등의 single value를 위한 Container&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Nested Container&lt;/b&gt; : Keyed Container와 같이 내부에 Container를 한번 더 감싸는 Container&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 대표적으로 구성되고 Nested container는 똑같은 역할에서 하나 더 Depth가 깊어지기 때문에, 따로 다루지는 않았습니다. 구조는 이렇게 되는데 이제 이걸 어떻게 사용하는지 각각 예시로 알아보아야겠죠?&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;span style=&quot;color: #006dd7;&quot;&gt;Keyed Container&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 Keyed Container는 간단히 사용하는 법을 알아보았기 때문에, 이번에는 좀 더 특별한 상황에 사용할 수 있는 방법을 알아볼게요.&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;우리가 지금까지 디코딩을 진행했던 모델들은 Key : Value로 Key 값이 확실한 상황이었잖아요..? 만약 여기서 Key 값이 유동적으로 변한다면 어떻게 디코딩을 할 수 있을까요. 해당 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1663145881527&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct AnyCodingKey: CodingKey {
    var stringValue: String
    var intValue: Int?
    
    init?(intValue: Int) {
        self.stringValue = &quot;\(intValue)&quot;
        self.intValue = intValue
    }
    
    init?(stringValue: String) {
        self.stringValue = stringValue
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CodingKey를 채택하고 구현하는 Struct인 AnyCodingKey를 하나 선언해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 String, Int가 Key로 들어왔을 때, 해당 값을 Key로하는 CodingKey를 초기화할 수 있게 사용합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 이 Struct를 활용해서 바로 디코딩을 해보겠습니다&lt;/p&gt;
&lt;pre id=&quot;code_1663146227290&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct PracData: Codable {
    
    var values = [String: Any]()
    
    func encode(to encoder: Encoder) throws {
        let error = EncodingError.Context(codingPath: encoder.codingPath, debugDescription: &quot;Not Supported&quot;)
        throw EncodingError.invalidValue(encoder.codingPath, error)
    }
    
    init(from decoder: Decoder) throws {
        // 1
        let container = try decoder.container(keyedBy: AnyCodingKey.self)
        
        var values = [String: Any]()
        
        // 2
        container.allKeys.forEach { key in
            // 3
            if let intValue = try? container.decodeIfPresent(Int.self, forKey: key) {
                values[key.stringValue] = intValue
            } else if let stringValue = try? container.decodeIfPresent(String.self, forKey: key) {
                values[key.stringValue] = stringValue
            } else if let boolValue = try? container.decodeIfPresent(Bool.self, forKey: key) {
                values[key.stringValue] = boolValue
            }
        }
        self.values = values
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;보통 enum으로 Key들의 집합을 넘겨줬었는데, 현재는 해당 모델의 Key 값이 어떻게 내려올지 모르기 떄문에 이전에 구현했던 AnyCodingKey를 넘겨줍니다.&lt;/li&gt;
&lt;li&gt;Container의 모든 Key들을 iteration 합니다.&lt;/li&gt;
&lt;li&gt;Key에 어떤 타입이 들어있을 줄 모르니 필요한 타입들로 디코딩합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 CodingKey를 채택하는 AnyCodingKey를 하나 구현함으로서 어떤 Key 값이 있을줄 모르는 객체를 디코딩 할 수 있는데요.&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;지금은 예시를 보여주기 위해 Dictionary를 이용해서 받고있지만 사용할 때는 저희가 하나의 Struct를 따로 구현해서 프로퍼티로 디코딩한 값을 넣어주는 방법도 사용할 수 있습니다.&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;AnyCodingKey 구현체를 이용하면 자주는 아니지만 종종 유용하게 사용할 수 있을 것 같네요  &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;span style=&quot;color: #006dd7;&quot;&gt;Single Value Container&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 Primitive Type을 위한 Container라고 정의했었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Key : Value의 형태가 아닌 Primitive Type 하나만 들어왔을 때, 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1662983392562&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct PracData: Codable {
    
    let stringValue: String?
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        self.stringValue = try container.decode(String.self)
    }
    
}

let stringJSON = &quot;\&quot;ABC\&quot;&quot;
if let stringData = stringJSON.data(using: .utf8) {
    let decodedData = JSONDecoder().decode(PracData.self, from: stringData)
    print(decodedData.stringValue) // &quot;ABC&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Key가 없이 Primitive Type만 들어오는 경우 활용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 &lt;i&gt;&lt;b&gt;Unkeyed Container&lt;/b&gt;&lt;/i&gt;도 있었죠..?&amp;nbsp;비슷하다고 생각할 수도 있는데 조금 다릅니다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Unkeyed Container&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;보통 Key가 없는 Container로 활용되는데 대표적으로 Set, Array와 같은 타입들에 활용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1663050473401&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct PracData: Codable {
    
    var arrayValue = [Any]()
    
    func encode(to encoder: Encoder) throws {
        let error = EncodingError.Context(codingPath: encoder.codingPath, debugDescription: &quot;Not Supported&quot;)
        throw EncodingError.invalidValue(encoder.codingPath, error)
    }
    
    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        
        var values = [Any]()
        while container.isAtEnd == false {
            if let stringValue = try? container.decodeIfPresent(String.self) {
                values.append(stringValue)
            } else if let boolValue = try? container.decodeIfPresent(Bool.self) {
                values.append(boolValue)
            } else if let intValue = try? container.decodeIfPresent(Int.self) {
                values.append(intValue)
            }
        }
        self.arrayValue = values
    }
    
}

let arrayJSON = &quot;&quot;&quot;
[1, true, 20, 50]
&quot;&quot;&quot;

if let arrayData = arrayJSON.data(using: .utf8) {
    let decodedData = try? JSONDecoder().decode(PracData.self, from: arrayData)
    print(decodedData?.arrayValue)  // [1, true, 20, 50]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;b&gt;Single Value Container&lt;/b&gt;와 비슷할수도있지만 Key가 없고 Array, Set과 같은 타입으로 넘어왔을 때, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;isAtEnd&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;또한 count, currentIndex와 같은 프로퍼티들로 Container에 몇개의 요소가 있고 현재 디코딩이 필요한 부분은 어딘지도 확인이 가능합니다. 자세한건 &lt;a href=&quot;https://developer.apple.com/documentation/swift/unkeyeddecodingcontainer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;애플 문서&lt;/a&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;span style=&quot;color: #006dd7;&quot;&gt;Nested Container&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Container는 Keyed Container와 동일하지만 내부에 포함되어 있는 Container를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1663148007398&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct PracData: Codable {
    
    let name: String
    let latitude: String
    let longitude: String
    
    func encode(to encoder: Encoder) throws {
        let error = EncodingError.Context(codingPath: encoder.codingPath, debugDescription: &quot;Not Supported&quot;)
        throw EncodingError.invalidValue(encoder.codingPath, error)
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
    
        // 1
        let locationContainer = try container.nestedContainer(keyedBy: LocationKeys.self, forKey: .location)
         
        // 2
        self.latitude = try locationContainer.decode(String.self, forKey: .latitude)
        self.longitude = try locationContainer.decode(String.self, forKey: .longitude)
    }
    
    enum CodingKeys: String, CodingKey {
        case name
        case location
    }
    
    enum LocationKeys: String, CodingKey {
        case latitude = &quot;위도&quot;
        case longitude = &quot;경도&quot;
    }

}

let nestedJSON = &quot;&quot;&quot;
{
    &quot;name&quot;: &quot;DongDong&quot;,
    &quot;location&quot;: {
        &quot;위도&quot;: &quot;37.29782535558624&quot;,
        &quot;경도&quot;: &quot;127.06937480940317&quot;
    }
}
&quot;&quot;&quot;

if let nestedData = nestedJSON.data(using: .utf8) {
    let decodedData = try? JSONDecoder().decode(PracData.self, from: nestedData)
    print(decodedData)
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;.location으로 된 키 값의 Container를 새롭게 정의한 LocationKeys로 받아옵니다.&lt;/li&gt;
&lt;li&gt;해당 Container에서 각 값들을 디코딩해서 현재 정의된 객체의 프로퍼티로 넣어줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 또 Location을 위한 객체를 정의해서도 가능하겠지만 이렇게 Nested Container를 이용해서도 디코딩이 가능합니다.&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;오늘은 저번 포스팅에 이어 Codable 심화과정(?)에 대해 알아봤는데 잘 사용하면 유용할 것 같네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 잘못된 내용이나 궁금한 내용이 있으면 댓글달아주세요  &amp;zwj;♂️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>SWIFT</category>
      <category>Codable</category>
      <category>CodingKey</category>
      <category>IOS</category>
      <category>iOS Dev</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/80</guid>
      <comments>https://dongminyoon.tistory.com/80#entry80comment</comments>
      <pubDate>Wed, 14 Sep 2022 18:40:39 +0900</pubDate>
    </item>
    <item>
      <title>[Combine] Publisher</title>
      <link>https://dongminyoon.tistory.com/79</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 &lt;b&gt;Combine&lt;/b&gt;이 무엇인지 어떤 목적을 위해 만들어졋는지에 대해 간단히 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 그 중에서 &lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&gt;라는 것에 대해서 알아보려고 합니다  &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;&lt;span style=&quot;color: #006dd7;&quot;&gt;Publisher란?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 맛보기 글에서는 &lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&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;시간의 흐름에 따라 값을 방출한다는데 이 말이 잘 이해가 안되면 아래 그림을 보면 조금 이해가 편할 것 같습니다. (RxSwift를 공부하신 분이라면 조금 익숙한 그림이죠??  )&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-05-08 오후 4.50.29.png&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RE8p2/btrBuzGHC4i/TJN3drsey31MAqFUX4zoQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RE8p2/btrBuzGHC4i/TJN3drsey31MAqFUX4zoQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RE8p2/btrBuzGHC4i/TJN3drsey31MAqFUX4zoQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRE8p2%2FbtrBuzGHC4i%2FTJN3drsey31MAqFUX4zoQ1%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;651&quot; height=&quot;130&quot; data-filename=&quot;스크린샷 2022-05-08 오후 4.50.29.png&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&gt;에서 방출한 값은 하나 또는 그 이상의 &lt;b&gt;Subscriber&lt;/b&gt;에게 전달된다고 하는데요. 그러면 이제 이 &lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&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초에 화면에 &amp;ldquo;Hi&amp;rdquo;가 그려지고 2초에 &amp;ldquo;I&amp;rsquo;m&amp;ldquo;이 그려지고 하는 형식이됩니다.&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;span style=&quot;color: #006dd7;&quot;&gt;Publisher 구현체&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&gt;가 시간에 따른 값을 방출하는 주체다. 그렇다면 Swift에서는 &lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&gt;를 어떻게 구현하고 있을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 기본적으로 프로토콜로 구현되고 extension을 이용해 수평확장하는 구조를 가지고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1651996289566&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protocol Publisher {
    associatedtype Output
    associatedtype Failure : Error

    func receive&amp;lt;S&amp;gt;(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Output&lt;/b&gt; : 기본적으로 &lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;Publisher&lt;/span&gt;가 값을 방출하는 주체이기 때문에, 어떤 타입의 값을 방출할지를 associatedtype으로 선언한 것입니다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;Failure&lt;/span&gt;&lt;/b&gt; : &lt;span style=&quot;color: #333333;&quot;&gt;Publisher가 예상치 못한 에러를 방출해야하는 경우 내보내는 타입입니다. 만약, Publisher가 에러를 방출하지 않는 경우 Never로 사용해줍니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;b&gt;receive(subscriber: Subscriber)&lt;/b&gt; : 이&lt;span style=&quot;color: #333333;&quot;&gt; 부분은 다음 포스팅에서 알아보겠지만, Publisher의 subscriber(subscriber: Subscriber) 메소드가 불렸을 때, 구독자를 받았다고 알리는 메소드입니다. 꼭 구현되어야 하는 메소드입니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&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. &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;PassthroughSubject&lt;/span&gt;&lt;/b&gt; : &lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;Subject&lt;/span&gt;의 subclass인 해당 객체를 이용해서 on-demand 방식으로 값을 발행할 수 있는 객체를 이용&lt;/p&gt;
&lt;pre id=&quot;code_1651996440886&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let subject = PassthroughSubject&amp;lt;Int, Never&amp;gt;()
subject
    .sink(receiveValue: { value in
        print(&quot;Value : \(value)&quot;)
    })
    .store(in: &amp;amp;self.cancellables)
        
subject.send(10)

// Value : 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;CurrentValueSubject&lt;/b&gt;&lt;/span&gt; : 새로운 값이 할당될 때마다 방출하는 객체를 이용하는 방법입니다. 역시 Subject의 subclass로 다만 현재 할당된 값에 접근이 가능합니다 ㅎㅎ..&lt;/p&gt;
&lt;pre id=&quot;code_1651996522298&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let subject = CurrentValueSubject&amp;lt;Int, Never&amp;gt;(10)
subject
    .sink(receiveValue: { value in
        print(&quot;Value : \(value)&quot;)
    })
    .store(in: &amp;amp;self.cancellables)

print(&quot;Current Value : \(subject.value)&quot;)
subject.send(15)
print(&quot;Current Value : \(subject.value)&quot;)
// Current Value : 10
// Value : 15
// Current Value : 15&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;@Published&lt;/b&gt;&lt;/span&gt; : propertyWrapper로 구현된 해당 annotation을 이용해 값이 업데이트 될 때마다 방출할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1651996566551&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TextManager {
    @Published var text: String = &quot;Initial&quot;

    init() {
        self.$text
            .sink(receiveValue: { text in
                print(&quot;Text : \(text)&quot;)
            })
            .store(in: &amp;amp;self.cancellables)
        
        text = &quot;Hi&quot;
    }
}

// Text : Initial
// Text : Hi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 개발자가 직접 Publisher를 구현하는 것이 아닌 애플에서 권장하고 있는 방법을 3가지 알아보았습니다. &lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&gt;를 사용할 일이 있으면 이렇게 3가지를 이용하는 것이 좋을 것 같네요 ㅎㅎ..&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;Combine Framework&lt;/b&gt;에서 제공해주는 &lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&gt; 구현체들이 있습니다. 더 알아보도록할게요~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Just&lt;/span&gt;&lt;/b&gt; : 딱 한 번의 Output을 방출해주는 구현체입니다. 한 번의 방출 후에 Completion이 들어오게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1651996622190&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let just = Just&amp;lt;Int&amp;gt;(10)
just
    .sink(receiveCompletion: { completion in
        print(&quot;Completion : \(completion)&quot;)
    }, receiveValue: { value in
        print(&quot;Value : \(value)&quot;)
    })
    .store(in: &amp;amp;self.cancellables)

// Value : 10
// Completion : finished&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;Future&lt;/b&gt;&lt;/span&gt; :&amp;nbsp; 역시 단 한번의 Output을 방출해주는 구현체입니다. 다만, 비동기에 대한 값을 방출할 때 유용한 구현체입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1651996672662&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let future = Future&amp;lt;Int, Never&amp;gt; { promise in
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        promise(.success(10))
    }
}

future
    .sink(receiveCompletion: { completion in
        print(&quot;Completion : \(completion)&quot;)
    }, receiveValue: { value in
        print(&quot;Value : \(value)&quot;)
    })
    .store(in: &amp;amp;self.cancellables)

// Value : 10
// Completion : finished&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Deffered : 새로운 Subscriber가 등록될 때마다 새로운 Publisher를 생성해 구독되어지는 구현체입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1651996693802&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let deffered = Deffered { Just(Void()) } 
deffered
    .sink(receiveValue: { print(&quot;Deffered&quot;) }
    .store(in: &amp;amp;self.cancellables)

// Deffered&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;Empty&lt;/b&gt;&lt;/span&gt; : 아무런 값도 방출하지 않으며, 종료되는 구현체이다. (개인적으로 언제 사용할 수 있을지 의문이네요 &lt;span&gt; &lt;/span&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1651996722289&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let empty = Empty&amp;lt;Void, Never&amp;gt;(completeImmediately: true)
empty.sink(receiveCompletion: { completion in
        print(&quot;Completion : \(completion)&quot;)
    }, receiveValue: { value in 
        print(&quot;Value : \(value)&quot;)
    })
    .store(in: &amp;amp;self.cancellables)

// Completion : finished

let empty2 = Empty&amp;lt;Void, Never&amp;gt;(completeImmediately: false)
empty2.sink(receiveCompletion: { completion in
        print(&quot;Completion : \(completion)&quot;)
    }, receiveValue: { value in 
        print(&quot;Value : \(value)&quot;)
    })
    .store(in: &amp;amp;self.cancellables)

// ... Nothing&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;Fail&lt;/b&gt;&lt;/span&gt; : 즉시 Error와 함께 종료되는 구현체입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1651996738392&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let fail = Fail&amp;lt;Void, Error&amp;gt;(error: NSError(domain: &quot;Error&quot;, code: -1))
fail.sink(receiveCompletion: { completion in
    print(&quot;Completion : \(completion)&quot;)
}, receiveValue: { value in
    print(&quot;Value : \(value)&quot;)
})
.store(in: &amp;amp;self.cancellables)

// Completion : failure(error....)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;Record&lt;/b&gt;&lt;/span&gt; : 기록된 Input, Completion을 나중에 &lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;Subscriber&lt;/span&gt;가 새롭게 등록되었을 때, 반복할 수 있는 구현체입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1651996760852&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let record = Record&amp;lt;Int, Never&amp;gt; { recoding in
    print(&quot;@Start Recording&quot;)
    recoding.receive(1)
    recoding.receive(2)
    recoding.receive(completion: .finished)
}

record.sink {
    print($0)
} receiveValue: {
    print($0)
}.store(in: &amp;amp;self.cancellables)
// @Start Recording
// 1
// 2
// finished

record.sink {
    print($0)
} receiveValue: {
    print($0)
}.store(in: &amp;amp;self.cancellables)
// 1
// 2
// fini&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;오늘은 이렇게 &lt;b&gt;Combine Framework&lt;/b&gt;의 한 요소인 &lt;i&gt;&lt;b&gt;Publisher&lt;/b&gt;&lt;/i&gt;에 대해 알아보았습니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 Subscriber과 나머지 요소들에 대해서도 알아보도록할게요&lt;/p&gt;</description>
      <category>Combine</category>
      <category>combine</category>
      <category>Combine Framework</category>
      <category>Combine Publisher</category>
      <category>IOS</category>
      <category>iOS Dev</category>
      <category>publisher</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/79</guid>
      <comments>https://dongminyoon.tistory.com/79#entry79comment</comments>
      <pubDate>Sun, 8 May 2022 17:01:05 +0900</pubDate>
    </item>
    <item>
      <title>[Combine] Combine 맛보기</title>
      <link>https://dongminyoon.tistory.com/78</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;너무 늦은 감이 없지 않아 있지만, Combine Framework에 대해서 공부를 해보려고 합니다..⭐️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 RxSwift를 많이 쓰니까 하면서 위안했던 자신이 부끄..ㄹ&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;span style=&quot;color: #006dd7;&quot;&gt;Combine이란?&lt;/span&gt;&lt;/h4&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-05-07 오후 8.39.25.png&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mVyTM/btrBrru1C4O/3uuXhknLFsC8J78bVvI5kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mVyTM/btrBrru1C4O/3uuXhknLFsC8J78bVvI5kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mVyTM/btrBrru1C4O/3uuXhknLFsC8J78bVvI5kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmVyTM%2FbtrBrru1C4O%2F3uuXhknLFsC8J78bVvI5kk%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;1482&quot; height=&quot;314&quot; data-filename=&quot;스크린샷 2022-05-07 오후 8.39.25.png&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;뒤에 설명되어 있는 부분을 조금 더 읽어보면, 선언형 Swift API로서 &lt;b&gt;Publisher&lt;/b&gt;를 이용해 값을 방출하고 &lt;b&gt;Subscriber&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;사실 &lt;b&gt;Combine&lt;/b&gt;이 뭐다 설명했지만 &lt;b&gt;RxSwift&lt;/b&gt;랑 비슷한 목적으로 애플에서 '우리가 제공해줄게 이거 사용해~~!' 같은 느낌이에요 ㅎㅎ..&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Combine 3 요소&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 앞의 'Combine이란'에서 &lt;b&gt;Publisher&lt;/b&gt;, &lt;b&gt;Subscriber&lt;/b&gt; 두 가지가 먼저 나왔죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Combine&lt;/b&gt;에서 가장 중요한 3요소에 대해서 먼저 어떤 것이 있는지 간단히 오늘은 알아보도록할게요&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Publisher &lt;/b&gt;: 시간에 따른 값의 흐름을 방출하는 주체입니다 뭔가 RxSwift Observable과 비슷한 친구인 것 같아요 ㅎㅎ..&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Operator &lt;/b&gt;: Publisher에서 방출한 값에 따라 이를 연산해서 뭔가 변형시키거나 요리해줄 수 있는 친구입니다. map, filter 같은 것들이 있는데 뭔가 RxSwift의 Operator와 비슷하죠? ㅎㅎ...&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Subscriber &lt;/b&gt;: Publisher로부터 방출되는 값을 받아서 뭔가를 처리하는 주체입니다. 바뀐 값에 따라 View에 반영한다거나 특정 로직을 수행한다거나 할 것 같죠???&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 3가지를 알아봤는데, 모식도로 보면 다음과 같은 관계를 가지고 있어요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-05-07 오후 8.54.09.png&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJCHCi/btrBqDbHiA0/faSzjPrg6HOp9qbojVrUek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJCHCi/btrBqDbHiA0/faSzjPrg6HOp9qbojVrUek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJCHCi/btrBqDbHiA0/faSzjPrg6HOp9qbojVrUek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJCHCi%2FbtrBqDbHiA0%2FfaSzjPrg6HOp9qbojVrUek%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;695&quot; height=&quot;175&quot; data-filename=&quot;스크린샷 2022-05-07 오후 8.54.09.png&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RxSwift에서도 &lt;b&gt;Observable&lt;/b&gt;에서 이벤트를 방출하면 &lt;b&gt;Operator&lt;/b&gt;를 이용해 중간에 이벤트들을 가공한 후, &lt;b&gt;Subscriber&lt;/b&gt;에서 구독해서 비동기 이벤트에 대해 처리하는 방식이었죠?? &lt;i&gt;&lt;b&gt;Combine&lt;/b&gt;&lt;/i&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;Observable&lt;/b&gt;의 역할을 &lt;b&gt;Publisher&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;i&gt;&lt;b&gt;Combine&lt;/b&gt;&lt;/i&gt;이 어떤 목적으로 제공되고 있고 내부에 어떤 요소들이 있는지만 찍먹만 해봤습니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 이 글에서는 'Combine이 이런 목적이구나', '어떤 요소를 가지고 있네?' 정도만 머리속에 남겨도 괜찮을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음, 다다음(?) 글에서는 Publisher, Subscriber, Operator들이 각각 무엇이고 어떻게 구성되는지를 공부하면서 좀 더 자세히 포스팅해보겠습니다  &lt;/p&gt;</description>
      <category>Combine</category>
      <category>IOS</category>
      <category>iOS Dev</category>
      <category>Swift Combine</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/78</guid>
      <comments>https://dongminyoon.tistory.com/78#entry78comment</comments>
      <pubDate>Sat, 7 May 2022 21:00:35 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Codable (1)</title>
      <link>https://dongminyoon.tistory.com/77</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;블로그에 너무 소홀했는데, 오랜만에 정신을 차리고 다시 포스팅을 진행해보려고 합니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 그동안 스터디를 진행하면서 이 부분은 꼭 따로 공부해서 써봐야지 했던 부분인 &lt;i&gt;&lt;b&gt;Codable&lt;/b&gt;&lt;/i&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;i&gt;&lt;b&gt;JSON &lt;/b&gt;&lt;/i&gt;모델을 요리 조리 볶을 때, &lt;i&gt;&lt;b&gt;Codable&lt;/b&gt;&lt;/i&gt;을 채택해서 편하게 Decoding, Encoding을 하고 있는데 여기서 &lt;i&gt;&lt;b&gt;Codable&lt;/b&gt;&lt;/i&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;서론&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;우선 &lt;i&gt;&lt;b&gt;Codable&lt;/b&gt;&lt;/i&gt;을 알아보기 전에 &lt;i&gt;Serailization&lt;/i&gt;, &lt;i&gt;Deserailization&lt;/i&gt;이라는 용어에 대해서 간단히 알아볼게요. 우선 &lt;i&gt;&lt;b&gt;Codable&lt;/b&gt;&lt;/i&gt;을 사용하는 이유라고 할 수 있는데요. Swift의 데이터 타입을 외부에서 사용할 수 있는 데이터(external representation) 타입으로 변환하기 위해서 사용하는데, 이런 기술을&amp;nbsp;&lt;i&gt;Serailization&lt;/i&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;그렇다면 반대로 외부에서 사용하는 데이터(external representation)를 Swift의 데이터 타입으로 변환하는 기술은 &lt;i&gt;&lt;span data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;Deserailization&lt;/span&gt;&lt;/i&gt;이겠죠? 저도 사용해보지는 않았지만 이전에는 &lt;i&gt;&lt;b&gt;NSCoding&lt;/b&gt;&lt;/i&gt;을 이용해서 이 &lt;i&gt;Serailization&lt;/i&gt; 기술을 사용하고 있었는데, &lt;i&gt;&lt;b&gt;NSCoding&lt;/b&gt;&lt;/i&gt;에서는 너무 큰 추상화를 제공해서 많은 이슈들이 존재했던 것 같고 이를 보완하기 위해 &lt;i&gt;&lt;b&gt;Codable&lt;/b&gt;&lt;/i&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Codable&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;우선 &lt;i&gt;&lt;b&gt;Codable&lt;/b&gt;&lt;/i&gt;은 프로토콜로서 아래의 합성어입니다. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Decodable &amp;amp; Encodable&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;즉, 채택한 모델은 Decoding, Encoding이 모두 가능하다는 뜻입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650791628569&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typealias Codable = Decodable &amp;amp; Encodable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;Decodable&lt;/span&gt; or &lt;span style=&quot;background-color: #dddddd;&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;Encodable&lt;/span&gt;은 Encoding, Decoding을 위한 추상화를 제공하는데, 이를 채택함으로서 &lt;span style=&quot;background-color: #dddddd;&quot; data-token-index=&quot;4&quot; data-reactroot=&quot;&quot;&gt;JSONEncoder&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd;&quot; data-token-index=&quot;6&quot; data-reactroot=&quot;&quot;&gt;JSONDecoder&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;우리는 간단하게 사용하지만 내부적으로는 해당 프로토콜을 통해 방법을 제공하고 있기 때문에, 이를 이용하면 더 다양한 형식으로 Encoding, Decoding이 가능하게 됩니다 &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;/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;i&gt;&lt;b&gt;가당 간단한 케이스&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650791738603&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Person: Codable {
    let name: String
    let twitter: String
    let github: URL
    let birthday: Date
}

// Decoding
let decoder = JSONDecoder()
let person = try? decoder.decode(Person.self, from: jsonData)

// Encoding
let encoder = JSONEncoder()
let jsonData = try? encoder.encode(jsonData)&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;요렇게 사용함으로 간단하게 Decoding, Encoding이 가능해집니다.&lt;br /&gt;Decoding, Encoding을 하는 방법은 &lt;i&gt;&lt;b&gt;Codable&lt;/b&gt;&lt;/i&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;i&gt;&lt;b&gt;Key Decoding Strategies&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650791855945&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[
    {
        &quot;name&quot;: &quot;Dongmin&quot;,
        &quot;age&quot;: 20,
        &quot;github_link&quot;: &quot;https://github.com/dongminyoon&quot; 
    }, ...
]
// JSON

struct Person: Codable {
    let name: String
    let age: Int
    let githubLink: String
}

do {
    let decoder = JSONDecoder()
    let person = try decoder.decode([Person].self, from: jsonData)
} catch let error {
    print(error)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이런 JSON이 있고 Decoding을 하고 싶을 때, 위의 코드와 같이 실행하면 성공할까요..??&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;우선 name, age 키 값에서는 큰 문제는 보이지 않는데 나머지 github_link의 키 값이 JSON과 상이하죠 그렇기 때문에, 다음과 같은 에러가 떨어집니다  &lt;/p&gt;
&lt;pre id=&quot;code_1650791949131&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;keyNotFound(CodingKeys(stringValue: &quot;githubLink&quot;, intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: &quot;Index 0&quot;, intValue: 0)], debugDescription: &quot;No value associated with key CodingKeys(stringValue: \&quot;githublink\&quot;, intValue: nil) (\&quot;githublink\&quot;).&quot;, underlyingError: nil))&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;예로 지금 같은 경우는 서버에서는 키 값을 &lt;b&gt;snake case&lt;/b&gt;를 활용하고 있고 클라이언트에서는 &lt;b&gt;snake case&lt;/b&gt;를 활용하지 않고 받고싶은 경우라고 볼 수 있겠죠? 이 같은 경우는 다음과 같이 간단하게 &lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;decodingStarategy&lt;/span&gt;를 지정하는 것으로 사용이 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650792084429&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;do {
    let decoder = JSONDecoder() 
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let person = try decoder.decode([Person].self, from: jsonData)
} catch let error {
    print(error)
}&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;&lt;i&gt;&lt;b&gt;Data Decoding Strategies&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650792114441&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;name&quot;: &quot;Clean Code&quot;,
    &quot;imageData&quot;: &quot;iVBORw0KGgoAAAANSUhEUgAAAfAAAAHGC...&quot;  // base64 String
}
// JSON

struct Book: Codable {
    let name: String
    let imageData: Data

    var image: UIImage? { UIImage(data: self.imageData) } 
}

do {
    let decoder = JSONDecoder()
    decoder.dataDecodingStrategy = .base64
    let book = try decoder.decode(Book.self, from: jsonString)
} catch let error {
    print(error)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 예로 base64로 Encoding된 String을 받는 경우 사용할 수 있는 방법입니다.&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;String을 받는 경우 별도로 String을 받아서 &lt;span style=&quot;color: #333333; background-color: #dddddd;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;Data(base64Encoded: base64String)&lt;/span&gt;와 같은 과정이 필요할 수 있지만 &lt;span style=&quot;color: #333333; background-color: #dddddd;&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;dataDecodingStrategy&lt;/span&gt;를 이용하면 이와 같은 과정을 생략할 수 있습니다 &lt;span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아!! 그리고 원래 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;JSONDecoder&lt;/span&gt;의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;dataDecodingStrategy&lt;/span&gt;의 기본 값이 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.base64&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;&lt;i&gt;&lt;b&gt;&lt;span&gt;Custom Key Decoding Strategies&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;/i&gt;&lt;span&gt;이번에는 Swift Standard Library에 정의되어 있지 않은 경우의 커스텀한 Key Decoding을 하는 경우입니다.&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&gt;우선 상황을 가정하면 위에서 사용했던 &lt;b&gt;snake case&lt;/b&gt; 이외에 '-'로 구분을하는 &lt;b&gt;kebab case&lt;/b&gt;로 내려오면 어떻게 해야할까요 &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;어쩔수없이 받기 위해서는 통일을 하던가 그게 싫다면 별도로 무언가를 구현해야겠죠?!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 때 사용할 수 있는 것이 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;decodingStrategy = .custom&lt;/span&gt; 설정입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650792781806&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;level-One&quot;: &quot;Mushroom&quot;,
    &quot;level-Two&quot;: &quot;Ant&quot;
}
// JSON

struct Level: Codable {
    let levelOne: String
    let levelTwo: String
}&lt;/code&gt;&lt;/pre&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;자 우선 .custom 정의를 볼게요 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;case custom((_ codingPath: [CodingKey]) -&amp;gt; CodingKey)&lt;/span&gt; 연관 값으로 클로저를 받고 있죠. 아마 저게 custom 한 정의를 위해 사용하는 클로저이고 내부에서 불리면서 키들을 매칭하는 것 같아요. 이제 사용을 해볼게요.&lt;/p&gt;
&lt;pre id=&quot;code_1650792854051&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct AnyCodingkey: CodingKey {
    let stringValue: String
    let intValue: Int?
    
    init?(stringValue: String) {
        self.stringValue = stringValue
        self.intValue = nil
    }
    
    init?(intValue: Int) {
        self.intValue = intValue
        self.stringValue = &quot;\(intValue)&quot;
    }
}

do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .custom { keys in
        let codingKey = keys.last!                        // 1
        let key = codingKey.stringValue                   // 2
        
        guard key.contains(&quot;-&quot;) else {return codingKey }  // 3
        
        let words = key.components(separatedBy: &quot;-&quot;)      // 4
        let camelCased = words[0] + words[1...].map(\.capitalized).joined()
        return AnyCodingkey(stringValue: camelCased)!     // 5
    }
    let someData = try decoder.decode(SomeData.self, from: someJSON)
    print(someData)
} catch let error {
    print(error)
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;우선 custom의 정의처럼 &lt;span style=&quot;background-color: #dddddd; color: #333333;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;[CodingKey]&lt;/span&gt;가 파라메터로 들어오는데 이 값은 codingPath로 Decoding에서 이 값을 받아오는데 사용되는 Key의 경로라고 합니다. 가장 마지막 값만 JSON Key 값이 들어오는데, 그래서 last 값만 사용해요.&lt;/li&gt;
&lt;li&gt;Key 값의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;stringValue&lt;/span&gt;를 가져와야겠죠 (ex. level-One, level-Two)&lt;/li&gt;
&lt;li&gt;kebab case인 경우는 '-'를 포함하기 때문에, 포함하지 않는 경우 그대로 리턴&lt;/li&gt;
&lt;li&gt;'-'을 기준으로 나누어줍니다&lt;/li&gt;
&lt;li&gt;마지막으로 리턴값이 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;CodingKey&lt;/span&gt; 타입이잖아요. 이를 위해 지금 만들었던 키 값을 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;stringValue&lt;/span&gt;로 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;AnyCodingKey&lt;/span&gt;라는 인스턴스를 리턴해줍니다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 사용하면 조금 다른 키 값들도 커스텀하게 Decoding이 가능해집니다. 반복적으로 이런 키값들이 들어오고 커스텀해야한다면 static으로 정의해서 사용하면 의미있을 것 같네요 &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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;CodingKeys&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;i&gt;&lt;b&gt;CodingKey&lt;/b&gt;&lt;/i&gt;에 대해 알아보려고 합니다. 그렇다면 &lt;i&gt;&lt;b&gt;CodingKey&lt;/b&gt;&lt;/i&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;Swift에 구현된 것을 보면 프로토콜의 형태를 가지고 있고 사용할 때에는 특정 키에 대해 커스텀하게 표현할 수 있는 키로 매칭이 가능했습니다. 특정 속성의 키가 어떻게 표현되는지를 설명하기 위해 도와주는 프로토콜로 이해하면 될 것 같네요?&lt;/p&gt;
&lt;pre id=&quot;code_1650793124041&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protocol CodingKey {
    var stringValue: String { get }
    var intValue: Int? { get }
    
    init(stringValue: String)
    init?(intValue: Int)  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이전에 Decoding을 진행할 때에도 JSON의 값과 완벽하게 키 값이 일치했다면 별도의 과정이 필요없지만 일치하지 않는 경우에 사용했었잖아요? &lt;i&gt;&lt;b&gt;CodingKey&lt;/b&gt;&lt;/i&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;pre id=&quot;code_1650793199637&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[
    {
        &quot;name&quot;: &quot;Dongmin&quot;,
        &quot;age&quot;: 20,
        &quot;github_link&quot;: &quot;https://github.com/dongminyoon&quot; 
    }, ...
]
// JSON

struct Person: Codable {
    let name: String
    let age: Int
    let githubLink: String

    enum CodingKeys: String, CodingKey {
        case name, age
        case githubLink = &quot;github_link'
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;keyDecodingStrategy&lt;/span&gt;을 지정해서 &lt;b&gt;snake case&lt;/b&gt;인 키 값을 매칭했었죠? &lt;i&gt;&lt;b&gt;CodingKey&lt;/b&gt;&lt;/i&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;또 만약 JSON으로부터 값을 받는데, JSON 값에는 있을 수도 있고 없을 수도 있는데 기본값으로 받고싶은 경우는 다음과 같이 CodingKey를 이용해 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1650793413561&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension Person {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
    
        self.name = try values.decode(String.self, forKey: .name)
        self.age = try values.decode(Int.self, forKey: .age)
        
        // GithubLink가 JSON에서 없지만 Default로 써야하는 경우
        self.githubLink = try values.decodeIfPresent(String.self, forKey: .githubLink) ?? &quot;https://github.com&quot;
    }
}&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;우선 오늘은 Codable에 대해 조금(?) 심화된 사용법들을 알아보았는데요.&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;오늘 내용이 도움이 되었으면 좋겠네요~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 잘못된 점이나 궁금한 점이 있으면 말씀해주세요&lt;/p&gt;</description>
      <category>SWIFT</category>
      <category>Codable</category>
      <category>IOS</category>
      <category>iOS Dev</category>
      <category>SWIFT</category>
      <category>Swift Codable</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/77</guid>
      <comments>https://dongminyoon.tistory.com/77#entry77comment</comments>
      <pubDate>Sun, 24 Apr 2022 18:45:36 +0900</pubDate>
    </item>
    <item>
      <title>[SWIFT] ArraySlice</title>
      <link>https://dongminyoon.tistory.com/76</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 개발을 하면서 &lt;b&gt;Swift&lt;/b&gt;에서 &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;를 나누게 되면 생길 수 있는 &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&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;span style=&quot;color: #006dd7;&quot;&gt;ArraySlice&lt;br /&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;를 다루다보면 볼 수 있는 타입인데, 사실 뭔가 미묘했다. 분명 &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;인데...(?) 왜 굳이 나눠서 따로 있는거고 그냥 &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;를 사용하면 되지 않는가 의문이 생겼다. 특히, 보통 사용할 때, &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-02-25 오후 7.24.24.png&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lHupz/btruh6SRcSF/QG7lpVTZfO9cLFNPz3L3KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lHupz/btruh6SRcSF/QG7lpVTZfO9cLFNPz3L3KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lHupz/btruh6SRcSF/QG7lpVTZfO9cLFNPz3L3KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlHupz%2Fbtruh6SRcSF%2FQG7lpVTZfO9cLFNPz3L3KK%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;655&quot; height=&quot;114&quot; data-filename=&quot;스크린샷 2022-02-25 오후 7.24.24.png&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;, &lt;i&gt;&lt;b&gt;ContiguousArray&lt;/b&gt;&lt;/i&gt;, &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;의 Slice라고 설명되어 있습니다. 즉, &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;를 잘라도 &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;이고 &lt;i&gt;&lt;b&gt;ContiguousArray&lt;/b&gt;&lt;/i&gt;, &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;를 모두 잘라도 &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;라는 것입니다. 그리고 &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;에서도 &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&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 data-ke-size=&quot;size16&quot;&gt;공식문서를 보면 설명이 나오고 있는데, ArraySlice를 사용하면 큰 배열에서 작업을 더 효율적으로 할 수 있고 내의 Element들을 새롭게 복사해서 저장하는 것이 아닌(copy on write) 기존 배열에 대한 보기를 제공한다고 하고 있어요.&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;기존에 배열은 copy on write 방식으로 새롭게 이동 및 생성하기 위해서 메모리를 그만큼 할당하고 옮기는 작업이 있어야 하는데, &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&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;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;는 배열을 나누어서 작업하는데 기존의 &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt; 처럼 copy on write 방식이 아닌 레퍼런싱만을 제공해서 활용할 수 있다는 것과 &lt;i&gt;&lt;b&gt;Arrray&lt;/b&gt;&lt;/i&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;pre id=&quot;code_1645785314890&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let absences = [0, 2, 0, 4, 0, 3, 1, 0]

let midpoint = absences.count / 2

let firstHalf = absences[..&amp;lt;midpoint]     // ArraySlice
let secondHalf = absences[midpoint...]    // ArraySlice&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 결석자들이 있고 반반으로 나누어서 더 많은 결석자가 있는 부분을 알고 싶을 때, 다음과 같이 작성이 가능해집니다..!!&lt;/p&gt;
&lt;pre id=&quot;code_1645785353378&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let firstHalfSum = firstHalf.reduce(0, +)
let secondHalfSum = secondHalf.reduce(0, +)

if firstHalfSum &amp;gt; secondHalfSum {
    print(&quot;More absences in the first half.&quot;)
} else {
    print(&quot;More absences in the second half.&quot;)
}
// Prints &quot;More absences in the first half.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오.. &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;에서 기존의 &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;와 같은 &lt;i&gt;&lt;b&gt;reduce&lt;/b&gt;&lt;/i&gt; 인터페이스에 접근이 가능하고 사용이 되죠?&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;다만, 주의해야할 점이 있는 것 같습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;는 기존 &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;에 대한 레퍼런싱을 제공한다고 했죠?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에, ARC로 동작하는 Swift에서 메모리 릭이 발생할 수 있는 구멍이 되는 것 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;장기적인 ArraySlice 인스턴스 보관은 권장하지 않습니다. ArraySlice는 기존 Array의 저장장소를 레퍼런싱하기 때문에 Array의 생명주기가 끝나고 나서도 ArraySlice가 소멸되지 않으면 메모리 릭이 발생할 수 있습니다.&lt;/blockquote&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;span style=&quot;color: #006dd7;&quot;&gt;Slices Maintain Indices&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;를 사용하면서 주의(?)보다는 유의해야하는 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 Index에 관한 부분입니다. 공식문서에 의하면 &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;의 시작 인덱스는 &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;와 다르게 항상 0이 아니라고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;는 기존에 잘라지기 전의 &lt;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;와 동일한 index를 유지하고 있다고 합니다. 즉, &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&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;말로만 해서는 잘 감이 오지 않으니 예시로 알아볼게요&lt;/p&gt;
&lt;pre id=&quot;code_1645785955578&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let absences = [0, 2, 0, 4, 0, 3, 1, 0]

if let i = absences.firstIndex(where: { $0 &amp;gt; 0 }) {                 // 1
    let absencesAfterFirst = absences[(i + 1)...]                   // 2
    if let j = absencesAfterFirst.firstIndex(where: { $0 &amp;gt; 0 }) {   // 3
        print(&quot;The first day with absences had \(absences[i]).&quot;)    // 4
        print(&quot;The second day with absences had \(absences[j]).&quot;)
    }
}
// Prints &quot;The first day with absences had 2.&quot;
// Prints &quot;The second day with absences had 4.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 첫번째 조건에서 index가 1이 나왔습니다. 그렇다면 다음 2부터 [0, 4, 0, 3, 1, 0]의 &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;가 만들어지고 여기서 0보다 큰 first Index를 찾게 되면 두번째에 있어서 j = 1이 되어야 하는데, 3이 출력되죠..?!&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;i&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/i&gt;의 index가 유지되는구나.. &amp;zwj; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;를 사용할 때, 뭔가 index를 이용해서 사용해야하는 경우는 유의해야겠죠?!&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: #dddddd;&quot;&gt;startIndex&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;endIndex&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;i&gt;&lt;b&gt;ArraySlice&lt;/b&gt;&lt;/i&gt;에 대해서 간단히 알아보았습니다&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;</description>
      <category>SWIFT</category>
      <category>ArraySlice</category>
      <category>IOS</category>
      <category>iOS Dev</category>
      <category>SWIFT</category>
      <category>Swift ArraySlice</category>
      <author>윤동민</author>
      <guid isPermaLink="true">https://dongminyoon.tistory.com/76</guid>
      <comments>https://dongminyoon.tistory.com/76#entry76comment</comments>
      <pubDate>Fri, 25 Feb 2022 19:51:35 +0900</pubDate>
    </item>
  </channel>
</rss>