<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title> </title>
    <link>https://codekodo.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 6 May 2026 14:40:58 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>kodo_o</managingEditor>
    <image>
      <title> </title>
      <url>https://tistory1.daumcdn.net/tistory/4385700/attach/4ffc49dcd74a409790b39219bb0d616f</url>
      <link>https://codekodo.tistory.com</link>
    </image>
    <item>
      <title>[iOS / SwiftUI] .mask를 활용하여 Custom ProgressView를 구현해보자</title>
      <link>https://codekodo.tistory.com/214</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다른 컴포넌트들은 디자인이 구상이 되어 있어서, 이를 그대로 구현하면 되는 상황이었지만 ProgressView는 명시된 디자인이 없었다. 그래서 어떻게 구현할지 고민하다가, 앱의&lt;span style=&quot;color: #f89009;&quot;&gt; &lt;b&gt;Identity와 UX&lt;/b&gt;&lt;/span&gt;라는 두 마리의 토끼를 모두 잡아보기로 결정했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이를 위해, 우리 앱의 로고를 사용하기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 단순히 로고만 띄어주기엔 이쁘지도 않고, 일단 UX적으로 마음에 들지 않았다.&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Why?  &lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ProgressView는 어떠한 요청(ex, API Request)에 대한 응답을 받기 전까지의 로딩을 시작적으로 표현하는 것인데, 단순히 로고만 띡 띄어놓으면 사용자는 이게 무슨 상황인지 전혀 알 방법이 없다. 그래서 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Dynamic한 요소&lt;/b&gt;&lt;/span&gt;가 필요했는데,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그러던 중 Shimmer라는 기법(?)이 떠올랐다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;자주 참고하는 SwiftUI 관련 유튜버인데,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해당 영상에는 .mask를 활용해서 빛이 이동하면서 발생하는 그림자 효과를 주고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=SvcKjnkprN8&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/mkDnE/hySpBmBKWQ/VcdQ9NiYRKRmVyKgPxOh3K/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/SvcKjnkprN8&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이와 비슷하게 구현하되, 다른 느낌을 주고 싶었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇게 구현을 시작했고, &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;구현 결과는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-video-to-gif (5).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQBYEq/btscSoClU8g/ErUcSJadCM2tyBpZUoEhn1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQBYEq/btscSoClU8g/ErUcSJadCM2tyBpZUoEhn1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQBYEq/btscSoClU8g/ErUcSJadCM2tyBpZUoEhn1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bQBYEq/btscSoClU8g/ErUcSJadCM2tyBpZUoEhn1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;867&quot; data-filename=&quot;ezgif.com-video-to-gif (5).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱의 로고를 활용하여 Identity를 가져가면서, 동시에 사용자에게 로딩 중이라는 정보를 제공할 수 있게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;구현&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;View&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 첫 번째로 Base가 되는 View이다.&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-27 오후 2.25.34.png&quot; data-origin-width=&quot;1616&quot; data-origin-height=&quot;1314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgZngI/btscUvm8B1S/5Iqz4yMHR9hesuE1BRK4sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgZngI/btscUvm8B1S/5Iqz4yMHR9hesuE1BRK4sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgZngI/btscUvm8B1S/5Iqz4yMHR9hesuE1BRK4sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgZngI%2FbtscUvm8B1S%2F5Iqz4yMHR9hesuE1BRK4sK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;650&quot; data-filename=&quot;스크린샷 2023-04-27 오후 2.25.34.png&quot; data-origin-width=&quot;1616&quot; data-origin-height=&quot;1314&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ZStack 내부에 같은 이미지를 두 장 배치했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2번 이미지에 컬러를 채워주고, .mask modifier를 붙여준다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 .mask에 대해 간단하게 설명하자면, 그냥 우리가 사용하는 마스크를 떠올리면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;얼굴을 가리는 것처럼, content를 가릴 수 있게 해주는 modifier이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;이해를 돕기 위해, 위에 작성한 코드에서 .mask를 조금 수정해봤다.&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-27 오후 11.26.17.png&quot; data-origin-width=&quot;1650&quot; data-origin-height=&quot;936&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZVUYK/btsc1O61LPi/8cJeG4oaN6xCfVGrulUgQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZVUYK/btsc1O61LPi/8cJeG4oaN6xCfVGrulUgQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZVUYK/btsc1O61LPi/8cJeG4oaN6xCfVGrulUgQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZVUYK%2Fbtsc1O61LPi%2F8cJeG4oaN6xCfVGrulUgQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;454&quot; data-filename=&quot;스크린샷 2023-04-27 오후 11.26.17.png&quot; data-origin-width=&quot;1650&quot; data-origin-height=&quot;936&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-27 오후 11.27.20.png&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;924&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBXQ9l/btscZsYoTz3/K1tznA7De3AKoYLWZG5ppK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBXQ9l/btscZsYoTz3/K1tznA7De3AKoYLWZG5ppK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBXQ9l/btscZsYoTz3/K1tznA7De3AKoYLWZG5ppK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBXQ9l%2FbtscZsYoTz3%2FK1tznA7De3AKoYLWZG5ppK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;793&quot; data-filename=&quot;스크린샷 2023-04-27 오후 11.27.20.png&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;924&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로고에 .mask가 씌어져서 양 옆에 가려진 것을 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(사실 .mask를 쓰지 않아도, ZStack이나 Overlay로도 충분히 구현 가능하다는 생각이 들긴하지만...  )&lt;/p&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2번 이미지를 가릴 수 있는 Rectangle 형태의 마스크를 선언한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;초기에는 Rectangle의 크기가 0이고, 애니메이션이 시작되면 로고의 너비까지 증가한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;offset도 -(로고의 크기 * 0.35)이고, 애니메이션이 시작되면 0까지 증가한다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 해주면 왼쪽에서 오른쪽으로 컬러가 채워지는 느낌을 줄 수 있게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ViewModifier&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-27 오후 2.52.05.png&quot; data-origin-width=&quot;1928&quot; data-origin-height=&quot;1214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTWX5l/btscZah2CZt/1SDO1NrOwEj7jEKSBacLI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTWX5l/btscZah2CZt/1SDO1NrOwEj7jEKSBacLI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTWX5l/btscZah2CZt/1SDO1NrOwEj7jEKSBacLI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTWX5l%2FbtscZah2CZt%2F1SDO1NrOwEj7jEKSBacLI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;504&quot; data-filename=&quot;스크린샷 2023-04-27 오후 2.52.05.png&quot; data-origin-width=&quot;1928&quot; data-origin-height=&quot;1214&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞서 구현한 ProgressView를 사용할 ViewModifier이다. ProgressView에서는 형태를 정의했다면, 이 곳에서는 애니메이션을 부여하고 Background를 지정하는 역할을 수행한다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ZStack을 활용하여 ProgressView가 보여질 때, Touch Event를 막기 위해 Background를 지정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 onAppear를 활용하여 View가 보여지면 애니메이션을 작동하도록 구현했다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;isPresented와 isAnimated가 헷갈릴 수 있는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;isPresented는 ProgressView를 띄우는 프로퍼티,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;isAnimated는 ProgressView 내부의 애니메이션을 작동하는 프로퍼티라고 생각하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Extension&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞서 구현한 ViewModifier를 Extension을 활용하여 Modifier의 형태로 사용할 수 있도록 맵핑한다.&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-27 오후 2.57.21.png&quot; data-origin-width=&quot;2094&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oMxIG/btscZtBFFy8/lvKYjjqATAC8fmLKSg1xxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oMxIG/btscZtBFFy8/lvKYjjqATAC8fmLKSg1xxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oMxIG/btscZtBFFy8/lvKYjjqATAC8fmLKSg1xxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoMxIG%2FbtscZtBFFy8%2FlvKYjjqATAC8fmLKSg1xxK%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;798&quot; height=&quot;142&quot; data-filename=&quot;스크린샷 2023-04-27 오후 2.57.21.png&quot; data-origin-width=&quot;2094&quot; data-origin-height=&quot;372&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 결과, 최종적으로는 View에서 Extension만 호출해서 사용한다.&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-27 오후 2.58.10.png&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S0vju/btscZbH2tev/5O4s4jdPMRElco8JkBkJzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S0vju/btscZbH2tev/5O4s4jdPMRElco8JkBkJzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S0vju/btscZbH2tev/5O4s4jdPMRElco8JkBkJzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS0vju%2FbtscZbH2tev%2F5O4s4jdPMRElco8JkBkJzK%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;798&quot; height=&quot;51&quot; data-filename=&quot;스크린샷 2023-04-27 오후 2.58.10.png&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;132&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이런 식으로 ProgressView가 필요한 곳에서 modifier의 형태로 접근한다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;isPresented가 true이면 ProgressView가 보여지고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;false이면 ProgressView가 사라지게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;TMI...&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드량이 많지는 않다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래도 코드량에 비해 많은 고민을 했던 컴포넌트였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 사용하는 과정에서 다른 애니메이션이랑 겹치는 문제가 발생했는데, &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;최상단 View에서 호출하는 것으로 해당 문제를 해결할 수 있었다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>  개발/iOS</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/214</guid>
      <comments>https://codekodo.tistory.com/214#entry214comment</comments>
      <pubDate>Thu, 27 Apr 2023 15:02:32 +0900</pubDate>
    </item>
    <item>
      <title>[iOS / SwiftUI] View Memory Graph Hierarchy를 활용해보자</title>
      <link>https://codekodo.tistory.com/212</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;멋쟁이사자처럼 iOS 앱 스쿨에서 최종 프로젝트로 단어장 앱을 만들면서,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;AVSpeechSynthesizer&lt;/b&gt;&lt;/span&gt;를 활용하여 TTS 기능을 구현하려고 했다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 구현 과정에서 예상치 못한? 오류를 만났고 생각보다 골머리를 썩였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드 상으로는 전혀 문제가 없었는데 말이다. (&lt;s&gt;진짜로? 문제 없는 거 맞아?&lt;/s&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇게 고민하던 중, 결국&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;View Memory Graph Hierarchy&lt;/span&gt;&lt;/b&gt;를 통해 오류를 해결할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 전에, AVSpeechSynthesizer는 어떻게 동작하는지에 대해 알아보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;AVSpeechSynthesizer의 동작 과정&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;3300&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/svXPt/btr3GTKIKgT/wm9iwhaBgHpKDoyDlV5HN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/svXPt/btr3GTKIKgT/wm9iwhaBgHpKDoyDlV5HN0/img.png&quot; data-alt=&quot;여기서 instance는 AVSpeechSynthesizer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/svXPt/btr3GTKIKgT/wm9iwhaBgHpKDoyDlV5HN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsvXPt%2Fbtr3GTKIKgT%2Fwm9iwhaBgHpKDoyDlV5HN0%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;797&quot; height=&quot;113&quot; data-origin-width=&quot;3300&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;여기서 instance는 AVSpeechSynthesizer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 AVSpeechSynthesizer에는 AVSpeechUtterance를 담을 수 있는 그릇(Queue)이 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이곳에 AVSpeechUtterance 인스턴스가 담기고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;speak 메서드를 호출하면 Queue에서 Pop되면서 TTS가 작동하게 된다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;ezgif.com-video-to-gif (2).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oAFLS/btr3uEtTUni/owxApnkBIW45DelTmp6nS0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oAFLS/btr3uEtTUni/owxApnkBIW45DelTmp6nS0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oAFLS/btr3uEtTUni/owxApnkBIW45DelTmp6nS0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/oAFLS/btr3uEtTUni/owxApnkBIW45DelTmp6nS0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;392&quot; data-filename=&quot;ezgif.com-video-to-gif (2).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;294&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제 상황  &lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;뭐, 정말 별 거 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위 애니메이션만 보면 '아... 이렇게 동작하는구나...' 정도로 생각할 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;간단한 애니메이션처럼 실제로 코드 상으로도 간단하게 구현할 수 있다. (&lt;s&gt;간단할 줄 알았다...&lt;/s&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;1548&quot; data-origin-height=&quot;1794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uKFz1/btr3JXk3YOH/gBgynKdGC3tVB0dwXjcK2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uKFz1/btr3JXk3YOH/gBgynKdGC3tVB0dwXjcK2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uKFz1/btr3JXk3YOH/gBgynKdGC3tVB0dwXjcK2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuKFz1%2Fbtr3JXk3YOH%2FgBgynKdGC3tVB0dwXjcK2K%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;549&quot; height=&quot;636&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;1794&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 선택한 단어 듣기(1개)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 선택한 단어 듣기(여러 개)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 전체 단어 듣기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1개의 단어만 들을 때(1번)는 TTS가 실행, 종료된 이후에 다시 TTS를 실행했을 때 아무 문제가 없었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 2번과 3번처럼, TTS가 온전히 종료하기 전에 NavigationItems에 있는 &quot;취소&quot; 버튼으로 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;TTS를 강제 종료&lt;/b&gt;&lt;/span&gt;하는 순간 문제가 발생했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 TTS를 강제로 종료하게 되면 이후에 TTS가 실행되지 않는 치명적인 오류였다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래와 같은 오류를 내뿜으며 TTS가 작동하지 않았다.&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;2174&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rtehc/btr3kx3eISa/KmZAN70PgaIZRAmA274P0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rtehc/btr3kx3eISa/KmZAN70PgaIZRAmA274P0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rtehc/btr3kx3eISa/KmZAN70PgaIZRAmA274P0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frtehc%2Fbtr3kx3eISa%2FKmZAN70PgaIZRAmA274P0K%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;749&quot; height=&quot;134&quot; data-origin-width=&quot;2174&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아, 이거다!&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;구글링을 해봐도 내가 원하는 답변을 찾을 수 없었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그러던 중, 멘토님께서 하신 말씀이 머리를 스쳐 지나갔다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;'Xcode에서 메모리와 관련된 디버그 툴을 제공한다...'&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아, 이거다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;평소에 자주 사용하던 View Hierarchy와 같은 곳에 존재했다.&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;2352&quot; data-origin-height=&quot;1224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ejEQ1F/btr3uE8w2a9/Iqs0MXTqss71IYFk7V1r10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ejEQ1F/btr3uE8w2a9/Iqs0MXTqss71IYFk7V1r10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ejEQ1F/btr3uE8w2a9/Iqs0MXTqss71IYFk7V1r10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FejEQ1F%2Fbtr3uE8w2a9%2FIqs0MXTqss71IYFk7V1r10%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;750&quot; height=&quot;390&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;1224&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아무튼 이 친구를 통해서 메모리에 어떤 인스턴스들이 올라가있나 직접 확인해봤고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;3040&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTOQNx/btr3LrsFa1g/vNa5jkoP3431NDXMI1HPl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTOQNx/btr3LrsFa1g/vNa5jkoP3431NDXMI1HPl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTOQNx/btr3LrsFa1g/vNa5jkoP3431NDXMI1HPl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTOQNx%2Fbtr3LrsFa1g%2FvNa5jkoP3431NDXMI1HPl1%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;747&quot; height=&quot;178&quot; data-origin-width=&quot;3040&quot; data-origin-height=&quot;724&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;왼쪽 사진은 정상적으로 TTS가 종료됐을 때의 모습이고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오른쪽은 취소 버튼을 눌러서 TTS를 강제 종료됐을 떄의 모습이다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;빨간 밑줄 친 저 놈, AVSpeechUtterance가 생생하게 살아있는 것을 목격했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 얘를 메모리에서 제거하면 TTS가 정상적으로 실행될 거라는 확신이 생겼다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서, 어떻게 제거할건데?&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ARC를 활용하고자 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;wordUtterance를 nil로 만들어 줌으로써,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ARC에 의해 메모리에서 자동으로 제거되는 방향으로 접근했다.&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;1936&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfSSVP/btr3JYRPcZT/xGkKKfCm6ReXsVviqr5gWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfSSVP/btr3JYRPcZT/xGkKKfCm6ReXsVviqr5gWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfSSVP/btr3JYRPcZT/xGkKKfCm6ReXsVviqr5gWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfSSVP%2Fbtr3JYRPcZT%2FxGkKKfCm6ReXsVviqr5gWK%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;498&quot; height=&quot;194&quot; data-origin-width=&quot;1936&quot; data-origin-height=&quot;754&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;2268&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eb7EId/btr3kxIVpDr/OcKducYGAk4fs6DRxUXP7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eb7EId/btr3kxIVpDr/OcKducYGAk4fs6DRxUXP7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eb7EId/btr3kxIVpDr/OcKducYGAk4fs6DRxUXP7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feb7EId%2Fbtr3kxIVpDr%2FOcKducYGAk4fs6DRxUXP7k%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;797&quot; height=&quot;113&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;322&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;AVSpeechSynthesizer에서 wordUtterance에 대한 참조를 갖고 있었고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이로 인해 RC가 발생하여 ARC가 해당 인스턴스를 제거하지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 다른 방식으로 접근했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;wordUtterance를 제거하는 것이 아니라 이를 담고 있는 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;AVSpeechSynthesizer 자체를 제거하는 방향&lt;/b&gt;&lt;/span&gt;으로 말이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2178&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/poUQs/btr3GVhuOwC/kTKvEYPOlySFawCU9l1CiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/poUQs/btr3GVhuOwC/kTKvEYPOlySFawCU9l1CiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/poUQs/btr3GVhuOwC/kTKvEYPOlySFawCU9l1CiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpoUQs%2Fbtr3GVhuOwC%2FkTKvEYPOlySFawCU9l1CiK%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;797&quot; height=&quot;153&quot; data-origin-width=&quot;2178&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2178&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qdYwE/btr3ICIehzA/WB3Mb9UzPPzOd0Q2qAUvy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qdYwE/btr3ICIehzA/WB3Mb9UzPPzOd0Q2qAUvy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qdYwE/btr3ICIehzA/WB3Mb9UzPPzOd0Q2qAUvy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqdYwE%2Fbtr3ICIehzA%2FWB3Mb9UzPPzOd0Q2qAUvy1%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;797&quot; height=&quot;191&quot; data-origin-width=&quot;2178&quot; data-origin-height=&quot;522&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결과는 성공적이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞서 발생한 문제를 말끔하게 해결할 수 있었다!  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;디버깅이 아니였다면 해결하지 못한 이슈로 남았을 것이다...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞으로도 자주 활용해서, 메모리 관련 이슈를 말끔하게 해결해보자!  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;직접 부딪히고, 찾아보면서 작성한 글이라 잘못된 정보가 있을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;피드백은 언제든지 환영입니다. 감사합니다.  &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;전체 코드&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1678722769679&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import AVFoundation
import Foundation

enum SpeechType {
    case single
    case many
}

protocol TTSProtocol {
    /// 단어 하나를 읽어주는 메서드
    func speakWordAndMeaning(_ word: Word, to language: String, _ type: SpeechType)
    
    /// 여러개의 단어를 읽어주는 메서드
    func speakWordsAndMeanings(_ words: [Word], to language: String)
    
    /// 단어 읽기를 멈추는 메서드
    func stopSpeaking()
}

final class SpeechSynthesizer: NSObject, ObservableObject, TTSProtocol {
    // 싱글톤으로 정의
    private var instance: AVSpeechSynthesizer? = AVSpeechSynthesizer()
    
    // 단어와 의미 사이의 간격 (같은 단어 내에서)
    private let intervalOfWordAndMeaning = 0.3
    
    // 의미와 의미 사이의 간격 (같은 단어 내에서)
    private let intervalOfMeaningAndMeaning = 0.1
    
    // 단어와 단어 사이의 간격 (다른 단어끼리)
    private let intervalOfWordAndWord = 0.5
    
    // TODO: - 사용자가 설정한 언어에 따라 동적으로 뱌뀌는 코드 추가
    private let meaningUtteranceLanguage = &quot;ko-KR&quot;
    
    // 재생할 단어의 개수를 저장
    private var totalWordsCount: Int = 0
    private var spokenWordsCount: Int = 1
    
    // 단어 듣기의 재생 여부를 확인
    @Published var isPlaying = false
    
    override init() {
        super.init()
        instance?.delegate = self
    }
    
    /// 단어 하나를 읽어주는 메서드
    func speakWordAndMeaning(_ word: Word, to language: String, _ type: SpeechType) {
        if instance == nil {
            instance = AVSpeechSynthesizer()
            instance?.delegate = self
        }
        
        let wordUtterance = AVSpeechUtterance(string: word.word ?? &quot;&quot;) // TTS로 들려줄 단어 설정
        wordUtterance.voice = AVSpeechSynthesisVoice(language: language) // 언어 설정
        wordUtterance.postUtteranceDelay = intervalOfWordAndMeaning // 다음 단어와의 시간 간격 설정
        instance?.speak(wordUtterance) // TTS 작동
        
        if type == SpeechType.single { return } // contextMenu로 접근한 경우
        
        // meaning 타입이 [String?]라서 순회하는 방식으로 구현
        word.meaning?.forEach { meaning in
            let meaningUtterance = AVSpeechUtterance(string: meaning)
            meaningUtterance.voice = AVSpeechSynthesisVoice(language: meaningUtteranceLanguage)
            meaningUtterance.postUtteranceDelay = intervalOfMeaningAndMeaning
            instance?.speak(meaningUtterance)
        }
    }
    
    /// 여러 개의 단어를 읽어주는 메서드
    /// 단어 하나를 읽어주는 메서드를 재사용하는 방식으로 구현
    func speakWordsAndMeanings(_ words: [Word], to language: String) {
        totalWordsCount = calculateWordsAndMeanings(words) // 단어 개수 카운트
        spokenWordsCount = 1
        
        isPlaying = true // 재생 상태로 변경
        
        words.forEach { word in
            self.speakWordAndMeaning(word, to: language, .many)
        }
    }
    
    /// 단어 읽기를 멈추는 메서드 (onDisapper)
    /// TTS가 작동 중인 상태였으면 (선택 듣기, 전체 듣기 도중 종료) AVSpeechSynthesizer를 초기화하여 오류를 방지
    func stopSpeaking() {
        instance?.stopSpeaking(at: .immediate)
        instance = nil // 인스턴스 초기화
        isPlaying = false // 정지 상태로 변경
    }
    
    /// 재생할 단어의 개수를 계산하는 메서드
    private func calculateWordsAndMeanings(_ words: [Word]) -&amp;gt; Int {
        var cnt = 0

        for word in words {
            cnt += 1

            for _ in word.meaning ?? [] {
                cnt += 1
            }
        }

        return cnt
    }
}

extension SpeechSynthesizer: AVSpeechSynthesizerDelegate {
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        // 재생이 완료된 단어의 개수가 총 단어의 개수와 일치하면 정지
        if totalWordsCount == spokenWordsCount {
            isPlaying = false
        } else {
            spokenWordsCount += 1
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;광고&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 앱스럽지만(&lt;s&gt;의도한 디자인입니다 ㅎㅎ&lt;/s&gt;) 숨겨진 기능이 많습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;많이 사용해주시고, 저희가 발견하지 못한 버그가 있을 경우에 언제든지 제보해주시면 감사하겠습니다.  &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;figure id=&quot;og_1678725084881&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;&amp;lrm;TheVoca&quot; data-og-description=&quot;&amp;lrm;## 주요기능 손쉽게 단어를 추가하고, 단어장으로 묶어서 관리하세요. - 내가 공부하고 있는 단어와 의미를 직접 하나하나 등록할 수 있습니다. - 또한 많은 단어를 한 번에 등록하기 위해 엑셀&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app/thevoca/id6445853620&quot; data-og-url=&quot;https://apps.apple.com/kr/app/thevoca/id6445853620&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vn1kO/hyRVabl5rI/A2Mu6YerdOiEkknuY1KCfK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bzelR1/hyRVaWJPk5/mdtvkSZuBEWNKJNiqRoKZ0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/thevoca/id6445853620&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app/thevoca/id6445853620&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vn1kO/hyRVabl5rI/A2Mu6YerdOiEkknuY1KCfK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bzelR1/hyRVaWJPk5/mdtvkSZuBEWNKJNiqRoKZ0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lrm;TheVoca&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lrm;## 주요기능 손쉽게 단어를 추가하고, 단어장으로 묶어서 관리하세요. - 내가 공부하고 있는 단어와 의미를 직접 하나하나 등록할 수 있습니다. - 또한 많은 단어를 한 번에 등록하기 위해 엑셀&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>  개발/iOS</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/212</guid>
      <comments>https://codekodo.tistory.com/212#entry212comment</comments>
      <pubDate>Tue, 14 Mar 2023 00:21:42 +0900</pubDate>
    </item>
    <item>
      <title>[Architecture] MVVM + Clean Architecture를 알아보자</title>
      <link>https://codekodo.tistory.com/211</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SwiftUI에서는 @ObservableObject 덕분에 ViewModel(역할을 하는?)을 쉽게 구현할 수 있다. 덕분에 실습을 진행하면서 대부분의 프로젝트에서 ViewModel로 분리는 했지만 문제가 많았다. &lt;s&gt;일단 SwiftUI가 View 자체적으로 Data Binding이 가능하기 때문에 이미 ViewModel이 녹아들어간 느낌이다.&lt;/s&gt; 하지만 이것보다도 하나의 ViewModel에서 여러 작업을 진행하다보니 Massive ViewModel이랄까? ViewModel이 비대해진 느낌이 들었고, 그래서 해커톤 때는 이런 것들을 걷어내고 아키텍쳐적으로 조금 더 괜찮은 앱을 구현하고 싶었다. 다행히도 팀원 중 한 분이 클린 아키텍쳐에 대해 잘 알고 계셔서 도움을 많이 받았고,&amp;nbsp;전보다는 어느정도 완성된 앱을 만들 수 있었다.&amp;nbsp;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;서론이 길었지만 아무튼 해커톤을 계기로 Clean Architecure에 대해 다시 공부하고 싶어서 전에 북마크해둔 글을 보면서 개념을 정리해보는 시간을 가졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1673063726096&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;Clean Architecture and MVVM on iOS&quot; data-og-description=&quot;When we develop software it is important to not only use design patterns, but also architectural patterns. There are many different&amp;hellip;&quot; data-og-host=&quot;tech.olx.com&quot; data-og-source-url=&quot;https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3&quot; data-og-url=&quot;https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/k4CVi/hyRcKcTOCm/3ltWDM0UbU9SXYZFwGDRXK/img.jpg?width=1200&amp;amp;height=1145&amp;amp;face=0_0_1200_1145&quot;&gt;&lt;a href=&quot;https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/k4CVi/hyRcKcTOCm/3ltWDM0UbU9SXYZFwGDRXK/img.jpg?width=1200&amp;amp;height=1145&amp;amp;face=0_0_1200_1145');&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;Clean Architecture and MVVM on iOS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;When we develop software it is important to not only use design patterns, but also architectural patterns. There are many different&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tech.olx.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;figure id=&quot;og_1673063739697&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoor&quot; data-og-description=&quot;Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoordinator, DTO, Response Caching and one of the views in SwiftUI - GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Tem...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&quot; data-og-url=&quot;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gDYcf/hyRcBfXkik/bjZhkmu7zs0HqVQzDz6oPk/img.png?width=1200&amp;amp;height=600&amp;amp;face=995_121_1038_167&quot;&gt;&lt;a href=&quot;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gDYcf/hyRcBfXkik/bjZhkmu7zs0HqVQzDz6oPk/img.png?width=1200&amp;amp;height=600&amp;amp;face=995_121_1038_167');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoor&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoordinator, DTO, Response Caching and one of the views in SwiftUI - GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Tem...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Clean&amp;nbsp;Architecture&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-01-06 23.54.42.png&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/64giB/btrVEqhpbjR/Dhwoof5IE6lNGJFyV5A8Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/64giB/btrVEqhpbjR/Dhwoof5IE6lNGJFyV5A8Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/64giB/btrVEqhpbjR/Dhwoof5IE6lNGJFyV5A8Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F64giB%2FbtrVEqhpbjR%2FDhwoof5IE6lNGJFyV5A8Gk%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;328&quot; data-filename=&quot;스크린샷 2023-01-06 23.54.42.png&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;842&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 이런 형태를 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;의존 관계는 안쪽으로 향하게끔 설계를 하고, 안쪽에 있는 요소들은 바깥쪽에 있는 요소의 존재를 모른다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어, Presenters에 해당하는 ViewModel은 Domain에 해당하는 Usecase에 의존적이지만, (생성자를 통해 전달)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Domain은 ViewModel에 대해 알 필요가 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;종속성 방향&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-01-07 00.07.19.png&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oJ8UM/btrVzztGrl0/ISDg4iVWiTZZXkpIjKCko0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oJ8UM/btrVzztGrl0/ISDg4iVWiTZZXkpIjKCko0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oJ8UM/btrVzztGrl0/ISDg4iVWiTZZXkpIjKCko0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoJ8UM%2FbtrVzztGrl0%2FISDg4iVWiTZZXkpIjKCko0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;222&quot; data-filename=&quot;스크린샷 2023-01-07 00.07.19.png&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;222&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Presentation Layer &amp;rarr; Domain Layer &amp;larr; Data Layer&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Presentation Layer는 Domain Layer에 의존&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Domain Layer가 Data Layer에 의존 &amp;rarr; 추상화된 Repository에 의존하므로 의존성 역전&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;데이터 흐름&lt;/span&gt;&lt;/h2&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;View는 ViewModel에서 메서드를 호출한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ViewModel은 UseCase를 실행한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UseCase는 User와 Repository의 데이터를 결합한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Repository는 DB, Network와 연결되어 데이터를 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Domain Layer&lt;/span&gt;&lt;/h2&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Entity, Usecase&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Entity : 우리가 지금까지 Model로 작성한 것들&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Usecase: 소프트웨어 공학에서 기능 단위로 설명하는데 거의 비슷하다고 볼 수 있음. 위 사진에서 볼 수 있듯, 영화 검색(Movie Search) 유스케이스, 최신 영화 목록?(Fetch Recent Movie Queries) 유스케이스처럼 기능에 대한 단위&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-01-07 00.01.36.png&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MCwPJ/btrVzKn66sX/kLh3bsSPtN6CPv4sAJ0pt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MCwPJ/btrVzKn66sX/kLh3bsSPtN6CPv4sAJ0pt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MCwPJ/btrVzKn66sX/kLh3bsSPtN6CPv4sAJ0pt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMCwPJ%2FbtrVzKn66sX%2FkLh3bsSPtN6CPv4sAJ0pt1%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;503&quot; height=&quot;269&quot; data-filename=&quot;스크린샷 2023-01-07 00.01.36.png&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;768&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;안드로이드 공식 문서이지만, 아키텍쳐적인 관점에서는 iOS와 동일하므로 첨부했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Domain Layer에 대한 특징이 나와있다.&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;style3&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;  근데 유스케이스를 꼭 써야하나요?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;rarr; Clean Architecture를 접하기 전까지만 하더라도 나도 같은 생각이였다. 여러 글을 참고하면서 내린 결론은 인터페이스적인 개념이 적용됐다는 것이다. 물론 이렇게 나누지 않고, Presentation가 Repository를 직접 참조해도 상관은 없다고 생각한다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일단, 직관적이다. ViewModel만 딱 봤을 때, 기능 단위로 분류해놓은 Usecase를 보고 바로 이해할 수 있게 된다. &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-01-07 00.09.57.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dycz0c/btrVz9gPdkS/KvQ00zUneJbVuTut6dPhK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dycz0c/btrVz9gPdkS/KvQ00zUneJbVuTut6dPhK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dycz0c/btrVz9gPdkS/KvQ00zUneJbVuTut6dPhK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdycz0c%2FbtrVz9gPdkS%2FKvQ00zUneJbVuTut6dPhK0%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;207&quot; data-filename=&quot;스크린샷 2023-01-07 00.09.57.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;246&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다양한 Repository Layer를 참조하지 않고, 기능에 대한 최소 단위인 Usecase 참조하고 있으므로 ViewModel에서 어떤 기능(서비스)를 제공하고 있는지 명확하게 알 수 있다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 기능이 확장됨에 따라 하나의 Usecase에서 다양한 Repository를 참조하는 경우가 발생할 것이다. 하지만 ViewModel은? Usecase가 어떤 Repository와 연결되어 있는지 알 필요가 없다. 그냥 내가 구현하려는 기능에 해당하는 Usecase를 바라보면 된다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;가령, 특정 기능에 대한 내부적인 로직이 조금 변경될 필요가 있다고 가정해보자. Usecase가 아닌, Repository 를 직접 참조하게 된다면 Repository 수정에 따른 ViewModel의 수정이 불가피할 것이다. 그런데 Usecase를 참조한다면? VIewModel은 그냥 가만히 있어도 된다. 비즈니스 로직을 담고 있는 Usecase 내에서 수정이 일어나기 때문이다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;설명이 길었지만 결론은 다른 Layer들이 어떤 일을 하고 있는지 알 필요가 전혀 없다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;직접적으로 맞닿아있는 안쪽 Layer만 알고 있으면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;s&gt;내 윗집, 옆집에&amp;nbsp; 누가 살고 있는지만 알면 된다는 점&lt;/s&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론,&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;유지보수 용이&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;직관적인 요구사항&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Presentation Layer&lt;/span&gt;&lt;/h2&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ViewModel에는 import UIkit, SwiftUI가 없어야 한다. (모든 UI 프레임워크와의 단절)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 구현하면 UIKit &amp;rarr; SwiftUI 전환이 매우 용이해진다. 다른 부분은 신경쓰지 않고 UI만 변경해주면 되기 때문이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. ViewModel&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;protocol MoviesListViewModelInput {
    func didSearch(query: String)
    func didSelect(at indexPath: IndexPath)
}

protocol MoviesListViewModelOutput {
    var items: Observable&amp;lt;[MoviesListItemViewModel]&amp;gt; { get }
    var error: Observable&amp;lt;String&amp;gt; { get }
}

protocol MoviesListViewModel: MoviesListViewModelInput, MoviesListViewModelOutput { }
&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&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;pre class=&quot;swift&quot;&gt;&lt;code&gt;final class DefaultMoviesListViewModel: MoviesListViewModel {
    
    private let searchMoviesUseCase: SearchMoviesUseCase
    private let actions: MoviesListViewModelActions?
    
    private var movies: [Movie] = []
    
    // MARK: - OUTPUT
    let items: Observable&amp;lt;[MoviesListItemViewModel]&amp;gt; = Observable([])
    let error: Observable&amp;lt;String&amp;gt; = Observable(&quot;&quot;)
    
    init(searchMoviesUseCase: SearchMoviesUseCase,
         actions: MoviesListViewModelActions) {
        self.searchMoviesUseCase = searchMoviesUseCase
        self.actions = actions
    }
    
    private func load(movieQuery: MovieQuery) {
        
        searchMoviesUseCase.execute(movieQuery: movieQuery) { result in
            switch result {
            case .success(let moviesPage):
                // Note: We must map here from Domain Entities into Item View Models. Separation of Domain and View
                self.items.value += moviesPage.movies.map(MoviesListItemViewModel.init)
                self.movies += moviesPage.movies
            case .failure:
                self.error.value = NSLocalizedString(&quot;Failed loading movies&quot;, comment: &quot;&quot;)
            }
        }
    }
}
&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;생성자로 Domain Layer(Usecase)의 인스턴스를 전달받는다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;// Note: This item view model is to display data and does not contain any domain model to prevent views accessing it
struct MoviesListItemViewModel: Equatable {
    let title: String
}

extension MoviesListItemViewModel {
    init(movie: Movie) {
        self.title = movie.title ?? &quot;&quot;
    }
}
&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 Model을 직접적으로 참조하면 안된다. View가 직접적으로 접근하면 안되기 때문이다. (처음에 그림을 생각할 것)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. ViewController&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;import UIKit

final class MoviesListViewController: UIViewController, StoryboardInstantiable, UISearchBarDelegate {
    
    private var viewModel: MoviesListViewModel!
    
    final class func create(with viewModel: MoviesListViewModel) -&amp;gt; MoviesListViewController {
        let vc = MoviesListViewController.instantiateViewController()
        vc.viewModel = viewModel
        return vc
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        bind(to: viewModel)
    }
    
    private func bind(to viewModel: MoviesListViewModel) {
        viewModel.items.observe(on: self) { [weak self] items in
            self?.moviesTableViewController?.items = items
        }
        viewModel.error.observe(on: self) { [weak self] error in
            self?.showError(error)
        }
    }
    
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        guard let searchText = searchBar.text, !searchText.isEmpty else { return }
        viewModel.didSearch(query: searchText)
    }
}
&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UI는 비즈니스 로직이나 어플리케이션 로직에 직접적으로 접근할 수 없고, 항상 ViewModel에만 접근할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아까 말했던 것처럼 ViewModel에 Entity에 해당하는 프로퍼티(items)를 별도로 둬서 View에서 Entity에 대해 알 필요가 없게 만들었다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 View에서 ViewModel을 Observing하는 형태로 구현한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;weak self를 통해 순환 참조를 방지한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;protocol MoviesSearchFlowCoordinatorDependencies  {
    func makeMoviesListViewController() -&amp;gt; UIViewController
    func makeMoviesDetailsViewController(movie: Movie) -&amp;gt; UIViewController
}

final class MoviesSearchFlowCoordinator {
    
    private weak var navigationController: UINavigationController?
    private let dependencies: MoviesSearchFlowCoordinatorDependencies

    init(navigationController: UINavigationController,
         dependencies: MoviesSearchFlowCoordinatorDependencies) {
        self.navigationController = navigationController
        self.dependencies = dependencies
    }
    
    func start() {
        // Note: here we keep strong reference with actions closures, this way this flow do not need to be strong referenced
        let actions = MoviesListViewModelActions(showMovieDetails: showMovieDetails)
        let vc = dependencies.makeMoviesListViewController(actions: actions)
        
        navigationController?.pushViewController(vc, animated: false)
    }
    
    private func showMovieDetails(movie: Movie) {
        let vc = dependencies.makeMoviesDetailsViewController(movie: movie)
        navigationController?.pushViewController(vc, animated: true)
    }
}
&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;View와 ViewModel 사이에 Coordinator를 추가해서 ViewModel을 수정하지 않고 다른 뷰를 쉽게 사용할 수 있게 만들어 준다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 ViewController에 대한 책임을 줄여주는 역할을 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Delegate랑 비슷한듯? Coordinator는 좀 더 알아봐야겠다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Data Layer&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;final class DefaultMoviesRepository {
    
    private let dataTransferService: DataTransfer
    
    init(dataTransferService: DataTransfer) {
        self.dataTransferService = dataTransferService
    }
}

extension DefaultMoviesRepository: MoviesRepository {
    
    public func fetchMoviesList(query: MovieQuery, page: Int, completion: @escaping (Result&amp;lt;MoviesPage, Error&amp;gt;) -&amp;gt; Void) -&amp;gt; Cancellable? {
        
        let endpoint = APIEndpoints.getMovies(with: MoviesRequestDTO(query: query.query,
                                                                     page: page))
        return dataTransferService.request(with: endpoint) { (response: Result&amp;lt;MoviesResponseDTO, Error&amp;gt;) in
            switch response {
            case .success(let moviesResponseDTO):
                completion(.success(moviesResponseDTO.toDomain()))
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }
}

// MARK: - Data Transfer Object (DTO)
// It is used as intermediate object to encode/decode JSON response into domain, inside DataTransferService
struct MoviesRequestDTO: Encodable {
    let query: String
    let page: Int
}

struct MoviesResponseDTO: Decodable {
    private enum CodingKeys: String, CodingKey {
        case page
        case totalPages = &quot;total_pages&quot;
        case movies = &quot;results&quot;
    }
    let page: Int
    let totalPages: Int
    let movies: [MovieDTO]
}
...
// MARK: - Mappings to Domain
extension MoviesResponseDTO {
    func toDomain() -&amp;gt; MoviesPage {
        return .init(page: page,
                     totalPages: totalPages,
                     movies: movies.map { $0.toDomain() })
    }
}
...
&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Usecase가 의존하고 있는 Repository가 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;DTO(Data Transfer Object)가 정의되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;DB, Network로 데이터를 가져오고, Domain에 매핑된다. (completion으로 전달, Usecase를 거쳐 최종적으로 ViewModel까지)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개념적으로는 이해를 했지만 실제로 써보면서 요놈들이랑 더 친해져야겠다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>  개발/Architecture</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/211</guid>
      <comments>https://codekodo.tistory.com/211#entry211comment</comments>
      <pubDate>Sat, 7 Jan 2023 12:53:55 +0900</pubDate>
    </item>
    <item>
      <title>[iOS / SwiftUI] MapKit, 실시간으로 도로명 주소 변환하기</title>
      <link>https://codekodo.tistory.com/210</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #f89009; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;i&gt;23.01.13 - 앱을 처음 설치했을 때 현재 위치로 이동하지 않는 오류 해결&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 번 프로토타입에 이어 이번 주부터는 MVP를 진행하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존에 더미 데이터로 구현했던 것들을 실제 FireStore와 연동하고 구현하지 못했던 부분들을 구현하는 것을 목표로 잡았다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 주에 구현하려는 기능은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 사용자가 지도를 움직이면 움직인 좌표에 대한 도로명 주소를 실시간을 가져옴&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 사용자가 지도를 움직이면 마커가 살짝 위로 올라가고, 움직임이 멈추면 마커가 다시 내려옴&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 사용자의 현재 위치를 가져오고, 버튼을 클릭하면 현재 위치로 지도의 Focus를 변경함&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하나씩 살펴보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 사용자가 지도를 움직이면 움직인 좌표에 대한 도로명 주소를 실시간으로 가져오기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;말이 좀 길다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;간단하게 설명하자면 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;지도를 움직였을 때 도로명 주소 가져온다&lt;/b&gt;&lt;/span&gt;고 생각하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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-12-23 14.12.02.png&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lXQSd/btrUpCrgJJn/KhvXx6LKoFGA0uJ5HcinbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lXQSd/btrUpCrgJJn/KhvXx6LKoFGA0uJ5HcinbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lXQSd/btrUpCrgJJn/KhvXx6LKoFGA0uJ5HcinbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlXQSd%2FbtrUpCrgJJn%2FKhvXx6LKoFGA0uJ5HcinbK%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;699&quot; height=&quot;204&quot; data-filename=&quot;스크린샷 2022-12-23 14.12.02.png&quot; data-origin-width=&quot;1692&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CLLocationManagerDelgate를 채택하고 mapViewDidChangeVisibleRegion(_&amp;nbsp;mapView:&amp;nbsp;MKMapView)&amp;nbsp;메서드로&amp;nbsp;지도가&amp;nbsp;움직일&amp;nbsp;때&amp;nbsp;위치&amp;nbsp;정보를&amp;nbsp;가져오려고&amp;nbsp;했었다.&amp;nbsp;해당&amp;nbsp;메서드는&amp;nbsp;지도에서&amp;nbsp;보이는&amp;nbsp;영역이&amp;nbsp;변경되면&amp;nbsp;호출되는&amp;nbsp;메서드이다.&amp;nbsp;따라서&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;사용자가&amp;nbsp;지도를&amp;nbsp;움직이면&amp;nbsp;이&amp;nbsp;메서드가&amp;nbsp;호출되고,&amp;nbsp;이&amp;nbsp;메서드&amp;nbsp;내부에서&amp;nbsp;위,&amp;nbsp;경도를&amp;nbsp;도로명&amp;nbsp;주소로&amp;nbsp;변환해주는&amp;nbsp;ReverseGeocoding을&amp;nbsp;진행&lt;/b&gt;&lt;/span&gt;하면&amp;nbsp;될&amp;nbsp;것&amp;nbsp;같았다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자가 지도를 움직일 때, 즉각적으로 위치 정보는 잘 가져왔다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 이를 도로명 주소로 변환하기 위해서 reverseGeocoding을 적용하면 문제가 발생했다.&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-12-20 19.37.26.png&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KLzTe/btrT9XviucK/OPAK2thtFq2aD9zAUQCBjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KLzTe/btrT9XviucK/OPAK2thtFq2aD9zAUQCBjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KLzTe/btrT9XviucK/OPAK2thtFq2aD9zAUQCBjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKLzTe%2FbtrT9XviucK%2FOPAK2thtFq2aD9zAUQCBjk%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;700&quot; height=&quot;131&quot; data-filename=&quot;스크린샷 2022-12-20 19.37.26.png&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;480&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;에러를 읽어보면 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;API 호출을 너무 자주&lt;/b&gt;&lt;/span&gt;해주고 있다는 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 문제를 해결하기 위해선 가장 먼저 떠오른 방법은&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위, 경도가 바뀔 때마다 호출하는 것이 아니라, &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;TimeInterval을 줘서 특정 시간에 한 번씩만 호출&lt;/span&gt;&lt;/b&gt;되도록 하는 방법이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;검색해보니 어떤 분이 이 방식으로 같은 문제를 해결하고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1671533122428&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;[ios - Swift] MapView 사용하기 (MKMapView 2 / 2)&quot; data-og-description=&quot;이번 글에서는 지난 글에서 얘기한 것과 같이 지역 내 검색 및 좌표에 대한 정보를 추출하는 방법을 알아보도록 하겠습니다. 위 gif를 살펴보면 맵 뷰의 현재 위치, 중앙 위치의 주소, 주변 가게&quot; data-og-host=&quot;poky-develop.tistory.com&quot; data-og-source-url=&quot;https://poky-develop.tistory.com/27&quot; data-og-url=&quot;https://poky-develop.tistory.com/27&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cCYC9v/hyQYDLPJz0/TGBhrzhW4RBOinjvOg2bl1/img.gif?width=320&amp;amp;height=692&amp;amp;face=0_0_320_692,https://scrap.kakaocdn.net/dn/lq5cO/hyQYClRkW2/bkpCRBPMQwCOvKPV0a3GG1/img.gif?width=320&amp;amp;height=692&amp;amp;face=0_0_320_692&quot;&gt;&lt;a href=&quot;https://poky-develop.tistory.com/27&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://poky-develop.tistory.com/27&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cCYC9v/hyQYDLPJz0/TGBhrzhW4RBOinjvOg2bl1/img.gif?width=320&amp;amp;height=692&amp;amp;face=0_0_320_692,https://scrap.kakaocdn.net/dn/lq5cO/hyQYClRkW2/bkpCRBPMQwCOvKPV0a3GG1/img.gif?width=320&amp;amp;height=692&amp;amp;face=0_0_320_692');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[ios - Swift] MapView 사용하기 (MKMapView 2 / 2)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 지난 글에서 얘기한 것과 같이 지역 내 검색 및 좌표에 대한 정보를 추출하는 방법을 알아보도록 하겠습니다. 위 gif를 살펴보면 맵 뷰의 현재 위치, 중앙 위치의 주소, 주변 가게&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;poky-develop.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;전에 플러터로 GoogleMaps을 사용할 때 비슷한 기능을 제공하는 메서드가 있었고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;MapKit에도 있을 것이라고 생각하고 공식문서를 찾아봤다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;역시나 Mapkit에서도 제공하고 있었다.&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-12-20 19.50.31.png&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dux4L/btrT9AmTMtg/FP0eQHhK1jqjU2i8stxEVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dux4L/btrT9AmTMtg/FP0eQHhK1jqjU2i8stxEVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dux4L/btrT9AmTMtg/FP0eQHhK1jqjU2i8stxEVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDux4L%2FbtrT9AmTMtg%2FFP0eQHhK1jqjU2i8stxEVK%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;698&quot; height=&quot;276&quot; data-filename=&quot;스크린샷 2022-12-20 19.50.31.png&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;470&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;mapView(_:regionWillChangeAnimated:) 는 Position이 변경되기 직전에&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot; data-v-97436168=&quot;&quot; data-v-91bcffaa=&quot;&quot;&gt;mapViewDidChangeVisibleRegion 는 Position이 변경될&amp;nbsp;때&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot; data-v-97436168=&quot;&quot; data-v-91bcffaa=&quot;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;mapView(MKMapView, regionDidChangeAnimated:) 는 Position 변경이 종료된 후&lt;/span&gt;&lt;/b&gt;에 호출된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot; data-v-97436168=&quot;&quot; data-v-91bcffaa=&quot;&quot;&gt;그 중에서 나는 세 번째 메서드를 통해 ReverseGeocoding을 진행했고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이해를 돕기 위해 해당 메서드 내부에 print문을 찍어봤다.&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;ezgif.com-gif-maker (60).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;743&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9n02t/btrUrtnh1Nw/pR6PAytpiKpv1k5KhfukC0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9n02t/btrUrtnh1Nw/pR6PAytpiKpv1k5KhfukC0/img.gif&quot; data-alt=&quot;움직임을 멈추면 Changed가 찍힌다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9n02t/btrUrtnh1Nw/pR6PAytpiKpv1k5KhfukC0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/c9n02t/btrUrtnh1Nw/pR6PAytpiKpv1k5KhfukC0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;622&quot; data-filename=&quot;ezgif.com-gif-maker (60).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;743&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;움직임을 멈추면 Changed가 찍힌다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot; data-v-97436168=&quot;&quot; data-v-91bcffaa=&quot;&quot;&gt;이렇게 사용자가 지도를 움직이고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위도, 경도가 조금이라도 바뀌면 mapViewDidChangeVisibleRegion&amp;nbsp;메서드가&amp;nbsp;호출되면서&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1671773409831&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func convertCLLocationToAddress(location: CLLocation) {
        let geocoder = CLGeocoder()
        
        // Location To Address
        geocoder.reverseGeocodeLocation(location) { placemarks, error in
            if error != nil {
                return
            }
            
            guard let placemark = placemarks?.first else { return }
            self.startPlace = &quot;\(placemark.country ?? &quot;&quot;) \(placemark.locality ?? &quot;&quot;) \(placemark.name ?? &quot;&quot;)&quot;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위에&amp;nbsp;구현한&amp;nbsp;convertCLLocationToAddress&amp;nbsp;메서드가&amp;nbsp;호출되도록&amp;nbsp;구현했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;convertCLLocationToAddress은 Swift에서&amp;nbsp;자체적으로&amp;nbsp;제공하는&amp;nbsp;reverseGeocodeLocation을&amp;nbsp;활용했다.&amp;nbsp;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게&amp;nbsp;위,&amp;nbsp;경도를&amp;nbsp;도로명&amp;nbsp;주소로&amp;nbsp;변환하는&amp;nbsp;기능은&amp;nbsp;구현했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 사용자가 지도를 움직이면 마커가 살짝 위로 올라가고, 움직임이 멈추면 마커가 다시 내려옴&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다음은 두 번째, 역시 말이 길다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;사용자가 지도를 움직이면 마커에 특정 효과&lt;/span&gt;&lt;/b&gt;를 주고 싶었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 기능이 왜 필요하냐고 물어본다면... &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자 인터랙션을 좋아하기에... &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;1.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsRyCa/btr2hJuQovQ/woyC5mGcza65ZqPYBkr6Uk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsRyCa/btr2hJuQovQ/woyC5mGcza65ZqPYBkr6Uk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsRyCa/btr2hJuQovQ/woyC5mGcza65ZqPYBkr6Uk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bsRyCa/btr2hJuQovQ/woyC5mGcza65ZqPYBkr6Uk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;650&quot; data-filename=&quot;1.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mXTw6/btr2hIQePFy/7D6FEsrAP0igyBLhLwp8vK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mXTw6/btr2hIQePFy/7D6FEsrAP0igyBLhLwp8vK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mXTw6/btr2hIQePFy/7D6FEsrAP0igyBLhLwp8vK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/mXTw6/btr2hIQePFy/7D6FEsrAP0igyBLhLwp8vK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;650&quot; data-filename=&quot;2.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아무튼 이 기능이 좋아서 구현을 하려고 했고, &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;역시 위에서 언급한 메서드를 사용했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;사용자가 지도를 움직이고 있다는 상태를 저장하기 위한 프로퍼티를 정의해서 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1671534328800&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	@Published var isChanging: Bool = false // 지도의 움직임 여부를 저장하는 프로퍼티
	
	// ... 생략

	// MARK: - MapView에서 화면이 이동하면 호출되는 메서드
    func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
        DispatchQueue.main.async {
            self.isChanging = true
        }
    }
    
    // MARK: - MapView에서 화면 이동이 종료되면 호출되는 메서드
    func mapView(_ mapView: MKMapView, regionDidChangeAnimated: Bool) {
        let location: CLLocation = CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
        
        self.convertLocationToAddress(location: location)
        
        DispatchQueue.main.async {
            self.isChanging = false
        }
    }
    
    // ... 생략
    
    // MARK: - location을 도로명 주소로 변환해주는 메서드
    func convertLocationToAddress(location: CLLocation) {
        let geocoder = CLGeocoder()
        
        geocoder.reverseGeocodeLocation(location) { placemarks, error in
            if error != nil {
                return
            }
            
            guard let placemark = placemarks?.first else { return }
            
            self.startPlace = &quot;\(placemark.country ?? &quot;&quot;) \(placemark.locality ?? &quot;&quot;) \(placemark.name ?? &quot;&quot;)&quot;
        }
    }&lt;/code&gt;&lt;/pre&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 사용자의 현재 위치를 가져오고, 버튼을 클릭하면 현재 위치로 지도의 Focus를 변경함&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마지막 세 번째는 &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;사용자의 현재 위치를 가져오고, 버튼을 클릭하면 사용자의 현재 위치로 이동하는 기능&lt;/span&gt;&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;info 파일에 아래처럼 Key를 추가하고&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-12-20 20.08.50.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4gF9K/btrUbrWATub/jQ1SV9RTapuY7EG9h8Enuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4gF9K/btrUbrWATub/jQ1SV9RTapuY7EG9h8Enuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4gF9K/btrUbrWATub/jQ1SV9RTapuY7EG9h8Enuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4gF9K%2FbtrUbrWATub%2FjQ1SV9RTapuY7EG9h8Enuk%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;700&quot; height=&quot;162&quot; data-filename=&quot;스크린샷 2022-12-20 20.08.50.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;274&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자가 위치 권한을 설정했는지 확인하고, 위치 권한을 요청하는 메서드를 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1671534667970&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // MARK: - 사용자의 위치 권한 여부를 확인하고 요청하거나 현재 위치 MapView를 이동하는 메서드
    func configureLocationManager() {
        mapView.delegate = self
        manager.delegate = self
        
        let status = manager.authorizationStatus
        
        if status == .notDetermined {
            manager.requestAlwaysAuthorization()
        } else if status == .authorizedAlways || status == .authorizedWhenInUse {
            self.moveFocusOnUserLocation()
        }
    }
    
    // MARK: - 사용자의 현재 위치로 MapView를 이동하는 메서드
    func moveFocusOnUserLocation() {
        mapView.showsUserLocation = true
        mapView.setUserTrackingMode(.follow, animated: true)
    }&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 사용자가 위치 권한을 변경하면 호출되는 메서드에서도 moveFocusOnUserLocation을 호출했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서를 보다가 기존에 사용한 메서드는 Deprecated 된 메서드여서 새로운 메서드로 변경했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1671535975339&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // MARK: - 사용자에게 위치 권한이 변경되면 호출되는 메서드
    // Deprecated
//    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
//
//        if status == .authorizedAlways || status == .authorizedWhenInUse {
//            self.moveFocusOnUserLocation()
//        }
//    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if manager.authorizationStatus == .authorizedAlways || manager.authorizationStatus == .authorizedWhenInUse {
            self.moveFocusOnUserLocation()
        }
    }&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사실 locationManagerDidChangeAuthorization 메서드를 사용하지 않아도 앱은 구현하려는 기능은 정상적으로 작동한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;구현한 코드 상에서는 LocationManager가 EnvironmentObject로 생성되어 있고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 앱이 실행될 때 LocationManager의 인스턴스를 생성한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 이후에 configureLocationManger 메서드가 호출되는 방식이다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 사용자가 위치 권한은 변경했는지와 관계 없이 위치 권한이 &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;허용 상태&lt;/span&gt;&lt;/b&gt;이면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자의 현재 위치로 이동하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1671776084001&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Apple Developer Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/3563956-locationmanagerdidchangeauthoriz&quot; data-og-url=&quot;https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/3563956-locationmanagerdidchangeauthoriz&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/3563956-locationmanagerdidchangeauthoriz&quot; data-source-url=&quot;https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/3563956-locationmanagerdidchangeauthoriz&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 공식문서를 읽어보면 알 수 있는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해당 메서드는 LocationManager의 &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;인스턴스가 생성&lt;/span&gt;&lt;/b&gt;되거나 위치 권한이 변경될 때 호출된다고 나와있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;실제로 앱이 실행될 때 EnvironmentObject로 LocationManager의 인스턴스를 생성하므로 앱의 완성도를 좀 더 높힌다면?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;configureLocationManager에서 moveFocusOnUserLocation를 호출하지 않고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;locationManagerDidChangeAuthorization에서 호출하는 것이 더 적절하다고 생각한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (59).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xsQl7/btrUap54yXN/sc85Q7oRKAnb6mPpG9LjY0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xsQl7/btrUap54yXN/sc85Q7oRKAnb6mPpG9LjY0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xsQl7/btrUap54yXN/sc85Q7oRKAnb6mPpG9LjY0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/xsQl7/btrUap54yXN/sc85Q7oRKAnb6mPpG9LjY0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;749&quot; data-filename=&quot;ezgif.com-gif-maker (59).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1238&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;아무튼 여기까지 해서 버그 해결~  &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;&lt;i&gt;(23.01.13 - 앱을 처음 설치했을 때 현재 위치로 이동하지 않는 오류 해결)&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;i&gt;&lt;/i&gt;&lt;i&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선, 프로젝트에 대해 간단하게 설명하자면 간단한 일기 앱이고, 일기를 작성할 때 사용자의 위치를 등록할 수 있도록 구현할 계획이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 일기 작성 페이지에서 MapView를 .onSheet(또는 .FullScreen)로 적용하고 있었다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 프로젝트에 기존 코드를 그대로 적용을 했는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱을 처음 실행했을 때 위치 권한을 부여하고, 이후에 MapView가 정상적으로 작동하지 않는 오류가 있었다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오류를 살펴보면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 사용자의 현재 위치를 가져오지 못함.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2.&amp;nbsp;다른 기능(화면을 움직일 때 마커가 위, 아래로 움직이는 기능, 실시간으로 도로명 주소를 가져오는 기능)이 정상적으로 작동하지 않음.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드를 다시 하나 하나 살펴보면서 내린 결론은 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;LocationManager 인스턴스의 생성 시기&lt;/b&gt;&lt;/span&gt;였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존 코드는&amp;nbsp;MapView에서 LocationManager의 인스턴스를 생성하고 있었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이를 MapView가 보여지는 페이지가 아닌 이전 페이지(일기를 작성하는 페이지)에서 생성하고 파라미터로 넘겨주는 방식으로 변경했고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오류를 해결하면서 불필요한 코드를 제거하고, 위에서 언급한 moveFocusOnUserLocation의 역할을&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;locationManagerDidChangeAuthorization에서 처리하도록 변경했다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 didUpdateLocation 메서드의 경우 특정 조건에서만 실행되고, 이 메서드를 호출하지 않아도 기능을 정상적으로 구현할 수 있기에 내부의 코드를 지워줬다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;전체 코드&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1671536195642&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ... 생략
// MARK: - MapView 커스텀
struct MapViewCoordinator: UIViewRepresentable {
    @ObservedObject var locationManager: LocationManager
    
    func makeUIView(context: Context) -&amp;gt; some UIView {
        return locationManager.mapView
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) { }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1671536099928&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//
//  LocationManager.swift
//
//  Created by 고도 on 2022/12/20.
//

import Foundation
import MapKit

class LocationManager: NSObject, ObservableObject, MKMapViewDelegate, CLLocationManagerDelegate {
    @Published var mapView: MKMapView = .init()
    @Published var isChanging: Bool = false // 지도의 움직임 여부를 저장하는 프로퍼티
    @Published var currentPlace: String = &quot;&quot; // 현재 위치의 도로명 주소를 저장하는 프로퍼티
    
    private var manager: CLLocationManager = .init()
    private var currentGeoPoint: CLLocationCoordinate2D? // 현재 위치를 저장하는 프로퍼티
    
    override init() {
        super.init()
        
        self.configureLocationManager()
    }
    
    // MARK: - 사용자의 위치 권한 여부를 확인하고 요청하거나 현재 위치 MapView를 이동하는 메서드
    func configureLocationManager() {
        mapView.delegate = self
        manager.delegate = self
        
        let status = manager.authorizationStatus
        
        if status == .notDetermined {
            manager.requestAlwaysAuthorization()
        } else if status == .authorizedAlways || status == .authorizedWhenInUse {
            mapView.showsUserLocation = true // 사용자의 현재 위치를 확인할 수 있도록
        }
    }
    
    // MARK: - MapView에서 화면이 이동하면 호출되는 메서드
    func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
        DispatchQueue.main.async {
            self.isChanging = true
        }
    }
    
    // MARK: - MapView에서 화면 이동이 종료되면 호출되는 메서드
    func mapView(_ mapView: MKMapView, regionDidChangeAnimated: Bool) {
        let location: CLLocation = CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
        
        self.convertLocationToAddress(location: location)
        
        DispatchQueue.main.async {
            self.isChanging = false
        }
    }
    
    // MARK: - 특정 위치로 MapView의 Focus를 이동하는 메서드
    func mapViewFocusChange() {
        print(&quot;[SUCCESS] Map Focus Changed&quot;)
        let span = MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)
        let region = MKCoordinateRegion(center: self.currentGeoPoint ??  CLLocationCoordinate2D(latitude: 37.394776, longitude: 127.11116), span: span)
        mapView.setRegion(region, animated: true)
    }

    // MARK: - 사용자에게 위치 권한이 변경되면 호출되는 메서드 (LocationManager 인스턴스가 생성될 때도 호출)
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if manager.authorizationStatus == .authorizedAlways || manager.authorizationStatus == .authorizedWhenInUse {
            guard let location = manager.location else {
                print(&quot;[ERROR] No Location&quot;)
                return
            }
            
            self.currentGeoPoint = location.coordinate // 현재 위치를 저장하고
            self.mapViewFocusChange() // 현재 위치로 MapView를 이동
            self.convertLocationToAddress(location: location)
        }
    }
    
    // MARK: - 사용자의 위치가 변경되면 호출되는 메서드
    /// startUpdatingLocation 메서드 또는 requestLocation 메서드를 호출했을 때에만 이 메서드가 호출
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print(&quot;[SUCCESS] Did Update Locations&quot;)
    }
    
    // MARK: - 사용자의 현재 위치를 가져오는 것을 실패했을 때 호출되는 메서드
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print(error)
    }
    
    // MARK: - location을 도로명 주소로 변환해주는 메서드
    func convertLocationToAddress(location: CLLocation) {
        let geocoder = CLGeocoder()
        
        geocoder.reverseGeocodeLocation(location) { placemarks, error in
            if error != nil {
                return
            }
            
            guard let placemark = placemarks?.first else { return }
            
            self.startPlace = &quot;\(placemark.country ?? &quot;&quot;) \(placemark.locality ?? &quot;&quot;) \(placemark.name ?? &quot;&quot;)&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다음 게시물은 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;네이버 검색 API&lt;/b&gt;&lt;/span&gt;와 &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Katech 좌표를 WGS 좌표로 변환하는 네이버 Maps API&lt;/span&gt;&lt;/b&gt;을 활용해서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;목적지를 지도 상에 표시해주는 기능에 대한 게시물로!&lt;/span&gt;&lt;/p&gt;</description>
      <category>  개발/iOS</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/210</guid>
      <comments>https://codekodo.tistory.com/210#entry210comment</comments>
      <pubDate>Tue, 20 Dec 2022 20:05:32 +0900</pubDate>
    </item>
    <item>
      <title>[iOS / SwiftUI] OnAppear, OnDisappear는 언제 호출될까?</title>
      <link>https://codekodo.tistory.com/209</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SwiftUI로 개발을 진행하다가 View 내부에서 직접적으로 프로퍼티에 접근할 때 onAppear를 한 번쯤은 사용한 경험이 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;가령, print(프로퍼티)처럼&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;View 내부에서 접근하면 이런 에러가 뜬다.&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-12-01 00.05.25.png&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cl1CS/btrSxD5O4AG/RUuYdKJKOaFkJ60sFyysYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cl1CS/btrSxD5O4AG/RUuYdKJKOaFkJ60sFyysYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cl1CS/btrSxD5O4AG/RUuYdKJKOaFkJ60sFyysYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCl1CS%2FbtrSxD5O4AG%2FRUuYdKJKOaFkJ60sFyysYk%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;349&quot; height=&quot;68&quot; data-filename=&quot;스크린샷 2022-12-01 00.05.25.png&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;136&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;View를 반환해달라는 에러다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이런 에러와 마주치지 않기 위해 View 내부에서 직접적으로 접근하지 않고, onAppear 클로저 내부에서 접근하는 방식을 택한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아무튼 이럴 때 자주 사용하는 onAppear는 SwiftUI View Life Cycle에 속하고, 오늘은 Life Cycle에 대해 알아보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SwiftUI에선 3개의 Life Cycle이 있고, 각각의 Appear, Update, Disappear 이다.&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-12-01 00.07.27.png&quot; data-origin-width=&quot;2130&quot; data-origin-height=&quot;1044&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/60val/btrSvzXVHTm/TpnmfHNkCcQbc6FLqc4jVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/60val/btrSvzXVHTm/TpnmfHNkCcQbc6FLqc4jVk/img.png&quot; data-alt=&quot;https://www.vadimbulavin.com/swiftui-view-lifecycle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/60val/btrSvzXVHTm/TpnmfHNkCcQbc6FLqc4jVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F60val%2FbtrSvzXVHTm%2FTpnmfHNkCcQbc6FLqc4jVk%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;699&quot; height=&quot;343&quot; data-filename=&quot;스크린샷 2022-12-01 00.07.27.png&quot; data-origin-width=&quot;2130&quot; data-origin-height=&quot;1044&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.vadimbulavin.com/swiftui-view-lifecycle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. onAppear&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-12-01 00.12.23.png&quot; data-origin-width=&quot;2402&quot; data-origin-height=&quot;1834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lAYQD/btrSwsKJPH8/iLWhBdC5Z3Hk4qXbKsDG5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lAYQD/btrSwsKJPH8/iLWhBdC5Z3Hk4qXbKsDG5K/img.png&quot; data-alt=&quot;https://developer.apple.com/documentation/swiftui/view/onappear(perform:)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lAYQD/btrSwsKJPH8/iLWhBdC5Z3Hk4qXbKsDG5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlAYQD%2FbtrSwsKJPH8%2FiLWhBdC5Z3Hk4qXbKsDG5K%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;700&quot; height=&quot;534&quot; data-filename=&quot;스크린샷 2022-12-01 00.12.23.png&quot; data-origin-width=&quot;2402&quot; data-origin-height=&quot;1834&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developer.apple.com/documentation/swiftui/view/onappear(perform:)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;onAppear은 View가 보여지기 전에 호출된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UIkit으로 치면 viewWillAppear라고 생각하는데 아래 링크에서는 viewDidAppear라고 설명하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669825956901&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;How to respond to view lifecycle events: onAppear() and onDisappear() - a free SwiftUI by Example tutorial&quot; data-og-description=&quot;Was this page useful? Let us know! 1 2 3 4 5&quot; data-og-host=&quot;www.hackingwithswift.com&quot; data-og-source-url=&quot;https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear&quot; data-og-url=&quot;https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/VND0a/hyQK2rG4uU/qugAde7PnL7n4maH3FheM1/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/VND0a/hyQK2rG4uU/qugAde7PnL7n4maH3FheM1/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&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;How to respond to view lifecycle events: onAppear() and onDisappear() - a free SwiftUI by Example tutorial&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Was this page useful? Let us know! 1 2 3 4 5&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.hackingwithswift.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;실제로 공식 문서에서도 &quot;before this view appears&quot;로 나와있고, 뷰가 나타나기 전이니까 willAppear가 맞지 않을까 하는데...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;s&gt;(알고 게신분이 있으시면 도움 부탁드립니다 ㅠ)&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2.&amp;nbsp; onDisappear&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-12-01 00.18.30.png&quot; data-origin-width=&quot;2376&quot; data-origin-height=&quot;1794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xiMLD/btrSxldcFYv/hDk0OwRGRpCSzzSlYPHlx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xiMLD/btrSxldcFYv/hDk0OwRGRpCSzzSlYPHlx0/img.png&quot; data-alt=&quot;https://developer.apple.com/documentation/swiftui/view/ondisappear(perform:)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xiMLD/btrSxldcFYv/hDk0OwRGRpCSzzSlYPHlx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxiMLD%2FbtrSxldcFYv%2FhDk0OwRGRpCSzzSlYPHlx0%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;700&quot; height=&quot;529&quot; data-filename=&quot;스크린샷 2022-12-01 00.18.30.png&quot; data-origin-width=&quot;2376&quot; data-origin-height=&quot;1794&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developer.apple.com/documentation/swiftui/view/ondisappear(perform:)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 Disappear는?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;onAppear과 반대로 View가 사라진 뒤에 호출된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요건 UiKit에서의 viewDidDisappear라고 생각한다. 사라진 뒤에 호출되는 거니까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 뭔가 이렇게 세트로 맞아야할 것 같은데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;b&gt;(SwiftUI) - (UIKit)&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;onAppear - viewWillAppear&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;onDisappear - viewWillDisappear&lt;/i&gt;&lt;b&gt;&lt;i&gt;&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;내 생각대로하면 이런 느낌이라&lt;b&gt;&lt;i&gt;&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;b&gt;(SwiftUI) - (UIKit)&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;onAppear - viewWillAppear&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;onDisappear - viewDidDisappear&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;안 맞으니까 불편하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 부분은 좀 더 알아봐야겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. NavigationStack&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Update에 대해서는 나중에 다시 한 번 알아보도록 하고...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사실 오늘은 특정 뷰의 Life Cycle을 알아보다가 신기해서 적는 글이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;뭔가 전환이 일어나거나 특정 뷰 위에 존재하는 뷰인 NavigationStack - NavigationLink, ModalSheet, Alert.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 친구들에 대해서 알아봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 첫 번째로, NavigationStack에서의 언제 호출되는지 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669822132182&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import SwiftUI

// 첫 번째 뷰
struct FirstView: View {
    var body: some View {
        NavigationStack {
            NavigationLink(destination: SecondView()) {
                Text(&quot;다음으로 가볼까요?&quot;)
            }
        }
        .onAppear {
            print(&quot;FirstView Appear&quot;)
        }
        .onDisappear {
            print(&quot;FirstView Disappear&quot;)
        }
    }
}

// 두 번째 뷰
struct SecondView: View {
    var body: some View {
        NavigationLink(destination: ThridView()) {
            Text(&quot;다음으로 가볼까요?&quot;)
        }
        .onAppear {
            print(&quot;SecondView Appear&quot;)
        }
        .onDisappear {
            print(&quot;SecondView Disappear&quot;)
        }
    }
}

// 세 번째 뷰
struct ThridView: View {
    var body: some View {
        NavigationStack {
            Text(&quot;세 번째까지 왔습니다.&quot;)
                .onAppear {
                    print(&quot;ThirdView Appear&quot;)
                }
                .onDisappear {
                    print(&quot;ThirdView Disappear&quot;)
                }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 코드를 작성하고 아까 위에서 봤던 Flow를 그대로 실행하면 어떤 결과가 출력될까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위에서 얘기했던 것처럼 onAppear는 보여지기 전, onDisappear는 사라진 후에 호출된다고 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면&lt;b&gt; 다음 View의 onAppear&lt;/b&gt;가 먼저 출력되고 이후에 &lt;b&gt;이전 View의 onDisappear&lt;/b&gt;가 출력되지 않을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;b&gt;FirstView&amp;nbsp;Appear&lt;br /&gt;SecondView&amp;nbsp;Appear&lt;br /&gt;FirstView&amp;nbsp;Disappear&lt;br /&gt;ThirdView&amp;nbsp;Appear&lt;br /&gt;SecondView&amp;nbsp;Disppear&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 결과는 달랐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (51).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;863&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JOEX8/btrSy7ZAUf9/eKKegkvX2KwREsKW7Frcg1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JOEX8/btrSy7ZAUf9/eKKegkvX2KwREsKW7Frcg1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JOEX8/btrSy7ZAUf9/eKKegkvX2KwREsKW7Frcg1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/JOEX8/btrSy7ZAUf9/eKKegkvX2KwREsKW7Frcg1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;499&quot; height=&quot;718&quot; data-filename=&quot;ezgif.com-gif-maker (51).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;FirstView Disappear가 출력되지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UI에서 사라졌는데 onDisappear가 출력되지 않는게 이상해서 디버깅을 진행했고, 실제로 UI Hierarchy를 출력해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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/bG5NRe/btrSxPrDsPw/82WqpKOwUFhTctIiFkinKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG5NRe/btrSxPrDsPw/82WqpKOwUFhTctIiFkinKk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1582&quot; data-origin-height=&quot;1622&quot; data-filename=&quot;스크린샷 2022-11-30 23.08.26.png&quot; width=&quot;343&quot; height=&quot;352&quot; style=&quot;width: 32.0089%; margin-right: 10px;&quot; data-widthpercent=&quot;32.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG5NRe/btrSxPrDsPw/82WqpKOwUFhTctIiFkinKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG5NRe%2FbtrSxPrDsPw%2F82WqpKOwUFhTctIiFkinKk%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;1582&quot; height=&quot;1622&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KpDnG/btrSw6tE198/uozhtGskSVLhRCM6yN9pfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KpDnG/btrSw6tE198/uozhtGskSVLhRCM6yN9pfk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;1726&quot; data-filename=&quot;스크린샷 2022-11-30 23.10.34.png&quot; width=&quot;341&quot; height=&quot;340&quot; style=&quot;width: 32.9323%; margin-right: 10px;&quot; data-widthpercent=&quot;33.72&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KpDnG/btrSw6tE198/uozhtGskSVLhRCM6yN9pfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKpDnG%2FbtrSw6tE198%2FuozhtGskSVLhRCM6yN9pfk%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;1732&quot; height=&quot;1726&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWi5UC/btrSwsDX3KG/JGxoUUiMAB3eKOsLa4L7i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWi5UC/btrSwsDX3KG/JGxoUUiMAB3eKOsLa4L7i0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1540&quot; data-origin-height=&quot;1544&quot; data-filename=&quot;스크린샷 2022-11-30 23.12.10.png&quot; style=&quot;width: 32.7332%;&quot; data-widthpercent=&quot;33.51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWi5UC/btrSwsDX3KG/JGxoUUiMAB3eKOsLa4L7i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWi5UC%2FbtrSwsDX3KG%2FJGxoUUiMAB3eKOsLa4L7i0%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;1540&quot; height=&quot;1544&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;엥, 똑같다?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사진을 보면 UIKitNavigationController와 NavigationStackHostingController가 동일하게 배치되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;NavigationLink로 화면이 전환되도, 그 기반을 이루는 NavigationStack(Controller)는 실제로 보이진 않지만 그대로 유지되고 있기에 onDisappear가 호출되지 않은 것이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 NavigationStack이 아닌, 내부에 위치하고 있는 NavigationLink에 적용하면 어떻게 될까?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669822955298&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct FirstView: View {
    var body: some View {
        NavigationStack {
            NavigationLink(destination: SecondView()) {
                Text(&quot;다음으로 가볼까요?&quot;)
            }
            .onAppear {
                print(&quot;FirstView Appear&quot;)
            }
            .onDisappear {
                print(&quot;FirstView Disappear&quot;)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;뭔가, 이번엔 제대로 동작할 것 같다는 느낌이 들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (50).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;863&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qn95C/btrSvAide6q/SkO9LbKMhUvprce3okCIwk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qn95C/btrSvAide6q/SkO9LbKMhUvprce3okCIwk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qn95C/btrSvAide6q/SkO9LbKMhUvprce3okCIwk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/qn95C/btrSvAide6q/SkO9LbKMhUvprce3okCIwk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;719&quot; data-filename=&quot;ezgif.com-gif-maker (50).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번엔 우리가 처음에 예상했던 것처럼 출력됐다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;NavigationStack에서 onAppear나 onDisappear를 사용할 필요가 있을 때는 내부에 위치하고 있는 View에서 접근하는 것이 좋을 듯 하다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;NavigationStack이 이렇게 작동하는 걸 보니까 다른 View들도 궁금해졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. Modal View&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 다음 타자는, Modal View였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Model View도 다른 View과 함께 존재하므로 뭔가 알아보고 싶었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669818735810&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct FirstView: View {
    @State private var isShow: Bool = false
    
    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink(destination: SecondView()) {
                    Text(&quot;다음으로 가볼까요?&quot;)
                }
                Button(action: {
                    isShow.toggle()
                }) {
                    Text(&quot;모달 꺼내기&quot;)
                }
                .onAppear {
                    print(&quot;FirstView Appear&quot;)
                }
                .onDisappear {
                    print(&quot;FirstView Disappear&quot;)
                }
            }
            .sheet(isPresented: $isShow, onDismiss: {
                print(&quot;ModalSheet Dismiss&quot;)
            }) {
                Text(&quot;나? 모달&quot;)
            }
            .onAppear {
                print(&quot;ModelSheet Appear&quot;)
            }
            .onDisappear {
                print(&quot;ModelSheet Disappear&quot;)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Model View가 보여지고 닫히는 Flow를 실행하면 어떤 결과가 출력 될까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;b&gt;FirstView Appear&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;b&gt;ModalSheet Appear&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;b&gt;ModalSheet Dismiss&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;b&gt;ModalSheet Disappear&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 달랐다...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (52).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVc4kT/btrSz60wu13/x8DXmX8p53WkzYEntLntg1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVc4kT/btrSz60wu13/x8DXmX8p53WkzYEntLntg1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVc4kT/btrSz60wu13/x8DXmX8p53WkzYEntLntg1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cVc4kT/btrSz60wu13/x8DXmX8p53WkzYEntLntg1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;707&quot; data-filename=&quot;ezgif.com-gif-maker (52).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;FirstView Appear보다 ModalSheet Appear가 먼저 이뤄졌고 Modal이 사라졌을 때 Dismiss만 호출됐을 뿐, Disappear는 호출되지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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/bc4Unr/btrSwrZmy3S/0KohkIXIqicwkKJVxRPzg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc4Unr/btrSwrZmy3S/0KohkIXIqicwkKJVxRPzg1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;1408&quot; data-filename=&quot;스크린샷 2022-12-01 01.16.17.png&quot; style=&quot;width: 47.8531%; margin-right: 10px;&quot; data-widthpercent=&quot;48.42&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc4Unr/btrSwrZmy3S/0KohkIXIqicwkKJVxRPzg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc4Unr%2FbtrSwrZmy3S%2F0KohkIXIqicwkKJVxRPzg1%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;1386&quot; height=&quot;1408&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmT2PL/btrSwsjG8uF/zcsz0KDKrEVNBuvXG0zZzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmT2PL/btrSwsjG8uF/zcsz0KDKrEVNBuvXG0zZzK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;1558&quot; data-filename=&quot;스크린샷 2022-12-01 01.10.37.png&quot; width=&quot;573&quot; height=&quot;546&quot; style=&quot;width: 50.9841%;&quot; data-widthpercent=&quot;51.58&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmT2PL/btrSwsjG8uF/zcsz0KDKrEVNBuvXG0zZzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmT2PL%2FbtrSwsjG8uF%2Fzcsz0KDKrEVNBuvXG0zZzK%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;1634&quot; height=&quot;1558&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UI Hierarchy에서 Modal이 올라오기 전, 올라오고 난 후의 차이가 있음에도 ModalSheet Appear가 Modal이 실제로 올라왔을 때 출력되지 않는 이유에 대해서는 아직 잘 모르겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;s&gt;(알고 게신 분 있으시면 도움 부탁드립니다 ㅠ)&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서, 일단 NavigationStack처럼 내부에 존재하는 View에 onAppear와 onDisappear를 달아줬다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669825147241&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct FirstView: View {
    @State private var isShow: Bool = false
    
    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink(destination: SecondView()) {
                    Text(&quot;다음으로 가볼까요?&quot;)
                }
                Button(action: {
                    isShow.toggle()
                }) {
                    Text(&quot;모달 꺼내기&quot;)
                }
                .onAppear {
                    print(&quot;FirstView Appear&quot;)
                }
                .onDisappear {
                    print(&quot;FirstView Disappear&quot;)
                }
            }
            .sheet(isPresented: $isShow, onDismiss: {
                print(&quot;ModalSheet Dismiss&quot;)
            }) {
                Text(&quot;나? 모달&quot;)
                    .onAppear {
                        print(&quot;ModelSheet Appear&quot;)
                    }
                    .onDisappear {
                        print(&quot;ModelSheet Disappear&quot;)
                    }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;과연?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (53).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EE22X/btrSxkk5GWb/oGMhwrvh0EMUY7Ew19Od00/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EE22X/btrSxkk5GWb/oGMhwrvh0EMUY7Ew19Od00/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EE22X/btrSxkk5GWb/oGMhwrvh0EMUY7Ew19Od00/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/EE22X/btrSxkk5GWb/oGMhwrvh0EMUY7Ew19Od00/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;501&quot; height=&quot;708&quot; data-filename=&quot;ezgif.com-gif-maker (53).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번엔 정상적으로 작동한다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;NavigationStack과 마찬가지로, Sheet에서 onAppear나 onDisappear를 사용할 필요가 있을 때는 내부에 위치하고 있는 View에서 접근하는 것이 좋을 듯 하다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5. Alert&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마지막으로 Alert, 이 친구도 궁금해졌다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669819555380&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct FirstView: View {
    @State private var isShow: Bool = false
    @State private var isAlert: Bool = false
    
    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink(destination: SecondView()) {
                    Text(&quot;다음으로 가볼까요?&quot;)
                }
                Button(action: {
                    isShow.toggle()
                }) {
                    Text(&quot;모달 꺼내기&quot;)
                }
                .onAppear {
                    print(&quot;FirstView Appear&quot;)
                }
                .onDisappear {
                    print(&quot;FirstView Disappear&quot;)
                }
            }
            .sheet(isPresented: $isShow, onDismiss: {
                print(&quot;ModalSheet Dismiss&quot;)
            }) {
                VStack {
                    Text(&quot;나? 모달&quot;)
                    
                    Button(action: {
                        self.isAlert.toggle()
                    }) {
                        Text(&quot;알랏 띄우기&quot;)
                    }
                    .alert(&quot;안녕&quot;, isPresented: $isAlert) {
                        Button(&quot;확인&quot;) {}
                    } message: {
                        Text(&quot;나? 알랏&quot;)
                    }
                    .onAppear {
                        print(&quot;Alert Appear&quot;)
                    }
                    .onDisappear {
                        print(&quot;Alert Disappear&quot;)
                    }
                }
                .onAppear {
                    print(&quot;ModelSheet Appear&quot;)
                }
                .onDisappear {
                    print(&quot;ModelSheet Disappear&quot;)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번에도 앞서 봤던 다른 View들처럼 마지막에 modifer를 추가해줬다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (54).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;863&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhBjj9/btrSySImdfh/08INkSD4p0UOscVKcCSea0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhBjj9/btrSySImdfh/08INkSD4p0UOscVKcCSea0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhBjj9/btrSySImdfh/08INkSD4p0UOscVKcCSea0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dhBjj9/btrSySImdfh/08INkSD4p0UOscVKcCSea0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;719&quot; data-filename=&quot;ezgif.com-gif-maker (54).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Alert이 보여지는 View인, Modal View가 보여질 때 Alert의 onAppear도 호출된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;역시나 우리의 생각대로 움직이지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 기존에 View의 마지막에 추가해줬던 modifier를 View의 내부로 이동시켰다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669855170094&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct FirstView: View {
    @State private var isShow: Bool = false
    @State private var isAlert: Bool = false
    
    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink(destination: SecondView()) {
                    Text(&quot;다음으로 가볼까요?&quot;)
                }
                Button(action: {
                    isShow.toggle()
                }) {
                    Text(&quot;모달 꺼내기&quot;)
                }
                .onAppear {
                    print(&quot;FirstView Appear&quot;)
                }
                .onDisappear {
                    print(&quot;FirstView Disappear&quot;)
                }
            }
            .sheet(isPresented: $isShow, onDismiss: {
                print(&quot;ModalSheet Dismiss&quot;)
            }) {
                VStack {
                    Text(&quot;나? 모달&quot;)
                    
                    Button(action: {
                        self.isAlert.toggle()
                    }) {
                        Text(&quot;알랏 띄우기&quot;)
                            .onAppear {
                                print(&quot;Alert Appear&quot;)
                            }
                            .onDisappear {
                                print(&quot;Alert Disappear&quot;)
                            }
                    }
                    .alert(&quot;안녕&quot;, isPresented: $isAlert) {
                        Button(&quot;확인&quot;) {}
                    } message: {
                        Text(&quot;나? 알랏&quot;)
                    }
                }
                .onAppear {
                    print(&quot;ModelSheet Appear&quot;)
                }
                .onDisappear {
                    print(&quot;ModelSheet Disappear&quot;)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;과연?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (55).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dm0idr/btrSxPrMQSX/ZscOGU08islMNC6WY2K4Xk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dm0idr/btrSxPrMQSX/ZscOGU08islMNC6WY2K4Xk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dm0idr/btrSxPrMQSX/ZscOGU08islMNC6WY2K4Xk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dm0idr/btrSxPrMQSX/ZscOGU08islMNC6WY2K4Xk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;499&quot; height=&quot;705&quot; data-filename=&quot;ezgif.com-gif-maker (55).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번엔 우리가 생각했던 대로 호출된다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UI hierarchy가 궁금해서 출력해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-30 23.48.33.png&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;1948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIB5pP/btrSxDLGSLS/n07y43KYO03Uqqlqckzf61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIB5pP/btrSxDLGSLS/n07y43KYO03Uqqlqckzf61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIB5pP/btrSxDLGSLS/n07y43KYO03Uqqlqckzf61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIB5pP%2FbtrSxDLGSLS%2Fn07y43KYO03Uqqlqckzf61%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;655&quot; data-filename=&quot;스크린샷 2022-11-30 23.48.33.png&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;1948&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;역시 이번에도 Controller가 존재하는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Controller가 존재하는 뷰라고 할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이러한 뷰들은 뷰 자체에 Life cycle modifier를 달지 말고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;내부에 존재하는 뷰에 modifier를 달아야&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우리가 의도했던 대로 작동한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 포스팅에서도 ScrollView 자체에 달지 않고, 내부에 존재하는 LectureItemView에 달아준 것처럼 말이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만약 Life cycle이 제대로 동작하지 않는다면, Controller에 달려있나 확인해 볼 것!&lt;/span&gt;&lt;/p&gt;</description>
      <category>  개발/iOS</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/209</guid>
      <comments>https://codekodo.tistory.com/209#entry209comment</comments>
      <pubDate>Thu, 1 Dec 2022 00:24:29 +0900</pubDate>
    </item>
    <item>
      <title>[에러와의 동침] 22년 11월 4주차</title>
      <link>https://codekodo.tistory.com/208</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;22.11.21&lt;/span&gt;&lt;/h2&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;json에 존재하는 key, value를 struct에서 정의하지 않을 경우에는 정상적으로 Decoding&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;반대로 json에 존재하지 않는 key, value를 struct에서 정의하는 경우에는&amp;nbsp;KeyNotFound Error가 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특정 value에 key-value가 부분적으로 존재할 때는 Optional을 적용&lt;/span&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만약, key-value를 제대로 정의했는데 KeyNotFound Error가 발생하면 특정 key-value가 부분적으로 존재하는지 확인할 것&lt;/span&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;22.11.22&lt;/span&gt;&lt;/h2&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;json에는 id가 없지만 identifiable을 만족시키기 위해 uuid를 넣어주는 경우 var이 아닌 let으로 선언&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;EnvironmentObject를 사용할 때, 최상단에서 인스턴스를 넣어주기에 뷰의 게층 구조 내에서만 접근이 가능&lt;/span&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;뷰의 계층 구조에서만 접근이 가능한 게 당연한 말이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만약, 접근할 수 없는데 접근하는 경우에 아래와 같은 오류가 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;SwiftUI/EnvironmentObject.swift:70: Fatal error: No ObservableObject of type &amp;lt;ClassType&amp;gt; found.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;22.11.23&lt;/span&gt;&lt;/h2&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;NSURLConnection SSL error&lt;/span&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;API, 특히 OpenAPI 사용하다보면 자주 접하는 에러인데 App Transport Security Settings을 설정하면 해결&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;암호화가 적용되지 않은 http를 사용하면 발생하는 에러 또한 이걸로 해결 가능&lt;/span&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-filename=&quot;스크린샷 2022-11-28 01.16.19.png&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4OS69/btrR9NvoXGi/9DGyToMaKCmDvFDC4CNJJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4OS69/btrR9NvoXGi/9DGyToMaKCmDvFDC4CNJJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4OS69/btrR9NvoXGi/9DGyToMaKCmDvFDC4CNJJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4OS69%2FbtrR9NvoXGi%2F9DGyToMaKCmDvFDC4CNJJ1%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;601&quot; height=&quot;114&quot; data-filename=&quot;스크린샷 2022-11-28 01.16.19.png&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;192&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ObservableObject로 VIewModel을 구현하고 특정 데이터에 변화를 줄 때 보라색 에러가 발생&lt;/span&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;DispatchQueue를 통해 Main Thread에서 변경하도록 수정&lt;/span&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;22.11.24&lt;/span&gt;&lt;/h2&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Async-Await-Throw 관련 코드 좀 더 찾아보기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지오코딩 관련 이슈 분석&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>  개발/에러와의 동침</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/208</guid>
      <comments>https://codekodo.tistory.com/208#entry208comment</comments>
      <pubDate>Mon, 28 Nov 2022 01:08:25 +0900</pubDate>
    </item>
    <item>
      <title>[iOS / SwiftUI] 스크롤, 무한으로 즐겨요~ (LazyVStack으로 무한 스크롤 구현하기)</title>
      <link>https://codekodo.tistory.com/207</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오늘은 ScrollView와 LazyVStack을 활용하여 SwiftUI에서 무한 스크롤을 구현해보려고 한다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사실, LazyVStack이 조금 생소할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;LazyVStack은 말 그대로 Lazy하게 VStack을 그린다는 느낌으로,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;VStack으로 보여줄 항목이 실제로 UI에 보여질 때 렌더링을 진행하는 View이다.&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;edited_스크린샷 2022-11-28 00.00.59.png&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UUfAD/btrSf9XZWiL/ktK3YWnO9K0UFMW1EAAdPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UUfAD/btrSf9XZWiL/ktK3YWnO9K0UFMW1EAAdPk/img.png&quot; data-alt=&quot;Apple Developer Docs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UUfAD/btrSf9XZWiL/ktK3YWnO9K0UFMW1EAAdPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUUfAD%2FbtrSf9XZWiL%2FktK3YWnO9K0UFMW1EAAdPk%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;645&quot; height=&quot;90&quot; data-filename=&quot;edited_스크린샷 2022-11-28 00.00.59.png&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Apple Developer Docs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1669561618735&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Apple Developer Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/swiftui/lazyvstack&quot; data-og-url=&quot;https://developer.apple.com/documentation/swiftui/lazyvstack&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/lazyvstack&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/swiftui/lazyvstack&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 기존에 사용하던 VStack과는 어떤 차이가 있을까?&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;평소에 사용하던 VStack은 뷰가 보여질 때(onAppear) 모든 항목을 렌더링한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇기에 ScrollView + VStack 조합으로 List를 나타낸다면 초기에 많은 리소스를 소모하게 된다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;적은 개수의 간단한 항목들을 표시할 경우에는 상관이 없지만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;가령, 중고거래 플랫폼 앱이 있는데 앱을 켰을 때 특정 카테고리에 있는 모든 항목을 가져온다고 해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;엄청난 리소스를 소모하게 될 것이다. &lt;s&gt;(사용자의 셀룰러 데이터나, CPU 혹은 RAM과 같은 자원이 될 수 있다.)&lt;/s&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 초기에는 10개, 혹은 20개와 같이 적은 개수의 데이터만 보여주고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;onAppear에 print를 달아주고 테스트를 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;평소에 사용하던 VStack은&amp;nbsp;View가 보여질 때 모든 항목이 렌더링 되는 것을 알 수 있다. (실제로는 보여지지 않더라도)&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-11-28 00.13.50.png&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qMCk9/btrR9WZ8K0J/TcaEB858CXaFKLeVz20mV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qMCk9/btrR9WZ8K0J/TcaEB858CXaFKLeVz20mV1/img.png&quot; data-alt=&quot;VStack&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qMCk9/btrR9WZ8K0J/TcaEB858CXaFKLeVz20mV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqMCk9%2FbtrR9WZ8K0J%2FTcaEB858CXaFKLeVz20mV1%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;459&quot; height=&quot;675&quot; data-filename=&quot;스크린샷 2022-11-28 00.13.50.png&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VStack&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;LazyVStack은 View가 보여질 때 모든 항목이 렌더링 되지 않고, 실제로 보여지는 것만 렌더링 되는 것을 알 수 있다.&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;ezgif.com-gif-maker (46).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8EhjY/btrSbUge69s/E1BkLGra5ENsJkKVw4SNj0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8EhjY/btrSbUge69s/E1BkLGra5ENsJkKVw4SNj0/img.gif&quot; data-alt=&quot;LazyVStack&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8EhjY/btrSbUge69s/E1BkLGra5ENsJkKVw4SNj0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/8EhjY/btrSbUge69s/E1BkLGra5ENsJkKVw4SNj0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;460&quot; height=&quot;676&quot; data-filename=&quot;ezgif.com-gif-maker (46).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LazyVStack&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한&amp;nbsp;자주 쓰는 List 역시 Lazy하게 동작한다. &lt;s&gt;(List를 애용하도록 하자)&lt;/s&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;ezgif.com-gif-maker (47).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp7dd7/btrShA18KSr/BbmkdghketUZ1lpdk9YkX0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp7dd7/btrShA18KSr/BbmkdghketUZ1lpdk9YkX0/img.gif&quot; data-alt=&quot;List&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp7dd7/btrShA18KSr/BbmkdghketUZ1lpdk9YkX0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bp7dd7/btrShA18KSr/BbmkdghketUZ1lpdk9YkX0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;678&quot; data-filename=&quot;ezgif.com-gif-maker (47).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;List&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 파일 구조는 이렇게 잡았고, MVVM 패턴으로 구현했다.&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-11-27 23.11.51.png&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCN7rM/btrSeOsZgIc/K2fbSCEmA1XaaLZcqz4u91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCN7rM/btrSeOsZgIc/K2fbSCEmA1XaaLZcqz4u91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCN7rM/btrSeOsZgIc/K2fbSCEmA1XaaLZcqz4u91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCN7rM%2FbtrSeOsZgIc%2FK2fbSCEmA1XaaLZcqz4u91%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;281&quot; height=&quot;281&quot; data-filename=&quot;스크린샷 2022-11-27 23.11.51.png&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;478&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Model&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 Model의 경우, API를 호출했을 때 받아오는 Json의 구조를 그대로 구현해줬다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669558442851&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Foundation

struct Lecture: Codable {
    let pagination: Pagination
    let results: [Result]
}

struct Pagination: Codable {
    let count: Int
    let numPages: Int
    let next: URL
    
    enum CodingKeys: String, CodingKey {
        case count
        case numPages = &quot;num_pages&quot;
        case next
    }
}

struct Result: Codable, Identifiable {
    let id: UUID = UUID() // List를 사용하기 위해 identifiable 추가
    let name: String
    let media: Media
    let teachers: String
    let shortDesc: String
    let startDisplay: String
    
    enum CodingKeys: String, CodingKey {
        case name
        case teachers
        case media
        case shortDesc = &quot;short_description&quot;
        case startDisplay = &quot;start_display&quot;
    }
}

struct Media: Codable {
    let image: ImagePerSize
}

struct ImagePerSize: Codable {
    let raw: URL
    let small: URL
    let large: URL
}&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용한 API에 대한 정보는 아래 링크에서 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669560919134&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;국가평생교육진흥원_K-MOOC_강좌정보API&quot; data-og-description=&quot;한국형 온라인 공개강좌(K-MOOC)의 강좌 목록 API 및 강좌 세부 정보 API 제공&quot; data-og-host=&quot;www.data.go.kr&quot; data-og-source-url=&quot;https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15042355&quot; data-og-url=&quot;https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HRFxm/hyQH6IGXrZ/FuU0b1dxDum141XYIOxpm0/img.png?width=390&amp;amp;height=158&amp;amp;face=0_0_390_158,https://scrap.kakaocdn.net/dn/bucH2a/hyQH4xl8IF/Sncbmev4IFBFSaMdqTMyG0/img.png?width=1068&amp;amp;height=692&amp;amp;face=0_0_1068_692,https://scrap.kakaocdn.net/dn/hbDkq/hyQHWTCGDK/5zIdkgraT5TeGIdrs3vCNK/img.png?width=860&amp;amp;height=558&amp;amp;face=0_0_860_558&quot;&gt;&lt;a href=&quot;https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15042355&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15042355&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HRFxm/hyQH6IGXrZ/FuU0b1dxDum141XYIOxpm0/img.png?width=390&amp;amp;height=158&amp;amp;face=0_0_390_158,https://scrap.kakaocdn.net/dn/bucH2a/hyQH4xl8IF/Sncbmev4IFBFSaMdqTMyG0/img.png?width=1068&amp;amp;height=692&amp;amp;face=0_0_1068_692,https://scrap.kakaocdn.net/dn/hbDkq/hyQHWTCGDK/5zIdkgraT5TeGIdrs3vCNK/img.png?width=860&amp;amp;height=558&amp;amp;face=0_0_860_558');&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;국가평생교육진흥원_K-MOOC_강좌정보API&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;한국형 온라인 공개강좌(K-MOOC)의 강좌 목록 API 및 강좌 세부 정보 API 제공&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.data.go.kr&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. ViewModel&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ViewModel에서는 Lecture를 담고 있는 리스트를 @Published로 정의했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 앱을 처음 켰을 때 강좌를 가져오는 메소드와 스크롤을 끝까지 내렸을 때 추가적으로 강좌를 가져오는 메소드를 정의했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669558659270&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Foundation

class LectureViewModel: ObservableObject {
    @Published var items: [Result] = []
    private var nextURL: String = &quot;&quot;
    private var APIKEY: String = &quot;&quot;
    
    init() {}
    
    // 앱을 처음 켰을 때 강좌를 가져오기 위한 메소드
    func getLecturesOnServer() async throws {
        let responseData: Lecture = try await WebService.shared.loadJson(&quot;https://apis.data.go.kr/B552881/kmooc/courseList?serviceKey=\(APIKEY)&amp;amp;Page=1&amp;amp;Org=FUNMOOC&amp;amp;Mobile=1&quot;)
 
        DispatchQueue.main.async {
            self.items = responseData.results
            self.nextURL = &quot;\(responseData.pagination.next)&quot;
        }
    }
    
    // 스크롤을 끝까지 내렸을 때 추가적으로 강좌를 가져오기 위한 메소드
    func getLecturesOnServerAtFinishedScroll() async throws {
        let responseData: Lecture = try await WebService.shared.loadJson(nextURL)
        
        // 기존 강좌 목록에 새로 가져온 강좌 목록을 추가
        DispatchQueue.main.async {
            self.items += responseData.results
            self.nextURL = &quot;\(responseData.pagination.next)&quot;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;프로퍼티 중에 nextURL의 경우, json에서 다음 강좌 목록에 해당하는 URL을 넘겨주는데 이를 활용했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. WebService&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;WebService에서는 API를 호출할 때 사용하는 메소드를 정의해줬다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669558806096&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Foundation

class WebService {
    static let shared = WebService()
    
    // json을 T타입의 데이터로 디코딩하는 메소드
    func loadJson&amp;lt;T: Decodable&amp;gt;(_ url: String) async throws -&amp;gt; T {
        do {
            let url = URL(string: url)!
            let (data, _) = try await URLSession.shared.data(from: url)
            return try JSONDecoder().decode(T.self, from: data)
        } catch {
            fatalError(&quot;Unable to parse data : \(error)&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;타입에 상관없이 해당 클래스와 메소드를 사용할 수 있도록 제네릭을 적용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. View&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ViewModel에서 @Published로 정의한 items를 활용했고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱을 실행했을 때는 items에 어떠한 데이터도 들어있지 않기에, 이 때는 ProgressBar가 보여지도록 했다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 동시에 API를 호출하여 강좌 목록을 가져오게 하였고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;강좌 목록을 가져오는데 성공하면 items에 데이터가 들어있게 되고, 이후에는 ProgressBar가 사라지도록 했다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ViewModel, 아니 ObservableObject를 활용하면 이러한 형태의 앱을 정말 손쉽게 구현할 수 있다. (SwiftUI 짱짱!!  )&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669562777542&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import SwiftUI

struct LectureListView: View {
    @EnvironmentObject var lectureVM: LectureViewModel
    @State private var num: Int = 0
    
    var body: some View {
        if lectureVM.items.isEmpty {
            ProgressView()
            // 앱을 처음 실행했을 때만 ProgressView를 보여줌
                .task {
                    do {
                        try await lectureVM.getLecturesOnServer()
                    } catch (let error) {
                        print(&quot;Unable to get data : \(error)&quot;)
                    }
                }
        } else {
            ScrollView {
                LazyVStack {
                    ForEach(lectureVM.items) { item in
                        LectureItemView(lecture: item)
                            .onAppear {
                                guard let index = lectureVM.items.firstIndex(where: { $0.id == item.id }) else { return }
                                
                                if index % 10 == 9 {
                                    Task {
                                        do {
                                            try await lectureVM.getLecturesOnServerAtFinishedScroll()
                                        } catch (let error) {
                                            print(&quot;Unable to get data : \(error)&quot;)
                                        }
                                    }
                                    
                                }
                            }
                    }
                }
            }
            .navigationTitle(&quot;K-MOOC 강좌 목록&quot;)
        }
        
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위에서 설명했던 것처럼 LazyVStack은 특정 아이템이 UI에 그려질 타이밍에 렌더링을 진행한다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이를 활용해서 불러온 강좌 목록에서 마지막 강좌가 보여지면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;강좌 목록을 보여주는 ScrollView에서 하나의 강좌를 보여주는 View인, LectureItemView는 이렇게 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669563236345&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import SwiftUI

struct LectureItemView: View {
    var lecture: Result
    
    var body: some View {
        HStack {
            AsyncImage(url: lecture.media.image.small) { image in
                image
                    .resizable()
                
            } placeholder: {
                ProgressView()
                .frame(width: 140)
            }
            .scaledToFill()
            .frame(width: 140)
            .clipped()
            .cornerRadius(8)
            
            VStack(alignment: .leading) {
                Text(lecture.name)
                    .font(.body)
                    .fontWeight(.bold)
                
                Spacer()
                
                Text(lecture.teachers)
                    .font(.footnote)
                    .lineLimit(1)
                
                Spacer()
                
                Text(lecture.startDisplay)
                    .font(.footnote)
            }
            .padding(4)
            
            Spacer()
        }
        .padding(8)
    }
}&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (49).gif&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;1148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhTkqP/btrSgaii3I5/R6pFgYmUjM5HY7zlkbgxDK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhTkqP/btrSgaii3I5/R6pFgYmUjM5HY7zlkbgxDK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhTkqP/btrSgaii3I5/R6pFgYmUjM5HY7zlkbgxDK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bhTkqP/btrSgaii3I5/R6pFgYmUjM5HY7zlkbgxDK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;349&quot; height=&quot;703&quot; data-filename=&quot;ezgif.com-gif-maker (49).gif&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;1148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 강좌가 보여질 때 API를 호출하여 10개의 강좌를 더 가져오는 것을 볼 수 있다!&lt;/p&gt;</description>
      <category>  개발/iOS</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/207</guid>
      <comments>https://codekodo.tistory.com/207#entry207comment</comments>
      <pubDate>Mon, 28 Nov 2022 00:34:21 +0900</pubDate>
    </item>
    <item>
      <title>&amp;quot;let us: Go! 2022 Fall&amp;quot;에 다녀왔습니다!</title>
      <link>https://codekodo.tistory.com/206</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;두근두근 렛어스고!  &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 주에 렛어스고에 다녀왔다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;11월 5일에 다녀왔지만! 지금 회고를 쓰고 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;s&gt;원래 회고는 일주일 뒤에 쓰는게 국룰!  &lt;/s&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 렛어스고는 지금 하고 있는 멋사 앱스쿨에서 한 분이 정보를 공유해주셔서 알게 됐고, 운 좋게 티켓팅에 성공해서 참가하게 됐다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;티켓은 학생 티켓과 일반 티켓이 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;학생 티켓이 훨씬 저렴하지만 수량이 적어서, 학생 티켓 예매에는 실패했고 일반 티켓을 예매했다!&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버스를 타고 서울까지 가야하는 상황이라 조금 고민이 많았지만, 그래도 iOS 세미나를 참가해보고 싶어서 아침 일찍 서울을 향했다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발표 장소는 코엑스 앞에 있는 Finda였다.&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;769&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byJvnq/btrRcF5znMq/xWeLGTZRWxYslMzT1V396K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byJvnq/btrRcF5znMq/xWeLGTZRWxYslMzT1V396K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byJvnq/btrRcF5znMq/xWeLGTZRWxYslMzT1V396K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyJvnq%2FbtrRcF5znMq%2FxWeLGTZRWxYslMzT1V396K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;301&quot; height=&quot;401&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;30분 정도 일찍 도착했더니 운영진을 제외하고는 거의 없었다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;뒤에 계신 분과 얘기를 나눌까했지만 같이 오신 분이 있는 것 같아서 그냥 조용히 혼자 있었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;769&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/loJlT/btrRcEMl6st/dkEkdjLfiX7p9VmIQ1SMn1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/loJlT/btrRcEMl6st/dkEkdjLfiX7p9VmIQ1SMn1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/loJlT/btrRcEMl6st/dkEkdjLfiX7p9VmIQ1SMn1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FloJlT%2FbtrRcEMl6st%2FdkEkdjLfiX7p9VmIQ1SMn1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;399&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;1024&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;13시가 되자 세미나가 시작됐다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;간단하게 개회식을 진행하고 바로 첫 번째 Speaker님의 발표로 이어졌다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Rubin님의 발표였고, UIKit을 SwiftUI처럼 구현하기에 관한 주제였다. 신기했다!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발표와는 별개로 주변 사람들과 친해지고 싶었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만.. 뭔가 다른 분들은 다들 이미 친분이 있으신듯한? 느낌이었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 사람들을 만나는 걸 좋아하는 나인데, 이미 친하신 모습을 보니까 말 걸기가 더 어려워졌다... ㅠ&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇게 고민하다가 내 양쪽에 계신 분한테 말을 걸었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;한 분은 야곰아카데미를 수강하고 있는 분이셨고, 다른 한 분은 개발자 &amp;amp;&amp;amp; 대학생이신 분이었다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아무튼 그렇게 간단하게 자기 소개를 하니 쉬는 시간이 끝나서 다음 세션으로 넘어 갔다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그러다 네트워킹 및 기념사진 촬영 시간이 되었고, 서로 인사도 나누고 명함도 받았다. &lt;s&gt;명함 많이 받았어요~&lt;/s&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대부분 현업 개발자분들이었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;나처럼 부트 캠프를 듣고 있는, 대학생분들도 꽤 있었다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;한 가지 놀랐던 것은 고등학생 개발자분들이 꽤나 많았다는 사실이었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;부럽기도 하면서 되게 멋있어 보였다. &lt;s&gt;나는 그 나이에 무엇을 하고 있었던가...&lt;/s&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;거의 한 시간정도 네트워킹 시간을 가졌다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다른 분들과 얘기할 수 있어서 너무 좋았다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이후에 3개의 발표 세션이 더 있었는데 그 중, Clyne님과 Sueaty님의 발표가 너무 재밌었다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Clyne님은 인터페이스 모듈을 통해 의존성을 분리하는 내용이었는데 어려운 내용을 되게 쉽게 설명해주셔서 더 인상 깊었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Sueaty님은 &lt;s&gt;연예인&lt;/s&gt;, 비개발적인 주제였고, 회사를 즐겁게 다니는 방법에 대해 발표해주셨는데 너무 재밌었다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;두 분의 발표를 듣고 너무 감명받아서 직접 자리에 찾아가서 인사까지 드리고 왔다! 명함도 받았따.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다른 분들의 발표도 너무 재밌었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;승진님의 UITesting, 태승님의 모두를 위한 앱 만들기, Rubin, 태태님의 발표까지...&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;769&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUR6To/btrRd6BbN0E/rqUZ4wxG4Go52kI7KSfXa1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUR6To/btrRd6BbN0E/rqUZ4wxG4Go52kI7KSfXa1/img.jpg&quot; data-alt=&quot;태태님의 발표!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUR6To/btrRd6BbN0E/rqUZ4wxG4Go52kI7KSfXa1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUR6To%2FbtrRd6BbN0E%2FrqUZ4wxG4Go52kI7KSfXa1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;399&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;1024&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;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식적인 일정이 끝나고 근처 치킨집에서 뒷풀이가 진행됐다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 사람들과 사담을 나눌 수 있어서 너무 좋았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사실 가기 전에, 많은 생각이 들었다. 아직 iOS를 시작한지 얼마안됐기에 이러한 세미나를 참석해도 될까? 라는 고민이었지만 결과는 대만족이었다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;현업 개발자분들과 대학생분들, iOS에 진심인 분들을 실제로 만나뵙고 이런 저런 얘기를 나누면서 동기 부여가 확실하게 됐다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;안드로이드에겐 미안하지만 iOS에 발을 들인게 정말 잘한 선택이라고 생각한다.   너무 재밌다!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기회가 된다면 내년에도 참석해보려고 한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;11월 30일에 더 큰 행사인 렛츠스위프트가 열리는데 이건 평일이라 참석하지 못할 듯하다. 아쉽다... ㅠ&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;LetSwift2022:Swift Playgrounds&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;iOS 개발자들의 놀이터, 레츠스위프트 컨퍼런스 2022 에서 온라인의 갈증을 해소해보세요.&quot; data-og-host=&quot;letswift.kr&quot; data-og-source-url=&quot;https://letswift.kr/2022/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ACkmD/hyQzreQhMb/6IBpYd90bkMCZ7sc2uMdNk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bdDKre/hyQzqAd46u/7UTZPHJO5N7GtRKzWXpEpk/img.png?width=300&amp;amp;height=300&amp;amp;face=108_141_172_211,https://scrap.kakaocdn.net/dn/q1U7H/hyQzbQCTL3/gTtweoBcRdUHaBJSOyBko0/img.png?width=300&amp;amp;height=300&amp;amp;face=116_74_207_173&quot; data-og-url=&quot;http://letswift.kr/2022/&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;http://letswift.kr/2022/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://letswift.kr/2022/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ACkmD/hyQzreQhMb/6IBpYd90bkMCZ7sc2uMdNk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bdDKre/hyQzqAd46u/7UTZPHJO5N7GtRKzWXpEpk/img.png?width=300&amp;amp;height=300&amp;amp;face=108_141_172_211,https://scrap.kakaocdn.net/dn/q1U7H/hyQzbQCTL3/gTtweoBcRdUHaBJSOyBko0/img.png?width=300&amp;amp;height=300&amp;amp;face=116_74_207_173');&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;LetSwift2022:Swift Playgrounds&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;iOS 개발자들의 놀이터, 레츠스위프트 컨퍼런스 2022 에서 온라인의 갈증을 해소해보세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;letswift.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발표 자료&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 태태님 - MVI Architecture&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub - uuu1101/Letusgo-2022-fall: Let us go ! 2022 fall 발표자료&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Let us go ! 2022 fall 발표자료. Contribute to uuu1101/Letusgo-2022-fall development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/uuu1101/Letusgo-2022-fall&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/XYkeh/hyQzcvdohC/ZawgiTI0MbyT2qyTBbmgZ1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot; data-og-url=&quot;https://github.com/uuu1101/Letusgo-2022-fall&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/uuu1101/Letusgo-2022-fall&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/uuu1101/Letusgo-2022-fall&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/XYkeh/hyQzcvdohC/ZawgiTI0MbyT2qyTBbmgZ1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - uuu1101/Letusgo-2022-fall: Let us go ! 2022 fall 발표자료&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Let us go ! 2022 fall 발표자료. Contribute to uuu1101/Letusgo-2022-fall development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 태승님 - 공정사회를 위한 엔지니어링: 모두를 위한 앱 만들기&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub - ryan-son/let-us-go-2022-fall-engineering-for-equity: let us: Go! 2022 가을, 공정 사회를 위한 엔지니어링: &quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;let us: Go! 2022 가을, 공정 사회를 위한 엔지니어링: 모두를 위한 제품 만들기 섹션 자료입니다. - GitHub - ryan-son/let-us-go-2022-fall-engineering-for-equity: let us: Go! 2022 가을, 공정 사회를 위한 엔지니어링: &quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/ryan-son/let-us-go-2022-fall-engineering-for-equity&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gqdwy/hyQzq1jlhu/HR7zeJUWBhvUIzFpIYv1EK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot; data-og-url=&quot;https://github.com/ryan-son/let-us-go-2022-fall-engineering-for-equity&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/ryan-son/let-us-go-2022-fall-engineering-for-equity&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/ryan-son/let-us-go-2022-fall-engineering-for-equity&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gqdwy/hyQzq1jlhu/HR7zeJUWBhvUIzFpIYv1EK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - ryan-son/let-us-go-2022-fall-engineering-for-equity: let us: Go! 2022 가을, 공정 사회를 위한 엔지니어링:&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;let us: Go! 2022 가을, 공정 사회를 위한 엔지니어링: 모두를 위한 제품 만들기 섹션 자료입니다. - GitHub - ryan-son/let-us-go-2022-fall-engineering-for-equity: let us: Go! 2022 가을, 공정 사회를 위한 엔지니어링:&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. Clyne님 - 느슨한 결합을 몸으로 느껴보자&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub - clyne-kim/letusgo2022: 렛어스고 2022 가을 세미나 자료&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;렛어스고 2022 가을 세미나 자료. Contribute to clyne-kim/letusgo2022 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/clyne-kim/letusgo2022&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OURyT/hyQzioEtno/BIYDbimr84umXerfLbRpK0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot; data-og-url=&quot;https://github.com/clyne-kim/letusgo2022&quot;&gt;&lt;a href=&quot;https://github.com/clyne-kim/letusgo2022&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/clyne-kim/letusgo2022&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OURyT/hyQzioEtno/BIYDbimr84umXerfLbRpK0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub - clyne-kim/letusgo2022: 렛어스고 2022 가을 세미나 자료&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;렛어스고 2022 가을 세미나 자료. Contribute to clyne-kim/letusgo2022 development by creating an account on GitHub.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;github.com&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>⛹️ 라이프/회고</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/206</guid>
      <comments>https://codekodo.tistory.com/206#entry206comment</comments>
      <pubDate>Mon, 14 Nov 2022 19:38:06 +0900</pubDate>
    </item>
    <item>
      <title>[iOS / Swift] Swift 문자열 정복하기 (aka 'Character')</title>
      <link>https://codekodo.tistory.com/202</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift로 문자열 문제를 풀다보면 오류를 자주 접하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(&lt;s&gt;error: cannot convert value of type 'String.Element' (aka 'Character') to closure result type 'String)&lt;/s&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;'이게 왜 안됨? ㅋㅋ' 라는 생각이 들 정도로 안된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아무튼 이런 오류를 자주 접했기에 글로 정리하면서 다시 한 번 복습하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-03 18.49.02.png&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCX2m1/btrQh47adgT/UvP9Dqy195PohfpQqdI1M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCX2m1/btrQh47adgT/UvP9Dqy195PohfpQqdI1M0/img.png&quot; data-alt=&quot;파이썬 1승&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCX2m1/btrQh47adgT/UvP9Dqy195PohfpQqdI1M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCX2m1%2FbtrQh47adgT%2FUvP9Dqy195PohfpQqdI1M0%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;296&quot; height=&quot;168&quot; data-filename=&quot;스크린샷 2022-11-03 18.49.02.png&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파이썬 1승&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;1. 문자열 형 변환하기&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. String -&amp;gt; Int&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다른 언어에서처럼 Int()로 형 변환을 진행한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 때, String인지 SubString인지 Character인지 잘 살펴본다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(그렇지 않으면...&amp;nbsp;&lt;s&gt;error: cannot convert value of type 'String.Element' (aka 'Character') to closure result type 'String&lt;/s&gt;')&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667485970054&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var a: String = &quot;1&quot;
var b: Substring = &quot;2&quot;
var c: Character = &quot;3&quot;

print(Int(a)) // Optional(1)
print(Int(b)) // Optional(2)
print(Int(c)) // Error!&lt;/code&gt;&lt;/pre&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Int -&amp;gt; String&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1667485999314&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var a: Int = 1

print(String(a)) // 1&lt;/code&gt;&lt;/pre&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 다른 기본 타입들&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;String -&amp;gt; Int 와 동일하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;String과 Character, Optional에 주의한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #f89009; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;2. 문자열을 한 개씩 분리하기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하나의 문자열을 하나의 문자로 나눠야할 때는 map을 사용한다. 반환 값이 &lt;b&gt;[Character] 타입&lt;/b&gt;이므로 주의한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667485679028&quot; class=&quot;dart&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;abcdefg&quot;

var characters: [Character] = s.map { $0 } // [Character]
var strings: [String] = s.map { String($0) } // [String]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;3. 문자열을 특정 문자를 기준으로 나누기&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문자열을 공백이나 기호를 기준으로 나눌 때는&amp;nbsp;&lt;b&gt;split&lt;/b&gt;이나 &lt;b&gt;components&lt;/b&gt;를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. split&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;반환 결과가&lt;b&gt; [SubString] 타입&lt;/b&gt;이므로 [String] 타입이 필요한 경우 형 변환을 해준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667473613819&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;a b c d e f g&quot; // 공백이 존재

var subStrings: [SubString] = s.split(separator: &quot; &quot;).map { $0 } // [SubString]
var strings: [String] = s.split(separator: &quot; &quot;).map { String($0) } // [String]&lt;/code&gt;&lt;/pre&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. components&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;비슷하게 components가 있고, Foundation import를 통해 사용할 수 있으며 반환 값이 [String] 타입이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667474077806&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Foundation

var s: String = &quot;a b c d e f g&quot; // 공백이 존재
var strings: [String] = s.components(separatedBy: &quot; &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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;4. 문자열 내 특정 문자 포함 여부&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. contains&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1667484607138&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;kodo&quot;
var a: String = &quot;k&quot;
var b: Character = &quot;k&quot;
var c: Substring = &quot;k&quot;

print(s.contains(a)) // true
print(s.contains(b)) // true
print(s.contains(c)) // true&lt;/code&gt;&lt;/pre&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. hasPrefix&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞에서부터 문자열을 비교한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;hasPrefix 메소드에 넘겨주는 인자는 &lt;b&gt;String 타입&lt;/b&gt;이어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667484747272&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;kodo&quot;
var a: String = &quot;k&quot;
var b: Character = &quot;k&quot;

print(s.hasPrefix(a)) // true
print(s.hasPrefix(b)) // Error!&lt;/code&gt;&lt;/pre&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. hasSuffix&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;hasPrefix와 반대로 뒤에서부터 문자열을 비교한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667485179440&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;kodo&quot;
var a: String = &quot;k&quot;
var b: Character = &quot;k&quot;

print(s.hasSuffix(a)) // false
print(s.hasSuffix(b)) // Error!

var c: String = &quot;o&quot;
var d: String = &quot;do&quot;
var e: String = &quot;odo&quot;
var f: String = &quot;kod&quot;

print(s.hasSuffix(c)) // true
print(s.hasSuffix(d)) // true
print(s.hasSuffix(e)) // true
print(s.hasSuffix(f)) // false&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #f89009; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;5. 문자열 내 특정 위치, 문자 반환&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. startIndex, endIndex&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift에서는 문자열에 인덱스로 직접적으로 접근할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;String[index]가 불가능하고, String[String.index]로 접근할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;startIndex는 문자열의 첫 인덱스를, &lt;b&gt;endIndex는 문자열의 마지막 인덱스 + 1&lt;/b&gt;을 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667487450456&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;kodo&quot;
var start: String.Index = s.startIndex
var end: String.Index = s.endIndex

print(s[0]) // Error!
print(s[start]) // k
print(s[end]) // 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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이를 활용해서 문자열 파싱이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667487792644&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;kodo&quot;
var start: String.Index = s.startIndex
var end: String.Index = s.endIndex

print(s[start..&amp;lt;end - 1]) // kodo
print(s[start...) // kodo&lt;/code&gt;&lt;/pre&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. firstIndex&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;firstIndex 메소드에 넘겨주는 인자가 처음 등장하는 인덱스를 반환한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;인자는 Character 타입이고, 반환 값이 없을 수도 있기에 해당 메소드의 반환 타입은 Optional이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667488070880&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;kodo&quot;

var e1: String = &quot;o&quot;
var index: String.Index? = s.firstIndex(of: e1) // Error!

var e2: Character = &quot;o&quot;
var index: String.Index? = s.firstIndex(of: e2) // Optional(...)&lt;/code&gt;&lt;/pre&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. index&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특정 인덱스 이전, 이후의 인덱스에 접근할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;after의 경우 after 이후의 인덱스를, before의 경우 before 이전의 인덱스를 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667489152978&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;kodo&quot;
var start = s.startIndex // 0
var end = s.endIndex // 4

print(s[s.index(after: start)]) // s[1] = o
print(s[s.index(start, offsetBy: 0)]) // s[0] = k
print(s[s.index(start, offsetBy: 1)]) // s[1] = o

print(s[end]) // Out of Bounds
print(s[s.index(before: end)]) // s[3] = o
print(s[s.index(end, offsetBy: -1)]) s[3] = o&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;6.&amp;nbsp; 아스키코드 변환하기&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문자를 아스키코드로 변환하거나, 아스키코드를 문자로 변환할 때는&lt;b&gt; UnicodeScalar&lt;/b&gt;를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. String -&amp;gt; ASCII&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아스키코드로 변환할 때는 UnicodeScalar에 넘겨주는 인자가 String 타입이어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 변환 결과가 Optional이므로 강제 언래핑을 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667483997020&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;kodo&quot;

var stringToAscii: [Int] = s.map { UnicodeScalar($0)?.value } // Error!
var stringToAscii: [Int] = s.map { UnicodeScalar(String($0))?.value } // Error!
var stringToAscii: [UInt32] = s.map { UnicodeScalar(String($0))!.value } // 107 ...
var stringToAscii: [Int] = s.map { Int(UnicodeScalar(String($0))!.value) } // 107 ...&lt;/code&gt;&lt;/pre&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. ASCII -&amp;gt; String&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;역시 UnicodeScalar의 결과가 Optional이므로 강제 언래핑을 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667484229370&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var asciis: [Int] = [107, 111, 100, 111] 
var asciiToString = asciis.map { String(UnicodeScalar($0)!) }.joined() // joined로 문자열 변환&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;7. 문자열 뒤집기&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Collections 타입의&lt;b&gt; reversed&lt;/b&gt;를 활용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667530655177&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var s: String = &quot;kodo&quot;
print(s.map { String($0) }.reversed().joined()) // &quot;odok&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>  개발/iOS</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/202</guid>
      <comments>https://codekodo.tistory.com/202#entry202comment</comments>
      <pubDate>Thu, 3 Nov 2022 20:16:48 +0900</pubDate>
    </item>
    <item>
      <title>[iOS / SwiftUI] 다양한 상태 프로퍼티들을 알아보자!</title>
      <link>https://codekodo.tistory.com/201</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f89009; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;State와 Binding&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 시간 @State와 @Binding 프로퍼티 래퍼에 대해 학습했다.&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. @State&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;@State 프로퍼티 래퍼를 사용해서 상태 프로퍼티를 작성하면 &lt;b&gt;해당 프로퍼티가 선언된 뷰와 바인딩&lt;/b&gt;할 수 있게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;좀 더 쉽게 설명하자면 &lt;b&gt;뷰와 바인딩이 되어 있는 상태프로퍼티에 변경이 일어나면 자동으로 뷰가 갱신&lt;/b&gt;된다는 말이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래 코드는 버튼을 클릭하면 숫자가 1씩 증가하도록 구현한 앱이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666586472579&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @State private var number: Int = 0
    
    var body: some View {
        VStack {
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (38).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bputgX/btrPoxBszf7/Dz3BjnLzb6pq50fOArXUQ1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bputgX/btrPoxBszf7/Dz3BjnLzb6pq50fOArXUQ1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bputgX/btrPoxBszf7/Dz3BjnLzb6pq50fOArXUQ1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bputgX/btrPoxBszf7/Dz3BjnLzb6pq50fOArXUQ1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;275&quot; height=&quot;600&quot; data-filename=&quot;ezgif.com-gif-maker (38).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱을 실행시키고 버튼을 눌러보면 누를 때마다 숫자가 1씩 증가하는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 @Binding는 무엇일까?&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. @Binding&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위에서 작성한 코드는 상태 프로퍼티를 선언한 뷰에서 상태 프로퍼티를 직접 사용하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;즉, &lt;b&gt;선언과 사용이 하나의 뷰(Struct)에서 진행된다&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 메인 뷰(부모)에서 선언한 상태 프로퍼티를 서브 뷰(자식)에서 사용한다면 어떻게 될까?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666586891231&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @State private var number: Int = 0
    
    var body: some View {
        VStack {
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
    }
}

struct SubView: View {
    var body: some View {
        VStack {
            Text(&quot;This is SubView&quot;)
            Text(&quot;\(self.number)&quot;)
        }
        .padding()
        .background(.gray)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드를 작성하고 빌드를 진행하면 컴파일 에러가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-10-24 13.48.48.png&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q6iHm/btrPiuMohA6/wx55dlD2jZ628pyfSDA3ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q6iHm/btrPiuMohA6/wx55dlD2jZ628pyfSDA3ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q6iHm/btrPiuMohA6/wx55dlD2jZ628pyfSDA3ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq6iHm%2FbtrPiuMohA6%2Fwx55dlD2jZ628pyfSDA3ok%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;656&quot; height=&quot;78&quot; data-filename=&quot;스크린샷 2022-10-24 13.48.48.png&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;94&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;SubView 내부에 number 프로퍼티가 선언되어 있지 않기 때문&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 SubView 내부에 number 프로퍼티를 선언하면 어떻게 될까?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666588165122&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @State private var number: Int = 0
    
    var body: some View {
        VStack {
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
    }
}

struct SubView: View {
	private var number: Int = 0
    
    var body: some View {
        VStack {
            Text(&quot;This is SubView&quot;)
            Text(&quot;\(self.number)&quot;)
        }
        .padding()
        .background(.gray)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번엔 빌드는 정상적으로 진행된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (35).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nWV0z/btrPpX0YZPH/BxPh5PzrxBFwOvRzAApsmk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nWV0z/btrPpX0YZPH/BxPh5PzrxBFwOvRzAApsmk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nWV0z/btrPpX0YZPH/BxPh5PzrxBFwOvRzAApsmk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/nWV0z/btrPpX0YZPH/BxPh5PzrxBFwOvRzAApsmk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;274&quot; height=&quot;597&quot; data-filename=&quot;ezgif.com-gif-maker (35).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 버튼을 클릭해도 서브뷰에 있는 텍스트는 변경되지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;SubView의 number와 ContentView의 number는 서로 다른 프로퍼티&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 두 개를 연결해주면 문제를 해결해 줄 수 있지 않을까?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666587556454&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @State private var number: Int = 0
    
    var body: some View {
        VStack {
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
            
            SubView(number: number)
        }
    }
}

struct SubView: View {
	// 생성자를 통해 number 전달
    init(number: Int) {
        self.number = number
    }
    
    private var number: Int = 0
    
    var body: some View {
        VStack {
            Text(&quot;This is SubView&quot;)
            Text(&quot;\(self.number)&quot;)
        }
        .padding()
        .background(.gray)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (36).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AR8R9/btrPlIjcjEG/akmyKNg4LLkwrKRcVBJiA0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AR8R9/btrPlIjcjEG/akmyKNg4LLkwrKRcVBJiA0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AR8R9/btrPlIjcjEG/akmyKNg4LLkwrKRcVBJiA0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/AR8R9/btrPlIjcjEG/akmyKNg4LLkwrKRcVBJiA0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;275&quot; height=&quot;600&quot; data-filename=&quot;ezgif.com-gif-maker (36).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;생성자를 통해서 메인 뷰의 number를 전달했더니 서브 뷰에서도 화면이 갱신되는 것을 볼 수 있다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;서론이 길었지만&lt;b&gt; @Binding 프로퍼티 래퍼를 통해서 간단한 형태로 작성&lt;/b&gt;할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666588648323&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @State private var number: Int = 0
    
    var body: some View {
        VStack {
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
            
            SubView(number: $number)
        }
    }
}

struct SubView: View {
    @Binding var number: Int
    
    var body: some View {
        VStack {
            Text(&quot;This is SubView&quot;)
            Text(&quot;\(self.number)&quot;)
        }
        .padding()
        .background(.gray)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;생성자를 통해서 프로퍼티를 전달한 방식과 유사하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;차이점은 서브 뷰에서 @Binding 프로퍼티 래퍼로 변수를 선언만 한다는 것이다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;메인 뷰에서는 Argument로 $number를 전달한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여기서 명심해야할 것은 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;$(달러)!&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;$(달러)기호를 빼먹으면 컴파일 에러가 발생&lt;/b&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-10-24 14.17.15.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baBcx3/btrPrnLZEDR/gDgQfhW4AyeG2MGv7SqWQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baBcx3/btrPrnLZEDR/gDgQfhW4AyeG2MGv7SqWQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baBcx3/btrPrnLZEDR/gDgQfhW4AyeG2MGv7SqWQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaBcx3%2FbtrPrnLZEDR%2FgDgQfhW4AyeG2MGv7SqWQK%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;652&quot; height=&quot;50&quot; data-filename=&quot;스크린샷 2022-10-24 14.17.15.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;104&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;서브 뷰에서 Binding&amp;lt;Int&amp;gt; 형으로 number를 선언했는데 Int형 number를 전달해서 발생하는 타입 에러이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (39).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rpkcb/btrPnfgQ48L/sXOTLDGhUsJnEF1ZlWBUvk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rpkcb/btrPnfgQ48L/sXOTLDGhUsJnEF1ZlWBUvk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rpkcb/btrPnfgQ48L/sXOTLDGhUsJnEF1ZlWBUvk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/rpkcb/btrPnfgQ48L/sXOTLDGhUsJnEF1ZlWBUvk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;298&quot; height=&quot;650&quot; data-filename=&quot;ezgif.com-gif-maker (39).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&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;style3&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;하지만 의문점이 한 가지가 있다. &lt;span style=&quot;background-color: #fcfcfc;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;생성자를 통해서 전달하는 방식도 있는데 굳이 @Binding이라는 프로퍼티 래퍼가 등장한 이유는 무엇일까?&lt;/b&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지금 예시들은 뷰 사이에서 단방향으로 데이터를 주고 받고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;메인 뷰(ContentView) -&amp;gt; 서브 뷰(SubView) 방향으로, 다시 말해 서브 뷰에 파라미터로 메인 뷰의 프로퍼티를 전달하고 있다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;양방향으로 데이터를 주고 받도록 코드를 변경해보면 &lt;b&gt;@Binding을 사용하려는 이유&lt;/b&gt;에 대해 명확하게 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666607562117&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @State private var number: Int = 0
    
    var body: some View {
        VStack {
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
            
            SubView(number: self.number)
        }
    }
}

struct SubView: View {
    private var number: Int = 0
    
    init(number: Int) {
        self.number = number
    }
    
    var body: some View {
        VStack {
            Text(&quot;This is SubView&quot;)
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
            	self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
        .padding()
        .background(.gray)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;서브 뷰에도 버튼을 달아서 SubView 값도 변경할 수 있도록 코드를 변경해줬다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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-10-24 19.33.57.png&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5W4oe/btrPtJgUHNW/XAluvKSWm2G6pieyeqjg41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5W4oe/btrPtJgUHNW/XAluvKSWm2G6pieyeqjg41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5W4oe/btrPtJgUHNW/XAluvKSWm2G6pieyeqjg41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5W4oe%2FbtrPtJgUHNW%2FXAluvKSWm2G6pieyeqjg41%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;602&quot; height=&quot;70&quot; data-filename=&quot;스크린샷 2022-10-24 19.33.57.png&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;118&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;View 자체가 Struct이므로 Immutable하기 때문이다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;컴파일 자체가 안되지만 &lt;b&gt;논리적으로 생각해보면 우리가 구현하려는 기능은 일반적인 프로퍼티로는 불가능하다&lt;/b&gt;는 것을 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;메인 뷰 내부에 서브 뷰 인스턴스를 생성하고 파라미터로 프로퍼티 값을 전달하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 때, &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;@Binding 프로퍼티 래퍼&lt;/b&gt;&lt;/span&gt;를 사용하면 된다!&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666607970774&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @State private var number: Int = 0
    
    var body: some View {
        VStack {
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
            
            SubView(number: $number)
        }
    }
}

struct SubView: View {
    @Binding var number: Int
    
    var body: some View {
        VStack {
            Text(&quot;This is SubView&quot;)
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
        .padding()
        .background(.gray)
    }
    
    mutating func addNumber() {
        self.number += 1
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (40).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6vTYx/btrPs3toM5n/e30b5GaANOkj7brrH2hwi0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6vTYx/btrPs3toM5n/e30b5GaANOkj7brrH2hwi0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6vTYx/btrPs3toM5n/e30b5GaANOkj7brrH2hwi0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/c6vTYx/btrPs3toM5n/e30b5GaANOkj7brrH2hwi0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;275&quot; height=&quot;600&quot; data-filename=&quot;ezgif.com-gif-maker (40).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;정상적으로 컴파일이 정상적으로 진행되고 앱을 실행해보면 메인 뷰의 number와 서브 뷰의 number가 같은 값을 가지고 있고, 양방향으로 데이터를 주고 받고 있는 것을 볼 수 있다!&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;이것이 바로 @Binding을 사용하는 이유이다.&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 @Binding 역시 단점은 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞서 말했던 것처럼 상태 프로퍼티는 프로퍼티를 선언한 뷰에서만 접근이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;하위 뷰가 아니거나 바인딩이 구현되어 있지 않으면&lt;/b&gt; 다른 뷰에서 접근이 불가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 뷰 자체에 종속적인 개념이므로 뷰가 사라지면 프로퍼티 역시 사라진다.&amp;nbsp;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이러한 문제를 해결해주는 것이 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Observable 객체&lt;/b&gt;&lt;/span&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞서 말한 문제점들을 해결하고, 다른 뷰에서 접근할 수 있는 영구적인 데이터를 표현하기 위해 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f89009; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Observable 객체&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래 코드와 빌드된 앱을 보면 @Binding 프로퍼티 래퍼를 사용해서, 페이지가 변경되어도 하나의 값을 가르키고 이를 변경하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666610681949&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @State private var number: Int = 0
    
    var body: some View {
        NavigationView {
            VStack {
                Text(&quot;\(self.number)&quot;)
                
                Button(action: {
                    self.number += 1
                }) {
                    Text(&quot;눌러주세요!&quot;)
                }
                
                SubView(number: $number)
                
                
                NavigationLink(destination: SecondView(number: $number)) {
                    Text(&quot;Second View로 이동&quot;)
                }
            }
        }
    }
}

struct SubView: View {
    @Binding var number: Int
    
    var body: some View {
        VStack {
            Text(&quot;This is SubView&quot;)
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
        .padding()
        .background(.gray)
    }
}

struct SecondView: View {
    @Binding var number: Int
    
    var body: some View {
        VStack {
            Text(&quot;This is Second View&quot;)
            Text(&quot;\(self.number)&quot;)
            
            Button(action: {
                self.number += 1
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
        .padding()
        .background(.gray)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-gif-maker (41).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pkVRE/btrPtiD2S5e/QKnzCgKg4qKCGQT6Ytj3lk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pkVRE/btrPtiD2S5e/QKnzCgKg4qKCGQT6Ytj3lk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pkVRE/btrPtiD2S5e/QKnzCgKg4qKCGQT6Ytj3lk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/pkVRE/btrPtiD2S5e/QKnzCgKg4qKCGQT6Ytj3lk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;275&quot; height=&quot;600&quot; data-filename=&quot;ezgif.com-gif-maker (41).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1308&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 &lt;b&gt;메인 뷰 내부에 Second View가 종속적으로 선언&lt;/b&gt;되어 있고, &lt;b&gt;매번 @Binding를 달아준 프로퍼티를 선언&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단순히 하나의 페이지(부모)의 프로퍼티를 다른 페이지(자식)에서 사용하는 경우에는 기존 방식대로 @Binding으로 전달해도 되지만, &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이런 문제들을 Observable이 해결해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ViewModel과 비슷한 역할을 수행하는데, 특정 상황에 따라 변경되는 데이터를 수집하고 관리하는 역할을 수행한다고 볼 수 있다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Observable 객체는 Observer 객체와 하나의 세트를 이룬다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Observable 객체는 프로퍼티를 게시(Publish)하고 Observer 객체는 이를 구독하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666622004174&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Foundation
import Combine

class CustomNumber: ObservableObject {
    @Published var number = 0
    
    func increateNumber() {
        self.number += 1
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1666612608792&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @ObservedObject var number: CustomNumber
    
    var body: some View {
        NavigationView {
            VStack {
                Text(&quot;\(self.number.number)&quot;)
                
                Button(action: {
                    self.number.increateNumber()
                }) {
                    Text(&quot;눌러주세요!&quot;)
                }
                
                SubView(number: number)
                
                NavigationLink(destination: SecondView(number: number)) {
                    Text(&quot;Second View로 이동&quot;)
                }
            }
        }
    }
}

struct SubView: View {
    @ObservedObject var number: CustomNumber
    
    var body: some View {
        VStack {
            Text(&quot;This is SubView&quot;)
            Text(&quot;\(self.number.number)&quot;)
            
            Button(action: {
                self.number.increateNumber()
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
        .padding()
        .background(.gray)
    }
}

struct SecondView: View {
    @ObservedObject var number: CustomNumber
    
    var body: some View {
        VStack {
            Text(&quot;This is Second View&quot;)
            Text(&quot;\(self.number.number)&quot;)
            
            Button(action: {
                self.number.increateNumber()
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
        .padding()
        .background(.gray)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존에 @Binding으로 작성한 코드와 비교해보면 number 증가에 대한 로직을 ObservableObject 프로토콜을 채택한 CustomNumber에서 담당하고 있는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #f89009;&quot;&gt;Environment 객체&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;number 증가에 대한 로직을 CustomNumber 클래스로 분리했지만 여전히 Navigation을 진행할 때 구독 객체에 대한 참조체를 전달해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이럴 때 사용하는 것이 &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Environment 객체&lt;/span&gt;&lt;/b&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법은 Observable 객체와 동일하지만 &lt;b&gt;뷰에서 뷰로 전달할 필요 없이 모든 뷰가 접근할 수 있다&lt;/b&gt;는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666620599405&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import SwiftUI

@main
struct _21024_ObservableApp: App {
    let customNumber: CustomNumber = CustomNumber()
    
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(customNumber)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 사용하기 전에 Sence에서 각각의 뷰에서 참조하려는 ObservableObject를 등록해줘야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1666620988480&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct ContentView: View {
    @EnvironmentObject var number: CustomNumber
    
    var body: some View {
        NavigationView {
            VStack {
                Text(&quot;\(self.number.number)&quot;)
                
                Button(action: {
                    self.number.increateNumber()
                }) {
                    Text(&quot;눌러주세요!&quot;)
                }
                
                SubView()
                
                NavigationLink(destination: SecondView()) {
                    Text(&quot;Second View로 이동&quot;)
                }
            }
        }
    }
}

struct SubView: View {
    @EnvironmentObject var number: CustomNumber
    
    var body: some View {
        VStack {
            Text(&quot;This is SubView&quot;)
            Text(&quot;\(self.number.number)&quot;)
            
            Button(action: {
                self.number.increateNumber()
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
        .padding()
        .background(.gray)
    }
}

struct SecondView: View {
    @EnvironmentObject var number: CustomNumber
    
    var body: some View {
        VStack {
            Text(&quot;This is Second View&quot;)
            Text(&quot;\(self.number.number)&quot;)
            
            Button(action: {
                self.number.increateNumber()
            }) {
                Text(&quot;눌러주세요!&quot;)
            }
        }
        .padding()
        .background(.gray)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존 방식과 다르게 뷰와 뷰 사이 간에 Navigation을 진행할 때 매개변수로 넘겨주지 않아도 된다는 장점이 있다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사실 @State, @Binding, @ObservedObject, @EnvironmentObject 중에 어느 것을 쓸 지는 개발자 마음이고 어떻게 구현하느냐에 따라 달라진다고 생각한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래도 각 프로퍼티 래퍼별로 장점이 있으니, 장점에 맞게 사용하면 될 듯하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f89009; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;정리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하나의 뷰, 혹은 특정 뷰의 종속적인 뷰에서 단방향으로 데이터를 주고 받을 때 -&amp;gt; @State&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;양방향으로 데이터를 주고 받을 때 -&amp;gt; @Binding&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;양방향으로 주고 받으면서, 로직을 분리하고 싶을 때 -&amp;gt; @ObservedObject&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모든 뷰에서 참조할 수 있는 데이터와 로직을 분리하고 싶을 때 -&amp;gt; @EnvironmentObject&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;%F-%-F%--%--%--%ED%-A%-C%EA%B-%A-&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  회고&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Environment 객체의 경우 Flutter의 Provider와 매우 유사한 형태를 지녔다. Provider를 통해 하위 위젯에서 데이터를 접근할 수 있는데 이것과 완전히 똑같았다. 한 가지 궁금한 점은 Flutter에서는 값의 변경이 일어나면 해당 위젯을 전부 다시 렌더링을 진행한다. (Consumer를 적용해서 특정 부분 렌더링도 가능하다.)&amp;nbsp; SwiftUI에서는 자체적으로 특정 부분만 렌더링을 하는건지 전부 렌더링을 진행하는 것인지 좀 더 알아봐아겠다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>  개발/iOS</category>
      <author>kodo_o</author>
      <guid isPermaLink="true">https://codekodo.tistory.com/201</guid>
      <comments>https://codekodo.tistory.com/201#entry201comment</comments>
      <pubDate>Mon, 24 Oct 2022 13:52:27 +0900</pubDate>
    </item>
  </channel>
</rss>