<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>샘성의 iOS 개발 일지</title>
    <link>https://iossammy.tistory.com/</link>
    <description>안 되면, 되게 한다 ..!</description>
    <language>ko</language>
    <pubDate>Tue, 11 Aug 2026 01:45:46 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>SamusesApple</managingEditor>
    <image>
      <title>샘성의 iOS 개발 일지</title>
      <url>https://tistory1.daumcdn.net/tistory/5729526/attach/f5ef82c4bd354614a101ebffb2a80956</url>
      <link>https://iossammy.tistory.com</link>
    </image>
    <item>
      <title>[Web] CSS Flexbox와 Grid를 배워보자!</title>
      <link>https://iossammy.tistory.com/entry/Web-CSS-Flexbox%EC%99%80-Grid%EB%A5%BC-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;header&gt;
&lt;h1&gt;CSS 의 꽃, Flexbox와 Grid 부시기&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;CSS 레이아웃 기술인 Flexbox와 Grid를 배울 수 있는 재미있고 유익한 리소스를 소개합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/header&gt;&lt;main&gt;
&lt;section&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  재미있는 CSS 학습 게임&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. Flexbox Froggy:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://flexboxfroggy.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;게임 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;개구리에게 CSS 코드를 사용하여 연꽃잎에 도달하도록 도와주는 게임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2. Grid Garden:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://cssgridgarden.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;게임 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CSS Grid 레이아웃을 활용하여 당근밭을 가꾸는 게임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;3. Flexbox Adventure
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://codingfantasy.com/games/flexboxadventure&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;게임 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;모험을 통해 Flexbox 개념을 배우는 게임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;4. CSS Dinner
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://flukeout.github.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;게임 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CSS 선택자에 대한 지식을 테스트하는 재미있는 게임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;5. Flexbox Defense
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://www.flexboxdefense.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;게임 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CSS Flexbox를 사용하여 적의 침입을 막는 디펜스 게임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;6. Knights of the Flexbox Table
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://knightsoftheflexboxtable.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;게임 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Flexbox 개념을 활용하여 문제를 해결하는 게임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;section&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  CSS 학습 리소스&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;7. Codecademy - Learn CSS
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.codecademy.com/learn/learn-css&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;코스 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CSS를 체계적으로 학습할 수 있는 온라인 코스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;8. CSS Reference
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://cssreference.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;사이트 바로가기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CSS 속성에 대한 자세한 설명과 예제를 제공하는 참고 사이트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/main&gt;</description>
      <category>Programming</category>
      <category>CSS</category>
      <category>CSS게임</category>
      <category>css학습</category>
      <category>flexbox</category>
      <category>Grid</category>
      <category>레이아웃</category>
      <category>웹개발</category>
      <category>웹디자인</category>
      <category>프론트엔드</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/100</guid>
      <comments>https://iossammy.tistory.com/entry/Web-CSS-Flexbox%EC%99%80-Grid%EB%A5%BC-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#entry100comment</comments>
      <pubDate>Fri, 6 Dec 2024 17:34:19 +0900</pubDate>
    </item>
    <item>
      <title>[Five Lines of Code] 2. 리펙터링 깊게 들여다보기</title>
      <link>https://iossammy.tistory.com/entry/Five-Lines-of-Code-2-%EB%A6%AC%ED%8E%99%ED%84%B0%EB%A7%81-%EA%B9%8A%EA%B2%8C-%EB%93%A4%EC%97%AC%EB%8B%A4%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 가독성 및 유지보수성 향상&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의도 전달로 가독성 향상&lt;/li&gt;
&lt;li&gt;불변속성의 범위 제한으로 유지보수성 향상&lt;/li&gt;
&lt;li&gt;범위 밖의 코드에 영향 안 주고 위의 2항목 실행&lt;/li&gt;
&lt;li&gt;가독성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(의도한대로 코드가 잘 동작한다면)의도 전달하기 위한 코드의 상징&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;유지보수성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시작: 현존하는 일부 기능을 변경 할 때마다 &amp;lsquo;새 코드의 위치 선정을 위한 조사하기&amp;lsquo;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&amp;rsquo;전역상태&amp;lsquo;로 인해 예상치 못한 사이드 이펙트(취약성)이 발생할 수 있다&lt;/li&gt;
&lt;li&gt;전역상태 찾는 트릭
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;중괄호 찾기 (중괄호 내부의 변수에 외부 데이터가 할당되는 경우가 많음)&lt;/li&gt;
&lt;li&gt;전역적으로 사용되는 외부 데이터 관찰 및 불변속성 제거하기&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;코드의 작업을 변경하지 않은 채로 유지보수 하는 법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리펙터링 중에는 코드가 느려져도 신경X (보통 가독성과 유지보수성보다 성능의 가치가 낮다)&lt;/li&gt;
&lt;li&gt;성능이 중요한 경우, 리펙토링 다 끝나고 프로파일링 도구 및 성능 전문가의 도움 받아 다른 단계에서 하자&lt;/li&gt;
&lt;li&gt;리펙터링 할 코드를 &amp;lsquo;예약&amp;rsquo;하자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병합 충돌 이슈 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 속도, 유연성 및 안정성 확보&lt;/h2&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속 보다는 Composition을 사용하자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;깔끔한 코드 결합과 재사용이 가능해짐 &amp;rarr; 유연성!&lt;/li&gt;
&lt;li&gt;예시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수에 클래스 객체 생성해서 할당하기&lt;/li&gt;
&lt;li&gt;프로토콜의 컴포지션 활용하기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수정 대신 &amp;rsquo;추가에 의한 변경&amp;lsquo;을!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포지션을 사용함으로써 &amp;lsquo;추가&amp;rsquo;가 용이해짐!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 기능에 영향 주지 않고 새로운 기능 추가 혹은 변경 (개방 폐쇄 원칙)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프로그래밍 속도 상승
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기존 기능의 코드를 두고 새로운 코드를 추가&lt;/li&gt;
&lt;li&gt;코드 베이스 늘어남&lt;/li&gt;
&lt;li&gt;더이상 사용하지 않는 코드가 무엇인지 잘 살펴보고 빠르게 제거하기&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;안정성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 코드가 실패한 경우, 기존 코드로 대체하기 용이&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 리펙터링과 일상 업무&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  &quot;항상 여러분이 왔을 때보다 더 좋게 만들어 놓고 떠나세요&quot;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레거시 시스템에서는 변경하기 전에 리펙터링을 하자&lt;/li&gt;
&lt;li&gt;코드 변경 후에도 리펙터링 하기&lt;/li&gt;
&lt;li&gt;코드 전달하기 전에 리팩터링을 수행하자&lt;/li&gt;
&lt;/ul&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;gt; 리팩터링을 연습하면 코드에 대한 독특한 관점을 얻을 수 있으며, 이로 인해 더 나은 해결책을 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>five lines of cdde</category>
      <category>리팩터링</category>
      <category>클린코드</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/99</guid>
      <comments>https://iossammy.tistory.com/entry/Five-Lines-of-Code-2-%EB%A6%AC%ED%8E%99%ED%84%B0%EB%A7%81-%EA%B9%8A%EA%B2%8C-%EB%93%A4%EC%97%AC%EB%8B%A4%EB%B3%B4%EA%B8%B0#entry99comment</comments>
      <pubDate>Mon, 29 Jul 2024 11:34:02 +0900</pubDate>
    </item>
    <item>
      <title>[Five Lines of Code] 1. 리팩터링 리팩터링하기</title>
      <link>https://iossammy.tistory.com/entry/Five-Lines-of-Code-1-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81%ED%95%98%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 리팩터링이란?&lt;/b&gt;&lt;/h2&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분의 프로그래머는 코드를 치는 것보다 코드를 이해하는데에 더 많은 시간을 소비한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드를 더 빠르게 동작하게 하기&lt;/li&gt;
&lt;li&gt;더 작은 코드를 만들기&lt;/li&gt;
&lt;li&gt;더 일반적이거나 재사용 가능한 코드 만들기&lt;/li&gt;
&lt;li&gt;코드의 가독성 상승, 유지보수 용이
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가독성을 높여서 시간을 절약하자 (생각하기 편하게, 새로운 기능 추가하기 쉽게)&lt;/li&gt;
&lt;li&gt;유지보수가 용이해지면 버그가 줄고 수정이 용이해진다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;테스트 주도 개발을 하면 효과적으로 할 수 있다 (하지만 여기선 다루지 않음)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 무엇을?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;  A. 냄새 나는 코드! (aka 코드 스멜)&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;/li&gt;
&lt;li&gt;5줄이 넘는 긴 함수&lt;/li&gt;
&lt;li&gt;중복되는 함수&lt;/li&gt;
&lt;li&gt;여러개의 기능을 가진 클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 언제?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;  코딩 할 때마다! (매일 샤워 하듯..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;탐색&lt;/li&gt;
&lt;li&gt;명세화&lt;/li&gt;
&lt;li&gt;구현&lt;/li&gt;
&lt;li&gt;테스트&lt;/li&gt;
&lt;li&gt;리펙터링&lt;/li&gt;
&lt;li&gt;전달&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의 순서로 진행된다.&lt;/p&gt;</description>
      <category>Programming</category>
      <category>five lines of code</category>
      <category>리팩터링</category>
      <category>클린코드</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/98</guid>
      <comments>https://iossammy.tistory.com/entry/Five-Lines-of-Code-1-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81%ED%95%98%EA%B8%B0#entry98comment</comments>
      <pubDate>Mon, 29 Jul 2024 11:06:07 +0900</pubDate>
    </item>
    <item>
      <title>[인앱결제] StoreKit2로 마이그레이션 하기 (+ 유실 방지 로직)</title>
      <link>https://iossammy.tistory.com/entry/%EC%9D%B8%EC%95%B1%EA%B2%B0%EC%A0%9C-StoreKit2%EB%A1%9C-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%ED%95%98%EA%B8%B0-%EC%9C%A0%EC%8B%A4-%EB%B0%A9%EC%A7%80-%EB%A1%9C%EC%A7%81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;StoreKit1 -&amp;gt; StoreKit2로 마이그레이션 하기 + 결제 정보를 서버 DB에서 안전하게 유지하도록 유실 방지 로직 추가하기&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;StoreKit 에서 StoreKit2로 변경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StoreKit은 Apple에서 제공하는 인앱 결제 라이브러리이다. 기존 StoreKit에서는 영수증 검증을 위해 &lt;code&gt;transactionId&lt;/code&gt; endpoint를 사용했지만 deprecated되면서 영수증 검증을 자동으로 해주는 StoreKit2로 변경하게 되었다....&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;StoreKit2로 마이그레이션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StoreKit에서 StoreKit2로 변경하면서 기본적인 결제를 처리하는 코드&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;public func purchaseSubscription(_ productId: String) async throws -&amp;gt; Transaction? {
    // 제품 정보 가져오기
    guard let product = try await fetchProductById(productId) else {
        throw PurchaseError.productNotFound
    }
    // 구매 프로세스 시작
    let result = try await product.purchase()

    switch result {
    case .success(let verification):
        // 결제 검증
        let transaction = try await checkTransaction(verification)
        // 서버에 결제 정보 전송
        await sendTransactionToServer(transaction)

        return transaction

    case .userCancelled, .pending:
        // 유저가 결제를 취소하거나 결제가 진행 중인 경우
        return nil

    @unknown default:
        print(&quot;Unexpected Error&quot;)
        return nil
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 설명&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;제품 정보 가져오기:여기서는 &lt;code&gt;productId&lt;/code&gt;를 기반으로 제품 정보를 가져오기! 해당되는 제품 정보를 가져오지 못하면 &lt;code&gt;PurchaseError.productNotFound&lt;/code&gt; 에러를 던진다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-swift&quot;&gt; guard let product = try await fetchProductById(productId) else {
     throw PurchaseError.productNotFound
 }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;구매 프로세스 시작:&lt;code&gt;product.purchase()&lt;/code&gt; 메서드를 사용하여 구매 프로세스를 시작하기. 이 메서드는 async 메서드로, 비동기로 앱스토어 구매를 진행한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-swift&quot;&gt; let result = try await product.purchase()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;결제 검증:&lt;code&gt;purchase()&lt;/code&gt; 메서드가 성공적으로 끝나면, 결제 검증하기. &lt;code&gt;checkTransaction&lt;/code&gt; 메서드는 &lt;code&gt;VerificationResult&lt;/code&gt;를 매개변수로 받아서 검증된 &lt;code&gt;Transaction&lt;/code&gt; 객체를 반환한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-swift&quot;&gt; let transaction = try await checkTransaction(verification)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;서버에 결제 정보 전송: 마지막으로 결제된 정보를 서버로 전송한다. 이를 통해 서버에서도 결제 정보를 업데이트할 수 있다!&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-swift&quot;&gt; await sendTransactionToServer(transaction)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;유실 방지 로직 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결제 정보가 서버에 제대로 반영되지 않으면 앱스토어 결제 자체는 성공했지만, 서버에는 기록되지 않는 경우가 발생할 수 있다. 이를 방지하기 위해 유실 방지 로직을 추가하자!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유실 방지 로직 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 결제 정보를 안전하게 전송하는 로직&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;public func restoreSubscriptionPurchaseDataToServer() async {
    guard await shouldCheckIfPurchaseIsMissing() else {
        print(&quot;Need not restore purchase data&quot;)
        return
    }
    guard let lastTransaction = try await fetchLastTransaction(), !lastTransaction.isExpired else {
        print(&quot;No valid transaction found&quot;)
        return
    }
    await sendTransactionToServer(lastTransaction)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 설명&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;결제 유실 여부 확인:결제가 유실되었는지 확인하기. 유실되지 않았다면 로직 종료.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-swift&quot;&gt; guard await shouldCheckIfPurchaseIsMissing() else {
     print(&quot;Need not restore purchase data&quot;)
     return
 }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;마지막 트랜잭션 가져오기:마지막 트랜잭션을 가져와 유효한 결제 정보인지 확인하기. 만약 유효하지 않다면 로직을 종료.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-swift&quot;&gt; guard let lastTransaction = try await fetchLastTransaction(), !lastTransaction.isExpired else {
     print(&quot;No valid transaction found&quot;)
     return
 }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;서버로 결제 정보 전송: 마지막 트랜잭션 정보를 서버로 전송하여 결제 정보 업데이트.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-swift&quot;&gt; await sendTransactionToServer(lastTransaction)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서버상 트랜잭션 정보 DB 유효성 검사&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;private func shouldCheckIfPurchaseIsMissing() async -&amp;gt; Bool {
    // 유저가 로그인했는지 확인
    guard let userId = fetchUserId() else { return false }

    // 유저의 결제 정보 확인 필요 여부
    let shouldCheck = UserDefaults.standard.bool(forKey: &quot;SHOULD_CHECK_PURCHASE&quot;)
    return shouldCheck
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저가 로그인했는지 확인하고, 결제 정보 확인이 필요한지 UserDefaults에서 값을 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, true값이 온다면 다시 서버에 유저의 마지막 결제 정보를 보내는 POST 요청을 보내면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/Swift</category>
      <category>iAP</category>
      <category>IOS</category>
      <category>storekit2</category>
      <category>SWIFT</category>
      <category>유실방지</category>
      <category>인앱결제</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/97</guid>
      <comments>https://iossammy.tistory.com/entry/%EC%9D%B8%EC%95%B1%EA%B2%B0%EC%A0%9C-StoreKit2%EB%A1%9C-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%ED%95%98%EA%B8%B0-%EC%9C%A0%EC%8B%A4-%EB%B0%A9%EC%A7%80-%EB%A1%9C%EC%A7%81#entry97comment</comments>
      <pubDate>Tue, 23 Jul 2024 17:13:03 +0900</pubDate>
    </item>
    <item>
      <title>[Tuist] 모듈 생성 자동화 하기 (tuist scaffold, .stencil)</title>
      <link>https://iossammy.tistory.com/entry/Tuist-%EB%AA%A8%EB%93%88-%EC%83%9D%EC%84%B1-%EC%9E%90%EB%8F%99%ED%99%94-%ED%95%98%EA%B8%B0-tuist-scaffold-stencil</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;목적&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;원하는 모듈을 생성할 때마다 필요한 파일을 하나하나 세팅해주는 번거로움 해결하기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;rarr; 모듈에 필요한 코드를 템플릿 파일에 미리 작성해두고 tuist scaffold 명령어를 실행하면 자동으로 해당되는 모듈에 대한 파일 및 코드가 작성 됨.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;준비물&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Tuist 폴더 안에 Templates 폴더 생성&lt;/li&gt;
&lt;li&gt;Templates 폴더의 하위 폴더로 '원하는 모듈 이름' 폴더 생성하기&lt;/li&gt;
&lt;li&gt;'원하는 모듈 이름' 폴더 안에 '원하는모듈이름.swift' 파일 직접 생성하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;파일 구조&lt;/h3&gt;
&lt;pre id=&quot;code_1718680016781&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
  └── Tuist
      └── Templates
          └── Domain
              └── Domain.swift       # Domain 모듈에 대한 템플릿 전체 설정
              └── Package.stencil    # Domain 모듈에 대한 패키지 템플릿 파일
              └── Project.stencil    # Domain 모듈에 대한 기본 프로젝트 템플릿 파일
              └── InfoPlist.stencil  # Domain 모듈에 대한 InfoPlist 템플릿 파일
              └── Test.stencil       # Domain 모듈의 테스트 파일에 대한 템플릿 파일&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 style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vx2D9/btsH1E89Z9f/XC3YW7ANrCueZCKgKZf1N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vx2D9/btsH1E89Z9f/XC3YW7ANrCueZCKgKZf1N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vx2D9/btsH1E89Z9f/XC3YW7ANrCueZCKgKZf1N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvx2D9%2FbtsH1E89Z9f%2FXC3YW7ANrCueZCKgKZf1N0%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;358&quot; height=&quot;211&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style7&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Domain.swift 파일&lt;/h3&gt;
&lt;pre class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;import ProjectDescription

// scaffold 명령어 시 받을 인자 정의
let name: Template.Attribute = .required(&quot;name&quot;) // 하위모듈명 (파일명)
let author: Template.Attribute = .required(&quot;author&quot;)  // 생성자
let currentDate: Template.Attribute = .required(&quot;currentDate&quot;) // 생성 날짜

// 템플릿 선언
let domainTemplate = Template(
    description: &quot;A template for a new Domain modules&quot;,
    attributes: [
        name,
        author,
        currentDate
    ],
    items: DomainTemplate.allCases.map { $0.item }
)

enum DomainTemplate: CaseIterable {
    case package
    case project
    case infoPlist
    case test

    // 템플릿 내부에 추가할 파일
    var item: Template.Item {
        switch self {
        case .package:
            return .file(path: .basePath + &quot;/Package.swift&quot;,
                        templatePath: &quot;Package.stencil&quot;)
        case .project:
            return .file(path: .basePath + &quot;/Sources/Project.swift&quot;,
                         templatePath: &quot;Project.stencil&quot;)
        case .infoPlist:
            return .file(path: .basePath + &quot;/\(name)-Info.plist&quot;, 
                        templatePath: &quot;InfoPlist.stencil&quot;)
        case .test:
            return .file(path: .basePath + &quot;/Tests/&quot; + .testName + &quot;/&quot; + .testName + &quot;.swift&quot;,
                         templatePath: &quot;Test.stencil&quot;)
        }
    }
}

// 명령어 실행시 Domain 모듈 파일이 생성될 기본 경로.
extension String {
    static var basePath: Self {
        return &quot;Modules/Domain/\(name)&quot;
    }

    static var testName: Self {
        return &quot;\(name)Tests&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Package.stencil 파일&lt;/h3&gt;
&lt;pre id=&quot;code_1718681098550&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// swift-tools-version: 5.10

import PackageDescription

let package = Package(
    name: &quot;{{ name }}&quot;,
    platforms: [
        .iOS(.v17) // 타겟 버전
    ],
    products: [
        .library(
            name: &quot;{{ name }}&quot;,
            targets: [&quot;{{ name }}&quot;])
    ],
    dependencies: [
        // 필요한 의존성 추가
    ],
    targets: [
        .target(
            name: &quot;{{ name }}&quot;,
            dependencies: []
        )
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Project.stencil 파일&lt;/h3&gt;
&lt;pre id=&quot;code_1718681193779&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 빈 프로젝트 파일로 세팅

import Foundation

class {{ name }} { }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;InfoPlist.stencil 파일&lt;/h3&gt;
&lt;pre id=&quot;code_1718681350267&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;
&amp;lt;plist version=&quot;1.0&quot;&amp;gt;
&amp;lt;dict&amp;gt;
    &amp;lt;key&amp;gt;CFBundleDevelopmentRegion&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;en&amp;lt;/string&amp;gt; // region 설정
    &amp;lt;key&amp;gt;CFBundleExecutable&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;{{ name }}&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundleIdentifier&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;앱번들.{{ name }}&amp;lt;/string&amp;gt; // 번들 식별자 정의
    &amp;lt;key&amp;gt;CFBundleInfoDictionaryVersion&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;6.0&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundleName&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;{{ name }}&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundlePackageType&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;FMWK&amp;lt;/string&amp;gt;  // 패키지 타입 프레임워크로 세팅
    &amp;lt;key&amp;gt;CFBundleShortVersionString&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;1.0&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundleVersion&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;1&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundleSupportedPlatforms&amp;lt;/key&amp;gt;
    &amp;lt;array&amp;gt;
        &amp;lt;string&amp;gt;iPhoneOS&amp;lt;/string&amp;gt; // 타겟 플랫폼 설정
    &amp;lt;/array&amp;gt;
    &amp;lt;key&amp;gt;MinimumOSVersion&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;17.0&amp;lt;/string&amp;gt;  // 최소 타겟 버전
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Test.stencil 파일&lt;/h3&gt;
&lt;pre id=&quot;code_1718681247886&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Test 코드를 위한 파일 템플릿

import XCTest
@testable import {{ name }}

class {{ name }}Tests: XCTestCase {
    func testExample() {
        // Example test case
    }
}&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;템플릿으로 Domain 모듈 만들기&lt;/h3&gt;
&lt;pre id=&quot;code_1718686317704&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tuist scaffold Domain --name DomainSample --author Sam --current-date 2024/06/18&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;61&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7ULtc/btsH2WnGtFR/wBqzXK4w0egCwF7BXoqFBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7ULtc/btsH2WnGtFR/wBqzXK4w0egCwF7BXoqFBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7ULtc/btsH2WnGtFR/wBqzXK4w0egCwF7BXoqFBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7ULtc%2FbtsH2WnGtFR%2FwBqzXK4w0egCwF7BXoqFBk%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;642&quot; height=&quot;61&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;61&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0rL6W/btsH4eOr3By/Ncw3y25oEidQKIWhZhIOf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0rL6W/btsH4eOr3By/Ncw3y25oEidQKIWhZhIOf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0rL6W/btsH4eOr3By/Ncw3y25oEidQKIWhZhIOf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0rL6W%2FbtsH4eOr3By%2FNcw3y25oEidQKIWhZhIOf0%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;450&quot; height=&quot;165&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고로 Domain.swift 파일에서 'name', 'author', 'currentDate'를 필수 인자로 설정해두었기 때문에 셋 중 하나라도 빠지면 아래와 비슷한 오류가 발생하며 모듈 생성이 불가능하다.&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;blob&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BD8KJ/btsH1Lm8x1I/eGdX3n5t8LegKUm3o454kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BD8KJ/btsH1Lm8x1I/eGdX3n5t8LegKUm3o454kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BD8KJ/btsH1Lm8x1I/eGdX3n5t8LegKUm3o454kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBD8KJ%2FbtsH1Lm8x1I%2FeGdX3n5t8LegKUm3o454kk%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;567&quot; height=&quot;124&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style7&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;App의 Project.swift 파일에 모듈 추가하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 앱의 모듈 구성 및 의존성을 관리하는 Project.swift 파일에 새로 생성한 모듈 패키지 및 의존성을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1718687082597&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 앱의 Project.swift 파일

let project = Project(
    name: &quot;MyApp&quot;,
    packages: [
        .local(path: .relativeToRoot(&quot;Modules/Domain/DomainSample&quot;)) // 생성한 모듈 패키지 추가
    ],
    targets: [
        .target(
            name: &quot;MyApp&quot;,
            destinations: .iOS,
            product: .app,
            bundleId: &quot;io.tuist.myApp&quot;,
            deploymentTargets: .iOS(&quot;17.0&quot;),
            infoPlist: .extendingDefault(with: infoPlist),
            sources: [&quot;App/Sources/**&quot;],
            resources: [&quot;App/Resources/**&quot;],
            dependencies: [
            	.package(product: &quot;DomainSample&quot;) // 앱에 모듈 의존성 추가
            ],
        ),
        .target(
            name: &quot;MyAppTests&quot;,
            destinations: .iOS,
            product: .unitTests,
            bundleId: &quot;io.tuist.myAppTests&quot;,
            infoPlist: .default,
            sources: [&quot;App/Tests/**&quot;],
            resources: [],
            dependencies: [.target(name: &quot;MyApp&quot;)]
        ),
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. tuist generate 명령어를 통해 프로젝트를 재구성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1718687714383&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tuist generate&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;243&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjP1Au/btsH4hxNZ8V/NKDhn1KP7NEHkxI7EYZjKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjP1Au/btsH4hxNZ8V/NKDhn1KP7NEHkxI7EYZjKk/img.png&quot; data-alt=&quot;생성된 DomainSample 패키지 추가됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjP1Au/btsH4hxNZ8V/NKDhn1KP7NEHkxI7EYZjKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjP1Au%2FbtsH4hxNZ8V%2FNKDhn1KP7NEHkxI7EYZjKk%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;243&quot; height=&quot;165&quot; data-origin-width=&quot;243&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성된 DomainSample 패키지 추가됨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpQDMi/btsH3RTLgRV/T3WuyalXBsmOfFKkw3ISK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpQDMi/btsH3RTLgRV/T3WuyalXBsmOfFKkw3ISK0/img.png&quot; data-alt=&quot;앱에 Embeded 됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpQDMi/btsH3RTLgRV/T3WuyalXBsmOfFKkw3ISK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpQDMi%2FbtsH3RTLgRV%2FT3WuyalXBsmOfFKkw3ISK0%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;898&quot; height=&quot;646&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;앱에 Embeded 됨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/Swift</category>
      <category>IOS</category>
      <category>modular</category>
      <category>Modular Architecture</category>
      <category>SWIFT</category>
      <category>tuist</category>
      <category>모듈화</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/95</guid>
      <comments>https://iossammy.tistory.com/entry/Tuist-%EB%AA%A8%EB%93%88-%EC%83%9D%EC%84%B1-%EC%9E%90%EB%8F%99%ED%99%94-%ED%95%98%EA%B8%B0-tuist-scaffold-stencil#entry95comment</comments>
      <pubDate>Tue, 18 Jun 2024 15:50:23 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] 추상 팩토리 패턴</title>
      <link>https://iossammy.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;서로 연관되거나 의존적인 객체들의 조합&lt;/b&gt;을 만드는 인터페이스를 제공하는&lt;b&gt; 생성자 패턴&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;팩토리 메서드 패턴과의 차이점?&lt;/h3&gt;
&lt;figure id=&quot;og_1718678672114&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;[디자인 패턴] 팩토리 메서드 패턴&quot; data-og-description=&quot;객체 생성을 캡슐화 하여 &amp;lsquo;팩토리 클래스&amp;rsquo;를 통해 생성하도록 하는 생성 디자인 패턴&amp;nbsp;구조CreatorProduct 객체를 생성하는 메서드 정의 (protocol / class)Concrete CreatorConcrete Product를 생성하기 위한 구&quot; data-og-host=&quot;iossammy.tistory.com&quot; data-og-source-url=&quot;https://iossammy.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%8C%A8%ED%84%B4&quot; data-og-url=&quot;https://iossammy.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%8C%A8%ED%84%B4&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/4Nfxl/hyWoIucbOF/OD4s0wJ2jI1sinvTZojlX1/img.png?width=735&amp;amp;height=322&amp;amp;face=0_0_735_322,https://scrap.kakaocdn.net/dn/DhVBZ/hyWoPUo5xP/kPPRY006tHphDBxzeUVXw0/img.png?width=735&amp;amp;height=322&amp;amp;face=0_0_735_322,https://scrap.kakaocdn.net/dn/n0y7x/hyWoEk1QL7/MbaotyeAkrYEOVITehEQGK/img.jpg?width=1125&amp;amp;height=1142&amp;amp;face=405_496_692_808&quot;&gt;&lt;a href=&quot;https://iossammy.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%8C%A8%ED%84%B4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://iossammy.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%8C%A8%ED%84%B4&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/4Nfxl/hyWoIucbOF/OD4s0wJ2jI1sinvTZojlX1/img.png?width=735&amp;amp;height=322&amp;amp;face=0_0_735_322,https://scrap.kakaocdn.net/dn/DhVBZ/hyWoPUo5xP/kPPRY006tHphDBxzeUVXw0/img.png?width=735&amp;amp;height=322&amp;amp;face=0_0_735_322,https://scrap.kakaocdn.net/dn/n0y7x/hyWoEk1QL7/MbaotyeAkrYEOVITehEQGK/img.jpg?width=1125&amp;amp;height=1142&amp;amp;face=405_496_692_808');&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;[디자인 패턴] 팩토리 메서드 패턴&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;객체 생성을 캡슐화 하여 &amp;lsquo;팩토리 클래스&amp;rsquo;를 통해 생성하도록 하는 생성 디자인 패턴&amp;nbsp;구조CreatorProduct 객체를 생성하는 메서드 정의 (protocol / class)Concrete CreatorConcrete Product를 생성하기 위한 구&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;iossammy.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;팩토리 매서드&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 팩토리 - 1 객체 생성&lt;/li&gt;
&lt;li&gt;구체적 객체 생성 과정을 하위 클래스에 구현하도록 하는 것이 point&lt;/li&gt;
&lt;li&gt;&amp;rarr; 메서드 레벨에서의 구체화된 인스턴스의 생성 및 구성에 대한 의존성 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;추상 팩토리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 팩토리 - 연관된 여러 종류의 객체 생성&lt;/li&gt;
&lt;li&gt;연관된 여러 종류 객체의 묶음을 구체적인 클래스에 의존하지 않고 생성 가능하도록 하는 것이 point
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 레벨에서의 구체화된 객체의 생성 및 구성에 대한 의존성 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Swift 예제 코드&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;/*
 [배경]
    이전 View에서 **애플 키보드 마우스 세트** or **윈도우 키보드 마우스 세트**를 유저가 선택 할 수 있는 상황
    이전 View에서 어떤 세트를 선택했느냐에 따라 결제 View를 다르게 띄워줘야 한다.
 */

enum TechBrand: CaseIterable {
    case apple
    case window
}

// 추상 팩토리 인터페이스 - 연관된 추상 제품들을 반환하는 메서드들의 집합
protocol KeyboardMouseFactory {
    func createMouse() -&amp;gt; Mouse
    func createKeyboard() -&amp;gt; Keyboard
}

struct AppleKeyboardMouseFactory: KeyboardMouseFactory {
    func createMouse() -&amp;gt; any Mouse {
        return AppleMouse()
    }
    
    func createKeyboard() -&amp;gt; any Keyboard {
        return AppleKeyboard()
    }
}

struct WindowKeyboardMouseFactory: KeyboardMouseFactory {
    func createMouse() -&amp;gt; any Mouse {
        return WindowMouse()
    }
    
    func createKeyboard() -&amp;gt; any Keyboard {
        return WindowKeyboard()
    }
}

// 이미 선택된 TechBrand에 따라 다른 '키보드 + 마우스 세트'를 보여줘야하는 결제 화면
final class PurchaseViewController: UIViewController {
    
    private var keyboardMouseFactory: KeyboardMouseFactory
    
    private lazy var keyboard: Keyboard = keyboardMouseFactory.createKeyboard()
    private lazy var mouse: Mouse = keyboardMouseFactory.createMouse()
    
    // 팩토리 유형이 선택된 TechBrand에 따라 유연하게 바뀐다.
    init(selected type: TechBrand) {
        switch type {
        case .apple:
            self.keyboardMouseFactory = AppleKeyboardMouseFactory()
        case .window:
            self.keyboardMouseFactory = WindowKeyboardMouseFactory()
        }
    }
    
    .
    .
    .
    
    
    func getTotalPrice() {
        print(self.keyboard.price + self.mouse.price)
    }
}

// 마우스
protocol Mouse {
    var price: Int { get }
    
    func click()
    func scroll()
}

struct AppleMouse: Mouse {
    
    var price: Int = 70000
    
    func click() {
        print(&quot;애플 마우스 - 클릭&quot;)
    }
    
    func scroll() {
        print(&quot;애플 마우스 - 스크롤&quot;)
    }
}

struct WindowMouse: Mouse {
    
    var price: Int = 50000
    
    func click() {
        print(&quot;윈도우 마우스 - 클릭&quot;)
    }
    
    func scroll() {
        print(&quot;윈도우 마우스 - 스크롤&quot;)
    }
}

// 키보드
protocol Keyboard {
    
    var price: Int { get }
    
    func type(_ letter: String)
}

struct AppleKeyboard: Keyboard {
    
    var price: Int = 130000
    
    func type(_ letter: String) {
        print(&quot;애플 키보드 -'\\(letter)'&quot;)
    }
    
}

struct WindowKeyboard: Keyboard {
    
    var price: Int = 110000
    
    func type(_ letter: String) {
        print(&quot;윈도우 키보드 -'\\(letter)'&quot;)
    }
    
}

// 1. 애플 세트 선택
let applePurchaseViewController = PurchaseViewController(selected: .apple)

applePurchaseViewController.getTotalPrice() &amp;gt;&amp;gt; 200000

// 2. 윈도우 세트 선택
let windowPurchaseViewController = PurchaseViewController(selected: .window)

windowPurchaseViewController.getTotalPrice() &amp;gt;&amp;gt; 160000

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style7&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;언제 사용하면 좋을까?&lt;/h3&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트 단에서 구체적인 클래스를 모를 때&lt;/li&gt;
&lt;li&gt;서로 연관된 여러 객체들을 함께 생성해야 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의 할 점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추상 팩토리 인터페이스에 연관되는 객체가 새로 추가 될 때마다 바꿔야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g.) KeyboardMouseFactory에 &amp;lsquo;키링&amp;rsquo;이 추가 된 경우, AppleKeyboardMouseFactory, WindowKeyboardMouseFactory에 키링에 관련된 코드를 추가해야한다. &amp;rarr; 번거로움&lt;/li&gt;
&lt;/ul&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참고:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/GOF-&quot;&gt;https://inpa.tistory.com/entry/GOF-&lt;/a&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%ACAbstract-Factory-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot;&gt; -추상-팩토리Abstract-Factory-패턴-제대로-배워보자&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://refactoring.guru/ko/design-patterns/abstract-factory&quot;&gt;https://refactoring.guru/ko/design-patterns/abstract-factory&lt;/a&gt;&lt;/p&gt;</description>
      <category>iOS/HIG &amp;amp; 디자인 패턴</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/94</guid>
      <comments>https://iossammy.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4#entry94comment</comments>
      <pubDate>Tue, 18 Jun 2024 11:44:51 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] 팩토리 메서드 패턴</title>
      <link>https://iossammy.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%8C%A8%ED%84%B4</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;객체 생성을 캡슐화 하여 &amp;lsquo;팩토리 클래스&amp;rsquo;를 통해 생성하도록 하는&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;생성 디자인 패턴&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구조&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Creator
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Product &lt;b&gt;객체를 생성하는 메서드&lt;/b&gt; 정의 (protocol / class)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Concrete Creator
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Concrete Product를 &lt;b&gt;생성하기 위한 구체화 된 메서드&lt;/b&gt;를 가진 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Product
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Creator을 통해 생성될 수 있는 객체의 &lt;b&gt;추상화 된 공통 Interface 선언&lt;/b&gt; (protocol)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Concrete Product:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Product에서 선언된 &lt;b&gt;Interface가 구체화 된 실제 객체&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/we5YQ/btsH27IYfIJ/KskoBSO9EP6EIKpgKdAkE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/we5YQ/btsH27IYfIJ/KskoBSO9EP6EIKpgKdAkE1/img.png&quot; data-alt=&quot;출처:&amp;amp;nbsp; https://www.pentalog.com/blog/design-patterns/factory-method-design-pattern/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/we5YQ/btsH27IYfIJ/KskoBSO9EP6EIKpgKdAkE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwe5YQ%2FbtsH27IYfIJ%2FKskoBSO9EP6EIKpgKdAkE1%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;810&quot; height=&quot;355&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처:&amp;nbsp; https://www.pentalog.com/blog/design-patterns/factory-method-design-pattern/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style7&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Swift 예제 코드&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// Product
protocol Bread {
    var name: String { get }
}

// Concrete Product
struct CreamBread: Bread {
    var name: String = &quot;크림빵&quot;
}

struct SoboruBread: Bread {
    var name: String = &quot;소보루빵&quot;
}

// Creator
protocol BreadCreator {
    associatedtype Product: Bread
    
    func make() -&amp;gt; Product
}

// Concrete Creator
final class CreamBreadCreator: BreadCreator {
    typealias Product = CreamBread
    
    func make() -&amp;gt; CreamBread {
        CreamBread()
    }
}

// Concrete Creator
final class SoboruBreadCreator: BreadCreator {
    typealias Product = SoboruBread
    
    func make() -&amp;gt; SoboruBread {
        SoboruBread()
    }
}
&lt;/code&gt;&lt;/pre&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;style7&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;언제 사용하면 좋을까?&lt;/h3&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&amp;ldquo;다양한 구현체가 필요할 때&amp;rdquo;&lt;/b&gt; 음악 재생 앱에서 다양한 형태의 음악 파일을 재생해야 할 경우, 각 파일 형식에 맞는 재생기를 생성하는 데 팩토리 메서드 패턴을 사용하면 유연성이 높아질 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;ldquo;확장성이 중요한 경우&amp;rdquo;&lt;/b&gt; 애플리케이션의 요구 사항이 변경되어 새로운 객체 타입을 추가해야 할 때, 팩토리 메서드를 사용하면 기존 코드를 수정하지 않고도 새로운 클래스를 쉽게 추가할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;ldquo;객체 생성 로직을 숨기고 싶을 때&amp;rdquo;&lt;/b&gt; 객체 생성에 복잡한 로직이나 조건이 필요한 경우, 이를 클라이언트로부터 숨기고 싶을 때 유용하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의 할 점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&amp;ldquo;객체 생성 메서드의 다수 필요&amp;rdquo;&lt;/b&gt; 각 객체를 위한 팩토리 메서드를 각각 제공해야 합니다. 이는 단일 팩토리 클래스가 많은 수의 팩토리 메서드를 포함하게 되어, 클래스의 복잡도가 증가할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;ldquo;코드의 복잡성 증가&amp;rdquo;&lt;/b&gt; 간단한 객체 생성에 팩토리 패턴을 적용할 경우, 오히려 코드의 이해와 유지보수를 어렵게 만들 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;ldquo;투머치 확장성과 유연성&amp;rdquo;&lt;/b&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/HIG &amp;amp; 디자인 패턴</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/93</guid>
      <comments>https://iossammy.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%8C%A8%ED%84%B4#entry93comment</comments>
      <pubDate>Tue, 18 Jun 2024 11:39:56 +0900</pubDate>
    </item>
    <item>
      <title>[iOS 16.4~] Inspector로 iOS 앱 내 띄운 웹뷰 디버깅 하기</title>
      <link>https://iossammy.tistory.com/entry/iOS-164-Inspector%EB%A1%9C-iOS-%EC%95%B1-%EB%82%B4-%EB%9D%84%EC%9A%B4-%EC%9B%B9%EB%B7%B0-%EB%94%94%EB%B2%84%EA%B9%85-%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Inspector 사용 이유&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앱 내에 띄운 WebView를 디버깅 할 때 Inspector를 사용한다.&lt;/p&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;예를 들면 필자는 네이버 로그인을 웹뷰로 구현하는 도중, 웹에서는 콜백 데이터를 넘겨주지만 앱에서는 해당 콜백 데이터를 받지 못하고 계속 흰색 빈 화면이 띄워지는 문제를 겪고 있었다. Xcode 디버깅으로는 웹 내 작업을 정확히 파악하기 힘들어 Inspector를 활용한 경험이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;웹뷰 디버깅을 위한 Inspector 활성화 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;iOS 16.4 버전&lt;/b&gt;부터는 WKWebView의 &lt;b&gt;isInspectable 속성을 &lt;span style=&quot;color: #000000;&quot;&gt;true&lt;/span&gt;&lt;/b&gt;로 설정해야 Inspector를 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1711543149611&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if #available(iOS 16.4, *) {
    webView.isInspectable = true
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Safari 앱 &amp;gt; 설정 &amp;gt; 고급 &amp;gt; &lt;b&gt;'웹 개발자를 위한 기능 보기' 체크&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgnrI0/btsGaxI84YO/45jaG2E4HcQn5Jj0FGduX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgnrI0/btsGaxI84YO/45jaG2E4HcQn5Jj0FGduX1/img.png&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;774&quot; data-is-animation=&quot;false&quot; style=&quot;width: 42.4669%; margin-right: 10px;&quot; data-widthpercent=&quot;42.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgnrI0/btsGaxI84YO/45jaG2E4HcQn5Jj0FGduX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgnrI0%2FbtsGaxI84YO%2F45jaG2E4HcQn5Jj0FGduX1%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;852&quot; height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DK4MR/btsF86Z4sv3/s9q7uuwsHMOUdjBhqdkXS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DK4MR/btsF86Z4sv3/s9q7uuwsHMOUdjBhqdkXS0/img.png&quot; data-origin-width=&quot;1806&quot; data-origin-height=&quot;1236&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;57.03&quot; style=&quot;width: 56.3704%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DK4MR/btsF86Z4sv3/s9q7uuwsHMOUdjBhqdkXS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDK4MR%2FbtsF86Z4sv3%2Fs9q7uuwsHMOUdjBhqdkXS0%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;1806&quot; height=&quot;1236&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Inspector로 웹뷰 디버깅 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&lt;/b&gt;&amp;nbsp; 띄우고자하는웹뷰.&lt;b&gt;isInspectable = true&lt;/b&gt; 설정 (iOS 16.4 이후 부터) &amp;gt; &lt;b&gt;빌드하기 (시뮬레이터 or 실 디바이스)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2686&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDZ5uD/btsF9eDMJ81/571mKx5zrQWtaG9KlrC3LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDZ5uD/btsF9eDMJ81/571mKx5zrQWtaG9KlrC3LK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDZ5uD/btsF9eDMJ81/571mKx5zrQWtaG9KlrC3LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDZ5uD%2FbtsF9eDMJ81%2F571mKx5zrQWtaG9KlrC3LK%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;2686&quot; height=&quot;992&quot; data-origin-width=&quot;2686&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.&lt;/b&gt; Safari 앱 &amp;gt; '&lt;b&gt;개발자용&lt;/b&gt;' &amp;gt; &lt;b&gt;빌드 된 시뮬레이터 선택&lt;/b&gt; &amp;gt; 'm.naver.com' 선택 (&lt;b&gt;빌드된 앱의 웹뷰에 띄워진 url&lt;/b&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/medix/btsF8vZ34hD/XFBq1cuWjI6RYi0LQXzxA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/medix/btsF8vZ34hD/XFBq1cuWjI6RYi0LQXzxA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/medix/btsF8vZ34hD/XFBq1cuWjI6RYi0LQXzxA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmedix%2FbtsF8vZ34hD%2FXFBq1cuWjI6RYi0LQXzxA1%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;2054&quot; height=&quot;780&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;780&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Inspector로 디버깅이 필요한 부분 체크하기&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot;&gt;(필자는 활용하여 Javascript 콜백&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot;&gt;들어오는 url endpoint까지 도달했는지 + 웹뷰에서 API 요청이 정상적으로 들어갔는지 여부를 Inspector를 통해 확인 할 수 있었다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;1494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ch9ASx/btsF9a9esIR/eBzF4BIyZedowSxYXVCLi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ch9ASx/btsF9a9esIR/eBzF4BIyZedowSxYXVCLi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ch9ASx/btsF9a9esIR/eBzF4BIyZedowSxYXVCLi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fch9ASx%2FbtsF9a9esIR%2FeBzF4BIyZedowSxYXVCLi1%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;2448&quot; height=&quot;1494&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;1494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;번외 +) HTTP 로 된 웹 띄우기 위한 설정&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;보안에 깐깐한 애플이기에 기본적으로 'https://웹주소'만 허용되어 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 http로 시작한 웹을 띄우기 위해서는 Info.plist에 key-value 설정을 추가해야한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1a1918; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EYoCP/btsGaBxXn1U/repDe7JP66XZ6t8lsDMEJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EYoCP/btsGaBxXn1U/repDe7JP66XZ6t8lsDMEJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EYoCP/btsGaBxXn1U/repDe7JP66XZ6t8lsDMEJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEYoCP%2FbtsGaBxXn1U%2FrepDe7JP66XZ6t8lsDMEJk%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;1684&quot; height=&quot;384&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;384&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;1.&lt;b&gt; App Transport Security Settings&lt;/b&gt; &lt;b&gt;딕셔너리 추가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. App Transport Security Settings&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;딕셔너리에&amp;nbsp;&lt;/span&gt;&lt;b&gt; Allow Arbitrary Loads&lt;/b&gt;를 &lt;b&gt;YES&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;</description>
      <category>iOS/UiKit</category>
      <category>Inspector</category>
      <category>iOS앱 디버깅</category>
      <category>Safari Inspector</category>
      <category>webkit</category>
      <category>WKWebView</category>
      <category>디버깅</category>
      <category>웹앱</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/91</guid>
      <comments>https://iossammy.tistory.com/entry/iOS-164-Inspector%EB%A1%9C-iOS-%EC%95%B1-%EB%82%B4-%EB%9D%84%EC%9A%B4-%EC%9B%B9%EB%B7%B0-%EB%94%94%EB%B2%84%EA%B9%85-%ED%95%98%EA%B8%B0#entry91comment</comments>
      <pubDate>Wed, 27 Mar 2024 22:29:22 +0900</pubDate>
    </item>
    <item>
      <title>[HIG] Launching - 실제 적용하여 리펙토링 하기(1)</title>
      <link>https://iossammy.tistory.com/entry/HIG-Launching-%EC%8B%A4%EC%A0%9C-%EC%A0%81%EC%9A%A9%ED%95%98%EC%97%AC-%EB%A6%AC%ED%8E%99%ED%86%A0%EB%A7%81-%ED%95%98%EA%B8%B01</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;저번주에 HIG 스터디에서 Launching 부분을 주제로 공부하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스터디에서 출시한 앱 중 한 앱이 &amp;lsquo;앱을 처음으로 설치 &amp;rarr; 실행&amp;rsquo; 경우 사용자 정보를 요구한 것이 떠올라 이를 회고하고 리펙토링 하기로 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qzKr5/btsAm6qDmPa/zWKgTQkDkHRHTreOeUNuBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qzKr5/btsAm6qDmPa/zWKgTQkDkHRHTreOeUNuBK/img.png&quot; data-alt=&quot;스터디 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qzKr5/btsAm6qDmPa/zWKgTQkDkHRHTreOeUNuBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqzKr5%2FbtsAm6qDmPa%2FzWKgTQkDkHRHTreOeUNuBK%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;766&quot; height=&quot;784&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스터디 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;리펙토링 계획&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 해당 앱은 MVC 패턴, CoreData, Podfile, SPM등의 외부 라이브러리가 전혀 들어가있지 않은 상태이다. (심지어 네트워킹도 URLSession으로 함)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;정보를 요청하는 화면 제거하기&lt;/li&gt;
&lt;li&gt;MainVC에서 유저의 이름 부분을 터치 가능하도록 하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유저 정보 없을 시에, &quot;User&quot;를 이름 대신 노출&amp;nbsp;&lt;/li&gt;
&lt;li&gt;터치시, 이름 등록 및 수정 하는 얼럿창 노출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;지역을 나타내는 글자 옆에 버튼 추가하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 날씨 제공 지역은 '서울'로 설정하기&lt;/li&gt;
&lt;li&gt;버튼 터치시 CLLocation으로부터 현재 위치 좌표 받기&lt;/li&gt;
&lt;li&gt;새로 받은 위치 좌표로 날씨 정보 요청하기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;리펙토링 시작&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 이 글에서는 하단의 2가지 내용을 다룰 것이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;정보를 요청하는 화면 제거&lt;/b&gt;하기&lt;/li&gt;
&lt;li&gt;MainVC에서 &lt;b&gt;유저의 이름 부분을 터치 가능하도록&lt;/b&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 정보를 요청하는 화면 제거하기&lt;/b&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;/li&gt;
&lt;li&gt;유저 이름을 최초에는 &quot;User&quot;로 임의 설정하기&lt;/li&gt;
&lt;li&gt;CoreData에 저장된 지역 데이터가 없을 경우 네트워킹을 요청할 때 default 지역으로 '서울'의 데이터를 보여주도록 하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;143&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4cOQQ/btsAjO5oy2U/KcOpx7VOmr1eL4JLwxfEa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4cOQQ/btsAjO5oy2U/KcOpx7VOmr1eL4JLwxfEa1/img.png&quot; data-alt=&quot;1. 유저 정보 유무에 따라 화면 다르게 세팅하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4cOQQ/btsAjO5oy2U/KcOpx7VOmr1eL4JLwxfEa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4cOQQ%2FbtsAjO5oy2U%2FKcOpx7VOmr1eL4JLwxfEa1%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;571&quot; height=&quot;143&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;143&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1. 유저 정보 유무에 따라 화면 다르게 세팅하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZ6QFq/btsAfCxX4oh/oKFGhPs8suypINY3sjPR60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ6QFq/btsAfCxX4oh/oKFGhPs8suypINY3sjPR60/img.png&quot; data-alt=&quot;2. default 지역을 &amp;quot;Seoul&amp;quot; 로 세팅하도록 변경 /&amp;amp;amp;nbsp; 앱 최초 설치 후의 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ6QFq/btsAfCxX4oh/oKFGhPs8suypINY3sjPR60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZ6QFq%2FbtsAfCxX4oh%2FoKFGhPs8suypINY3sjPR60%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;687&quot; height=&quot;462&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2. default 지역을 &quot;Seoul&quot; 로 세팅하도록 변경 /&amp;amp;nbsp; 앱 최초 설치 후의 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;nbsp;2. MainVC에서 &lt;b&gt;유저의 이름 부분을 터치 가능하도록&lt;/b&gt; 하기&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;유저 이름이 표시된 label에 TabGestureRecognizer 추가하기&lt;/li&gt;
&lt;li&gt;label이 탭 된 경우 이름을 변경할 수 있는 얼럿창 띄우기&lt;/li&gt;
&lt;li&gt;얼럿창의 확인 버튼이 눌리면 CoreData에 변경된 이름 반영 및 view 다시 그리기&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;195&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dINU3s/btsAmFNEhiG/90n3JikhGRKvWPzWY8x8LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dINU3s/btsAmFNEhiG/90n3JikhGRKvWPzWY8x8LK/img.png&quot; data-alt=&quot;1. label에 제스처 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dINU3s/btsAmFNEhiG/90n3JikhGRKvWPzWY8x8LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdINU3s%2FbtsAmFNEhiG%2F90n3JikhGRKvWPzWY8x8LK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;771&quot; height=&quot;195&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;195&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1. label에 제스처 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dC58PE/btsAmCDJ6h5/1n1BI4uEFAf9G1JTI02g2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dC58PE/btsAmCDJ6h5/1n1BI4uEFAf9G1JTI02g2k/img.png&quot; data-alt=&quot;2. CoreData에 유저 이름 없을 경우 -&amp;amp;gt; 생성(Create) / 있는 경우 -&amp;amp;gt; 수정(Update)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dC58PE/btsAmCDJ6h5/1n1BI4uEFAf9G1JTI02g2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdC58PE%2FbtsAmCDJ6h5%2F1n1BI4uEFAf9G1JTI02g2k%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;902&quot; height=&quot;494&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2. CoreData에 유저 이름 없을 경우 -&amp;gt; 생성(Create) / 있는 경우 -&amp;gt; 수정(Update)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시연 영상&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbiwtc/btsAp2HTuve/XvwQsXSQ997Pec66ptg0vk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbiwtc/btsAp2HTuve/XvwQsXSQ997Pec66ptg0vk/img.gif&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot; data-is-animation=&quot;true&quot; data-origin-height=&quot;651&quot; data-origin-width=&quot;300&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbiwtc/btsAp2HTuve/XvwQsXSQ997Pec66ptg0vk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbiwtc%2FbtsAp2HTuve%2FXvwQsXSQ997Pec66ptg0vk%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;651&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baUhbn/btsAjS8axw3/B8PmKjZ9g2J05SOwHNVKdK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baUhbn/btsAjS8axw3/B8PmKjZ9g2J05SOwHNVKdK/img.gif&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;651&quot; data-is-animation=&quot;true&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baUhbn/btsAjS8axw3/B8PmKjZ9g2J05SOwHNVKdK/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaUhbn%2FbtsAjS8axw3%2FB8PmKjZ9g2J05SOwHNVKdK%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;651&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>iOS/HIG &amp;amp; 디자인 패턴</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/90</guid>
      <comments>https://iossammy.tistory.com/entry/HIG-Launching-%EC%8B%A4%EC%A0%9C-%EC%A0%81%EC%9A%A9%ED%95%98%EC%97%AC-%EB%A6%AC%ED%8E%99%ED%86%A0%EB%A7%81-%ED%95%98%EA%B8%B01#entry90comment</comments>
      <pubDate>Wed, 15 Nov 2023 14:21:38 +0900</pubDate>
    </item>
    <item>
      <title>[정보처리기사] 2. 재공학</title>
      <link>https://iossammy.tistory.com/entry/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-2-%EC%9E%AC%EA%B3%B5%ED%95%99</link>
      <description>&lt;h1&gt;&lt;b&gt;1. 재공학&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-1) 소프트웨어 재공학(Software Reengineering)의 개념&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &amp;ldquo;기존의 시스템을 변경 혹은 재구조화 해서 사용하자.&amp;rdquo;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소프트웨어 위기를 유지보수의 생산성으로 해결하려는 방법을 뜻함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-2) 재공학의 장점, 목표, 과정&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발 시간 및 비용 감소&lt;/li&gt;
&lt;li&gt;품질 향상 (기존에 존재하는 것을 다시 만든 것이니까)&lt;/li&gt;
&lt;li&gt;신뢰성 향상 (기존에 사용했기 때문에 검증됨)&lt;/li&gt;
&lt;li&gt;구축 방법에 대한 지식의 공유, 프로젝트 실패 위험 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;목표
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최우선 목표는 소프트웨어 유지보수성 향상이다.&lt;/li&gt;
&lt;li&gt;복잡한 시스템을 다루는 방법 구현, 다른 뷰의 생성, 잃어버린 정보 복구 및 제거&lt;/li&gt;
&lt;li&gt;재사용을 수월하게 해서 소프트웨어의 수명을 연장시키자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분석(사용 가치 여부 분석) &amp;rarr; 구성(구조 리펙토링) &amp;rarr; 역공학(기존에 있는 것을 뜯어보기) &amp;rarr; 이식(실제 필요한 곳에 적용시키기)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;2. 역공학&lt;/b&gt;&lt;/h1&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;h1&gt;&lt;b&gt;3. CASE&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  Computer Aided Software Engineering의 약자, 소프트웨어 개발의 자동화 도구&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-1) CASE가 제공하는 기능&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발을 신속하게 할 수 있음, 오류 수정이 쉬움 &amp;rarr; 소프트웨어 품질 향상됨&lt;/li&gt;
&lt;li&gt;소프트웨어 생명주기의 전체 단계를 연결해주고 자동화 및 통합 도구를 제공함&lt;/li&gt;
&lt;li&gt;소프트웨어 시스템의 문서화 및 명세화를 위한 그래픽 기능 제공&lt;/li&gt;
&lt;li&gt;소프트웨어 개발 단계 표준화 기할 수 있음, 자료 흐름도 작성 기능 제공&lt;/li&gt;
&lt;li&gt;모델들 사이의 모순 검사 기능 제공&lt;/li&gt;
&lt;li&gt;원천 기술
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구조적 기법&lt;/li&gt;
&lt;li&gt;프로토타이핑 기술&lt;/li&gt;
&lt;li&gt;정보 저장소 기술&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-2) CASE 사용의 장점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기간 단축, 비용 감소 &amp;rarr; 생산성 향상&lt;/li&gt;
&lt;li&gt;소프트웨어 설계, 구현, 유지보수를 모두 관리할 수 있는 문서화 도구&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-3) CASE의 분류&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상위 CASE
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요구 분석 및 설계 단계 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하위 CASE
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스 코드 작성, 테스트, 문서화 과정 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;통합 CASE
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소프트웨어 개발 주기 전체 과정 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-4) 요구사항 분석을 위한 CASE 도구&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SADT (Structured Analysis and Design Technique)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템 정의, 소프트웨어 요구사항 분석, 시스템/소프트웨어 설계를 위해 사용되어 온 구조적 분석 및 설계 도구&lt;/li&gt;
&lt;li&gt;SoftTech사에서 개발함&lt;/li&gt;
&lt;li&gt;구조적 요구분석을 위한 블록 다이어그램을 채택한 자동화 도구&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>정보처리기사/소프트웨어 설계</category>
      <category>시험에자주나오는것만</category>
      <category>정처기</category>
      <author>SamusesApple</author>
      <guid isPermaLink="true">https://iossammy.tistory.com/89</guid>
      <comments>https://iossammy.tistory.com/entry/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-2-%EC%9E%AC%EA%B3%B5%ED%95%99#entry89comment</comments>
      <pubDate>Thu, 9 Nov 2023 14:55:46 +0900</pubDate>
    </item>
  </channel>
</rss>