<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>아기개발자의 성장일기</title>
    <link>https://eunjin3786.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 10 Apr 2026 22:24:43 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>eungding</managingEditor>
    <image>
      <title>아기개발자의 성장일기</title>
      <url>https://tistory1.daumcdn.net/tistory/2767662/attach/f8d3ddae9ef442ada2aa8d8631c5c9b2</url>
      <link>https://eunjin3786.tistory.com</link>
    </image>
    <item>
      <title>[Git] Merge Commit &amp;gt; Interactive Rebase  (--rebase-merges 옵션)</title>
      <link>https://eunjin3786.tistory.com/686</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[1] Merge Commit 은 왜 기본적으로 Interactive Rebase 가 안될 까 ?&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Git의 rebase --interactive&amp;nbsp;&amp;nbsp;(또는 간단히 rebase -i) 는 히스토리를 직선(선형)으로 평탄화하려고 설계되어 있어서, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;부모가 2개 이상인 merge commit은 목록에 포함되지 않거나 편집 대상이 아닙니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그래서 기본적으로 머지 커밋을 Interactive Rebase (edit, fixup, reword 등) 할 수 없어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;  머지 커밋의 구조&lt;/b&gt;&lt;b&gt;&lt;/b&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: 'Nanum Gothic';&quot;&gt;머지 커밋은 &lt;b&gt;브랜치를 합치는 시점&lt;/b&gt;에 생기며,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;부모가 두 개 이상&lt;/b&gt; &amp;rarr; 양쪽 브랜치의 끝을 모두 가리킵니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1758956480370&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      D --- E   (feature 브랜치)
     /         
A -- B -------- M   (main 브랜치)
                &amp;uarr;
                머지 커밋 (부모 = B, E)&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: 'Nanum Gothic';&quot;&gt;여기서 M은 &lt;b&gt;부모가 두 개&lt;/b&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: 'Nanum Gothic';&quot;&gt;하나는 main 브랜치 쪽(B),&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하나는 feature 브랜치 쪽(E).&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;  그래서 interactive rebase가 힘든 이유&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: 'Nanum Gothic';&quot;&gt;git rebase -i는 &lt;b&gt;선형 히스토리&lt;/b&gt;(부모가 1개인 체인)만 전제로 동작합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;머지 커밋은 부모가 2개라 &amp;ldquo;어떤 선을 따라가야 할지&amp;rdquo; 모호하기 때문에,&lt;b&gt; 기본 rebase -i 에는 나오지 않아요.&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2] --rebase-merges 옵션&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하지만 따로 --rebase-merges 옵션을 쓰면 머지 커밋도 포함한 채로 리베이스할 수 있습니다.&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;✅&amp;nbsp; &lt;/span&gt;--rebase-merges 옵션이란?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;git rebase는 기본적으로 선형 히스토리만 다루기 때문에,&amp;nbsp; 머지 커밋(부모가 2개 이상인 커밋) 은 무시해 버립니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &lt;/span&gt;--rebase-merges&amp;nbsp;옵션을&amp;nbsp;붙이면:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 머지&amp;nbsp;커밋&amp;nbsp;구조를&amp;nbsp;보존하면서&amp;nbsp;리베이스할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; interactive&amp;nbsp;rebase(-i)와&amp;nbsp;함께&amp;nbsp;쓰면&amp;nbsp;todo&amp;nbsp;리스트에&amp;nbsp;merge&amp;nbsp;&amp;hellip;&amp;nbsp;라인이&amp;nbsp;나타나서,&amp;nbsp;머지&amp;nbsp;커밋도&amp;nbsp;edit,&amp;nbsp;reword,&amp;nbsp;drop&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;✅&amp;nbsp;정리&lt;br /&gt;&amp;bull; rebase&amp;nbsp;-i&amp;nbsp;=&amp;nbsp;선형&amp;nbsp;히스토리만&lt;br /&gt;&amp;bull; rebase&amp;nbsp;-i&amp;nbsp;--rebase-merges&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;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: 'Nanum Gothic';&quot;&gt;포크에서는 해당 옵션을 지원안하는 것 같고 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;터미널을 통해 해야하는 것 같아요!&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://github.com/fork-dev/TrackerWin/issues/806&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/fork-dev/TrackerWin/issues/806&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1758957912441&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;Add option for --rebase-merges when rebasing interactively &amp;middot; Issue #806 &amp;middot; fork-dev/TrackerWin&quot; data-og-description=&quot;I'm forced to leave Fork when I want to rewrite the history of a merged branch (--no-ff) as the default rebase puts the rebased commits into a linear branch. Could we have an option when rebasing t...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/fork-dev/TrackerWin/issues/806&quot; data-og-url=&quot;https://github.com/fork-dev/TrackerWin/issues/806&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Vm8nn/hyZJUyOgQV/4tRoF2CR7kxJWTx6Iu8S90/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bVjHMa/hyZJD5DwAE/N1CRmhW2ZIZeXdhogXDlsk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/fork-dev/TrackerWin/issues/806&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/fork-dev/TrackerWin/issues/806&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Vm8nn/hyZJUyOgQV/4tRoF2CR7kxJWTx6Iu8S90/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bVjHMa/hyZJD5DwAE/N1CRmhW2ZIZeXdhogXDlsk/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;Add option for --rebase-merges when rebasing interactively &amp;middot; Issue #806 &amp;middot; fork-dev/TrackerWin&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I'm forced to leave Fork when I want to rewrite the history of a merged branch (--no-ff) as the default rebase puts the rebased commits into a linear branch. Could we have an option when rebasing t...&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[3] &amp;nbsp;--rebase-merges 옵션 써보기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;일단 필요한 상황을 가정해볼게요~&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1) 추가 커밋 1을 머지 커밋에 합치고 싶을 때&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758958355269&quot; class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;추가 커밋 1
머지 커밋&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: 'Nanum Gothic';&quot;&gt;이런 상황에서는 --rebase-merges 옵션 안쓰고도 충분히 편하게 할 수 있어요!&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;추가 커밋 1을 soft 혹은 mixed reset 하고 amend (Amend Last Commit) 해서 머지커밋에 흡수시키면 됩니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;✅ 2) 추가 커밋2 를 머지 커밋에 합치고 싶을 때&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758958441815&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;추가 커밋 2
추가 커밋 1
머지 커밋&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 상황도 사실 체리픽 + 리셋 으로 쉽게 하면 되는데요,,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-27 오후 4.51.24.png&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgowl5/btsQTOAmumc/0Q5Fqy7pFh1qnygyiY0KM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgowl5/btsQTOAmumc/0Q5Fqy7pFh1qnygyiY0KM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgowl5/btsQTOAmumc/0Q5Fqy7pFh1qnygyiY0KM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgowl5%2FbtsQTOAmumc%2F0Q5Fqy7pFh1qnygyiY0KM0%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;1508&quot; height=&quot;582&quot; data-filename=&quot;스크린샷 2025-09-27 오후 4.51.24.png&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;582&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: 'Nanum Gothic';&quot;&gt;추가 커밋 1을 reset 범위에 포함시키지 않고 정석대로 리베이스 하고 싶다는 것을 전제로 해보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;전체적은 흐름은 다음과 같고&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758959829259&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  # 1. interactive rebase 시작 (에디터가 열림)
  # - 현재 HEAD 에서 3번째 전 커밋까지 rebase 대상으로 삼겠다.
  git rebase -i --rebase-merges HEAD~3

  # 2. 에디터에서 다음과 같이 수정:
  #    - &quot;pick d1d55ad 추가 커밋 2&quot;를 &quot;fixup d1d55ad 추가 커밋 2&quot;로 변경
  #    - 그 줄을 잘라내서 &quot;merge -C 324dcf5 main&quot; 바로 아래로 이동
  #
  # 수정 전:
  # merge -C 324dcf5 main # Merge branch 'main' into feature
  # pick 0f8b2db 추가 커밋 1  
  # pick d1d55ad 추가 커밋 2
  #
  # 수정 후:
  # merge -C 324dcf5 main # Merge branch 'main' into feature
  # fixup d1d55ad 추가 커밋 2
  # pick 0f8b2db 추가 커밋 1

  # 3. 저장하고 에디터 종료하면 rebase가 실행됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;1)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;우선 편하게 에디팅하기 위해&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: 'Nanum Gothic';&quot;&gt;저는 Sublime Text 로 열었는데&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758961218508&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  GIT_EDITOR=&quot;subl --wait&quot; git rebase -i --rebase-merges HEAD~3  # Sublime Text&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Atom 으로 열고 싶으면 아래 명령어를 사용하시면 됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758961302075&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  GIT_EDITOR=&quot;atom --wait&quot; git rebase -i --rebase-merges HEAD~3   # Atom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그럼 이런 git-rebase-todo 가 나오는데&amp;nbsp;&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;스크린샷 2025-09-27 오후 5.24.54.png&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ulwqX/btsQTUggf0R/ptnfykyEEW5ZiQ9eywITaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ulwqX/btsQTUggf0R/ptnfykyEEW5ZiQ9eywITaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ulwqX/btsQTUggf0R/ptnfykyEEW5ZiQ9eywITaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FulwqX%2FbtsQTUggf0R%2FptnfykyEEW5ZiQ9eywITaK%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;1604&quot; height=&quot;1158&quot; data-filename=&quot;스크린샷 2025-09-27 오후 5.24.54.png&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;1158&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: 'Nanum Gothic';&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;figure data-ke-type=&quot;image&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; data-ke-style=&quot;alignCenter&quot;&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20250927-phhk.png&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgOHFM/btsQQ1O5PoQ/VpJ0Cb3agvkbAcDNuGEEs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgOHFM/btsQQ1O5PoQ/VpJ0Cb3agvkbAcDNuGEEs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgOHFM/btsQQ1O5PoQ/VpJ0Cb3agvkbAcDNuGEEs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgOHFM%2FbtsQQ1O5PoQ%2FVpJ0Cb3agvkbAcDNuGEEs1%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;1518&quot; height=&quot;1154&quot; data-filename=&quot;SCR-20250927-phhk.png&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1154&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: 'Nanum Gothic';&quot;&gt;에디터를&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;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: 'Nanum Gothic';&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;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;요거 실행 후, &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758962237004&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git rebase -i --rebase-merges HEAD~3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;에디터로 수정안하고 포크를 열어서 GUI 로 수정할 수 있다고 AI 는 말하는데 &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;포크로 넘어가보면&amp;nbsp; Interactive Rebase 창이 안열립니다 ;;&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;--rebase-merges 옵션을 사용하는 경우,&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;CLI(Command Line Interface) + 포크(GUI툴)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /Git</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/686</guid>
      <comments>https://eunjin3786.tistory.com/686#entry686comment</comments>
      <pubDate>Sat, 27 Sep 2025 17:38:31 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Swift Concurrency &amp;gt; MainActor deinit 헷갈리는 것 정리</title>
      <link>https://eunjin3786.tistory.com/685</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[1] 목표&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Swift 에서 &lt;b&gt;deinit&lt;/b&gt; 은 항상 &lt;b&gt;nonisolated&lt;/b&gt; 입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;객체 해제는 즉시 일어나야 하고, Executor 전환으로 지연되면 안되기 때문에 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;deinit 을 특정 Executor (ex. @MainActor) 에 격리 불가능합니다.&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: 'Nanum Gothic';&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20250927-mhgl.png&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brBn4p/btsQSmdYaRz/0F8HWOKzc0LFwzx2eaj3s0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brBn4p/btsQSmdYaRz/0F8HWOKzc0LFwzx2eaj3s0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brBn4p/btsQSmdYaRz/0F8HWOKzc0LFwzx2eaj3s0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrBn4p%2FbtsQSmdYaRz%2F0F8HWOKzc0LFwzx2eaj3s0%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;507&quot; height=&quot;873&quot; data-filename=&quot;SCR-20250927-mhgl.png&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;1536&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;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2.1] 상황 1&lt;/span&gt;&lt;/h4&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;스크린샷 2025-09-27 오후 1.55.44.png&quot; data-origin-width=&quot;1854&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DVGnI/btsQRARbrMS/MJWOCxhZh1k5Norkwi6ka0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DVGnI/btsQRARbrMS/MJWOCxhZh1k5Norkwi6ka0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DVGnI/btsQRARbrMS/MJWOCxhZh1k5Norkwi6ka0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDVGnI%2FbtsQRARbrMS%2FMJWOCxhZh1k5Norkwi6ka0%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;1854&quot; height=&quot;806&quot; data-filename=&quot;스크린샷 2025-09-27 오후 1.55.44.png&quot; data-origin-width=&quot;1854&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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: 'Nanum Gothic';&quot;&gt;&lt;b&gt;현황)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 상황은 swift 5 에서도 에러 발생합니다.&amp;nbsp; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Strict Concurrency Checking 을 가장 낮은 수준 (Minimal) 로 해도 에러 발생.&lt;/span&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-27 오후 1.21.47.png&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQUV63/btsQQFMa1ch/4uDGmAgQLX4coMZvUgd93K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQUV63/btsQQFMa1ch/4uDGmAgQLX4coMZvUgd93K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQUV63/btsQQFMa1ch/4uDGmAgQLX4coMZvUgd93K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQUV63%2FbtsQQFMa1ch%2F4uDGmAgQLX4coMZvUgd93K%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;1256&quot; height=&quot;252&quot; data-filename=&quot;스크린샷 2025-09-27 오후 1.21.47.png&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;bull; &lt;/span&gt;deinit 안의 코드는 nonisolated&amp;nbsp; 상태로 실행됨.&lt;br /&gt;&amp;rarr;&amp;nbsp;그런데&amp;nbsp;effect1.stop()은&amp;nbsp;@MainActor&amp;nbsp;격리된&amp;nbsp;메서드.&lt;br /&gt;&amp;rarr;&amp;nbsp;nonisolated(deinit)에서&amp;nbsp;actor-isolated(stop())&amp;nbsp;호출&amp;nbsp;&amp;rarr;&amp;nbsp;컴파일러가&amp;nbsp;막음.&lt;br /&gt;&lt;br /&gt;&amp;bull; deinit은&amp;nbsp;&amp;ldquo;스레드&amp;nbsp;안&amp;nbsp;가리고&amp;nbsp;지금&amp;nbsp;당장&amp;nbsp;파괴해!&amp;rdquo;&amp;nbsp;모드.&lt;br /&gt;&amp;bull; 그런데 stop()은 &amp;ldquo;꼭 메인 액터에서만 불러!&amp;rdquo;라는 규칙.&lt;br /&gt;&amp;bull; 그래서&amp;nbsp;두&amp;nbsp;규칙이&amp;nbsp;충돌&amp;nbsp;&amp;rarr;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;해결)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758949336066&quot; class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// 1) 지역 변수로 뽑아내기
deinit {
    let e = effect1
    Task { @MainActor in
        e.stop()
    }
}

// 2) capture list로 직접 캡처
deinit {
    Task { @MainActor [effect1] in
        effect1.stop()
    }
}&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: 'Nanum Gothic';&quot;&gt;&lt;b&gt; &amp;bull; deinit은&amp;nbsp;nonisolated&amp;nbsp;&amp;rarr;&amp;nbsp;@MainActor&amp;nbsp;메서드(stop)를&amp;nbsp;직접&amp;nbsp;호출&amp;nbsp;불가.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;bull; 그래서 메인 액터로 hop (전환) 하는 작업만 예약 (Task { @MainActor in ... }) 하고 즉시 deinit을 끝냄&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&amp;bull; let e = effect1로 강하게 캡처해 &lt;b&gt;Task가 실행될 때까지 effect1의 수명을 잠시 연장 (사용 후 자동 해제).&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;+ 두 방법 다 내부적으로 동일한 동작을 하고 스타일 차이일 뿐입니다.&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;질문1) &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;만약 self 를 직접 캡쳐한다면 어떻게 되는가 ?&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-09-27 오후 2.08.50.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkRwg8/btsQR5C4EJm/JnYfg2jFOTkU2JWWVkgz4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkRwg8/btsQR5C4EJm/JnYfg2jFOTkU2JWWVkgz4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkRwg8/btsQR5C4EJm/JnYfg2jFOTkU2JWWVkgz4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkRwg8%2FbtsQR5C4EJm%2FJnYfg2jFOTkU2JWWVkgz4k%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;1824&quot; height=&quot;626&quot; data-filename=&quot;스크린샷 2025-09-27 오후 2.08.50.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;626&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;위의 코드에서 컴파일러는&amp;nbsp; effect1 에 접근하려면 결국 self 를 캡쳐해야한다고 판단하고&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&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: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;swift 5 에서는 워닝, swift 6 에서는 에러를 냅니다.&amp;nbsp; &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Capture of 'self' in a closure that outlives deinit&amp;nbsp;&lt;/span&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: 'Nanum Gothic';&quot;&gt;deinit 안에서 만든 비동기 클로저(Task)가 self보다 더 오래 살아있을 수 있는데, 그 클로저가 self(를 통해 effect1)를 캡처했다. 그래서 금지!&lt;br /&gt;&lt;br /&gt;- deinit { Task { &amp;hellip; } }에서 Task 클로저는 탈출(escaping) 하며, deinit 종료 후에도 실행될 수 있음&lt;br /&gt;- &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;effect1&lt;/span&gt;.stop()를 부르려면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;effect1&lt;/span&gt;(= self.&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;effect1&lt;/span&gt;)에 접근해야 하므로 self를 캡처함.&lt;br /&gt;- 하지만 deinit 이후 self는 해제되기 때문에, &amp;ldquo;deinit보다 오래 사는 클로저가 self를 잡는 것&amp;rdquo;은 안전하지 않아 컴파일러가 막음.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하지만 위의 해결 방안 처럼 하면 클로저는 self 가 아니라 effet1 만 캡쳐해서 괜찮은 것입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758950199671&quot; class=&quot;arduino&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1) 지역 변수로 뽑아내기
deinit {
    let e = effect1
    Task { @MainActor in
        e.stop()
    }
}

// 2) capture list로 직접 캡처
deinit {
    Task { @MainActor [effect1] in
        effect1.stop()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;두 경우 모두&amp;nbsp;&lt;b&gt;self를 직접 캡처하지 않고&lt;/b&gt;,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;effect1 라는&amp;nbsp;&lt;b&gt;프로퍼티의 값을 deinit 시점에 지역 변수처럼 캡처&lt;/b&gt;합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 클로저는 self가 아니라 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;effect1 &lt;/span&gt;만 들고 있게 되고,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;  &amp;ldquo;Capture of &amp;lsquo;self&amp;rsquo; in a closure that outlives deinit&amp;rdquo; 에러가 사라지는 거예요.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;질문 2)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;strong 이 아니라 weak 하게 캡쳐한다면 ? &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;스크린샷 2025-09-27 오후 2.24.54.png&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ezGjcV/btsQQ2tBFFP/r53xoK6wpLr65ToHkkVnc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ezGjcV/btsQQ2tBFFP/r53xoK6wpLr65ToHkkVnc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ezGjcV/btsQQ2tBFFP/r53xoK6wpLr65ToHkkVnc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FezGjcV%2FbtsQQ2tBFFP%2Fr53xoK6wpLr65ToHkkVnc0%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;1312&quot; height=&quot;604&quot; data-filename=&quot;스크린샷 2025-09-27 오후 2.24.54.png&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;604&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: 'Nanum Gothic';&quot;&gt; &amp;bull; Task가&amp;nbsp;도는&amp;nbsp;시점에&amp;nbsp;effect가&amp;nbsp;이미&amp;nbsp;해제됐으면&amp;nbsp;아무&amp;nbsp;일도&amp;nbsp;안&amp;nbsp;함.&lt;br /&gt;&amp;bull; &amp;rarr;&amp;nbsp;정리&amp;nbsp;로직이&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;bull; &amp;nbsp;&lt;/span&gt;확실히&amp;nbsp;정리해야&amp;nbsp;함&amp;nbsp;&amp;rarr;&amp;nbsp;strong&amp;nbsp;캡처&lt;br /&gt;&amp;bull; 있으면 하고, 없어도 됨 &amp;rarr; weak 캡처&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;stop이 꼭 호출돼야 하는 중요 리소스 정리(타이머 invalidate, 관찰 해제, 파일 핸들 닫기 등) 에서는 strong 권장&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2.2] 상황 2&lt;/span&gt;&lt;/h4&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;스크린샷 2025-09-27 오후 2.30.08.png&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QHQu1/btsQREM14lF/Jt0cVDkZn6DOykkxlj5zKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QHQu1/btsQREM14lF/Jt0cVDkZn6DOykkxlj5zKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QHQu1/btsQREM14lF/Jt0cVDkZn6DOykkxlj5zKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQHQu1%2FbtsQREM14lF%2FJt0cVDkZn6DOykkxlj5zKK%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;721&quot; height=&quot;566&quot; data-filename=&quot;스크린샷 2025-09-27 오후 2.30.08.png&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;760&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: 'Nanum Gothic';&quot;&gt;&lt;b&gt;현황)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Swift 6 + &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Strict Concurrency Checking 을 가장 높은 수준 (Complete) 로 해도 에러 안남&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;설명)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1. 프로퍼티 접근이 가능한 이유 = Sendable&lt;br /&gt;&lt;br /&gt;&amp;bull; nonisolated 컨텍스트에서 메인 액터 격리 프로퍼티에 접근하려면, 그 값이 실행자 경계를 안전하게 건널 수 있어야(Sendable) 하는데, SomeUIEffect2는 Sendable 이다.&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;deinit에서&amp;nbsp;effect2&amp;nbsp;접근&amp;nbsp;OK.&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: 'Nanum Gothic';&quot;&gt;2. 메서드 호출이 가능한 이유 = @MainActor 아님&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; stop()이&amp;nbsp;액터&amp;nbsp;격리(예:&amp;nbsp;@MainActor)가&amp;nbsp;아니므로,&amp;nbsp;hop(실행자&amp;nbsp;전환)&amp;nbsp;없이&amp;nbsp;동기&amp;nbsp;호출&amp;nbsp;가능&amp;nbsp;&amp;rarr;&amp;nbsp;OK.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;요약:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 프로퍼티&amp;nbsp;접근&amp;nbsp;측면:&amp;nbsp;Sendable이라&amp;nbsp;안전.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 메서드&amp;nbsp;호출&amp;nbsp;측면:&amp;nbsp;@MainActor가&amp;nbsp;아니니&amp;nbsp;hop&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2.3] 상황 3&lt;/span&gt;&lt;/h4&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;스크린샷 2025-09-27 오후 2.38.59.png&quot; data-origin-width=&quot;1820&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK5ZtP/btsQTVMWXTX/1bIpKknbNgmVA998rxPEOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK5ZtP/btsQTVMWXTX/1bIpKknbNgmVA998rxPEOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK5ZtP/btsQTVMWXTX/1bIpKknbNgmVA998rxPEOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK5ZtP%2FbtsQTVMWXTX%2F1bIpKknbNgmVA998rxPEOK%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;1820&quot; height=&quot;772&quot; data-filename=&quot;스크린샷 2025-09-27 오후 2.38.59.png&quot; data-origin-width=&quot;1820&quot; data-origin-height=&quot;772&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: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;swift5 에서는 워닝, swift 6 에서는 에러 발생&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;&amp;bull; SomeUI가 @MainActor라서 effect3 자체가 메인 액터에 격리돼 있어요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 그런데&amp;nbsp;deinit은&amp;nbsp;항상&amp;nbsp;nonisolated(아무&amp;nbsp;실행자에도&amp;nbsp;속하지&amp;nbsp;않음).&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; nonisolated 컨텍스트에서 메인 액터 격리 프로퍼티에 접근하려면, 그 값이 실행자 경계를 안전하게 건널 수 있어야(Sendable) 하는데, SomeUIEffect3는 Sendable이 아니므로 프로퍼티를 읽는 순간 막힙니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;rarr;&amp;nbsp;&quot;Cannot&amp;nbsp;access&amp;nbsp;property&amp;nbsp;'effect3'&amp;nbsp;with&amp;nbsp;a&amp;nbsp;non-Sendable&amp;nbsp;type&amp;nbsp;...&amp;nbsp;from&amp;nbsp;nonisolated&amp;nbsp;deinit&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;우선 1번과 같은 방식으로 해결할 수 없습니다.&amp;nbsp;&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;스크린샷 2025-09-27 오후 2.48.33.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kINPj/btsQS5P4jZf/07xgzM9AjhJhf0hB66mskK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kINPj/btsQS5P4jZf/07xgzM9AjhJhf0hB66mskK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kINPj/btsQS5P4jZf/07xgzM9AjhJhf0hB66mskK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkINPj%2FbtsQS5P4jZf%2F07xgzM9AjhJhf0hB66mskK%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;1830&quot; height=&quot;826&quot; data-filename=&quot;스크린샷 2025-09-27 오후 2.48.33.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;826&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: 'Nanum Gothic';&quot;&gt;deinit은 nonisolated이고, effect3는 @MainActor에 격리된 프로퍼티라서 그 접근 자체가 크로스-액터 접근이 되는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;non-sendable 이여서 크로스-액터 접근이 불가능하기 때문입니다.&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: 'Nanum Gothic';&quot;&gt;풀어서 이해해보면 다음과 같습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt; &amp;bull; deinit은&amp;nbsp;nonisolated라서&amp;nbsp;어떤&amp;nbsp;액터에도&amp;nbsp;속하지&amp;nbsp;않습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; effect3는&amp;nbsp;@MainActor&amp;nbsp;격리된&amp;nbsp;저장&amp;nbsp;프로퍼티라&amp;nbsp;**액터&amp;nbsp;경계&amp;nbsp;밖(=&amp;nbsp;nonisolated)**에서&amp;nbsp;접근하면&amp;nbsp;크로스-액터&amp;nbsp;접근이&amp;nbsp;됩니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 이때&amp;nbsp;**값을&amp;nbsp;액터&amp;nbsp;경계&amp;nbsp;밖으로&amp;nbsp;&amp;ldquo;꺼내도&amp;nbsp;안전한지&amp;rdquo;**를&amp;nbsp;컴파일러가&amp;nbsp;따지는데,&amp;nbsp;그&amp;nbsp;안전성의&amp;nbsp;기준이&amp;nbsp;Sendable&amp;nbsp;입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; SomeUIEffect3가&amp;nbsp;non-Sendable이므로&amp;nbsp;경계&amp;nbsp;밖으로&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;없어&amp;nbsp;접근&amp;nbsp;자체가&amp;nbsp;거부됩니다&amp;nbsp;&amp;rarr;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 요런 식으로 약간의 트릭을 써야합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758952878930&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final class DeinitAction {

    let action: () -&amp;gt; Void

    init(_ action: @escaping () -&amp;gt; Void) {
        self.action = action
    }

    deinit {
        action()
    }
}

@MainActor
public final class SomeUI {

    let effect3 = SomeUIEffect3()
    private let onDeinit: DeinitAction

    init() {
        onDeinit = DeinitAction { [effect3] in
            effect3.stop()

            // 만약 stop이 메인에서 실행되어야한다면
            // Task { @MainActor in effect3.stop() }
        }
        effect3.start()
    }

    deinit { /* 비워둬도 됨. onDeinit이 해제되며 action 실행 */ }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[3] 정리&amp;nbsp;&lt;/span&gt;&lt;/h4&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;SCR-20250927-nqfh.png&quot; data-origin-width=&quot;2583&quot; data-origin-height=&quot;1579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHKz6x/btsQR6PycsM/foL6sdKypwgV6dAwIVZ2L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHKz6x/btsQR6PycsM/foL6sdKypwgV6dAwIVZ2L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHKz6x/btsQR6PycsM/foL6sdKypwgV6dAwIVZ2L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHKz6x%2FbtsQR6PycsM%2FfoL6sdKypwgV6dAwIVZ2L1%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;1073&quot; height=&quot;656&quot; data-filename=&quot;SCR-20250927-nqfh.png&quot; data-origin-width=&quot;2583&quot; data-origin-height=&quot;1579&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758953481081&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final class DeinitAction {

    let action: () -&amp;gt; Void

    init(_ action: @escaping () -&amp;gt; Void) {
        self.action = action
    }

    deinit {
        action()
    }
}

@MainActor
public final class SomeUI {

    let effect1 = SomeUIEffect1()
    let effect2 = SomeUIEffect2()
    let effect3 = SomeUIEffect3()

    private let onDeinit: DeinitAction

    init() {
        onDeinit = DeinitAction { [effect3] in
            effect3.stop()

            // 만약 stop이 메인에서 실행되어야한다면
            // Task { @MainActor in effect3.stop() }
        }

        effect1.start()
        effect2.start()
        effect3.start()
    }

    deinit {
        Task { @MainActor [effect1] in
            effect1.stop()
        }

        effect2.stop()
    }
}

@MainActor
final class SomeUIEffect1 {

    func start() {}
    func stop() {}
}

final class SomeUIEffect2: Sendable {

    func start() {}
    func stop() {}
}

final class SomeUIEffect3 {

    func start() {}
    func stop() {}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /Swift</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/685</guid>
      <comments>https://eunjin3786.tistory.com/685#entry685comment</comments>
      <pubDate>Sat, 27 Sep 2025 15:29:08 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] Visual intelligence &amp;gt; pixelBuffer 활용하기</title>
      <link>https://eunjin3786.tistory.com/684</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 문서: &lt;a href=&quot;https://developer.apple.com/documentation/visualintelligence/integrating-your-app-with-visual-intelligence&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Integrating&amp;nbsp;your&amp;nbsp;app&amp;nbsp;with&amp;nbsp;visual&amp;nbsp;intelligence&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 예제 프로젝트 : &lt;a href=&quot;https://developer.apple.com/documentation/appintents/adopting-app-intents-to-support-system-experiences&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Adopting&amp;nbsp;App&amp;nbsp;Intents&amp;nbsp;to&amp;nbsp;support&amp;nbsp;system&amp;nbsp;experiences&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[1] 배경&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;카메라나 스크린샷을 통해 Visual Search 를 하면 아래의 메소드가 타게 되고&amp;nbsp;&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;SCR-20250824-olvg.png&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cB4dXN/btsP5hwMeP1/RKyXKZcowlK67kOGzkhIHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cB4dXN/btsP5hwMeP1/RKyXKZcowlK67kOGzkhIHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cB4dXN/btsP5hwMeP1/RKyXKZcowlK67kOGzkhIHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcB4dXN%2FbtsP5hwMeP1%2FRKyXKZcowlK67kOGzkhIHK%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;1822&quot; height=&quot;778&quot; data-filename=&quot;SCR-20250824-olvg.png&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;778&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: 'Nanum Gothic';&quot;&gt;SemanticContentDescriptor 를 통해 이미지에 대한 정보를 얻어올 수 있다.&amp;nbsp;&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;SCR-20250824-omdb.png&quot; data-origin-width=&quot;2082&quot; data-origin-height=&quot;1544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oV23v/btsP4Hv032F/IGkY7Pkc5EQcAa2V3dUKkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oV23v/btsP4Hv032F/IGkY7Pkc5EQcAa2V3dUKkk/img.png&quot; data-alt=&quot;https://developer.apple.com/documentation/visualintelligence/semanticcontentdescriptor&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oV23v/btsP4Hv032F/IGkY7Pkc5EQcAa2V3dUKkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoV23v%2FbtsP4Hv032F%2FIGkY7Pkc5EQcAa2V3dUKkk%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;745&quot; height=&quot;552&quot; data-filename=&quot;SCR-20250824-omdb.png&quot; data-origin-width=&quot;2082&quot; data-origin-height=&quot;1544&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developer.apple.com/documentation/visualintelligence/semanticcontentdescriptor&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;SemanticContentDescriptor 는 labels, pixelBuffer 를 제공하는데 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;label 을 단독으로 쓸 수 없다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;굉장히 광범위한 label 정보를 주기 때문이다. (&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예를들어 스콘을 이미지 검색하면 bread 도 아니고 food 라는 label 을 준다.)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;label 은 부가적인 정보일 뿐이고 visual intelligence 기능은 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;pixelBuffer 를 메인으로 사용해야하며&amp;nbsp;&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: 'Nanum Gothic';&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;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그럼 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;pixelBuffer 를 통한 이미지 검색을 제공하지 않는 곳은 정확한 label 정보를 얻기 위해&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;한단계를 더 거쳐야하는데, 어떤 방법이 있을 지 &amp;amp; 성능이 괜찮을 지 알아보자!&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;[2] 사전 지식 : CVReadOnlyPixelBuffer -&amp;gt; CVPixelBuffer 로 변환하기&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/corevideo/cvreadonlypixelbuffer?changes=lat_3_1_4_6_1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CVReadOnlyPixelBuffer&lt;/a&gt; (&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;iOS 26+) 를&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기존에 쓰던 pixelBuffer 로 변환하려면 &lt;a href=&quot;https://developer.apple.com/documentation/corevideo/cvreadonlypixelbuffer/withunsafebuffer(_:)?changes=lat_3_1_4_6_1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;withUnsafeBuffer&lt;/a&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20250825-jdvy.png&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwGRdi/btsP4uwOc5T/dnkAVrovTW2fdiKsDqD5f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwGRdi/btsP4uwOc5T/dnkAVrovTW2fdiKsDqD5f0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwGRdi/btsP4uwOc5T/dnkAVrovTW2fdiKsDqD5f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwGRdi%2FbtsP4uwOc5T%2FdnkAVrovTW2fdiKsDqD5f0%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;740&quot; height=&quot;215&quot; data-filename=&quot;SCR-20250825-jdvy.png&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;AI 는 자꾸 CVPixelBuffer 로 타입캐스팅 하라고 코드를 짜주는 데 &lt;b&gt;타입캐스팅 실패&lt;/b&gt;한다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이 메소드를 기억하자.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;[3] PixelBuffer 로 Label 을 구하는 방법&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;1) &lt;a href=&quot;https://ai.google.dev/edge/mediapipe/solutions/vision/object_detector/ios?hl=ko%EF%BB%BF&amp;amp;_gl=1*1rz7091*_up*MQ..*_ga*MTY4OTk0NTQyLjE3NTYwODM2NzU.*_ga_P1DBVKWT6V*czE3NTYwODM2NzUkbzEkZzAkdDE3NTYwODM2NzUkajYwJGwwJGg1NDIwMjQw&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Google&amp;nbsp;MediaPipe의&amp;nbsp;Object&amp;nbsp;Detector&lt;/a&gt;&amp;nbsp;&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- &lt;a href=&quot;https://ai.google.dev/edge/mediapipe/solutions/vision/object_detector?hl=ko%EF%BB%BF#models&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;모델&lt;/a&gt;을 선택해서 앱에 넣는 방식&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ㄴ 테스트해보니 문서에 나온 것처럼 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;EfficientDet-Lite2 가 EfficientDet-Lite0 에 비해 속도는 느리지만 정확성이 높아서 이 모델로 선택!&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;- SPM 지원안해서 cocoapod 으로 의존성 설치해야함&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;2) &lt;a href=&quot;https://developer.apple.com/documentation/vision&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Core ML + Vision&lt;/a&gt;&amp;nbsp;&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: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/machine-learning/models/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;모델&lt;/a&gt;을 선택해서 앱에 넣는 방식&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;ㄴ 과거와 달리 mlmodel 이 아니라 mlpackage 인데 그냥 다운받아서 넣으면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 예전에는&amp;nbsp;Core&amp;nbsp;ML&amp;nbsp;모델이&amp;nbsp;보통&amp;nbsp;.mlmodel&amp;nbsp;하나의&amp;nbsp;파일로&amp;nbsp;배포됐습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 요즘(특히 Core ML Tools 4+ / iOS 14+)은 모델이 크거나 여러 파일이 필요할 때 **.mlpackage**라는 디렉토리 번들 형식으로 내보내기도 해요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;- Build하면 Xcode가 자동으로 .mlmodelc(컴파일된 Core ML 모델)로 바꿔서 번들 안에 포함시킵니다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;- .mlmodel이든 .mlpackage든 직접 번들에 넣으면 Xcode가 빌드 과정에서 mlmodelc로 변환합니다. 런타임에서는 항상 .mlmodelc를 불러오게 돼요.&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: 'Nanum Gothic';&quot;&gt;ㄴ 테스트해보니 FastViT, DETRResnet50SemanticSegmentationF16 보다 YOLOv3 결과가 더 잘나와서 선택!&amp;nbsp; (근데 FastViT, DETR 이 더 최신 모델이고 정확도가 높다고 한다... 흠)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;3) &lt;a href=&quot;https://cloud.google.com/vision/docs?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Google Cloud Vision API&amp;nbsp;&lt;/span&gt;&lt;/a&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- API Request 하는 방식 (&lt;a href=&quot;https://cloud.google.com/vision/pricing?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;과금&lt;/a&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ㄴ 매월 1,000건까지는 무료&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- &lt;a href=&quot;https://console.cloud.google.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;구글 콘솔&lt;/a&gt; 접속 -&amp;gt;&amp;nbsp; 프로젝트 생성 -&amp;gt; Cloud Vision API 를 활성화 -&amp;gt; API 키 생성 하는 과정이 필요하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ㄴ &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;API 키로 처음 요청하면 에러에&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;결제 계좌 등록 링크가 내려오는 데 이것까지 해줘야한다!&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[4] 비교&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;SemanticContentDescriptor &amp;gt; labels&amp;nbsp; (confidence 미제공)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;2. GoogleObjectDetector&amp;nbsp; with &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;EfficientDet-Lite2 모델&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;3. CoreML&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Detector &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;with &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;YOLOv3&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;모델&lt;/span&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: 'Nanum Gothic';&quot;&gt;4. GoogleVisionAPIObjectDetector&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;4가지를 비교해보자&amp;nbsp; (스샷 후, 물체를 특정해서 검색함)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;GoogleVisionAPI 압승이다 압승...&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 606px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 538px;&quot;&gt;
&lt;td style=&quot;width: 34.0697%; height: 538px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1809.jpg&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;1029&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yHgx2/btsP38undMY/skZ0gokMPU1mpA6tEBvkz0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yHgx2/btsP38undMY/skZ0gokMPU1mpA6tEBvkz0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yHgx2/btsP38undMY/skZ0gokMPU1mpA6tEBvkz0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyHgx2%2FbtsP38undMY%2FskZ0gokMPU1mpA6tEBvkz0%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;794&quot; height=&quot;1029&quot; data-filename=&quot;IMG_1809.jpg&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;1029&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 65.9303%; height: 538px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20250825-kjiu.png&quot; data-origin-width=&quot;1226&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXACsL/btsP6LjRAzF/SHTZhLCTy71QK8C4AQIV9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXACsL/btsP6LjRAzF/SHTZhLCTy71QK8C4AQIV9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXACsL/btsP6LjRAzF/SHTZhLCTy71QK8C4AQIV9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXACsL%2FbtsP6LjRAzF%2FSHTZhLCTy71QK8C4AQIV9k%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;526&quot; height=&quot;196&quot; data-filename=&quot;SCR-20250825-kjiu.png&quot; data-origin-width=&quot;1226&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 34.0697%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;View recent photos-2 2.png&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOAfeH/btsP5KTd8p5/P2S67PsT10v28VcLPG0XxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOAfeH/btsP5KTd8p5/P2S67PsT10v28VcLPG0XxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOAfeH/btsP5KTd8p5/P2S67PsT10v28VcLPG0XxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOAfeH%2FbtsP5KTd8p5%2FP2S67PsT10v28VcLPG0XxK%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;779&quot; height=&quot;1024&quot; data-filename=&quot;View recent photos-2 2.png&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 65.9303%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20250825-klhv.png&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIlr71/btsP4T383Oa/5XtdiOYkb6LmdddvwoXIpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIlr71/btsP4T383Oa/5XtdiOYkb6LmdddvwoXIpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIlr71/btsP4T383Oa/5XtdiOYkb6LmdddvwoXIpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIlr71%2FbtsP4T383Oa%2F5XtdiOYkb6LmdddvwoXIpk%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;1882&quot; height=&quot;410&quot; data-filename=&quot;SCR-20250825-klhv.png&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 34.0697%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;View recent photos-2 3.png&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;1023&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v4hf5/btsP20XRXM8/KkLmlJ5ZIJS74BJGAOz161/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v4hf5/btsP20XRXM8/KkLmlJ5ZIJS74BJGAOz161/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v4hf5/btsP20XRXM8/KkLmlJ5ZIJS74BJGAOz161/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv4hf5%2FbtsP20XRXM8%2FKkLmlJ5ZIJS74BJGAOz161%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;812&quot; height=&quot;1023&quot; data-filename=&quot;View recent photos-2 3.png&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;1023&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 65.9303%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20250825-klzv.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUtOY2/btsP213yxSu/pCWpTEa9LmqjiwSTsk443K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUtOY2/btsP213yxSu/pCWpTEa9LmqjiwSTsk443K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUtOY2/btsP213yxSu/pCWpTEa9LmqjiwSTsk443K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUtOY2%2FbtsP213yxSu%2FpCWpTEa9LmqjiwSTsk443K%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;1800&quot; height=&quot;354&quot; data-filename=&quot;SCR-20250825-klzv.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 34.0697%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;View recent photos-2 4.png&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceqErl/btsP2Iv8Pvn/NDdzahir7ab54nx6qPSKJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceqErl/btsP2Iv8Pvn/NDdzahir7ab54nx6qPSKJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceqErl/btsP2Iv8Pvn/NDdzahir7ab54nx6qPSKJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceqErl%2FbtsP2Iv8Pvn%2FNDdzahir7ab54nx6qPSKJK%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;856&quot; height=&quot;774&quot; data-filename=&quot;View recent photos-2 4.png&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 65.9303%; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;메론빵도&amp;nbsp;안다구&amp;nbsp;??&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20250825-kmnt.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0VZoA/btsP4nkttRC/L1BV6v9YxwQxME1pKHCUQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0VZoA/btsP4nkttRC/L1BV6v9YxwQxME1pKHCUQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0VZoA/btsP4nkttRC/L1BV6v9YxwQxME1pKHCUQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0VZoA%2FbtsP4nkttRC%2FL1BV6v9YxwQxME1pKHCUQk%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;728&quot; height=&quot;127&quot; data-filename=&quot;SCR-20250825-kmnt.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 34.0697%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;View recent photos-2 5.png&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MJfyP/btsP3ay6rL9/jyLSTpGvTbLKN4OIu5Gcf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MJfyP/btsP3ay6rL9/jyLSTpGvTbLKN4OIu5Gcf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MJfyP/btsP3ay6rL9/jyLSTpGvTbLKN4OIu5Gcf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMJfyP%2FbtsP3ay6rL9%2FjyLSTpGvTbLKN4OIu5Gcf1%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;718&quot; height=&quot;692&quot; data-filename=&quot;View recent photos-2 5.png&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 65.9303%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20250825-knob.png&quot; data-origin-width=&quot;1876&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3TzhK/btsP4XesJTU/RVikbkCbE9C87kNnpBEb9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3TzhK/btsP4XesJTU/RVikbkCbE9C87kNnpBEb9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3TzhK/btsP4XesJTU/RVikbkCbE9C87kNnpBEb9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3TzhK%2FbtsP4XesJTU%2FRVikbkCbE9C87kNnpBEb9k%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;1876&quot; height=&quot;342&quot; data-filename=&quot;SCR-20250825-knob.png&quot; data-origin-width=&quot;1876&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.4884%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;View recent photos-2 6.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1046&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1nhwM/btsP4JnjPbi/SyrjKj3ZTuN1OSt5C4r8qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1nhwM/btsP4JnjPbi/SyrjKj3ZTuN1OSt5C4r8qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1nhwM/btsP4JnjPbi/SyrjKj3ZTuN1OSt5C4r8qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1nhwM%2FbtsP4JnjPbi%2FSyrjKj3ZTuN1OSt5C4r8qK%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;330&quot; height=&quot;431&quot; data-filename=&quot;View recent photos-2 6.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1046&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 66.5116%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20250825-kpdu.png&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q377X/btsP4xAj77X/IlGPNbCe3dzi5gQY44ZSJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q377X/btsP4xAj77X/IlGPNbCe3dzi5gQY44ZSJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q377X/btsP4xAj77X/IlGPNbCe3dzi5gQY44ZSJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ377X%2FbtsP4xAj77X%2FIlGPNbCe3dzi5gQY44ZSJ1%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;1892&quot; height=&quot;320&quot; data-filename=&quot;SCR-20250825-kpdu.png&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.4884%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;View recent photos-2 7.png&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX1aCJ/btsP4VHIpGQ/POiUKMKai4QsYOYkUibxPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX1aCJ/btsP4VHIpGQ/POiUKMKai4QsYOYkUibxPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX1aCJ/btsP4VHIpGQ/POiUKMKai4QsYOYkUibxPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX1aCJ%2FbtsP4VHIpGQ%2FPOiUKMKai4QsYOYkUibxPK%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;809&quot; height=&quot;786&quot; data-filename=&quot;View recent photos-2 7.png&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 66.5116%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-25 오전 11.43.48.png&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TnYL1/btsP2ZdzdW8/cz0W6bdkNyhIwABMMLgfak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TnYL1/btsP2ZdzdW8/cz0W6bdkNyhIwABMMLgfak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TnYL1/btsP2ZdzdW8/cz0W6bdkNyhIwABMMLgfak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTnYL1%2FbtsP2ZdzdW8%2Fcz0W6bdkNyhIwABMMLgfak%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;1858&quot; height=&quot;270&quot; data-filename=&quot;스크린샷 2025-08-25 오전 11.43.48.png&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.4884%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 66.5116%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[5] 결론&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;GoogleVisionAPI&amp;nbsp;&amp;nbsp;를 사용하면 꽤 명확한 label 정보를 얻을 수 있다 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;(+ food, ingredient, table 관련 label 필터링 하고 쓰면)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;(+ 사용자가 정확하게 물체를 선택해서 검색하면)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;완벽하진 않지만, Visual Intelligence -&amp;gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;GoogleVisionAPI -&amp;gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;label 정보로 검색결과 제공할 수 있을 듯!&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /iOS</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/684</guid>
      <comments>https://eunjin3786.tistory.com/684#entry684comment</comments>
      <pubDate>Mon, 25 Aug 2025 11:54:49 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Foundation Models</title>
      <link>https://eunjin3786.tistory.com/683</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[1] Foundation Models&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Foundation Models 는 온디바이스 LLM 을 제공하는 프레임워크입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/foundationmodels&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1749816554625&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;Foundation Models | Apple Developer Documentation&quot; data-og-description=&quot;Perform tasks with the on-device model that specializes in language understanding, structured output, and tool calling.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/foundationmodels&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/foundationmodels&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dt4Q2G/hyY8ZmsRMY/DjQskXLPQUkpkEUjvbEGPk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/mHmvb/hyY44prNeG/clSSzvptTdoCHl3szhhqQ1/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/foundationmodels&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dt4Q2G/hyY8ZmsRMY/DjQskXLPQUkpkEUjvbEGPk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/mHmvb/hyY44prNeG/clSSzvptTdoCHl3szhhqQ1/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Foundation Models | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Perform tasks with the on-device model that specializes in language understanding, structured output, and tool calling.&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: 'Nanum Gothic';&quot;&gt;HIG 에 &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/generative-ai&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Generative&amp;nbsp;AI&lt;/a&gt; 도 함께 추가되었는데 사용하면서 함께 읽어보면 좋을 것 같아요&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2] 사용법&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LanguageModelSession&lt;/a&gt; 을 만들고 session 에 prompt 를 넘겨서 response 를 받습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;성능이 아쉽지만...... 무료니까...&amp;nbsp;&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;Screenshot 2025-06-13 at 9.27.47 PM.png&quot; data-origin-width=&quot;2752&quot; data-origin-height=&quot;1062&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5nEQr/btsOCARCR5a/c1lEOFZ8vCakNxQ9z4Bss1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5nEQr/btsOCARCR5a/c1lEOFZ8vCakNxQ9z4Bss1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5nEQr/btsOCARCR5a/c1lEOFZ8vCakNxQ9z4Bss1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5nEQr%2FbtsOCARCR5a%2Fc1lEOFZ8vCakNxQ9z4Bss1%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;2752&quot; height=&quot;1062&quot; data-filename=&quot;Screenshot 2025-06-13 at 9.27.47 PM.png&quot; data-origin-width=&quot;2752&quot; data-origin-height=&quot;1062&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: 'Nanum Gothic';&quot;&gt;Session 에는 다음과 같은 이니셜라이저가 제공되는데 문서에 필드 설명이 잘 나와있어요&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession/init(model:guardrails:tools:instructions:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;init(model:guardrails:tools:instructions:)&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;i&gt;ㄴ&amp;nbsp;model 중 &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/systemlanguagemodel/usecase/contenttagging&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;content tagging&lt;/a&gt;&amp;nbsp; 유용해보임&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-13 at 10.24.22 PM.png&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w2Xh5/btsOA89Yj5c/T62n7lF1GkrGXxuVkLKhgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w2Xh5/btsOA89Yj5c/T62n7lF1GkrGXxuVkLKhgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w2Xh5/btsOA89Yj5c/T62n7lF1GkrGXxuVkLKhgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw2Xh5%2FbtsOA89Yj5c%2FT62n7lF1GkrGXxuVkLKhgK%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;1718&quot; height=&quot;502&quot; data-filename=&quot;Screenshot 2025-06-13 at 10.24.22 PM.png&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;502&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: 'Nanum Gothic';&quot;&gt;또한 다양한 Request 함수를 제공하고 있습니다. (참고: &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession#Generating-a-request&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Generating a request&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2.1] Structured Ouput&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/generable&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Generable&lt;/a&gt; 매크로를 활용해서 LLM이 구조화된(Generable) 응답을 반환할 수 있게 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/guide(description:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Guide&lt;/a&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;Screenshot 2025-06-13 at 10.08.08 PM.png&quot; data-origin-width=&quot;2318&quot; data-origin-height=&quot;1404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CcC05/btsOCwPfttl/EuyG3b6ozOnEtH9jDlzHNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CcC05/btsOCwPfttl/EuyG3b6ozOnEtH9jDlzHNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CcC05/btsOCwPfttl/EuyG3b6ozOnEtH9jDlzHNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCcC05%2FbtsOCwPfttl%2FEuyG3b6ozOnEtH9jDlzHNk%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;923&quot; height=&quot;559&quot; data-filename=&quot;Screenshot 2025-06-13 at 10.08.08 PM.png&quot; data-origin-width=&quot;2318&quot; data-origin-height=&quot;1404&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: 'Nanum Gothic';&quot;&gt;&lt;i&gt;(MyTextEditor 는 앱 이름..)&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Generable 은 struct 뿐만 아니라 enum 에도 붙일 수 있고 재귀도 가능해서 편하게 쓸 수 있을 것 같고&amp;nbsp;&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;Screenshot 2025-06-14 at 8.24.31 PM.png&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH9HfN/btsOBZEDeij/6opjs4tGiYxUMw3d6P4hh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH9HfN/btsOBZEDeij/6opjs4tGiYxUMw3d6P4hh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH9HfN/btsOBZEDeij/6opjs4tGiYxUMw3d6P4hh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH9HfN%2FbtsOBZEDeij%2F6opjs4tGiYxUMw3d6P4hh1%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;1132&quot; height=&quot;482&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.24.31 PM.png&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;482&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: 'Nanum Gothic';&quot;&gt;Guide 도 다양하게 쓸 수 있습니다. (&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Regex 도 되다니..  )&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&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/vs2d9/btsOBPPQI0L/GSuy26D690mdwudEU8ah8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vs2d9/btsOBPPQI0L/GSuy26D690mdwudEU8ah8k/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;538&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.26.49 PM.png&quot; data-widthpercent=&quot;50.44&quot; style=&quot;width: 49.854254%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vs2d9/btsOBPPQI0L/GSuy26D690mdwudEU8ah8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvs2d9%2FbtsOBPPQI0L%2FGSuy26D690mdwudEU8ah8k%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;1236&quot; height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wpsTf/btsOBBEhH8X/XaWojL0mhOCozeFCApkcck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wpsTf/btsOBBEhH8X/XaWojL0mhOCozeFCApkcck/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;552&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.26.15 PM.png&quot; data-widthpercent=&quot;49.56&quot; style=&quot;width: 48.982956%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wpsTf/btsOBBEhH8X/XaWojL0mhOCozeFCApkcck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwpsTf%2FbtsOBBEhH8X%2FXaWojL0mhOCozeFCApkcck%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;1246&quot; height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&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/b97P5H/btsOB1oVdNR/WvJHy4qhID4h6NbGlxnDd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b97P5H/btsOB1oVdNR/WvJHy4qhID4h6NbGlxnDd0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;556&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.26.23 PM.png&quot; data-widthpercent=&quot;51.73&quot; style=&quot;width: 51.1317%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b97P5H/btsOB1oVdNR/WvJHy4qhID4h6NbGlxnDd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb97P5H%2FbtsOB1oVdNR%2FWvJHy4qhID4h6NbGlxnDd0%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;1234&quot; height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbTTP4/btsOCnkX0kw/nXK5ctAwL6Mxy21M2dIOKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbTTP4/btsOCnkX0kw/nXK5ctAwL6Mxy21M2dIOKk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;594&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.28.33 PM.png&quot; data-widthpercent=&quot;48.27&quot; style=&quot;width: 47.705509%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbTTP4/btsOCnkX0kw/nXK5ctAwL6Mxy21M2dIOKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbTTP4%2FbtsOCnkX0kw%2FnXK5ctAwL6Mxy21M2dIOKk%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;1230&quot; height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;참고: &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/generating-swift-data-structures-with-guided-generation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Generating&amp;nbsp;Swift&amp;nbsp;data&amp;nbsp;structures&amp;nbsp;with&amp;nbsp;guided&amp;nbsp;generation&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2.2] 런타임 동적 스키마&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위의 방식이 '컴파일 타임 스키마'라면 (&lt;span style=&quot;color: #333333; text-align: start;&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: 'Nanum Gothic';&quot;&gt;런타임 동적 스키마를 사용하는 방법도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/dynamicgenerationschema&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DynamicGenerationSchema&lt;/a&gt; 로 요청을 하고 &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/generatedcontent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GeneratedContent&lt;/a&gt; 타입을 받아서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/generatedcontent/value(_:forproperty:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;value(_:forProperty:)&lt;/a&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&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/bWdp7n/btsOBCbQKdV/AhKK13Ezzvr0yE2mgNvBVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWdp7n/btsOBCbQKdV/AhKK13Ezzvr0yE2mgNvBVK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;694&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.35.28 PM.png&quot; data-widthpercent=&quot;50.53&quot; style=&quot;width: 49.941834%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWdp7n/btsOBCbQKdV/AhKK13Ezzvr0yE2mgNvBVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWdp7n%2FbtsOBCbQKdV%2FAhKK13Ezzvr0yE2mgNvBVK%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;1236&quot; height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mgsSW/btsOBEU03yU/DYaPvPSA5ROA3sT6RE01fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mgsSW/btsOBEU03yU/DYaPvPSA5ROA3sT6RE01fK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;710&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.35.47 PM.png&quot; data-widthpercent=&quot;49.47&quot; style=&quot;width: 48.895375%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mgsSW/btsOBEU03yU/DYaPvPSA5ROA3sT6RE01fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmgsSW%2FbtsOBEU03yU%2FDYaPvPSA5ROA3sT6RE01fK%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;1238&quot; height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/div&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: 'Nanum Gothic';&quot;&gt;참고: &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/generating-swift-data-structures-with-guided-generation#Define-a-dynamic-schema-at-runtime&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;예제&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[3] Tool Calling&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Tool Calling 은 모델의 기능을 여러분의 사용 사례에 맞게 확장할 수 있는 수단을 제공합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Tool Calling 을 통해 모델은 여러분이 만든 코드와 상호작용할 수 있으며,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이를 통해 다음과 같은 일을 할 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 최신 정보를 가져오기 (ex. 외부 API 호출)&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 별도로 제공하는 신뢰가능한 데이터에 기반해서 응답 생성하기 (ex. Contacts 나 HealthKit 같은 다른 프레임워크와 연동, 앱의 데이터베이스에서 항목을 조회하고 이를 응답에 참조하도록 하기)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 사이드 이펙트 실행하기 (ex. 다크모드 켜기)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[3.1] Tool Calling 사용법&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/tool&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Tool 프로토콜&lt;/a&gt;을 채택해서 Tool 을 만든 다음, &amp;nbsp;Session 의 이니셜라이저에 넘기는 방식으로 사용합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예제 1) 날씨 정보 요청&amp;nbsp;&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;Screenshot 2025-06-13 at 10.19.50 PM.png&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/omuhe/btsOCzrFV7W/sIpGnsKxrCuvK2KS8NlMA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/omuhe/btsOCzrFV7W/sIpGnsKxrCuvK2KS8NlMA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/omuhe/btsOCzrFV7W/sIpGnsKxrCuvK2KS8NlMA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fomuhe%2FbtsOCzrFV7W%2FsIpGnsKxrCuvK2KS8NlMA1%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;1256&quot; height=&quot;700&quot; data-filename=&quot;Screenshot 2025-06-13 at 10.19.50 PM.png&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-13 at 10.18.39 PM.png&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ybovn/btsOBYZMlQB/CKnXbwhkYDSI9PwUOYLkOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ybovn/btsOBYZMlQB/CKnXbwhkYDSI9PwUOYLkOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ybovn/btsOBYZMlQB/CKnXbwhkYDSI9PwUOYLkOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fybovn%2FbtsOBYZMlQB%2FCKnXbwhkYDSI9PwUOYLkOk%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;1236&quot; height=&quot;606&quot; data-filename=&quot;Screenshot 2025-06-13 at 10.18.39 PM.png&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;606&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: 'Nanum Gothic';&quot;&gt;예제 2) &amp;nbsp;연락처(Contacts) 통합&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;온디바이스 모델의 강점을 잘 보여주는 예제&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;개인 정보가 서버로 업로드되지 않기 때문에 프라이버시를 보장하며 이런 기능을 통합할 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/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;Screenshot 2025-06-14 at 8.51.14 PM.png&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKevPZ/btsOCiYfgzT/kwZ6WhjtMh8IVjWfE5R7x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKevPZ/btsOCiYfgzT/kwZ6WhjtMh8IVjWfE5R7x0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKevPZ/btsOCiYfgzT/kwZ6WhjtMh8IVjWfE5R7x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKevPZ%2FbtsOCiYfgzT%2FkwZ6WhjtMh8IVjWfE5R7x0%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;1232&quot; height=&quot;708&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.51.14 PM.png&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.50.31 PM.png&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yBvCs/btsOCE7RaR0/VHsJVQIBYYP01KM2ja6Llk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yBvCs/btsOCE7RaR0/VHsJVQIBYYP01KM2ja6Llk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yBvCs/btsOCE7RaR0/VHsJVQIBYYP01KM2ja6Llk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyBvCs%2FbtsOCE7RaR0%2FVHsJVQIBYYP01KM2ja6Llk%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;1020&quot; height=&quot;236&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.50.31 PM.png&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;236&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: 'Nanum Gothic';&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.46.42 PM.png&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDL0Z7/btsOBpX6onl/lluoKGf7KCdfDaKG6nVaNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDL0Z7/btsOBpX6onl/lluoKGf7KCdfDaKG6nVaNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDL0Z7/btsOBpX6onl/lluoKGf7KCdfDaKG6nVaNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDL0Z7%2FbtsOBpX6onl%2FlluoKGf7KCdfDaKG6nVaNK%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;676&quot; height=&quot;638&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.46.42 PM.png&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;638&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[3.2] Tool Calling 작동방식 &amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;작동방식은 다음과 같습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 세션 시작 시 Tool 과 Instruction 를 함께 등록&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 모델이 프롬프트를 분석해서 적절한 툴 판단&lt;br /&gt;-&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Tool 과 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Instruction&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;기반으로 Tool 에 전달될 Input Arguments 생성&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;- Tool 의 call() 메서드 호출 &amp;rarr; 결과 반환 후 모델이 다음 응답 생성&lt;br /&gt;&lt;/span&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;ex) 캘린더 툴을 통합했을 때&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;Screenshot 2025-06-14 at 8.56.54 PM.png&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LG786/btsOAVQS2Px/3jPqopYzkeZ3wdCCZrx8Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LG786/btsOAVQS2Px/3jPqopYzkeZ3wdCCZrx8Lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LG786/btsOAVQS2Px/3jPqopYzkeZ3wdCCZrx8Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLG786%2FbtsOAVQS2Px%2F3jPqopYzkeZ3wdCCZrx8Lk%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;1244&quot; height=&quot;688&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.56.54 PM.png&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Instruction 에 있는 날짜 정보랑 사용자가 세션에 입력한 프롬프트를 보고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&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: 'Nanum Gothic';&quot;&gt;Arguments 를 만들어서 캘린더 툴의 call 을 호출한 후, Ouput 을 참고해서 응답을 생성합니다.&amp;nbsp;&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;Screenshot 2025-06-14 at 8.49.12 PM.png&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vvu3H/btsOBoLGOAD/LugLv7NdA0lp227298NZKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vvu3H/btsOBoLGOAD/LugLv7NdA0lp227298NZKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vvu3H/btsOBoLGOAD/LugLv7NdA0lp227298NZKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvvu3H%2FbtsOBoLGOAD%2FLugLv7NdA0lp227298NZKk%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;1234&quot; height=&quot;696&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.49.12 PM.png&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;툴은 하나의 요청에 의해 여러번 호출될 수 있고 call 메서드는 병렬로 실행됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;따라서 툴 내부에서 데이터를 접근하거나 처리할 때는 동시성(concurrency) 를 반드시 고려해야합니다.&amp;nbsp;&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;Screenshot 2025-06-14 at 9.10.49 PM.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8Obn9/btsOADQtTUB/6VGMsvV13IWfsSXc4R1JfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8Obn9/btsOADQtTUB/6VGMsvV13IWfsSXc4R1JfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8Obn9/btsOADQtTUB/6VGMsvV13IWfsSXc4R1JfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8Obn9%2FbtsOADQtTUB%2F6VGMsvV13IWfsSXc4R1JfK%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;1254&quot; height=&quot;684&quot; data-filename=&quot;Screenshot 2025-06-14 at 9.10.49 PM.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;참고: &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/expanding-generation-with-tool-calling&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Expanding&amp;nbsp;generation&amp;nbsp;with&amp;nbsp;tool&amp;nbsp;calling&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[4] Custom Adapter&lt;/span&gt;&lt;/h4&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;Screenshot 2025-06-13 at 10.29.50 PM.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/25Uqw/btsOAiewc9j/TQHE0JY8RkVI0uyKLhCK40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/25Uqw/btsOAiewc9j/TQHE0JY8RkVI0uyKLhCK40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/25Uqw/btsOAiewc9j/TQHE0JY8RkVI0uyKLhCK40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F25Uqw%2FbtsOAiewc9j%2FTQHE0JY8RkVI0uyKLhCK40%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;1280&quot; height=&quot;696&quot; data-filename=&quot;Screenshot 2025-06-13 at 10.29.50 PM.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;696&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: 'Nanum Gothic';&quot;&gt;특수한&amp;nbsp;사용&amp;nbsp;사례와&amp;nbsp;커스텀&amp;nbsp;데이터셋을&amp;nbsp;가진&amp;nbsp;머신러닝&amp;nbsp;전문가라면,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Apple의 어댑터 학습 툴킷(adapter training toolkit)을 사용해&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;맞춤형&amp;nbsp;어댑터를&amp;nbsp;직접&amp;nbsp;학습시킬&amp;nbsp;수도&amp;nbsp;있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하지만&amp;nbsp;유의할&amp;nbsp;점은,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Apple이&amp;nbsp;모델을&amp;nbsp;지속적으로&amp;nbsp;개선함에&amp;nbsp;따라&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;어댑터를&amp;nbsp;다시&amp;nbsp;학습시켜야&amp;nbsp;하는&amp;nbsp;책임이&amp;nbsp;따르기&amp;nbsp;때문에&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;상당한&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;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[5] 기타 유용한 기능&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession#Streaming-a-response&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;streamResponse&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt; streaming 기능을 제공합니다. 응답으로 async sequence 를 반환합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이&amp;nbsp;시퀀스의&amp;nbsp;각&amp;nbsp;요소는&amp;nbsp;부분적으로&amp;nbsp;생성된&amp;nbsp;타입의&amp;nbsp;인스턴스이며,&lt;br /&gt;각 요소는 갱신된 스냅샷(snapshot) 포함하고 있습니다.&lt;br /&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;Screenshot 2025-06-15 at 12.16.25 AM.png&quot; data-origin-width=&quot;2336&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBymYZ/btsOCLZ1zri/blcRKJMKTlfs2kSs2peMz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBymYZ/btsOCLZ1zri/blcRKJMKTlfs2kSs2peMz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBymYZ/btsOCLZ1zri/blcRKJMKTlfs2kSs2peMz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBymYZ%2FbtsOCLZ1zri%2FblcRKJMKTlfs2kSs2peMz1%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;2336&quot; height=&quot;556&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.16.25 AM.png&quot; data-origin-width=&quot;2336&quot; data-origin-height=&quot;556&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: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;스트림&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: 'Nanum Gothic';&quot;&gt;UI가 실시간으로 생동감 있게 구성되는 것을 볼 수 있으며 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;지연(latency)를 감출 수 있습니다.&lt;/span&gt;&lt;br /&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;Screenshot 2025-06-14 at 12.07.50 AM.png&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpb8cl/btsOAjkbDOD/6qDsXi2aklGOOqaCilj3T1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpb8cl/btsOAjkbDOD/6qDsXi2aklGOOqaCilj3T1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpb8cl/btsOAjkbDOD/6qDsXi2aklGOOqaCilj3T1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpb8cl%2FbtsOAjkbDOD%2F6qDsXi2aklGOOqaCilj3T1%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;1260&quot; height=&quot;694&quot; data-filename=&quot;Screenshot 2025-06-14 at 12.07.50 AM.png&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;694&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: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/view/contenttransition(_:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;contentTransition&lt;/a&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;Screenshot 2025-06-15 at 12.18.39 AM.png&quot; data-origin-width=&quot;3426&quot; data-origin-height=&quot;1706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rLD7J/btsOCgMTrOc/LwQKKl1aXnK2qImn7056e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rLD7J/btsOCgMTrOc/LwQKKl1aXnK2qImn7056e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rLD7J/btsOCgMTrOc/LwQKKl1aXnK2qImn7056e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrLD7J%2FbtsOCgMTrOc%2FLwQKKl1aXnK2qImn7056e0%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;3426&quot; height=&quot;1706&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.18.39 AM.png&quot; data-origin-width=&quot;3426&quot; data-origin-height=&quot;1706&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession/transcript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;transcript&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;session 의 모든 프롬프트와 응답을 포함하고 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;session 은 stateful 하기 때문에 (상태를 유지) &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;모든 프롬프트와 응답&lt;/span&gt;은 transcript에 기록됩니다.&lt;br /&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;IMG_0744.PNG&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk4Nnh/btsOCzk9cmw/0B7tOBeNhlh1UnIQEG3lF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk4Nnh/btsOCzk9cmw/0B7tOBeNhlh1UnIQEG3lF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk4Nnh/btsOCzk9cmw/0B7tOBeNhlh1UnIQEG3lF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk4Nnh%2FbtsOCzk9cmw%2F0B7tOBeNhlh1UnIQEG3lF0%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;2556&quot; height=&quot;1179&quot; data-filename=&quot;IMG_0744.PNG&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;디버깅 및 UI 표시에 사용할 수 있으며 새로운 세션을 만들 때 transcript 를 넘겨 컨텍스트 유지를 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;session 이 최대 컨텍스트 사이즈를 초과했을 때, 새로운 session 을 시작해야하는데 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;transript 를 넘기면 이전 대화 히스토리를 기억하며 대화흐름을 이어갑니다.&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: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession/init(model:guardrails:tools:transcript:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;init(model:guardrails:tools:transcript:)&amp;nbsp;&lt;/a&gt;&lt;br /&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;IMG_0746.PNG&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctyHuE/btsOB1WH8Fy/cC6sLicq0CF3jnT66UAJ0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctyHuE/btsOB1WH8Fy/cC6sLicq0CF3jnT66UAJ0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctyHuE/btsOB1WH8Fy/cC6sLicq0CF3jnT66UAJ0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctyHuE%2FbtsOB1WH8Fy%2FcC6sLicq0CF3jnT66UAJ0K%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;2556&quot; height=&quot;1179&quot; data-filename=&quot;IMG_0746.PNG&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0747.PNG&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7yWWh/btsOBdcAlXy/4q9uhSgE1c5PHMKLRq7bN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7yWWh/btsOBdcAlXy/4q9uhSgE1c5PHMKLRq7bN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7yWWh/btsOBdcAlXy/4q9uhSgE1c5PHMKLRq7bN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7yWWh%2FbtsOBdcAlXy%2F4q9uhSgE1c5PHMKLRq7bN0%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;2556&quot; height=&quot;1179&quot; data-filename=&quot;IMG_0747.PNG&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0748.PNG&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dR2PAX/btsOA5llEBa/E7bmkjkAiZMSstf6mvksFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dR2PAX/btsOA5llEBa/E7bmkjkAiZMSstf6mvksFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dR2PAX/btsOA5llEBa/E7bmkjkAiZMSstf6mvksFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdR2PAX%2FbtsOA5llEBa%2FE7bmkjkAiZMSstf6mvksFk%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;2556&quot; height=&quot;1179&quot; data-filename=&quot;IMG_0748.PNG&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&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: 'Nanum Gothic';&quot;&gt;히스토리를 전부 다 넘기는게 아니라 Summarizer 를 두고 transcript 를 요약해서 넘기는 게 좋습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;외부 라이브러리를 사용해서 할 수도 있고, &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Foundation Models 자체를 이용해 Transcript의 일부를 요약하는 것도 가능합니다. (위의 코드 스샷 참고)&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-14 at 7.43.30 PM.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbiwOj/btsOBn64PXN/4uGtPlKthvDL5vNkBrH0l0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbiwOj/btsOBn64PXN/4uGtPlKthvDL5vNkBrH0l0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbiwOj/btsOBn64PXN/4uGtPlKthvDL5vNkBrH0l0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbiwOj%2FbtsOBn64PXN%2F4uGtPlKthvDL5vNkBrH0l0%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;1254&quot; height=&quot;686&quot; data-filename=&quot;Screenshot 2025-06-14 at 7.43.30 PM.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;686&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[6] 모델 응답 생성 방식 세부 조정&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/generationoptions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GenerationOptions&lt;/a&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: 'Nanum Gothic';&quot;&gt;1) sampling : 출력 토큰을 선택하는 방식 설정&lt;br /&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;Screenshot 2025-06-14 at 8.11.31 PM.png&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl7uzm/btsOCjCN3vW/JEpCKMhQQkaKr9F6Z7XUKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl7uzm/btsOCjCN3vW/JEpCKMhQQkaKr9F6Z7XUKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl7uzm/btsOCjCN3vW/JEpCKMhQQkaKr9F6Z7XUKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl7uzm%2FbtsOCjCN3vW%2FJEpCKMhQQkaKr9F6Z7XUKk%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;1244&quot; height=&quot;176&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.11.31 PM.png&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;176&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: 'Nanum Gothic';&quot;&gt;이를 통해 출력 결과의 무작위성(randomness) 을 제어가능한데,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아래에서 추가적으로 살펴보겠습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2) temperature : 출력의 다양성 조정 (temperature 가 높을 수록 다른 결과를 생성)&amp;nbsp;&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;Screenshot 2025-06-14 at 8.11.48 PM.png&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8jCfx/btsOB0Q21Bv/vKybukyqEkda2tw38dEILk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8jCfx/btsOB0Q21Bv/vKybukyqEkda2tw38dEILk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8jCfx/btsOB0Q21Bv/vKybukyqEkda2tw38dEILk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8jCfx%2FbtsOB0Q21Bv%2FvKybukyqEkda2tw38dEILk%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;1240&quot; height=&quot;358&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.11.48 PM.png&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;358&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: 'Nanum Gothic';&quot;&gt;3) maximumResponseTokens: 너무 긴 응답을 방지하기 위한 제한&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[6.1] Sampling&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;모델이&amp;nbsp;출력을&amp;nbsp;생성할&amp;nbsp;때는&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;하나의&amp;nbsp;토큰씩&amp;nbsp;생성합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 과정은 각 토큰에 대한 확률 분포(distribution)를 만든 후, 그 중 하나를 선택하는 방식으로 진행됩니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Foundation&amp;nbsp;Models는&amp;nbsp;기본적으로&amp;nbsp;일정한&amp;nbsp;확률&amp;nbsp;범위&amp;nbsp;내에서&amp;nbsp;토큰을&amp;nbsp;선택합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예를&amp;nbsp;들어,&amp;nbsp;첫&amp;nbsp;번째&amp;nbsp;토큰으로&amp;nbsp;어떤&amp;nbsp;경우엔&amp;nbsp;&amp;ldquo;Ah&amp;rdquo;를,&amp;nbsp;다른&amp;nbsp;경우엔&amp;nbsp;&amp;ldquo;Well&amp;rdquo;을&amp;nbsp;선택할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이러한&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.08.06 PM.png&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zkU7M/btsOBAyACHE/Jt58kJ2JvkUMaqRkgbNdm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zkU7M/btsOBAyACHE/Jt58kJ2JvkUMaqRkgbNdm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zkU7M/btsOBAyACHE/Jt58kJ2JvkUMaqRkgbNdm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzkU7M%2FbtsOBAyACHE%2FJt58kJ2JvkUMaqRkgbNdm1%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;1242&quot; height=&quot;688&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.08.06 PM.png&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;688&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: 'Nanum Gothic';&quot;&gt;이러한&amp;nbsp;토큰&amp;nbsp;선택&amp;nbsp;행위를&amp;nbsp;&amp;ldquo;샘플링(sampling)&amp;ldquo;이라고&amp;nbsp;부르며,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기본&amp;nbsp;설정은&amp;nbsp;무작위(random)&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: 'Nanum Gothic';&quot;&gt;다양한 무작위성 출력이 필요한 경우 &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/generationoptions/samplingmode/random(probabilitythreshold:seed:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;random&lt;/a&gt; 옵션을 기본적으로 사용해도 좋지만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;일관된 출력이 필요한 경우 결정적(deterministic)&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;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이를 위해 &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/generationoptions/samplingmode/greedy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;greedy&lt;/a&gt; 옵션을 제공합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;샘플링 방식을 greedy로 설정하면 같은 세션 상태에서 같은 프롬프트를 입력시 항상 같은 출력을 얻게 됩니다.&lt;br /&gt;다만 이 보장은 같은 버전의 온디바이스 모델을 사용할 때만 유효합니다. (OS 업데이트로 모델이 바뀌면, 같은 프롬프트와 설정이어도 결과가 달라질 수 있습니다.)&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[7] 유의사항&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[7.1] Availability &lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1) Model Availability&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Scheme &amp;gt; Option 에서 설정을 바꿔볼 수 있으며&amp;nbsp;&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;Screenshot 2025-06-15 at 12.03.49 AM.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;1012&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pEA3s/btsOBeWXQWK/ZVlzmpVUS6hjvd0wEASBIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pEA3s/btsOBeWXQWK/ZVlzmpVUS6hjvd0wEASBIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pEA3s/btsOBeWXQWK/ZVlzmpVUS6hjvd0wEASBIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpEA3s%2FbtsOBeWXQWK%2FZVlzmpVUS6hjvd0wEASBIk%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;1886&quot; height=&quot;1012&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.03.49 AM.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;1012&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: 'Nanum Gothic';&quot;&gt;다음과 같이 대응하기를 권장합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.08.14 AM.png&quot; data-origin-width=&quot;3758&quot; data-origin-height=&quot;1888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r7iUn/btsOAJwa4De/EE0L4gglcd6niER4VeeMOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r7iUn/btsOAJwa4De/EE0L4gglcd6niER4VeeMOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r7iUn/btsOAJwa4De/EE0L4gglcd6niER4VeeMOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr7iUn%2FbtsOAJwa4De%2FEE0L4gglcd6niER4VeeMOk%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;3758&quot; height=&quot;1888&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.08.14 AM.png&quot; data-origin-width=&quot;3758&quot; data-origin-height=&quot;1888&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.10.29 AM.png&quot; data-origin-width=&quot;2978&quot; data-origin-height=&quot;1522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm8Dfo/btsOCfAtA5a/xrhZI3KVDy2E8ZFd1sylYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm8Dfo/btsOCfAtA5a/xrhZI3KVDy2E8ZFd1sylYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm8Dfo/btsOCfAtA5a/xrhZI3KVDy2E8ZFd1sylYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm8Dfo%2FbtsOCfAtA5a%2FxrhZI3KVDy2E8ZFd1sylYK%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;2978&quot; height=&quot;1522&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.10.29 AM.png&quot; data-origin-width=&quot;2978&quot; data-origin-height=&quot;1522&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: 'Nanum Gothic';&quot;&gt;2) Language Support&amp;nbsp;&lt;/span&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&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;합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이&amp;nbsp;경우에는&amp;nbsp;unsupportedLanguageOrLocale라는&amp;nbsp;전용&amp;nbsp;오류가&amp;nbsp;발생하며,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이를&amp;nbsp;포착하여&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이&amp;nbsp;오류를&amp;nbsp;활용하면&amp;nbsp;UI에서&amp;nbsp;사용자에게&amp;nbsp;맞춤&amp;nbsp;안내&amp;nbsp;메시지를&amp;nbsp;보여주는&amp;nbsp;데&amp;nbsp;유용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;또한,&amp;nbsp;특정&amp;nbsp;언어가&amp;nbsp;모델에서&amp;nbsp;지원되는지를&amp;nbsp;확인하는&amp;nbsp;API도&amp;nbsp;제공됩니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예를&amp;nbsp;들어,&amp;nbsp;사용자의&amp;nbsp;현재&amp;nbsp;언어가&amp;nbsp;지원되는지&amp;nbsp;확인하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지원되지 않는 경우 주의 문구(disclaimer) 를 표시할 수 있습니다.&lt;/span&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.17.31 PM.png&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRplLb/btsOA9nLkNU/QhbKgak7Ms4ka0k4Kxcmjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRplLb/btsOA9nLkNU/QhbKgak7Ms4ka0k4Kxcmjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRplLb/btsOA9nLkNU/QhbKgak7Ms4ka0k4Kxcmjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRplLb%2FbtsOA9nLkNU%2FQhbKgak7Ms4ka0k4Kxcmjk%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;1244&quot; height=&quot;508&quot; data-filename=&quot;Screenshot 2025-06-14 at 8.17.31 PM.png&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;508&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;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[7.2] Guardrails&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;입력 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(instructions, prompt, tool call input) 과 출력 (모델의 응답) 에 모두 작동하는 AI 안전장치 입니다.&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-14 at 9.36.18 PM.png&quot; data-origin-width=&quot;1226&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mc2mj/btsOCQ1hyvX/zNUPr4aqlKOCACaqweKkiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mc2mj/btsOCQ1hyvX/zNUPr4aqlKOCACaqweKkiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mc2mj/btsOCQ1hyvX/zNUPr4aqlKOCACaqweKkiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmc2mj%2FbtsOCQ1hyvX%2FzNUPr4aqlKOCACaqweKkiK%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;1226&quot; height=&quot;704&quot; data-filename=&quot;Screenshot 2025-06-14 at 9.36.18 PM.png&quot; data-origin-width=&quot;1226&quot; data-origin-height=&quot;704&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: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession/guardrails&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Guardrails&lt;/a&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;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;가드레일이 작동하면 에러로 전달되는데&amp;nbsp;&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;Screenshot 2025-06-14 at 9.38.51 PM.png&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NHBEQ/btsOClU0HNG/tyqEaPnOUyylLX1vnIv8IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NHBEQ/btsOClU0HNG/tyqEaPnOUyylLX1vnIv8IK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NHBEQ/btsOClU0HNG/tyqEaPnOUyylLX1vnIv8IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNHBEQ%2FbtsOClU0HNG%2FtyqEaPnOUyylLX1vnIv8IK%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;1242&quot; height=&quot;400&quot; data-filename=&quot;Screenshot 2025-06-14 at 9.38.51 PM.png&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;400&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: 'Nanum Gothic';&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-14 at 9.42.31 PM.png&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLg2Lv/btsOBTdCVC2/xoFNKnGzBFcdAvKQs5RDp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLg2Lv/btsOBTdCVC2/xoFNKnGzBFcdAvKQs5RDp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLg2Lv/btsOBTdCVC2/xoFNKnGzBFcdAvKQs5RDp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLg2Lv%2FbtsOBTdCVC2%2FxoFNKnGzBFcdAvKQs5RDp1%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;1214&quot; height=&quot;514&quot; data-filename=&quot;Screenshot 2025-06-14 at 9.42.31 PM.png&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[7.3] 온비다이스 모델에 대한 이해&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;대형 서버 기반 모델은 수백억~수천억 파라미터를 갖고 있고, 그만큼 복잡한 작업을 수행할 수 있지만, 온디바이스 모델은 몇 가지 한계가 있습니다:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 복잡한 추론 과제는 여러 단계를 나눠 프롬프트로 설계해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 계산기처럼 수학 계산을 맡기면 안 됩니다. 코드 생성 작업도 피하세요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 최신 정보에 대한 지식이 없기 때문에, 정확한 사실에 의존해서는 안 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[8] 최적화&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1) &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession/prewarm()&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;prewarm&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;session.respond를&amp;nbsp;호출하면,&amp;nbsp;운영체제는&amp;nbsp;해당&amp;nbsp;모델이&amp;nbsp;아직&amp;nbsp;메모리에&amp;nbsp;없다면&amp;nbsp;모델을&amp;nbsp;로드합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하지만&amp;nbsp;프리워밍(Prewarming)&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&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&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;수행하는&amp;nbsp;것이&amp;nbsp;가장&amp;nbsp;좋습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&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;타이밍입니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예제 앱의 경우, 사용자가 랜드마크를 탭하면 곧 &amp;ldquo;여행 일정 생성&amp;rdquo; 버튼을 누를 가능성이 높기 때문에,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2) &amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession/respond(to:generating:includeschemainprompt:options:isolation:)#discussion&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;includeSchemaInPrompt&lt;/a&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: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelsession/respond(to:generating:includeschemainprompt:options:isolation:)#discussion&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;respond(to:generating:includeSchemaInPrompt:options:isolation:)&lt;/a&gt; 를 사용하면&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;프레임워크는 해당 데이터 구조의 생성 스키마(generation schema) 를 자동으로 프롬프트에 삽입합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하지만 이렇게 하면 토큰 수가 증가해, 지연 시간(latency)과 컨텍스트 크기(context size)도 함께 증가하게 됩니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그런데 만약 모델이 이미 응답 형식(response format) 을 충분히 이해하고 있는 상황이라면, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;연속되는 request 에서는 includeSchemaInPrompt를&amp;nbsp;false로&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.54.12 AM.png&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kFbph/btsOAKWbuAb/y1OHOXqthIOHy7m0Dv05P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kFbph/btsOAKWbuAb/y1OHOXqthIOHy7m0Dv05P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kFbph/btsOAKWbuAb/y1OHOXqthIOHy7m0Dv05P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkFbph%2FbtsOAKWbuAb%2Fy1OHOXqthIOHy7m0Dv05P1%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;1256&quot; height=&quot;708&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.54.12 AM.png&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;708&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: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;첫 번째 경우는&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 동일한&amp;nbsp;유형의&amp;nbsp;요청을&amp;nbsp;여러&amp;nbsp;번&amp;nbsp;수행하는&amp;nbsp;멀티턴&amp;nbsp;대화에서입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 첫&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&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;두&amp;nbsp;번째&amp;nbsp;경우는&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; instructions&amp;nbsp;안에&amp;nbsp;예시&amp;nbsp;응답을&amp;nbsp;포함시킨&amp;nbsp;경우입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 구조가 간단하고 옵셔널 속성이 없는 경우, instructions에 포함된 예시만으로 충분합니다. 만약 구조체에 옵셔널 속성(optional properties) 이 있다면, 예시를&amp;nbsp;줄&amp;nbsp;때는&amp;nbsp;해당&amp;nbsp;속성이&amp;nbsp;채워진&amp;nbsp;경우와&amp;nbsp;nil인&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.55.47 AM.png&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUCeHw/btsOA8h4VK0/Oqz5QdqrpUtu2Fp7bwr1zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUCeHw/btsOA8h4VK0/Oqz5QdqrpUtu2Fp7bwr1zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUCeHw/btsOA8h4VK0/Oqz5QdqrpUtu2Fp7bwr1zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUCeHw%2FbtsOA8h4VK0%2FOqz5QdqrpUtu2Fp7bwr1zk%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;1214&quot; height=&quot;624&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.55.47 AM.png&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;624&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[9] 모델 피드백 하기&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 모델 latency 를 분석할 수 있는 instrument 가 제공됩니다.&amp;nbsp;&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;Screenshot 2025-06-15 at 12.26.36 AM.png&quot; data-origin-width=&quot;3834&quot; data-origin-height=&quot;2140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1dz6W/btsOCQtshJI/bihL7ay1ZdESGiV4UIT9IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1dz6W/btsOCQtshJI/bihL7ay1ZdESGiV4UIT9IK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1dz6W/btsOCQtshJI/bihL7ay1ZdESGiV4UIT9IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1dz6W%2FbtsOCQtshJI%2FbihL7ay1ZdESGiV4UIT9IK%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;3834&quot; height=&quot;2140&quot; data-filename=&quot;Screenshot 2025-06-15 at 12.26.36 AM.png&quot; data-origin-width=&quot;3834&quot; data-origin-height=&quot;2140&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: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/documentation/foundationmodels/languagemodelfeedbackattachment&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LanguageModelFeedbackAttachment&lt;/a&gt; 이라는 인코딩 가능한 피드백 첨부용 데이터 구조가 제공됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp; 이 구조를 통해 파일형태로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Feedback Assistant&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; 에 &lt;/span&gt;&lt;/span&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-13 at 10.36.12 PM.png&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnJbNz/btsOCm696OX/e2J4DBlQFkIBgOYhxvqQB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnJbNz/btsOCm696OX/e2J4DBlQFkIBgOYhxvqQB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnJbNz/btsOCm696OX/e2J4DBlQFkIBgOYhxvqQB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnJbNz%2FbtsOCm696OX%2Fe2J4DBlQFkIBgOYhxvqQB0%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;1278&quot; height=&quot;716&quot; data-filename=&quot;Screenshot 2025-06-13 at 10.36.12 PM.png&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&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: 'Nanum Gothic';&quot;&gt;[10] WWDC 영상&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2025/286&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC 25 &amp;gt; Meet&amp;nbsp;the&amp;nbsp;Foundation&amp;nbsp;Models&amp;nbsp;framework&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2025/301&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC 25 &amp;gt; Deep dive into the Foundation Models framework&amp;nbsp;&lt;/a&gt;&lt;br /&gt;- &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2025/248/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC 25 &amp;gt; Explore&amp;nbsp;prompt&amp;nbsp;design&amp;nbsp;&amp;amp;&amp;nbsp;safety&amp;nbsp;for&amp;nbsp;on-device&amp;nbsp;foundation&amp;nbsp;models&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2025/259/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC 25 &amp;gt; Code-along:&amp;nbsp;Bring&amp;nbsp;on-device&amp;nbsp;AI&amp;nbsp;to&amp;nbsp;your&amp;nbsp;app&amp;nbsp;using&amp;nbsp;the&amp;nbsp;Foundation&amp;nbsp;Models&amp;nbsp;framework&lt;/a&gt; &amp;gt; 최적화 (prewarm, includeSchemaInPrompt) 관련 내용이 있음&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category> /Swift</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/683</guid>
      <comments>https://eunjin3786.tistory.com/683#entry683comment</comments>
      <pubDate>Fri, 13 Jun 2025 22:43:45 +0900</pubDate>
    </item>
    <item>
      <title>[SwiftUI] TextEditor with AttributedString</title>
      <link>https://eunjin3786.tistory.com/682</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[ TextEditor ]&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/texteditor&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TextEditor&lt;/a&gt; 는 iOS 14+ 인데, &amp;nbsp;iOS 26 부터 쓸 수 있는 이니셜라이저가 추가되었다.&amp;nbsp;&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;Screenshot 2025-06-13 at 6.29.56 PM.png&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh7lkl/btsOAEnM6an/vkqkIcKdPkgYufCn5DmYXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh7lkl/btsOAEnM6an/vkqkIcKdPkgYufCn5DmYXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh7lkl/btsOAEnM6an/vkqkIcKdPkgYufCn5DmYXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh7lkl%2FbtsOAEnM6an%2FvkqkIcKdPkgYufCn5DmYXk%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;1598&quot; height=&quot;180&quot; data-filename=&quot;Screenshot 2025-06-13 at 6.29.56 PM.png&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;180&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: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundation/attributedstring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AttributedString&lt;/a&gt; 는 iOS 15+ 이고 &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/text/init(_:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Text 의 이니셜라이저&lt;/a&gt;에서 먼저 사용되고 있었는데 (iOS 15+)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이제 뷰어 뿐만아니라 에디터에서도 사용할 수 있게 된 것이다.  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;TextEditor 에 AttributedString 을 받는 이니셜라이저를 사용하면 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;String 을 받는 이니셜라이저와 달리 Format 이라는 메뉴가 뜨고&amp;nbsp;&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;Screenshot 2025-06-13 at 6.35.40 PM.png&quot; data-origin-width=&quot;1954&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ck7hmY/btsOAkb4sCy/uPguBRLBqoaBjqUGlVeIwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ck7hmY/btsOAkb4sCy/uPguBRLBqoaBjqUGlVeIwk/img.png&quot; data-alt=&quot;String&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ck7hmY/btsOAkb4sCy/uPguBRLBqoaBjqUGlVeIwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fck7hmY%2FbtsOAkb4sCy%2FuPguBRLBqoaBjqUGlVeIwk%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;1954&quot; height=&quot;890&quot; data-filename=&quot;Screenshot 2025-06-13 at 6.35.40 PM.png&quot; data-origin-width=&quot;1954&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;String&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-13 at 6.36.18 PM.png&quot; data-origin-width=&quot;1932&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFCy2u/btsOBsNBkrK/dri78hVHA0ShvQSq2xDvLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFCy2u/btsOBsNBkrK/dri78hVHA0ShvQSq2xDvLk/img.png&quot; data-alt=&quot;AttributedString&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFCy2u/btsOBsNBkrK/dri78hVHA0ShvQSq2xDvLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFCy2u%2FbtsOBsNBkrK%2Fdri78hVHA0ShvQSq2xDvLk%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;1932&quot; height=&quot;1002&quot; data-filename=&quot;Screenshot 2025-06-13 at 6.36.18 PM.png&quot; data-origin-width=&quot;1932&quot; data-origin-height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AttributedString&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기본으로 제공되는 rich text editing 을 지원할 수 있다.&amp;nbsp;&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N404t/btsOz6kPlgt/sA3Lo3zxIlPfrSgt9E1MK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N404t/btsOz6kPlgt/sA3Lo3zxIlPfrSgt9E1MK0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1264&quot; data-filename=&quot;Screenshot 2025-06-13 at 6.34.00 PM.png&quot; data-widthpercent=&quot;42.48&quot; style=&quot;width: 41.988914%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N404t/btsOz6kPlgt/sA3Lo3zxIlPfrSgt9E1MK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN404t%2FbtsOz6kPlgt%2FsA3Lo3zxIlPfrSgt9E1MK0%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;1024&quot; height=&quot;1264&quot;/&gt;&lt;/span&gt;&lt;/div&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: 'Nanum Gothic';&quot;&gt;맥 OS 는 이렇게 나옴&amp;nbsp;&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;Screenshot 2025-06-13 at 6.49.22 PM.png&quot; data-origin-width=&quot;2056&quot; data-origin-height=&quot;1594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwTc57/btsOAuS9ic1/FRidYsUAC2x73P9BwL87BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwTc57/btsOAuS9ic1/FRidYsUAC2x73P9BwL87BK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwTc57/btsOAuS9ic1/FRidYsUAC2x73P9BwL87BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwTc57%2FbtsOAuS9ic1%2FFRidYsUAC2x73P9BwL87BK%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;2056&quot; height=&quot;1594&quot; data-filename=&quot;Screenshot 2025-06-13 at 6.49.22 PM.png&quot; data-origin-width=&quot;2056&quot; data-origin-height=&quot;1594&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: 'Nanum Gothic';&quot;&gt;대부분의 attribute, paragraph style 이 지원되는 듯 보인다.&amp;nbsp;&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;Screenshot 2025-06-13 at 6.39.11 PM.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhB5Ny/btsOBYywviM/K1kwD0Usk1hgt1eDJcqQ51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhB5Ny/btsOBYywviM/K1kwD0Usk1hgt1eDJcqQ51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhB5Ny/btsOBYywviM/K1kwD0Usk1hgt1eDJcqQ51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhB5Ny%2FbtsOBYywviM%2FK1kwD0Usk1hgt1eDJcqQ51%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;776&quot; height=&quot;437&quot; data-filename=&quot;Screenshot 2025-06-13 at 6.39.11 PM.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;516&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: 'Nanum Gothic';&quot;&gt;다만 한국어는 아직 불안정한데 ;;;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;스페이스바나 엔터치면 이렇게 됨 ;;;; ;;&amp;nbsp;&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;Screenshot 2025-06-13 at 6.50.30 PM.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs30AB/btsOBkvpyQV/gEkQRNGsmXu60EH6erUGKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs30AB/btsOBkvpyQV/gEkQRNGsmXu60EH6erUGKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs30AB/btsOBkvpyQV/gEkQRNGsmXu60EH6erUGKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs30AB%2FbtsOBkvpyQV%2FgEkQRNGsmXu60EH6erUGKk%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;1184&quot; height=&quot;570&quot; data-filename=&quot;Screenshot 2025-06-13 at 6.50.30 PM.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[ Attributed String ]&lt;/span&gt;&lt;/h3&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;Screenshot 2025-06-13 at 7.00.33 PM.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkFXEz/btsOCkBkpL8/iQTkXiyfh0Dwcg5RxIuKdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkFXEz/btsOCkBkpL8/iQTkXiyfh0Dwcg5RxIuKdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkFXEz/btsOCkBkpL8/iQTkXiyfh0Dwcg5RxIuKdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkFXEz%2FbtsOCkBkpL8%2FiQTkXiyfh0Dwcg5RxIuKdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1262&quot; height=&quot;700&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.00.33 PM.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.00.26 PM.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/df3Oa8/btsOA0RE9Xm/VM9bQJQFsLPWVtlNzUpz5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/df3Oa8/btsOA0RE9Xm/VM9bQJQFsLPWVtlNzUpz5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/df3Oa8/btsOA0RE9Xm/VM9bQJQFsLPWVtlNzUpz5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdf3Oa8%2FbtsOA0RE9Xm%2FVM9bQJQFsLPWVtlNzUpz5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1270&quot; height=&quot;728&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.00.26 PM.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;728&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;background-color: #dddddd; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;NEW (iOS 26+)&amp;nbsp;&lt;/b&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: 'Nanum Gothic';&quot;&gt;(일부만 적음. 전체는 &lt;a href=&quot;https://developer.apple.com/documentation/foundation/attributedstring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt; 참고)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/documentation/foundation/attributedstring/textalignment&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AttributedString.TextAlignment&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/documentation/foundation/attributedstring/lineheight&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AttributedString.LineHeight&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;텍스트 Selection&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/documentation/SwiftUI/AttributedTextSelection&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AttributedTextSelection&lt;/a&gt;&amp;nbsp; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;Bidirectional text 를 지원한다고 함&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.06.48 PM.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2jOkl/btsOA0YqBhr/dgKFyeV1Wb8EhfN4QsZ3k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2jOkl/btsOA0YqBhr/dgKFyeV1Wb8EhfN4QsZ3k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2jOkl/btsOA0YqBhr/dgKFyeV1Wb8EhfN4QsZ3k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2jOkl%2FbtsOA0YqBhr%2FdgKFyeV1Wb8EhfN4QsZ3k1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1270&quot; height=&quot;700&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.06.48 PM.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;700&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: 'Nanum Gothic';&quot;&gt;&lt;b&gt;텍스트 변경&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/documentation/foundation/attributedstring/replaceselection(_:with:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;replaceSelection&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/documentation/foundation/attributedstring/transform(updating:body:)-9wpg2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;transform&lt;/a&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;Screenshot 2025-06-13 at 7.18.26 PM.png&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGGWJT/btsOAtmAAV1/jedQy782kSTcwAAHpMvzh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGGWJT/btsOAtmAAV1/jedQy782kSTcwAAHpMvzh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGGWJT/btsOAtmAAV1/jedQy782kSTcwAAHpMvzh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGGWJT%2FbtsOAtmAAV1%2FjedQy782kSTcwAAHpMvzh0%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;1250&quot; height=&quot;700&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.18.26 PM.png&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[ TextEditor &amp;gt; Custom Control ]&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;TextEditor 에 Custom Control 을 연결할 수도 있음&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Suggestion 해주는 Control 을 추가하는 코드&amp;nbsp;&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;Screenshot 2025-06-13 at 7.09.59 PM.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLNZFn/btsOAjK1Fn1/RHStnHROsCy40AcT7uEfYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLNZFn/btsOAjK1Fn1/RHStnHROsCy40AcT7uEfYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLNZFn/btsOAjK1Fn1/RHStnHROsCy40AcT7uEfYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLNZFn%2FbtsOAjK1Fn1%2FRHStnHROsCy40AcT7uEfYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1262&quot; height=&quot;682&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.09.59 PM.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.09.30 PM.png&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pB7zD/btsOBkPH4BN/WtqHuNsX8mQwGyqTuteieK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pB7zD/btsOBkPH4BN/WtqHuNsX8mQwGyqTuteieK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pB7zD/btsOBkPH4BN/WtqHuNsX8mQwGyqTuteieK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpB7zD%2FbtsOBkPH4BN%2FWtqHuNsX8mQwGyqTuteieK%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;1222&quot; height=&quot;700&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.09.30 PM.png&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[ TextEditor &amp;gt; Custom Text Format ]&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/attributedtextformattingdefinition&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AttributedTextFormattingDefinition&lt;/a&gt; 를 채택해서 커스텀 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;텍스트 포맷을 미리 정의해두고&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.28.44 PM.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coa9hl/btsOz4UZHdd/QnwhqDV7fk2wYcxnFNPCY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coa9hl/btsOz4UZHdd/QnwhqDV7fk2wYcxnFNPCY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coa9hl/btsOz4UZHdd/QnwhqDV7fk2wYcxnFNPCY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcoa9hl%2FbtsOz4UZHdd%2FQnwhqDV7fk2wYcxnFNPCY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1270&quot; height=&quot;594&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.28.44 PM.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;594&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: 'Nanum Gothic';&quot;&gt;TextEditor 에 적용할 수도 있음&amp;nbsp;&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;Screenshot 2025-06-13 at 7.28.13 PM.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0wLB3/btsOAh0QDN3/UlDa8yKZTORakgAEkmoJN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0wLB3/btsOAh0QDN3/UlDa8yKZTORakgAEkmoJN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0wLB3/btsOAh0QDN3/UlDa8yKZTORakgAEkmoJN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0wLB3%2FbtsOAh0QDN3%2FUlDa8yKZTORakgAEkmoJN1%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;1282&quot; height=&quot;694&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.28.13 PM.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;694&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;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/attributedtextvalueconstraint/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AttributedTextValueConstraint&lt;/a&gt; 사용해서 특정 attribute 의 value 를 원하는 대로 지정할 수도 있음&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아래는 forgregoundColor attribute 에 대한 예제&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;Screenshot 2025-06-13 at 7.36.44 PM.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjvCFg/btsOAik95m0/1KRk7rz10g6ZmBN4fil3XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjvCFg/btsOAik95m0/1KRk7rz10g6ZmBN4fil3XK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjvCFg/btsOAik95m0/1KRk7rz10g6ZmBN4fil3XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjvCFg%2FbtsOAik95m0%2F1KRk7rz10g6ZmBN4fil3XK%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;1282&quot; height=&quot;712&quot; data-filename=&quot;Screenshot 2025-06-13 at 7.36.44 PM.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;712&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[ 참고 ]&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;-&amp;nbsp;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://developer.apple.com/videos/play/wwdc2025/280&quot;&gt;WWDC 25 &amp;gt; Code-along: Cook up a rich text experience in SwiftUI with AttributedString&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;-&amp;nbsp;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://developer.apple.com/documentation/swiftui/building-rich-swiftui-text-experiences&quot;&gt;Building&amp;nbsp;rich&amp;nbsp;SwiftUI&amp;nbsp;text&amp;nbsp;experiences&lt;/a&gt;&lt;/span&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /SwiftUI + Combine</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/682</guid>
      <comments>https://eunjin3786.tistory.com/682#entry682comment</comments>
      <pubDate>Fri, 13 Jun 2025 19:40:37 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Swift Testing 헷갈리는 것 정리</title>
      <link>https://eunjin3786.tistory.com/677</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2024/10179&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC 24 &amp;gt; Meet&amp;nbsp;Swift&amp;nbsp;Testing&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2024/10195&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC 24 &amp;gt; Go&amp;nbsp;further&amp;nbsp;with&amp;nbsp;Swift&amp;nbsp;Testing&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/xcode/swift-testing/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;소개&lt;/a&gt; | &lt;a href=&quot;https://developer.apple.com/documentation/testing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;개발 문서&amp;nbsp;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[1] 언제 @Suite 를 직접 붙여야하는가 ?&amp;nbsp;&lt;/span&gt;&lt;/h4&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;스크린샷 2025-01-29 오후 3.31.23.png&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU7S6v/btsL3X9K7Tq/dKZgpnXHRKgTpu5yxhVCeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU7S6v/btsL3X9K7Tq/dKZgpnXHRKgTpu5yxhVCeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU7S6v/btsL3X9K7Tq/dKZgpnXHRKgTpu5yxhVCeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU7S6v%2FbtsL3X9K7Tq%2FdKZgpnXHRKgTpu5yxhVCeK%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;779&quot; height=&quot;434&quot; data-filename=&quot;스크린샷 2025-01-29 오후 3.31.23.png&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;700&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: 'Nanum Gothic';&quot;&gt;&lt;i&gt;문서:&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;i&gt;A&amp;nbsp;type&amp;nbsp;containing&amp;nbsp;test&amp;nbsp;functions&amp;nbsp;is&amp;nbsp;automatically&amp;nbsp;a&amp;nbsp;test&amp;nbsp;suite&amp;nbsp;and&amp;nbsp;can&amp;nbsp;be&amp;nbsp;optionally&amp;nbsp;annotated&amp;nbsp;with&amp;nbsp;the&amp;nbsp;@Suite&amp;nbsp;attribute.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;WWDC 와 문서에서 말하는 것 처럼, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;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: 'Nanum Gothic';&quot;&gt;@Suite 를 직접 붙일 필요가 없다.&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: 'Nanum Gothic';&quot;&gt;즉 두개는 똑같기 때문에 굳이 @Suite 를 안붙여줘도 됨.&amp;nbsp;&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;스크린샷 2025-01-29 오후 3.38.43.png&quot; data-origin-width=&quot;1662&quot; data-origin-height=&quot;938&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cb3NoM/btsL1GWt1RO/XaT4p184SDigkabQkajQS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cb3NoM/btsL1GWt1RO/XaT4p184SDigkabQkajQS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cb3NoM/btsL1GWt1RO/XaT4p184SDigkabQkajQS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcb3NoM%2FbtsL1GWt1RO%2FXaT4p184SDigkabQkajQS0%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;1662&quot; height=&quot;938&quot; data-filename=&quot;스크린샷 2025-01-29 오후 3.38.43.png&quot; data-origin-width=&quot;1662&quot; data-origin-height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-29 오후 3.42.34.png&quot; data-origin-width=&quot;1678&quot; data-origin-height=&quot;1196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mYM1s/btsL18kAji8/MFnjfKBucuLbRPjJZGZDkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mYM1s/btsL18kAji8/MFnjfKBucuLbRPjJZGZDkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mYM1s/btsL18kAji8/MFnjfKBucuLbRPjJZGZDkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmYM1s%2FbtsL18kAji8%2FMFnjfKBucuLbRPjJZGZDkk%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;1678&quot; height=&quot;1196&quot; data-filename=&quot;스크린샷 2025-01-29 오후 3.42.34.png&quot; data-origin-width=&quot;1678&quot; data-origin-height=&quot;1196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그럼 @Suite 를 언제 붙이면 좋을까 ?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;displayName 을 별도로 지정해주고 싶을 때, traits 지정이 필요할 때 사용하면 됨.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-29 오후 3.44.58.png&quot; data-origin-width=&quot;1738&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caiMZe/btsL03qIdVX/skIVUKKx4pzOTkctY7vAF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caiMZe/btsL03qIdVX/skIVUKKx4pzOTkctY7vAF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caiMZe/btsL03qIdVX/skIVUKKx4pzOTkctY7vAF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaiMZe%2FbtsL03qIdVX%2FskIVUKKx4pzOTkctY7vAF1%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;1738&quot; height=&quot;748&quot; data-filename=&quot;스크린샷 2025-01-29 오후 3.44.58.png&quot; data-origin-width=&quot;1738&quot; data-origin-height=&quot;748&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: 'Nanum Gothic';&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.10.51.png&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;1356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6doQX/btsL3xi92vr/AG3d2PJfzzmat9CEzvWAf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6doQX/btsL3xi92vr/AG3d2PJfzzmat9CEzvWAf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6doQX/btsL3xi92vr/AG3d2PJfzzmat9CEzvWAf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6doQX%2FbtsL3xi92vr%2FAG3d2PJfzzmat9CEzvWAf1%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;1666&quot; height=&quot;1356&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.10.51.png&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;1356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&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: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/testing/suitetrait&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SuiteTrait&lt;/a&gt; 과 &lt;a href=&quot;https://developer.apple.com/documentation/testing/testtrait&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TestTrait&lt;/a&gt; 은 모두 &lt;a href=&quot;https://developer.apple.com/documentation/testing/trait&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Trait&lt;/a&gt; 을 상속하고 있기 때문에&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;trait 종류가 거의 동일함.&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKU7u1/btsL2ITeOZS/TspmCp67v8KuUyaq4f0en1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKU7u1/btsL2ITeOZS/TspmCp67v8KuUyaq4f0en1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;68&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.02.47.png&quot; data-widthpercent=&quot;47.66&quot; style=&quot;width: 47.104834%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKU7u1/btsL2ITeOZS/TspmCp67v8KuUyaq4f0en1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKU7u1%2FbtsL2ITeOZS%2FTspmCp67v8KuUyaq4f0en1%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;610&quot; height=&quot;68&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yRqJi/btsL3QQtIcK/TBaOKexRdMf9adL9m1MQvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yRqJi/btsL3QQtIcK/TBaOKexRdMf9adL9m1MQvK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;54&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.03.07.png&quot; data-widthpercent=&quot;52.34&quot; style=&quot;width: 51.732376%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yRqJi/btsL3QQtIcK/TBaOKexRdMf9adL9m1MQvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyRqJi%2FbtsL3QQtIcK%2FTBaOKexRdMf9adL9m1MQvK%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;532&quot; height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;[2] 모든 trait 은 nested test suite 에도 같이 적용되는가 ?&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기본적으로 suite 에 적용한 trait 은 해당 suite 가 포함하는 test 함수 및 nested suite 에 다 적용됨 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;(가장 쉬운 예로 tag trait 을 생각해보면 됨)&amp;nbsp;&lt;/span&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;모든 trait 이 상속된다고 어디에 시원하게 나와있지 않지만, 그런 것 같음!&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;가장 의심이 되는 두가지인 serialized, timeLimit trait 을 아래에서 살펴보자.&amp;nbsp;&lt;/span&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;우선 &amp;nbsp;serialized trait 은 상속된다고 WWDC 에서 언급됨.&amp;nbsp;&lt;/span&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.26.57.png&quot; data-origin-width=&quot;1616&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blhWTf/btsL00AJhy9/jf7Dn0LVz1MwKke31zTuO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blhWTf/btsL00AJhy9/jf7Dn0LVz1MwKke31zTuO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blhWTf/btsL00AJhy9/jf7Dn0LVz1MwKke31zTuO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblhWTf%2FbtsL00AJhy9%2Fjf7Dn0LVz1MwKke31zTuO0%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;1616&quot; height=&quot;826&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.26.57.png&quot; data-origin-width=&quot;1616&quot; data-origin-height=&quot;826&quot;/&gt;&lt;/span&gt;&lt;/figure&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/testing/parallelization&quot;&gt;Running tests serially or in parallel 문서&lt;/a&gt;에도 다음과 같이 나와있음.&lt;/span&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; font-family: 'Nanum Gothic';&quot;&gt;&lt;i&gt;This&amp;nbsp;trait&amp;nbsp;is&amp;nbsp;recursively&amp;nbsp;applied:&amp;nbsp;if&amp;nbsp;it&amp;nbsp;is&amp;nbsp;applied&amp;nbsp;to&amp;nbsp;a&amp;nbsp;suite,&amp;nbsp;any&amp;nbsp;parameterized&amp;nbsp;tests&amp;nbsp;or&amp;nbsp;test&amp;nbsp;suites&amp;nbsp;contained&amp;nbsp;in&amp;nbsp;that&amp;nbsp;suite&amp;nbsp;are&amp;nbsp;also&amp;nbsp;serialized&amp;nbsp;(as&amp;nbsp;are&amp;nbsp;any&amp;nbsp;tests&amp;nbsp;contained&amp;nbsp;in&amp;nbsp;those&amp;nbsp;suites,&amp;nbsp;and&amp;nbsp;so&amp;nbsp;on.)&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;&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: 'Nanum Gothic';&quot;&gt;timeLimit trait 도 테스트해보니 잘 상속되고&amp;nbsp;&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;스크린샷 2025-01-29 오후 6.52.47.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tbpiZ/btsL2yCY6UG/WcJjMULenpHKidixvOXLC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tbpiZ/btsL2yCY6UG/WcJjMULenpHKidixvOXLC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tbpiZ/btsL2yCY6UG/WcJjMULenpHKidixvOXLC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtbpiZ%2FbtsL2yCY6UG%2FWcJjMULenpHKidixvOXLC1%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;1554&quot; height=&quot;766&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.52.47.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;766&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: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/testing/limitingexecutiontime&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Limiting the running time of tests 문서&lt;/a&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;color: #666666; font-family: 'Nanum Gothic';&quot;&gt;&lt;i&gt;When&amp;nbsp;a&amp;nbsp;time&amp;nbsp;limit&amp;nbsp;is&amp;nbsp;applied&amp;nbsp;to&amp;nbsp;a&amp;nbsp;test&amp;nbsp;suite,&amp;nbsp;it&amp;rsquo;s&amp;nbsp;recursively&amp;nbsp;applied&amp;nbsp;to&amp;nbsp;all&amp;nbsp;test&amp;nbsp;functions&amp;nbsp;and&amp;nbsp;child&amp;nbsp;test&amp;nbsp;suites&amp;nbsp;within&amp;nbsp;that&amp;nbsp;suite.&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: 'Nanum Gothic';&quot;&gt;그리고 여러 timeLimit trait 이 적용될 때, 가장 짧은 시간의 trait 이 사용된다고 함.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; font-family: 'Nanum Gothic';&quot;&gt;&lt;i&gt;⭐️ If multiple time limit traits apply to a test,&lt;span style=&quot;background-color: #f6e199;&quot;&gt; &lt;b&gt;the shortest time limit is used.&lt;/b&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;테스트 해보니 정말 가장 가까운 trait 이 아니라 가장 짧은 시간의 trait 이 적용되는 모습 을 확인할 수 있었음.&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;스크린샷 2025-02-01 오후 11.42.31.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9GO3U/btsL4nuOtEF/c7qKsedUUK2UYhp1oB9Vyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9GO3U/btsL4nuOtEF/c7qKsedUUK2UYhp1oB9Vyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9GO3U/btsL4nuOtEF/c7qKsedUUK2UYhp1oB9Vyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9GO3U%2FbtsL4nuOtEF%2Fc7qKsedUUK2UYhp1oB9Vyk%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;1600&quot; height=&quot;416&quot; data-filename=&quot;스크린샷 2025-02-01 오후 11.42.31.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;416&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: 'Nanum Gothic';&quot;&gt;+ enable, disable 도 함께 확인차 테스트 해봤는데, 예상대로 disabled 가 enabled 보다 더 우선순위로 먹힘.&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;스크린샷 2025-02-01 오후 11.47.14.png&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs7NHz/btsL4aCmteJ/DhRvO8PqwSgY4BYyJuyHvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs7NHz/btsL4aCmteJ/DhRvO8PqwSgY4BYyJuyHvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs7NHz/btsL4aCmteJ/DhRvO8PqwSgY4BYyJuyHvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs7NHz%2FbtsL4aCmteJ%2FDhRvO8PqwSgY4BYyJuyHvk%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;1570&quot; height=&quot;346&quot; data-filename=&quot;스크린샷 2025-02-01 오후 11.47.14.png&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-01 오후 11.48.58.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pn8s6/btsL5tAPAkW/KyAgGxLwQ8fTW8NjywjsDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pn8s6/btsL5tAPAkW/KyAgGxLwQ8fTW8NjywjsDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pn8s6/btsL5tAPAkW/KyAgGxLwQ8fTW8NjywjsDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpn8s6%2FbtsL5tAPAkW%2FKyAgGxLwQ8fTW8NjywjsDK%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;1596&quot; height=&quot;368&quot; data-filename=&quot;스크린샷 2025-02-01 오후 11.48.58.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;-&amp;gt; 모든 trait 은 재귀적으로 test function, nested suite 에 적용된다고 생각하면 될 듯.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;-&amp;gt; 하지만 헷갈리면 안되는 게 serialized 는 살짝 성격이 다름. &amp;nbsp;timeLimit 처럼 각 test function 마다 개별적으로 timeLimit 이 걸리는 게 아니라 &amp;nbsp;suite 단위 임. suite 내의 test 들이 serial 하게 실행되는 것이니까. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/testing/parallelization&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;의 예제코드 + 주석을 읽어보면 바로 이해가능.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1738412373891&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Suite(.serialized) struct FoodTruckTests {
  @Test(arguments: Condiment.allCases) func refill(condiment: Condiment) {
    // This function will be invoked serially, once per condiment, because the
    // containing suite has the .serialized trait.
  }


  @Test func startEngine() async throws {
    // This function will not run while refill(condiment:) is running. One test
    // must end before the other will start.
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[3] Swift Testing 과 XCTest 가 같이 있을 때, &amp;nbsp;테스트 옵션 지정한 건 둘다 적용 &amp;nbsp;되는 건가 ?&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Swift Testing 은&lt;b&gt; 디폴트가 병렬 실행&lt;/b&gt;이고 &amp;nbsp;순차 실행을 원하면 &lt;a href=&quot;https://developer.apple.com/documentation/testing/trait/serialized&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;serialized&lt;/a&gt; trait 을 붙여준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;반면 XCTest 는 디폴트가 순차 실행이고 병렬로 실행하기 위해 scheme 에서 테스트 옵션을 바꿔줬다.&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: 'Nanum Gothic';&quot;&gt;Xcode 몇버전 부터 바뀐 건지 기억이 안나는데, Test Plan 전에는 이렇게 세팅해줬고&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;스크린샷 2025-01-29 오후 4.29.04.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/couVog/btsL1zP6Tfe/kACuRKmOHQqMUyfYLUdH9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/couVog/btsL1zP6Tfe/kACuRKmOHQqMUyfYLUdH9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/couVog/btsL1zP6Tfe/kACuRKmOHQqMUyfYLUdH9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcouVog%2FbtsL1zP6Tfe%2FkACuRKmOHQqMUyfYLUdH9k%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;511&quot; height=&quot;228&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.29.04.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지금 (Xcode16) 은 이 동선으로 들어가서 TestPlan &amp;gt; Options 로 설정한다.&amp;nbsp;&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;스크린샷 2025-01-29 오후 4.36.37.png&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/09uDE/btsL3996foT/9ghgPM7m44MmHs4wH5mF21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/09uDE/btsL3996foT/9ghgPM7m44MmHs4wH5mF21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/09uDE/btsL3996foT/9ghgPM7m44MmHs4wH5mF21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F09uDE%2FbtsL3996foT%2F9ghgPM7m44MmHs4wH5mF21%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;2106&quot; height=&quot;552&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.36.37.png&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;552&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: 'Nanum Gothic';&quot;&gt;병렬 옵션에 이렇게 3개가 제공된다.&amp;nbsp;&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;스크린샷 2025-01-29 오후 4.37.03.png&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IjEi6/btsL24u0nCw/oM7K3M33avU6TuPIVlBwAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IjEi6/btsL24u0nCw/oM7K3M33avU6TuPIVlBwAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IjEi6/btsL24u0nCw/oM7K3M33avU6TuPIVlBwAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIjEi6%2FbtsL24u0nCw%2FoM7K3M33avU6TuPIVlBwAk%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;497&quot; height=&quot;270&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.37.03.png&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;400&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: 'Nanum Gothic';&quot;&gt;- Disabled : Swift Testing, XCTest 모두 병렬 X&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- Swift Testing Only : Swift Testing 병렬 O, XCTest 병렬 X&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- Enabled (If Possible) : &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Swift Testing, XCTest 모두 병렬 O&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: 'Nanum Gothic';&quot;&gt;Enabled 에 If Possible 이 있는 경우는&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;XCTest 는 MacOS 랑 simulator 에서만 병렬 실행이 가능하기 때문인 듯.&amp;nbsp;&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;스크린샷 2025-01-29 오후 4.13.32.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVZ7aI/btsL1UUpKMu/mPi0QLUoyAMsVVk7lJk4B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVZ7aI/btsL1UUpKMu/mPi0QLUoyAMsVVk7lJk4B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVZ7aI/btsL1UUpKMu/mPi0QLUoyAMsVVk7lJk4B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVZ7aI%2FbtsL1UUpKMu%2FmPi0QLUoyAMsVVk7lJk4B0%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;1258&quot; height=&quot;692&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.13.32.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Enabled (If Possible) 이&lt;/span&gt; 디폴트 값이니까 이제는 XCTest 도 디폴트가 병렬&lt;/b&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;XCTestCase&amp;nbsp;별로 병렬 옵션을 ON/OFF 할 수 있는 프로퍼티가 혹시 추가되었나 &lt;a href=&quot;https://developer.apple.com/documentation/xctest&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;를 봤는데 &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;없는 듯 함! &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;개별 지정은 Swift Testing 만..&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[4] XCTest 를 Swift Testing 으로 완전히 Migration 할 수 있는가 ?&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;대부분은 그렇다.&amp;nbsp;&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/txwAZ/btsL22DXXHK/QB0eKxgRElpO2REfWWZL1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/txwAZ/btsL22DXXHK/QB0eKxgRElpO2REfWWZL1K/img.png&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.12.46.png&quot; data-origin-height=&quot;700&quot; data-origin-width=&quot;1256&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;33.23&quot; style=&quot;width: 32.457418%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/txwAZ/btsL22DXXHK/QB0eKxgRElpO2REfWWZL1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtxwAZ%2FbtsL22DXXHK%2FQB0eKxgRElpO2REfWWZL1K%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;1256&quot; height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVZ7aI/btsL1UUpKMu/mPi0QLUoyAMsVVk7lJk4B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVZ7aI/btsL1UUpKMu/mPi0QLUoyAMsVVk7lJk4B0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;692&quot; data-filename=&quot;스크린샷 2025-01-29 오후 4.13.32.png&quot; data-widthpercent=&quot;33.67&quot; style=&quot;width: 32.88493%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVZ7aI/btsL1UUpKMu/mPi0QLUoyAMsVVk7lJk4B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVZ7aI%2FbtsL1UUpKMu%2FmPi0QLUoyAMsVVk7lJk4B0%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;1258&quot; height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qbALk/btsL3zadIMy/PWnxce9n9G8QcSgMAhbKWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qbALk/btsL3zadIMy/PWnxce9n9G8QcSgMAhbKWk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;696&quot; data-filename=&quot;스크린샷 2025-01-29 오후 5.16.17.png&quot; data-widthpercent=&quot;33.1&quot; style=&quot;width: 32.33207%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qbALk/btsL3zadIMy/PWnxce9n9G8QcSgMAhbKWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqbALk%2FbtsL3zadIMy%2FPWnxce9n9G8QcSgMAhbKWk%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;1244&quot; height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;/div&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: 'Nanum Gothic';&quot;&gt;&lt;i&gt;마이그레이션 문서: &lt;a href=&quot;https://developer.apple.com/documentation/testing/migratingfromxctest&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Migrating a test from XCTest&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문서에 너무 잘 나와있음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;문서에서 주의깊게 볼 것은 &lt;a href=&quot;https://developer.apple.com/documentation/xctest/xctfail(_:file:line:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;XCTFail&lt;/a&gt; 이 &lt;a href=&quot;https://developer.apple.com/documentation/testing/issue/record(_:sourcelocation:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;record&lt;/a&gt; 랑 대응된다는 점. &amp;nbsp;(XCTFail 같은 거 없는 줄 알았음 ;;;)&amp;nbsp;&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;스크린샷 2025-01-29 오후 5.08.53.png&quot; data-origin-width=&quot;1742&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/swJM5/btsL2bIlWcJ/jMhRd3Kof6eRLGYZqjnOU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/swJM5/btsL2bIlWcJ/jMhRd3Kof6eRLGYZqjnOU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/swJM5/btsL2bIlWcJ/jMhRd3Kof6eRLGYZqjnOU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FswJM5%2FbtsL2bIlWcJ%2FjMhRd3Kof6eRLGYZqjnOU0%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;1742&quot; height=&quot;708&quot; data-filename=&quot;스크린샷 2025-01-29 오후 5.08.53.png&quot; data-origin-width=&quot;1742&quot; data-origin-height=&quot;708&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: 'Nanum Gothic';&quot;&gt;그리고 &lt;a href=&quot;https://developer.apple.com/documentation/xctest/xctestexpectation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;expectation&lt;/a&gt; fullfill -&amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/testing/confirmation(_:expectedcount:isolation:sourcelocation:_:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;confirmation&lt;/a&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-29 오후 5.42.52.png&quot; data-origin-width=&quot;1740&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d51o0g/btsL18ZfvnS/xkXCHVdzLbsM3XmXT3KcTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d51o0g/btsL18ZfvnS/xkXCHVdzLbsM3XmXT3KcTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d51o0g/btsL18ZfvnS/xkXCHVdzLbsM3XmXT3KcTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd51o0g%2FbtsL18ZfvnS%2FxkXCHVdzLbsM3XmXT3KcTk%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;1740&quot; height=&quot;868&quot; data-filename=&quot;스크린샷 2025-01-29 오후 5.42.52.png&quot; data-origin-width=&quot;1740&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;하지만 계속 XCTest 를 써야하는 케이스도 있다.&amp;nbsp;&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;스크린샷 2025-01-29 오후 5.04.14.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkxUGB/btsL1Y9Zc8Z/402lIbHUwPDWSejydfnpQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkxUGB/btsL1Y9Zc8Z/402lIbHUwPDWSejydfnpQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkxUGB/btsL1Y9Zc8Z/402lIbHUwPDWSejydfnpQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkxUGB%2FbtsL1Y9Zc8Z%2F402lIbHUwPDWSejydfnpQ0%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;1258&quot; height=&quot;700&quot; data-filename=&quot;스크린샷 2025-01-29 오후 5.04.14.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;700&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: 'Nanum Gothic';&quot;&gt;&lt;i&gt;Continue&amp;nbsp;to&amp;nbsp;use&amp;nbsp;XCTest&amp;nbsp;for&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/xctest/user-interface-tests&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;User&amp;nbsp;Interface&amp;nbsp;Tests&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/xctest/performance-tests&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Performance&amp;nbsp;Tests&lt;/a&gt;.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[5] DisplayName 은 언제 쓰면 좋을까 ?&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Suite 나 Test 이름을 명확하게 지정해주면 DisplayName 은 필요없어보이는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;언제 쓰면 좋을까 ?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;WWDC 에서는 가독성을 위해 띄어쓰기 하는 용도로 dislayname 을 사용했다.&amp;nbsp;&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;스크린샷 2025-01-29 오후 5.22.15.png&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Docxp/btsL23JCfg9/OV327c2xTl7ahfJNy5zzEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Docxp/btsL23JCfg9/OV327c2xTl7ahfJNy5zzEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Docxp/btsL23JCfg9/OV327c2xTl7ahfJNy5zzEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDocxp%2FbtsL23JCfg9%2FOV327c2xTl7ahfJNy5zzEK%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;1298&quot; height=&quot;544&quot; data-filename=&quot;스크린샷 2025-01-29 오후 5.22.15.png&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;544&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: 'Nanum Gothic';&quot;&gt;이것도 좋고 parameterized test 에서 파라미터들이 많아서 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;테스트가 길어질 때 테스트 네임을 짧게 하고 displayName 을 길게 하는 식으로 사용해도 좋을 것 같다.&amp;nbsp;&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;스크린샷 2025-01-29 오후 5.24.51.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddEfFd/btsL23CSUzU/hOKQctAa7fcEH0kswGAkpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddEfFd/btsL23CSUzU/hOKQctAa7fcEH0kswGAkpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddEfFd/btsL23CSUzU/hOKQctAa7fcEH0kswGAkpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddEfFd%2FbtsL23CSUzU%2FhOKQctAa7fcEH0kswGAkpk%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;1258&quot; height=&quot;572&quot; data-filename=&quot;스크린샷 2025-01-29 오후 5.24.51.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[6] Tag 를 중첩시키면 어떻게 표시되나 ?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;[ 테스트 &amp;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: 'Nanum Gothic';&quot;&gt;tag 별로 해당하는 테스트함수가 모두 나옴&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;스크린샷 2025-01-29 오후 6.13.11.png&quot; data-origin-width=&quot;1678&quot; data-origin-height=&quot;1384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJkx7a/btsL25tUrt8/D9kbn43cUWqdvNM4g0fz60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJkx7a/btsL25tUrt8/D9kbn43cUWqdvNM4g0fz60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJkx7a/btsL25tUrt8/D9kbn43cUWqdvNM4g0fz60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJkx7a%2FbtsL25tUrt8%2FD9kbn43cUWqdvNM4g0fz60%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;1678&quot; height=&quot;1384&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.13.11.png&quot; data-origin-width=&quot;1678&quot; data-origin-height=&quot;1384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;상위 Tag 로 검색해도 하위 테스트 함수까지 잘 나옴&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;스크린샷 2025-01-29 오후 6.14.42.png&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;1392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ORh3G/btsL01Ndx58/EjhrLl4oHGGkCP30KqD3w0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ORh3G/btsL01Ndx58/EjhrLl4oHGGkCP30KqD3w0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ORh3G/btsL01Ndx58/EjhrLl4oHGGkCP30KqD3w0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FORh3G%2FbtsL01Ndx58%2FEjhrLl4oHGGkCP30KqD3w0%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;1698&quot; height=&quot;1392&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.14.42.png&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;1392&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: 'Nanum Gothic';&quot;&gt;&lt;b&gt;[ 테스트 리포트 ]&amp;nbsp;&lt;/b&gt;&lt;/span&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;태그 다 붙어서 나옴&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.16.15.png&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;534&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nyN7y/btsL1Dk9ahE/tkNle21kzkKkwYCpDanZlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nyN7y/btsL1Dk9ahE/tkNle21kzkKkwYCpDanZlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nyN7y/btsL1Dk9ahE/tkNle21kzkKkwYCpDanZlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnyN7y%2FbtsL1Dk9ahE%2FtkNle21kzkKkwYCpDanZlK%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;534&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.16.15.png&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;534&quot;/&gt;&lt;/span&gt;&lt;/figure&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;검색에 상위 Tag 를 제외해도 하위 테스트 (다른 태그를 달고 있는 테스트) 는 나옴.&amp;nbsp;&lt;/span&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.21.25.png&quot; data-origin-width=&quot;1628&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Q0hT/btsL1YJlimp/HrqCUz5JikySFk5GVk4nb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Q0hT/btsL1YJlimp/HrqCUz5JikySFk5GVk4nb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Q0hT/btsL1YJlimp/HrqCUz5JikySFk5GVk4nb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Q0hT%2FbtsL1YJlimp%2FHrqCUz5JikySFk5GVk4nb1%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;1628&quot; height=&quot;492&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.21.25.png&quot; data-origin-width=&quot;1628&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;/figure&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;[ 테스트 플랜 ]&lt;/b&gt;&lt;/span&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;상위 Tag 를 &amp;nbsp;Exclude 하면 하위 테스트까지 다 exclude 됨.&lt;/span&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.19.58.png&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx3zzE/btsL1XKnqxS/roqMobtUCoNySSsYWrs4kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx3zzE/btsL1XKnqxS/roqMobtUCoNySSsYWrs4kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx3zzE/btsL1XKnqxS/roqMobtUCoNySSsYWrs4kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx3zzE%2FbtsL1XKnqxS%2FroqMobtUCoNySSsYWrs4kk%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;757&quot; height=&quot;332&quot; data-filename=&quot;스크린샷 2025-01-29 오후 6.19.58.png&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /Swift</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/677</guid>
      <comments>https://eunjin3786.tistory.com/677#entry677comment</comments>
      <pubDate>Wed, 29 Jan 2025 17:34:39 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] NSCollectionLayoutSupplementaryItem 헷갈리는 것 정리</title>
      <link>https://eunjin3786.tistory.com/674</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[1] &lt;a href=&quot;https://developer.apple.com/documentation/uikit/nscollectionlayoutboundarysupplementaryitem&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NSCollectionLayoutBoundarySupplementaryItem&lt;/a&gt; &amp;gt; absoluteOffset&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;대놓고 이름이 offset 인데, inset 처럼 동작안해서 이상하다고 생각하고 있었다 ;;;;;&amp;nbsp;&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;SCR-20241211-sdni.png&quot; data-origin-width=&quot;2384&quot; data-origin-height=&quot;1037&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blP99v/btsLe4iPRUJ/IgmKi4y1F9cN73E0cd4TZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blP99v/btsLe4iPRUJ/IgmKi4y1F9cN73E0cd4TZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blP99v/btsLe4iPRUJ/IgmKi4y1F9cN73E0cd4TZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblP99v%2FbtsLe4iPRUJ%2FIgmKi4y1F9cN73E0cd4TZ1%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;2384&quot; height=&quot;1037&quot; data-filename=&quot;SCR-20241211-sdni.png&quot; data-origin-width=&quot;2384&quot; data-origin-height=&quot;1037&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: 'Nanum Gothic';&quot;&gt;위 그림에서&amp;nbsp;item (파란색) bottom 으로 부터 안쪽 100에 footer (초록색) 이 위치해야하는데, 이상하다 ? 고 생각함 ;;;;;&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: 'Nanum Gothic';&quot;&gt;왜그랬을까 생각해보면... 스유에서 alignment 에  값 주던 형태랑 비슷하니까 안쪽으로 들어간다고 생각했지 않을까 ?&amp;nbsp;&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;SCR-20241211-sfoy.png&quot; data-origin-width=&quot;2258&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bR1Yu6/btsLfoalrJF/Dmo06P0qeosoAGjosKrXz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bR1Yu6/btsLfoalrJF/Dmo06P0qeosoAGjosKrXz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bR1Yu6/btsLfoalrJF/Dmo06P0qeosoAGjosKrXz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbR1Yu6%2FbtsLfoalrJF%2FDmo06P0qeosoAGjosKrXz1%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;2258&quot; height=&quot;1200&quot; data-filename=&quot;SCR-20241211-sfoy.png&quot; data-origin-width=&quot;2258&quot; data-origin-height=&quot;1200&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: 'Nanum Gothic';&quot;&gt;갑자기 떠오르는 SwiftUI 초창기 시절에 들었던 발표,,,&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;스크린샷 2024-12-11 오후 8.53.05.png&quot; data-origin-width=&quot;2378&quot; data-origin-height=&quot;1244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sUHr7/btsLeMo6PAf/HYrb8HXDHjzqRbR1N3NbJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sUHr7/btsLeMo6PAf/HYrb8HXDHjzqRbR1N3NbJk/img.png&quot; data-alt=&quot;https://tv.kakao.com/channel/3693125/cliplink/414129341&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sUHr7/btsLeMo6PAf/HYrb8HXDHjzqRbR1N3NbJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsUHr7%2FbtsLeMo6PAf%2FHYrb8HXDHjzqRbR1N3NbJk%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;2378&quot; height=&quot;1244&quot; data-filename=&quot;스크린샷 2024-12-11 오후 8.53.05.png&quot; data-origin-width=&quot;2378&quot; data-origin-height=&quot;1244&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://tv.kakao.com/channel/3693125/cliplink/414129341&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: 'Nanum Gothic';&quot;&gt;아무리 사고방식이 바뀌었다고 해도&amp;nbsp;이름이 offset 이다.. 정신차려라.&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: 'Nanum Gothic';&quot;&gt;그리고 주석에도 그림이 적혀있음! &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-11 오후 9.05.23.png&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;1662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KWaPB/btsLeM3L6cX/FKfizSIMOzgwnAilYTqm7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KWaPB/btsLeM3L6cX/FKfizSIMOzgwnAilYTqm7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KWaPB/btsLeM3L6cX/FKfizSIMOzgwnAilYTqm7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKWaPB%2FbtsLeM3L6cX%2FFKfizSIMOzgwnAilYTqm7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2448&quot; height=&quot;1662&quot; data-filename=&quot;스크린샷 2024-12-11 오후 9.05.23.png&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;1662&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: 'Nanum Gothic';&quot;&gt;내가 헷갈렸던 footer 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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20241211-sido.png&quot; data-origin-width=&quot;1986&quot; data-origin-height=&quot;1098&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vNjWx/btsLeJTvuJH/WoF7OtW0DhG0U8fDcQtks1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vNjWx/btsLeJTvuJH/WoF7OtW0DhG0U8fDcQtks1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vNjWx/btsLeJTvuJH/WoF7OtW0DhG0U8fDcQtks1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvNjWx%2FbtsLeJTvuJH%2FWoF7OtW0DhG0U8fDcQtks1%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;1986&quot; height=&quot;1098&quot; data-filename=&quot;SCR-20241211-sido.png&quot; data-origin-width=&quot;1986&quot; data-origin-height=&quot;1098&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2] &lt;a href=&quot;https://developer.apple.com/documentation/uikit/nscollectionlayoutitem/init(layoutsize:supplementaryitems:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NSCollectionLayoutItem&lt;/a&gt; &amp;gt; fractionalOffset&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;헷갈리면 안되는게 offset 이지만 edges 에서 출발이다 (?)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;파리미터 보고 생각하면 되는데,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;edges&amp;nbsp; 에 맞게 배치시키고 그 다음에 offset 을 준다고 생각하면 됨.&amp;nbsp;&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;SCR-20241211-skks.png&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgy27w/btsLgeknG4P/Sl4IymQnxujVT64SscEP5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgy27w/btsLgeknG4P/Sl4IymQnxujVT64SscEP5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgy27w/btsLgeknG4P/Sl4IymQnxujVT64SscEP5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgy27w%2FbtsLgeknG4P%2FSl4IymQnxujVT64SscEP5K%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;2080&quot; height=&quot;756&quot; data-filename=&quot;SCR-20241211-skks.png&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;756&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: 'Nanum Gothic';&quot;&gt;bottom, leading edges 에 먼저 위치한 다음에 badge 넓이의 반만큼 offset x, y 이동.&amp;nbsp;&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;SCR-20241211-slnz.png&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmRMK7/btsLgtn3A6V/MvNTlTFMVlCP5kJ1h1MOmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmRMK7/btsLgtn3A6V/MvNTlTFMVlCP5kJ1h1MOmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmRMK7/btsLgtn3A6V/MvNTlTFMVlCP5kJ1h1MOmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmRMK7%2FbtsLgtn3A6V%2FMvNTlTFMVlCP5kJ1h1MOmk%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;2046&quot; height=&quot;716&quot; data-filename=&quot;SCR-20241211-slnz.png&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-11 오후 9.19.21.png&quot; data-origin-width=&quot;2124&quot; data-origin-height=&quot;874&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diaiMk/btsLeCNVcdh/o1Pg3VyikD6LePdf7p2kWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diaiMk/btsLeCNVcdh/o1Pg3VyikD6LePdf7p2kWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diaiMk/btsLeCNVcdh/o1Pg3VyikD6LePdf7p2kWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiaiMk%2FbtsLeCNVcdh%2Fo1Pg3VyikD6LePdf7p2kWK%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;2124&quot; height=&quot;874&quot; data-filename=&quot;스크린샷 2024-12-11 오후 9.19.21.png&quot; data-origin-width=&quot;2124&quot; data-origin-height=&quot;874&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;i&gt;✨ Special Thanks To 동료분들&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category> /iOS</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/674</guid>
      <comments>https://eunjin3786.tistory.com/674#entry674comment</comments>
      <pubDate>Wed, 11 Dec 2024 21:24:18 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] 매크로 (Macro)</title>
      <link>https://eunjin3786.tistory.com/673</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/documentation/swift/macros&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://github.com/swiftlang/swift-syntax/blob/main/Sources/SwiftSyntaxMacros/SwiftSyntaxMacros.docc/SwiftSyntaxMacros.md#Macro-Overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt; ⭐️&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2023/10166&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC 23 &amp;gt; Write&amp;nbsp;Swift&amp;nbsp;macros&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;i&gt;  Assisted by GPT&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[1] 매크로 템플릿 만들기&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Xcode &amp;gt; New &amp;gt; Pacakge &amp;gt; Swift Macro 선택해서 매크로 템플릿을 만들 수 있음.&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: 'Nanum Gothic';&quot;&gt;커맨드라인으로도 가능한데, 아래 명령어를 실행시키면 됨.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733566195259&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;swift package init --type macro&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;ㄴ dependency 로 &lt;a href=&quot;https://github.com/swiftlang/swift-syntax&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;swift-syntax&lt;/a&gt; 가 걸려있음.&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;br /&gt;ㄴ WWDC 에서 소개하는&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;stringify 매크로의 선언, 구현, 예제, 테스트 코드 들어가 있음.&amp;nbsp;&lt;/span&gt;&lt;br /&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-07 오후 6.41.46.png&quot; data-origin-width=&quot;1758&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m7gim/btsK9ykQJDY/uk1LnIIcW4t9KXwkfpiXkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m7gim/btsK9ykQJDY/uk1LnIIcW4t9KXwkfpiXkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m7gim/btsK9ykQJDY/uk1LnIIcW4t9KXwkfpiXkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm7gim%2FbtsK9ykQJDY%2Fuk1LnIIcW4t9KXwkfpiXkk%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;1758&quot; height=&quot;834&quot; data-filename=&quot;스크린샷 2024-12-07 오후 6.41.46.png&quot; data-origin-width=&quot;1758&quot; data-origin-height=&quot;834&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2] &lt;a href=&quot;https://github.com/swiftlang/swift-syntax/blob/main/Sources/SwiftSyntaxMacros/SwiftSyntaxMacros.docc/SwiftSyntaxMacros.md#macro-roles&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;매크로 유형&amp;nbsp;&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;일단 크게 두가지&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: 'Nanum Gothic';&quot;&gt;&lt;b&gt;1️⃣&amp;nbsp; Freestanding Macros&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 코드&amp;nbsp;블록&amp;nbsp;외부에서&amp;nbsp;동작.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 예:&amp;nbsp;디버깅&amp;nbsp;정보를&amp;nbsp;자동으로&amp;nbsp;추가.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 사용 예:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733566567942&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#debugLog(&quot;Message&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;2️⃣ Attached Macros&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 클래스,&amp;nbsp;구조체,&amp;nbsp;열거형&amp;nbsp;등에&amp;nbsp;속성처럼&amp;nbsp;부착되어&amp;nbsp;동작.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 예:&amp;nbsp;@Codable&amp;nbsp;매크로로&amp;nbsp;Codable&amp;nbsp;구현&amp;nbsp;자동&amp;nbsp;생성.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;bull; 사용&amp;nbsp;예:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733566579259&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Codable
struct User {
    let name: String
    let age: Int
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그리고 세부적으로는 freestanding 2개, attached 5개.&amp;nbsp;&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;스크린샷 2024-12-07 오후 6.48.39.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm3QOL/btsLbogKrPN/vCXkajdpeRVNK2odwVISK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm3QOL/btsLbogKrPN/vCXkajdpeRVNK2odwVISK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm3QOL/btsLbogKrPN/vCXkajdpeRVNK2odwVISK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm3QOL%2FbtsLbogKrPN%2FvCXkajdpeRVNK2odwVISK0%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;1246&quot; height=&quot;694&quot; data-filename=&quot;스크린샷 2024-12-07 오후 6.48.39.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;694&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: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;Macro 프로토콜이 있고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.19.06.png&quot; data-origin-width=&quot;1398&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xwEQ1/btsLa2EYJtt/INqBFxHtHLRVH6xPoOJP30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xwEQ1/btsLa2EYJtt/INqBFxHtHLRVH6xPoOJP30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xwEQ1/btsLa2EYJtt/INqBFxHtHLRVH6xPoOJP30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxwEQ1%2FbtsLa2EYJtt%2FINqBFxHtHLRVH6xPoOJP30%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;677&quot; height=&quot;390&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.19.06.png&quot; data-origin-width=&quot;1398&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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: 'Nanum Gothic';&quot;&gt;이걸 채택하는 FreestandingMacro, AttachedMacro 프로토콜이 있고&amp;nbsp;&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;스크린샷 2024-12-07 오후 7.20.16.png&quot; data-origin-width=&quot;2272&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl7BDs/btsLbCy235P/WiiWsKf4upRCt3HFry7O6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl7BDs/btsLbCy235P/WiiWsKf4upRCt3HFry7O6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl7BDs/btsLbCy235P/WiiWsKf4upRCt3HFry7O6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl7BDs%2FbtsLbCy235P%2FWiiWsKf4upRCt3HFry7O6K%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;2272&quot; height=&quot;810&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.20.16.png&quot; data-origin-width=&quot;2272&quot; data-origin-height=&quot;810&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: 'Nanum Gothic';&quot;&gt;Freestanding 의 하위 유형들&amp;nbsp; &lt;i&gt;(CodeItem 이 하나 더 추가되었나봄)&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.23.19.png&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCkCUB/btsLaFQRKlh/y0kyFdXsBbI7uWOzurio81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCkCUB/btsLaFQRKlh/y0kyFdXsBbI7uWOzurio81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCkCUB/btsLaFQRKlh/y0kyFdXsBbI7uWOzurio81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCkCUB%2FbtsLaFQRKlh%2Fy0kyFdXsBbI7uWOzurio81%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;336&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.23.19.png&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;454&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: 'Nanum Gothic';&quot;&gt;AttachedMacro 의 하위 유형들&lt;i&gt; &lt;/i&gt;이 있음&amp;nbsp; &lt;i&gt;(여기도 더 추가되었네..)&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.25.52.png&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/waMnY/btsLbR3PuTc/Tu6VcsbAa9gEYVwB6mEkP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/waMnY/btsLbR3PuTc/Tu6VcsbAa9gEYVwB6mEkP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/waMnY/btsLbR3PuTc/Tu6VcsbAa9gEYVwB6mEkP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwaMnY%2FbtsLbR3PuTc%2FTu6VcsbAa9gEYVwB6mEkP0%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;538&quot; height=&quot;449&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.25.52.png&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;852&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[3] Freestanding 매크로 만들어보기&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #f6e199;&quot;&gt;&lt;b&gt;1) ExpressionMacro&amp;nbsp;&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: 'Nanum Gothic';&quot;&gt;이건 기본템플릿에 구현된 예제로 퉁.&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: 'Nanum Gothic';&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.40.38.png&quot; data-origin-width=&quot;2178&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oQxqi/btsLbB1eJKk/ruAj5qq4Pq32JIbN36WE7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oQxqi/btsLbB1eJKk/ruAj5qq4Pq32JIbN36WE7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oQxqi/btsLbB1eJKk/ruAj5qq4Pq32JIbN36WE7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoQxqi%2FbtsLbB1eJKk%2FruAj5qq4Pq32JIbN36WE7k%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;2178&quot; height=&quot;666&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.40.38.png&quot; data-origin-width=&quot;2178&quot; data-origin-height=&quot;666&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: 'Nanum Gothic';&quot;&gt;# 구현&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.41.01.png&quot; data-origin-width=&quot;1624&quot; data-origin-height=&quot;1506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RwvHQ/btsLbTUSjr6/e6ENyMpJqqtPLgRBNlsrD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RwvHQ/btsLbTUSjr6/e6ENyMpJqqtPLgRBNlsrD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RwvHQ/btsLbTUSjr6/e6ENyMpJqqtPLgRBNlsrD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRwvHQ%2FbtsLbTUSjr6%2Fe6ENyMpJqqtPLgRBNlsrD0%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;725&quot; height=&quot;672&quot; data-filename=&quot;스크린샷 2024-12-07 오후 7.41.01.png&quot; data-origin-width=&quot;1624&quot; data-origin-height=&quot;1506&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: 'Nanum Gothic';&quot;&gt;# 사용&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733568098156&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let result = #stringify(1 + 2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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: 'Nanum Gothic'; background-color: #f6e199;&quot;&gt;&lt;b&gt;2) DeclarationMacro&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: 'Nanum Gothic';&quot;&gt;repeat 함수를 매크로를 통해 선언하는 예제를 작성. &lt;i&gt;(조금 안와닿을 수 있는 예제이지만,&amp;nbsp;&amp;nbsp;파라미터 두개를 받아보기 위해..)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;# 선언&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733568264629&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@freestanding(declaration, names: named(printRepeat))
public macro printRepeatFunction(_ value: String, _ count: Int) = #externalMacro(module: &quot;MyMacroMacros&quot;, type: &quot;PrintRepeatFunctionMacro&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;# 구현&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733574810065&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public struct PrintRepeatFunctionMacro: DeclarationMacro {

    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) -&amp;gt; [DeclSyntax] {
        guard let value = node.arguments.first?.expression else {
            fatalError(&quot;The macro requires a value&quot;)
        }

        guard let repeatCount = node.arguments.last?.expression else {
            fatalError(&quot;The macro requires a repeat count&quot;)
        }

        return [
          &quot;&quot;&quot;
            func printRepeat() {
                for _ in 0..&amp;lt;\(repeatCount) {
                    print(\(value))
                }
            }
            &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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;# 사용&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733574828216&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct SomeStruct {
    #printRepeatFunction(&quot;HELLO&quot;, 3)
}

SomeStruct().printRepeat()&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: 'Nanum Gothic';&quot;&gt;참고로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;객체 안에서 매크로로 선언한 function 은 호출가능이지만,&amp;nbsp;&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;스크린샷 2024-12-07 오후 9.34.06.png&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dv9Yop/btsLbUTyPkz/1VAoMCNPB4jPZk9sEAzUHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dv9Yop/btsLbUTyPkz/1VAoMCNPB4jPZk9sEAzUHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dv9Yop/btsLbUTyPkz/1VAoMCNPB4jPZk9sEAzUHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdv9Yop%2FbtsLbUTyPkz%2F1VAoMCNPB4jPZk9sEAzUHk%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;466&quot; height=&quot;279&quot; data-filename=&quot;스크린샷 2024-12-07 오후 9.34.06.png&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;472&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: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;전역 스코프에서 호출하면 에러 발생 (in &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;템플릿이 제공하는 main 파일)&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-07 오후 9.36.36.png&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brp1ZX/btsLa256Nrg/iAjr97yJgnmq8N1XV5MNNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brp1ZX/btsLa256Nrg/iAjr97yJgnmq8N1XV5MNNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brp1ZX/btsLa256Nrg/iAjr97yJgnmq8N1XV5MNNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbrp1ZX%2FbtsLa256Nrg%2FiAjr97yJgnmq8N1XV5MNNk%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;2074&quot; height=&quot;356&quot; data-filename=&quot;스크린샷 2024-12-07 오후 9.36.36.png&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #f6e199;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;3) &lt;/span&gt;CodeItemMacro (experimental feature)&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: 'Nanum Gothic';&quot;&gt;아직 실험기능이라 써볼 수는 없었음 (swift-syntax 패키지 버전 바꾸면 가능할지도..)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-07 오후 9.47.31.png&quot; data-origin-width=&quot;2082&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VaV82/btsLbu17jG6/940vv3FWxWBDJ8ESiYG2P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VaV82/btsLbu17jG6/940vv3FWxWBDJ8ESiYG2P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VaV82/btsLbu17jG6/940vv3FWxWBDJ8ESiYG2P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVaV82%2FbtsLbu17jG6%2F940vv3FWxWBDJ8ESiYG2P1%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;2082&quot; height=&quot;56&quot; data-filename=&quot;스크린샷 2024-12-07 오후 9.47.31.png&quot; data-origin-width=&quot;2082&quot; data-origin-height=&quot;56&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: 'Nanum Gothic';&quot;&gt;DeclarationMacro 는 class, function 등이 아니라 코드 블럭을 선언하면 에러가 발생하는데,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;CodeItemMacro 는 코드블럭을 선언할 수 있는 매크로.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733575856289&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public struct RepeatMacro: CodeItemMacro {

    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) -&amp;gt; [CodeBlockItemSyntax] {
        guard let value = node.arguments.first?.expression else {
            fatalError(&quot;The macro requires a value&quot;)
        }

        guard let repeatCount = node.arguments.last?.expression else {
            fatalError(&quot;The macro requires a repeat count&quot;)
        }

        return [
          &quot;&quot;&quot;
            {
               for _ in 0..&amp;lt;\(repeatCount) {
                    print(\(value))
                }
            }
            &quot;&quot;&quot;
        ]
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;[4] Attached&lt;/span&gt;&amp;nbsp;매크로 만들어보기&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #f6e199;&quot;&gt;&lt;b&gt;1) PeerMacro&amp;nbsp;&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;PeerMacro 는 적&lt;/span&gt;용된 선언과 동등한 수준(peer level)에 새로운 선언을 추가할 때 사용됩니다.&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예를 들어, 클래스나 구조체에 @attached(peer) 매크로를 적용하면, 매크로는 해당 클래스나 구조체의 외부에 새로운 선언을 생성합니다.&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: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;# 선언&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733578374806&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@attached(peer, names: prefixed(Mock))
public macro Mock() = #externalMacro(module: &quot;MyMacroMacros&quot;, type: &quot;MockMacro&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: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;[ names ]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; &amp;bull; arbitrary는 이름을 고정하지 않겠다는 선언입니다. (&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;매크로 실행 시 동적으로 결정됩니다)&lt;/span&gt;&lt;br /&gt;&amp;bull; 이&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;이름으로&amp;nbsp;설정될&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&amp;bull; 사용자로부터 이름을 전달받아 동적으로 생성하는 경우 예시: &lt;/span&gt;@Mock(name: &quot;CustomMockUserService&quot;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt; &amp;bull; prefixed(Mock):&amp;nbsp;생성된 선언의 이름이 Mock 접두사를 포함해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt; &amp;bull; 예를 들어, 매크로가 UserService에서 호출되면 MockUserService라는 이름으로 확장됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;# 구현&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733578393606&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public struct MockMacro: PeerMacro {

    public static func expansion(
        of node: AttributeSyntax,
        providingPeersOf declaration: some DeclSyntaxProtocol,
        in context: some MacroExpansionContext
    ) throws -&amp;gt; [DeclSyntax] {
        // Ensure the macro is applied to a protocol
        guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else {
            fatalError(&quot;The macro must be applied to a protocol&quot;)
        }

        let protocolName = protocolDecl.name.text
        let mockName = &quot;Mock\(protocolName)&quot;

        // 프로퍼티 및 주석에서 기본값 추출
        let members = protocolDecl.memberBlock.members.compactMap { member -&amp;gt; String? in
            if let variable = member.decl.as(VariableDeclSyntax.self) {
                let name = variable.bindings.first?.pattern.description ?? &quot;&quot;
                let type = variable.bindings.first?.typeAnnotation?.type.description ?? &quot;Any&quot;
                let comment = member.leadingTrivia.compactMap { trivia -&amp;gt; String? in
                    if case .lineComment(let text) = trivia {
                        return text.trimmingCharacters(in: .whitespacesAndNewlines)
                    }
                    return nil
                }.first
                let defaultValue = comment?.replacingOccurrences(of: &quot;//&quot;, with: &quot;&quot;).trimmingCharacters(in: .whitespacesAndNewlines) ?? defaultForType(type)

                return &quot;var \(name): \(type) = \(defaultValue)&quot;
            } else if let function = member.decl.as(FunctionDeclSyntax.self) {
                let name = function.name.text
                let params = function.signature.parameterClause.description
                let returnType = function.signature.returnClause?.type.description ?? &quot;Void&quot;
                return &quot;&quot;&quot;
                func \(name)\(params) -&amp;gt; \(returnType) {
                    fatalError(&quot;Mock implementation for \(name) not provided&quot;)
                }
                &quot;&quot;&quot;
            }
            return nil
        }

        // Mock 클래스 생성
        let mockClass = &quot;&quot;&quot;
        class \(mockName): \(protocolName) {
            \(members.joined(separator: &quot;\n&quot;))
        }
        &quot;&quot;&quot;

        // Parse the mock class as DeclSyntax
        return [
            DeclSyntax(stringLiteral: mockClass)
        ]
    }

    /// 기본 타입에 따른 기본값 반환
    private static func defaultForType(_ type: String) -&amp;gt; String {
        switch type {
        case &quot;String&quot;: return &quot;\&quot;\&quot;&quot;
        case &quot;Int&quot;, &quot;Float&quot;, &quot;Double&quot;: return &quot;0&quot;
        case &quot;Bool&quot;: return &quot;false&quot;
        case _ where type.hasSuffix(&quot;?&quot;): return &quot;nil&quot; // Optional 타입
        default: return &quot;fatalError(\&quot;No default value for type: \(type)\&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;# 사용&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733578464401&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Mock
protocol UserService {
    // &quot;Default Name&quot;
    var name: String { get set }

    // 25
    var age: Int { get set }

    // true
    var isActive: Bool { get set }

    func fetchUser(id: Int) -&amp;gt; String
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;# Expand Macro&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;스크린샷 2024-12-07 오후 11.26.29.png&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;944&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H8Z1b/btsLawtapPs/OLQDXclxUCPCqTD7tNx9gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H8Z1b/btsLawtapPs/OLQDXclxUCPCqTD7tNx9gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H8Z1b/btsLawtapPs/OLQDXclxUCPCqTD7tNx9gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH8Z1b%2FbtsLawtapPs%2FOLQDXclxUCPCqTD7tNx9gk%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;692&quot; height=&quot;428&quot; data-filename=&quot;스크린샷 2024-12-07 오후 11.26.29.png&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;944&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #f6e199;&quot;&gt;&lt;b&gt;2) AccessorMacro&amp;nbsp;&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: 'Nanum Gothic';&quot;&gt;위의 peer 랑 같이 쓰는 예제.&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: 'Nanum Gothic';&quot;&gt;# 선언&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733579637923&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@attached(peer, names: arbitrary)
@attached(accessor)
public macro Logged() = #externalMacro(module: &quot;MyMacroMacros&quot;, type: &quot;LoggedMacro&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;# 구현&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733581042566&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public struct LoggedMacro: AccessorMacro, PeerMacro {

    // PeerMacro: 내부 저장 프로퍼티 생성
    public static func expansion(
        of node: AttributeSyntax,
        providingPeersOf declaration: some DeclSyntaxProtocol,
        in context: some MacroExpansionContext
    ) throws -&amp;gt; [DeclSyntax] {
        // 매크로가 변수에 적용되었는지 확인
        guard let variableDecl = declaration.as(VariableDeclSyntax.self) else {
            fatalError(&quot;이 매크로는 변수에만 적용할 수 있습니다.&quot;)
        }

        // 프로퍼티 이름과 타입 추출
        guard let variableName = variableDecl.bindings.first?.pattern.description else {
            fatalError(&quot;변수 이름을 확인할 수 없습니다.&quot;)
        }
        guard let type = variableDecl.bindings.first?.typeAnnotation?.type.description else {
            fatalError(&quot;변수 타입을 확인할 수 없습니다.&quot;)
        }

        // 내부 저장 프로퍼티 생성
        let internalPropertyCode = &quot;&quot;&quot;
        private var _\(variableName): \(type) = \(variableDecl.bindings.first?.initializer?.value.description ?? &quot;\(type)()&quot;)
        &quot;&quot;&quot;

        // 문자열을 DeclSyntax로 변환
        return [DeclSyntax(stringLiteral: internalPropertyCode)]
    }

    // AccessorMacro: Getter, Setter 추가
    public static func expansion(
        of node: AttributeSyntax,
        providingAccessorsOf declaration: some DeclSyntaxProtocol,
        in context: some MacroExpansionContext
    ) -&amp;gt; [AccessorDeclSyntax] {
        guard let variableDecl = declaration.as(VariableDeclSyntax.self) else {
            fatalError(&quot;이 매크로는 변수에만 적용할 수 있습니다.&quot;)
        }
        guard let variableName = variableDecl.bindings.first?.pattern.description else {
            fatalError(&quot;변수 이름을 확인할 수 없습니다.&quot;)
        }

        let getter = &quot;&quot;&quot;
        get {
            _\(variableName)
        }
        &quot;&quot;&quot;
        let setter = &quot;&quot;&quot;
        set {
            let oldValue = _name
            print(&quot;Setting \(variableName) from \\(oldValue) to \\(newValue)&quot;)
            _\(variableName) = newValue
        }
        &quot;&quot;&quot;

        return [
            AccessorDeclSyntax(stringLiteral: getter),
            AccessorDeclSyntax(stringLiteral: setter)
        ]
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;# 사용&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733581479714&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct User {
    @Logged
    var name: String
}

var user = User()
user.name = &quot;Bob&quot;   // Setting name from  to Bob
user.name = &quot;Alice&quot; // Setting name from Bob to Alice&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;# Expand Macro&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;스크린샷 2024-12-07 오후 11.25.25.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/720Nc/btsLa9qpOoi/0XpKeS31J8kKjYUQfMHWD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/720Nc/btsLa9qpOoi/0XpKeS31J8kKjYUQfMHWD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/720Nc/btsLa9qpOoi/0XpKeS31J8kKjYUQfMHWD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F720Nc%2FbtsLa9qpOoi%2F0XpKeS31J8kKjYUQfMHWD0%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;790&quot; height=&quot;314&quot; data-filename=&quot;스크린샷 2024-12-07 오후 11.25.25.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /Swift</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/673</guid>
      <comments>https://eunjin3786.tistory.com/673#entry673comment</comments>
      <pubDate>Sat, 7 Dec 2024 23:35:03 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] @isolated(any)</title>
      <link>https://eunjin3786.tistory.com/672</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;✓ &lt;a href=&quot;https://github.com/swiftlang/swift-evolution/blob/main/proposals/0431-isolated-any-functions.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SE-0431&lt;/a&gt; @isolated(any) Function Types&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;  Assisted&amp;nbsp;by&amp;nbsp;GPT&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;[1] &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;@isolated(any)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;@isolated(any) 은 swift 6 에 추가된 attribute 입니다. (function only)&lt;br /&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: 'Nanum Gothic';&quot;&gt;&lt;b&gt;임의의 (하지만 정적으로 알 수 없는) 격리를 가진 함수&lt;/b&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: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swift/task/init(priority:operation:)-7f0zv&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Task&lt;/a&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;&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/bJDdTe/btsKMmpHWhU/NDcKnAVKPChxlMDBI6AViK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJDdTe/btsKMmpHWhU/NDcKnAVKPChxlMDBI6AViK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;492&quot; data-filename=&quot;스크린샷 2024-11-15 오후 8.58.11.png&quot; width=&quot;690&quot; height=&quot;382&quot; data-widthpercent=&quot;50.73&quot; style=&quot;width: 50.138673%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJDdTe/btsKMmpHWhU/NDcKnAVKPChxlMDBI6AViK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJDdTe%2FbtsKMmpHWhU%2FNDcKnAVKPChxlMDBI6AViK%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;888&quot; height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nZsTx/btsKLqsZBlP/N0D0w3EjPXVzXWt0R1JKz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nZsTx/btsKLqsZBlP/N0D0w3EjPXVzXWt0R1JKz1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;494&quot; data-filename=&quot;스크린샷 2024-11-15 오후 9.03.50.png&quot; width=&quot;690&quot; height=&quot;394&quot; data-widthpercent=&quot;49.27&quot; style=&quot;width: 48.698537%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nZsTx/btsKLqsZBlP/N0D0w3EjPXVzXWt0R1JKz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnZsTx%2FbtsKLqsZBlP%2FN0D0w3EjPXVzXWt0R1JKz1%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;866&quot; height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;[2] &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;@isolated(any) &amp;nbsp;도입 배경&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Task 이니셜라이저는 () async throws -&amp;gt; () 타입의 불투명 값 (opaque value) 을 받습니다. 그러나 이 값에서 격리를 동적으로 복구할 수 없기 때문에, 이니셜라이저는 해당 작업을 global concurrent executor 에서 시작할 수밖에 없습니다. 만약 이니셜라이저에 전달된 함수가 실제로 특정 액터에 격리되어 있다면, 해당 함수는 진입 시 즉시 그 액터로 전환해야 합니다. 이 과정은 추가적인 동기화를 요구하며, 작업을 다시 일시 중단해야 할 수도 있습니다.&lt;br /&gt;&lt;br /&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;않는다는&amp;nbsp;것입니다.&amp;nbsp;이니셜라이저가&amp;nbsp;처음부터&amp;nbsp;올바른&amp;nbsp;executor&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;br /&gt;&lt;br /&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;함수를&amp;nbsp;표현할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;타입을&amp;nbsp;추가하는&amp;nbsp;것입니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;표준 라이브러리의 모든 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;task-creation&lt;/span&gt; API는 @isolated(any) function 을 받아들이도록 업데이트 되었으며, 새 작업 (task) 를 적절한 executor 에 동기적으로 추가하게 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[3] &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;@isolated(any)&amp;nbsp; 사용하기&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;함수가 임의의 컨텍스트에서 호출될 때, 항상 isolation boundary 를 넘는다고 가정해야 합니다.&lt;/span&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: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;그래서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;비동기적으로 해당 함수를 호출해야 합니다. &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731673008817&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func doSomething(_ operation: @isolated(any) () -&amp;gt; Void) async {
    await operation()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[4] isolation property&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;@isolated(any) 함수 타입의 값은 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;isolation 이라는 &lt;/span&gt;특별한 프로퍼티를 가집니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 프로퍼티는 read-only 전용이며, 타입은 (any Actor)? 입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 값은 함수 값의 동적 격리 (&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;dynamic&amp;nbsp;isolation&lt;/span&gt;) 에 의해 결정됩니다.&amp;nbsp;&lt;/span&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;pre id=&quot;code_1731673862652&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func doSomething(_ operation: @isolated(any) () -&amp;gt; Void) async {
    let isolation = operation.isolation
    print(isolation) // ex. Optional(Swift.MainActor)
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 만약 함수가 동적으로 비격리(non-isolated) 상태라면, 격리 값은 nil입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 만약 함수가 동적으로 글로벌 액터 타입 G에 격리되어 있다면, 격리 값은 G.shared입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 만약 함수가 동적으로 특정 액터 참조에 격리되어 있다면, 격리 값은 해당 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: left;&quot;&gt;actor reference &lt;/span&gt;입니다.&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_1731678529207&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Foundation

func doSomething(_ operation: @Sendable @escaping @isolated(any) () -&amp;gt; Void) {
    print(operation.isolation)
    
    Task {
        print(operation.isolation)
        await operation()
    }
}

doSomething {
    // 출력 nil
}

doSomething { @MainActor in
    // 출력 Optional(Swift.MainActor)
}

doSomething { @MyGlobalActor in
    // 출력 Optional(__lldb_expr_631.MyGlobalActor)
}
    
@globalActor
actor MyGlobalActor {
    static let shared = MyGlobalActor()
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[5] 추후 계획&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;@isolated(any)&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;호출이&amp;nbsp;비동기적으로&amp;nbsp;처리되어야&amp;nbsp;한다는&amp;nbsp;것을&amp;nbsp;의미하며,&amp;nbsp;함수가&amp;nbsp;동기적이라&amp;nbsp;하더라도&amp;nbsp;await가&amp;nbsp;필요하고,&amp;nbsp;인수와&amp;nbsp;결과는&amp;nbsp;격리&amp;nbsp;경계를&amp;nbsp;넘는&amp;nbsp;호출에&amp;nbsp;대한&amp;nbsp;일반적인&amp;nbsp;송수신&amp;nbsp;가능성(sendability)&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;@isolated(any)&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;@isolated(any)&amp;nbsp;매개변수의&amp;nbsp;격리는&amp;nbsp;반드시&amp;nbsp;불투명&amp;nbsp;값(opaque&amp;nbsp;value)이기&amp;nbsp;때문에,&amp;nbsp;호출자가&amp;nbsp;함수와&amp;nbsp;동일한&amp;nbsp;격리&amp;nbsp;상태를&amp;nbsp;가져야&amp;nbsp;하는데,&amp;nbsp;이는&amp;nbsp;호출자가&amp;nbsp;값별&amp;nbsp;격리(value-specific&amp;nbsp;isolation)로&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&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;다음 규칙들은 @isolated(any)가 향후 언어 지원과 어떻게 상호작용할지에 대해 설명합니다. 이 규칙들을 제시하기 위해, 이 제안서는 클로저 격리 제어 구상에서 제안된 구문을 사용합니다. 이 구문에서 캡처 목록의 항목에 isolated를 추가하면 클로저가 그 값에 격리되도록 합니다.&amp;nbsp;&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_1731674239265&quot; class=&quot;swift&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;func delay(operation: @isolated(any) () -&amp;gt; ()) {
  let isolation = operation.isolation
  Task { [isolated isolation] in // &amp;lt;-- tentative syntax from the isolated captures pitch
    print(&quot;waking&quot;)
    operation() // &amp;lt;-- does not cross an isolation barrier and so is synchronous
    print(&quot;finished&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: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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: 'Nanum Gothic';&quot;&gt;[참고]&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위 proposal 의 &lt;a href=&quot;https://github.com/swiftlang/swift-evolution/blob/main/proposals/0431-isolated-any-functions.md#motivation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Motivation&lt;/a&gt; 쪽에 나오는 내용인데, 같이 읽으면 더 개념이 잡힘.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;# 함수의 3가지 격리 (isolation) 유형&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Swift에서 함수 선언과 클로저는 세 가지 형태의 actor 격리를 지원합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;&lt;b&gt;1) 비격리(non-isolated)&lt;/b&gt;&lt;br /&gt;함수나&amp;nbsp;클로저가&amp;nbsp;특정&amp;nbsp;actor에&amp;nbsp;격리되지&amp;nbsp;않고,&amp;nbsp;동시성&amp;nbsp;제약&amp;nbsp;없이&amp;nbsp;동작하는&amp;nbsp;경우를&amp;nbsp;의미합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2) &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;글로벌&amp;nbsp;actor&amp;nbsp;격리&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;함수나&amp;nbsp;클로저가&amp;nbsp;특정&amp;nbsp;글로벌&amp;nbsp;actor(예:&amp;nbsp;@MainActor)에&amp;nbsp;격리되어&amp;nbsp;해당&amp;nbsp;actor의&amp;nbsp;컨텍스트&amp;nbsp;내에서만&amp;nbsp;실행되도록&amp;nbsp;제한됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3) 특정 매개변수(parameter) 또는 캡처된 값(captured value)에 격리&lt;/b&gt;&lt;br /&gt;함수나&amp;nbsp;클로저가&amp;nbsp;전달받은&amp;nbsp;특정&amp;nbsp;매개변수&amp;nbsp;또는&amp;nbsp;클로저가&amp;nbsp;캡처한&amp;nbsp;값(예:&amp;nbsp;self)에&amp;nbsp;대해&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;3번은 이런 예제를 말함.&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: 'Nanum Gothic';&quot;&gt;&lt;i&gt;함수는 명시적으로 특정 매개변수를 isolated로 선언하여 그 매개변수에 스스로를 격리시킬 수 있습니다. 이런 격리는 actor 메서드에서 self 매개변수에도 암묵적으로 적용됩니다 (다른 격리를 명시적으로 사용하지 않은 경우).&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-15 오후 6.34.58.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;950&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCxfrh/btsKLqsQoOP/Ev1cSWZtE8DibLEtYhsSx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCxfrh/btsKLqsQoOP/Ev1cSWZtE8DibLEtYhsSx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCxfrh/btsKLqsQoOP/Ev1cSWZtE8DibLEtYhsSx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCxfrh%2FbtsKLqsQoOP%2FEv1cSWZtE8DibLEtYhsSx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;601&quot; data-filename=&quot;스크린샷 2024-11-15 오후 6.34.58.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;950&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;# &amp;nbsp;한계&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;function 이 직접 호출 될 때 (&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;called directly) &lt;/span&gt;, Swift 의 isolation checker 는 해당 함수의 isolation 을 정확히 분석하고 호출 컨텍스트의 격리와 비교할 수 있습니다. &amp;nbsp;그러나&amp;nbsp;호출&amp;nbsp;표현식이&amp;nbsp;함수&amp;nbsp;타입의&amp;nbsp;불투명&amp;nbsp;값(opaque&amp;nbsp;value)을&amp;nbsp;호출하는&amp;nbsp;경우,&amp;nbsp;Swift는&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&amp;nbsp;타입시스템으로 표현 가능 &amp;nbsp;&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: 'Nanum Gothic';&quot;&gt;- A function type with no isolation specifiers, such as () -&amp;gt; Int, represents a non-isolated function.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- A function type with a global actor attribute, such as @MainActor () -&amp;gt; Int, represents a function that's isolated to that global actor.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- A function type with an isolated parameter, such as (isolated MyActor) - &amp;gt; Int, represents a function that's isolated to that parameter.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 클로저가 캡처한 값 중 하나에 대해 격리될 수 있다는 것&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아래 예제에서 클로저는 캡처한 self 값에 대해 격리됩니다.&lt;/span&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;pre id=&quot;code_1731671200294&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;actor WorldModelObject {

  ...
  
  func updateLater() {
    Task {
      // This closure doesn't have an explicit isolation
      // specification, and it's being passed to the `Task`
      // initializer, so it will be inferred to have the same
      // isolation as its enclosing context.  The enclosing
      // context is isolated to its `self` parameter, which this
      // closure captures, so this closure will also be isolated
      // that value.
      
      // 이 클로저는 명시적인 격리 지정이 없습니다.
      // 그리고 이 클로저는 `Task`에  전달되고 있으므로,
      // 자신을 둘러싼 컨텍스트와 동일한 격리를 갖는 것으로 추론됩니다.
      // 둘러싼 컨텍스트는 `self` 매개변수에 격리되어 있으며,
      // 이 클로저가 이를 캡처하고 있으므로, 이 클로저 역시
      // 해당 값에 격리됩니다.
      self.update()
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;upcoming&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://forums.swift.org/t/closure-isolation-control/70378&quot;&gt;closure isolation control&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&amp;nbsp;proposal 에서는 이 개념이 훨씬 더 중요해질 것으로 예상됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start; font-family: 'Nanum Gothic';&quot;&gt;이 제안에 따르면, 격리된 캡처는 특정 코드 조각의 격리를 제어하는 강력한 일반 도구가 될 것입니다. 하지만 여전히 해당 클로저의 격리를 타입 시스템으로 표현할 방법은 제공되지 않을 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[ 추천 ]&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- &amp;nbsp;&lt;a href=&quot;https://enchanting-marquess-33b.notion.site/SE-0431-isolated-any-Function-Types-108af9d7e51e806589e5dc0a9b5362af&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;노션&lt;/a&gt;&amp;nbsp; (위의 proposal 을 쉽게 번역+정리 해주신 링크 발견 ✨)&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: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /Swift</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/672</guid>
      <comments>https://eunjin3786.tistory.com/672#entry672comment</comments>
      <pubDate>Fri, 15 Nov 2024 21:51:03 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] @unchecked, @preconcurrency, @retroactive</title>
      <link>https://eunjin3786.tistory.com/670</link>
      <description>&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: 'Nanum Gothic';&quot;&gt;- &lt;a href=&quot;https://eunjin3786.tistory.com/572&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Swift] Sendable&lt;/a&gt; 을 먼저 읽고 오시길 추천드립니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- Swift 6 기반의 글입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[1] @unchecked&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;GPT 의 설명&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: 'Nanum Gothic';&quot;&gt;Swift에서는 Sendable이라는 프로토콜을 통해 값이 스레드 간 안전하게 공유될 수 있는지를 나타내는데, 기본적으로 Sendable 프로토콜을 구현한 타입은 스레드 안전해야 합니다. 하지만, @unchecked를 사용하면 이 안전성 검사를 생략하고 강제로 Sendable을 준수하는 것으로 표시할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;예를&amp;nbsp;들어,&amp;nbsp;@unchecked&amp;nbsp;Sendable을&amp;nbsp;사용하면&amp;nbsp;Swift&amp;nbsp;컴파일러는&amp;nbsp;이&amp;nbsp;타입을&amp;nbsp;Sendable로&amp;nbsp;인식하지만,&amp;nbsp;실제로는&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730816629979&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct SomeType: @unchecked Sendable {
    // 스레드 안전하지 않은 코드가 포함될 수 있음
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;이&amp;nbsp;속성은&amp;nbsp;타입이&amp;nbsp;Sendable&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&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;문제는&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;공식 문서&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;# &lt;a href=&quot;https://developer.apple.com/documentation/swift/sendable&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Sendable&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;To declare conformance to Sendable without any compiler enforcement, write @unchecked Sendable. You are responsible for the correctness of unchecked sendable types, for example, by protecting all access to its state with a lock or a queue&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Q. enum, struct, class 말고 function이나 closure 에도 적용할 수 있나 ? &amp;nbsp;-&amp;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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.25.16.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7ltQi/btsKy070VGm/0UdqKllbWJhKjCyHgkS9nK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7ltQi/btsKy070VGm/0UdqKllbWJhKjCyHgkS9nK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7ltQi/btsKy070VGm/0UdqKllbWJhKjCyHgkS9nK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7ltQi%2FbtsKy070VGm%2F0UdqKllbWJhKjCyHgkS9nK%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;554&quot; height=&quot;156&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.25.16.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.26.36.png&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcm05c/btsKyd7RzR6/61kTVmEqO30GJiihjjpdWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcm05c/btsKyd7RzR6/61kTVmEqO30GJiihjjpdWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcm05c/btsKyd7RzR6/61kTVmEqO30GJiihjjpdWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcm05c%2FbtsKyd7RzR6%2F61kTVmEqO30GJiihjjpdWK%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;743&quot; height=&quot;95&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.26.36.png&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.26.50.png&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brUqU6/btsKyU05oTj/dsz3kug4D9wTtLVQOt7lWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brUqU6/btsKyU05oTj/dsz3kug4D9wTtLVQOt7lWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brUqU6/btsKyU05oTj/dsz3kug4D9wTtLVQOt7lWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrUqU6%2FbtsKyU05oTj%2Fdsz3kug4D9wTtLVQOt7lWk%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;758&quot; height=&quot;98&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.26.50.png&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;188&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: 'Nanum Gothic';&quot;&gt;Q. protocol 은 ? -&amp;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;스크린샷 2024-11-05 오후 11.42.56.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HtHSl/btsKwyk64kt/bPs2kTxrRBddpKscxazMM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HtHSl/btsKwyk64kt/bPs2kTxrRBddpKscxazMM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HtHSl/btsKwyk64kt/bPs2kTxrRBddpKscxazMM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHtHSl%2FbtsKwyk64kt%2FbPs2kTxrRBddpKscxazMM0%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;1424&quot; height=&quot;150&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.42.56.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.43.54.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CvqGR/btsKwtxDncl/MOkN2x3tn4U7SdJPR2LILK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CvqGR/btsKwtxDncl/MOkN2x3tn4U7SdJPR2LILK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CvqGR/btsKwtxDncl/MOkN2x3tn4U7SdJPR2LILK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCvqGR%2FbtsKwtxDncl%2FMOkN2x3tn4U7SdJPR2LILK%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;1440&quot; height=&quot;174&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.43.54.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;174&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: 'Nanum Gothic';&quot;&gt;대신 이런 식으로는 에러 해결할 수 있다. (좋은 방법 X)&amp;nbsp;&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;스크린샷 2024-11-05 오후 11.55.02.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JgG37/btsKxJ63Qme/JtTp0XnJUSARENk4huOX7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JgG37/btsKxJ63Qme/JtTp0XnJUSARENk4huOX7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JgG37/btsKxJ63Qme/JtTp0XnJUSARENk4huOX7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJgG37%2FbtsKxJ63Qme%2FJtTp0XnJUSARENk4huOX7k%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;1456&quot; height=&quot;296&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.55.02.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.55.14.png&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZd3Te/btsKw4DQlsf/YYJoMgmWB0FCkbEY3rKB2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZd3Te/btsKw4DQlsf/YYJoMgmWB0FCkbEY3rKB2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZd3Te/btsKw4DQlsf/YYJoMgmWB0FCkbEY3rKB2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZd3Te%2FbtsKw4DQlsf%2FYYJoMgmWB0FCkbEY3rKB2K%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;1458&quot; height=&quot;288&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.55.14.png&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Q. 암묵적으로 Sendable 을 conform 하고 있는 actor 에도 쓸 수 있나 ? -&amp;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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.28.53.png&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2yuk2/btsKxjViArs/I0Yb6xmWjhzUD5Hv0fYxkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2yuk2/btsKxjViArs/I0Yb6xmWjhzUD5Hv0fYxkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2yuk2/btsKxjViArs/I0Yb6xmWjhzUD5Hv0fYxkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2yuk2%2FbtsKxjViArs%2FI0Yb6xmWjhzUD5Hv0fYxkk%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;596&quot; height=&quot;127&quot; data-filename=&quot;스크린샷 2024-11-05 오후 11.28.53.png&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;174&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;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[2] @preconcurrency&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;GPT 의 설명&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: 'Nanum Gothic';&quot;&gt;@preconcurrency는&amp;nbsp;기존&amp;nbsp;비동기&amp;nbsp;코드가&amp;nbsp;도입된&amp;nbsp;코드가&amp;nbsp;새로운&amp;nbsp;concurrency&amp;nbsp;시스템과&amp;nbsp;호환되도록&amp;nbsp;강제하는&amp;nbsp;데&amp;nbsp;사용됩니다.&amp;nbsp;이&amp;nbsp;속성은&amp;nbsp;특히&amp;nbsp;이전&amp;nbsp;버전의&amp;nbsp;코드에서&amp;nbsp;Sendable이나&amp;nbsp;다른&amp;nbsp;concurrency&amp;nbsp;관련&amp;nbsp;검사를&amp;nbsp;피하기&amp;nbsp;위해&amp;nbsp;사용되며,&amp;nbsp;기존&amp;nbsp;코드가&amp;nbsp;새로운&amp;nbsp;concurrency&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;예를 들어, @preconcurrency는 concurrency 시스템이 없던 Swift 5.5 이전에 작성된 코드에서 사용할 수 있으며, 해당 코드가 concurrency 관련 새로운 규칙을 준수하지 않아도 되도록 허용합니다.&lt;br /&gt;&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_1730817251316&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@preconcurrency protocol OldProtocol {
    // concurrency가 없던 시점의 프로토콜 정의
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd; font-family: 'Nanum Gothic';&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;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#&amp;nbsp;&lt;a href=&quot;https://www.swift.org/migration/documentation/swift-6-concurrency-migration-guide/commonproblems#Sendable-Types&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;swift-6-concurrency-migration-guide&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;You&amp;nbsp;can&amp;nbsp;stage&amp;nbsp;in&amp;nbsp;diagnostics&amp;nbsp;caused&amp;nbsp;by&amp;nbsp;adding&amp;nbsp;global&amp;nbsp;actor&amp;nbsp;isolation&amp;nbsp;on&amp;nbsp;a&amp;nbsp;protocol&amp;nbsp;using&amp;nbsp;@preconcurrency.&amp;nbsp;This&amp;nbsp;will&amp;nbsp;preserve&amp;nbsp;source&amp;nbsp;compatibility&amp;nbsp;with&amp;nbsp;clients&amp;nbsp;that&amp;nbsp;have&amp;nbsp;not&amp;nbsp;yet&amp;nbsp;begun&amp;nbsp;adopting&amp;nbsp;concurrency.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730820057448&quot; class=&quot;less&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@preconcurrency @MainActor
protocol Styler {
    func applyStyle()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;#&amp;nbsp;&lt;a href=&quot;https://github.com/swiftlang/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SE-0337 Incremental migration to concurrency checking&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;When&amp;nbsp;applied&amp;nbsp;to&amp;nbsp;a&amp;nbsp;nominal&amp;nbsp;declaration,&amp;nbsp;the&amp;nbsp;@preconcurrency&amp;nbsp;attribute&amp;nbsp;indicates&amp;nbsp;that&amp;nbsp;a&amp;nbsp;declaration&amp;nbsp;existed&amp;nbsp;before&amp;nbsp;the&amp;nbsp;module&amp;nbsp;it&amp;nbsp;belongs&amp;nbsp;to&amp;nbsp;fully&amp;nbsp;adopted&amp;nbsp;concurrency,&amp;nbsp;so&amp;nbsp;the&amp;nbsp;compiler&amp;nbsp;should&amp;nbsp;take&amp;nbsp;steps&amp;nbsp;to&amp;nbsp;avoid&amp;nbsp;these&amp;nbsp;source&amp;nbsp;and&amp;nbsp;ABI&amp;nbsp;breaks.&lt;span style=&quot;background-color: #f3c000;&quot;&gt;&amp;nbsp;It&amp;nbsp;can&amp;nbsp;be&amp;nbsp;applied&amp;nbsp;to&amp;nbsp;any&amp;nbsp;enum,&amp;nbsp;enum&amp;nbsp;case,&amp;nbsp;struct,&amp;nbsp;class,&amp;nbsp;actor,&amp;nbsp;protocol,&amp;nbsp;var,&amp;nbsp;let,&amp;nbsp;subscript,&amp;nbsp;init&amp;nbsp;or&amp;nbsp;func&amp;nbsp;declaration.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.50.53.png&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ISHtI/btsKxLw13xt/11KdgC9zLptYLbXMKfn89K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ISHtI/btsKxLw13xt/11KdgC9zLptYLbXMKfn89K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ISHtI/btsKxLw13xt/11KdgC9zLptYLbXMKfn89K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FISHtI%2FbtsKxLw13xt%2F11KdgC9zLptYLbXMKfn89K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;298&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.50.53.png&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;508&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: 'Nanum Gothic';&quot;&gt;&lt;i&gt;단순히 modern concurrency 준비 안된 외부 모듈 import 할 때만 쓰는 거라고 알고 있었는데,&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;i&gt;import 문 외에도 쓸 수 있다는 것을 최근 알았다 ;;;; &amp;nbsp;actor 에도 붙일 수 있다니...&lt;/i&gt;&lt;/span&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;SomeStruct 를 Sendable 로 만들고 싶지만, 내부 필드가 Sendable 준비가 안된 상태 일 때&amp;nbsp;&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;스크린샷 2024-11-06 오전 12.01.40.png&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ruAed/btsKwvop7Z6/Bc6kAQgZtRy1TBoDF0iNC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ruAed/btsKwvop7Z6/Bc6kAQgZtRy1TBoDF0iNC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ruAed/btsKwvop7Z6/Bc6kAQgZtRy1TBoDF0iNC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FruAed%2FbtsKwvop7Z6%2FBc6kAQgZtRy1TBoDF0iNC0%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;1444&quot; height=&quot;494&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.01.40.png&quot; data-origin-width=&quot;1444&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;&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: 'Nanum Gothic';&quot;&gt;@preconcurrency 를 붙여 에러를 워닝으로 전환가능&amp;nbsp;&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;스크린샷 2024-11-06 오전 12.03.32.png&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FenBs/btsKyTukqYU/NXV3VRWuEnkoo0HntFJil0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FenBs/btsKyTukqYU/NXV3VRWuEnkoo0HntFJil0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FenBs/btsKyTukqYU/NXV3VRWuEnkoo0HntFJil0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFenBs%2FbtsKyTukqYU%2FNXV3VRWuEnkoo0HntFJil0%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;1460&quot; height=&quot;510&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.03.32.png&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;510&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: 'Nanum Gothic';&quot;&gt;만약 @unchecked 쓰면 워닝도 안나오게 할 수 있음&amp;nbsp;&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;스크린샷 2024-11-06 오전 12.04.43.png&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OAv5k/btsKyVloESm/vSkZvBSo0xCJCXCv8izKa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OAv5k/btsKyVloESm/vSkZvBSo0xCJCXCv8izKa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OAv5k/btsKyVloESm/vSkZvBSo0xCJCXCv8izKa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOAv5k%2FbtsKyVloESm%2FvSkZvBSo0xCJCXCv8izKa1%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;1402&quot; height=&quot;366&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.04.43.png&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;366&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이런 혼합도 문법적으로는 가능하고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.06.46.png&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFQ0A1/btsKyqe6hir/VjTeXkO7nleKB6z07ctiyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFQ0A1/btsKyqe6hir/VjTeXkO7nleKB6z07ctiyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFQ0A1/btsKyqe6hir/VjTeXkO7nleKB6z07ctiyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFQ0A1%2FbtsKyqe6hir%2FVjTeXkO7nleKB6z07ctiyk%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;1236&quot; height=&quot;350&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.06.46.png&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;350&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: 'Nanum Gothic';&quot;&gt;@preconcurrency 를 뒤로 보내면 워닝을 볼 수 있다.&amp;nbsp;&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;스크린샷 2024-11-06 오전 12.08.49.png&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YiSnE/btsKzd0pCuC/rjAi7URmMiHwfjKvpnazt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YiSnE/btsKzd0pCuC/rjAi7URmMiHwfjKvpnazt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YiSnE/btsKzd0pCuC/rjAi7URmMiHwfjKvpnazt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYiSnE%2FbtsKzd0pCuC%2FrjAi7URmMiHwfjKvpnazt1%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;1436&quot; height=&quot;248&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.08.49.png&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;248&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: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;actor 에 @preconcurrency 붙였을 때도, 위와 동일한 워닝이 발생해야할 것 같아서 테스트해보니&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;@preconcurrency 위치에 따라 워닝 여부가 다르다 ;;&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;스크린샷 2024-11-06 오전 12.59.41.png&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M8nrF/btsKwC18WQ7/nZHdgCv6gxK7K69p7TsMt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M8nrF/btsKwC18WQ7/nZHdgCv6gxK7K69p7TsMt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M8nrF/btsKwC18WQ7/nZHdgCv6gxK7K69p7TsMt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM8nrF%2FbtsKwC18WQ7%2FnZHdgCv6gxK7K69p7TsMt0%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;1466&quot; height=&quot;246&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.59.41.png&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.59.55.png&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJP40T/btsKwzdjs1j/MWJ8GPQCwljZlUg04d9Qr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJP40T/btsKwzdjs1j/MWJ8GPQCwljZlUg04d9Qr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJP40T/btsKwzdjs1j/MWJ8GPQCwljZlUg04d9Qr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJP40T%2FbtsKwzdjs1j%2FMWJ8GPQCwljZlUg04d9Qr0%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;1056&quot; height=&quot;154&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.59.55.png&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start; background-color: #dddddd;&quot;&gt;@unchecked vs &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;@preconcurrency (GPT)&amp;nbsp;&lt;/span&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;color: #000000; text-align: start; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;- @unchecked는 타입이 스레드 안전성을 검사하지 않고 Sendable을 준수한다고 선언할 때 사용합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;- @preconcurrency는 concurrency 이전에 작성된 코드와 호환성을 유지하면서 concurrency 검사를 피하고자 할 때 사용합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;두&amp;nbsp;속성&amp;nbsp;모두&amp;nbsp;concurrency와&amp;nbsp;관련이&amp;nbsp;있지만,&amp;nbsp;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;@unchecked는&amp;nbsp;주로&amp;nbsp;스레드&amp;nbsp;안전성&amp;nbsp;검사&amp;nbsp;생략에,&amp;nbsp;@preconcurrency는&amp;nbsp;기존&amp;nbsp;코드와의&amp;nbsp;호환성&amp;nbsp;유지에&amp;nbsp;사용된다&lt;/span&gt;는&amp;nbsp;점에서&amp;nbsp;차이가&amp;nbsp;있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;+&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;헷갈리면 안되는게 '호환'이라고 해서 문제 없이 동작한다는 게 아니라 컴파일 에러가 안생긴다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;예를들어 아래 예제에서 CaffeineThresholdDelegate 앞에 @preconcurrency 를 붙여서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;'이 메소드도 메인액터에서 격리된다' 고 가정했는데 아니라면 문제가 발생한다.&amp;nbsp;&lt;br /&gt;&lt;br /&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rBd7b/btsKyktHX3b/6ymRdyx85yh3ua4mfQjdZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rBd7b/btsKyktHX3b/6ymRdyx85yh3ua4mfQjdZk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1505&quot; data-origin-height=&quot;919&quot; data-filename=&quot;스크린샷 2024-11-07 오전 12.09.43.png&quot; data-widthpercent=&quot;47.26&quot; style=&quot;width: 46.708248%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rBd7b/btsKyktHX3b/6ymRdyx85yh3ua4mfQjdZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrBd7b%2FbtsKyktHX3b%2F6ymRdyx85yh3ua4mfQjdZk%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;1505&quot; height=&quot;919&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cltPPn/btsKyJUgRsT/vvdQYKJh7bkbn6JT9XHGYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cltPPn/btsKyJUgRsT/vvdQYKJh7bkbn6JT9XHGYk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;859&quot; data-filename=&quot;스크린샷 2024-11-07 오전 12.09.51.png&quot; data-widthpercent=&quot;52.74&quot; style=&quot;width: 52.128962%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cltPPn/btsKyJUgRsT/vvdQYKJh7bkbn6JT9XHGYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcltPPn%2FbtsKyJUgRsT%2FvvdQYKJh7bkbn6JT9XHGYk%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;1570&quot; height=&quot;859&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;https://developer.apple.com/videos/play/wwdc2024/10169&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[3] @retroactive&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd; font-family: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;# &amp;nbsp;&lt;a href=&quot;https://www.swift.org/migration/documentation/swift-6-concurrency-migration-guide/commonproblems#Retroactive-Sendable-Conformance&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Retroactive Sendable Conformance (swift 6 migration guide)&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Your&amp;nbsp;dependencies&amp;nbsp;may&amp;nbsp;also&amp;nbsp;expose&amp;nbsp;types&amp;nbsp;that&amp;nbsp;are&amp;nbsp;using&amp;nbsp;manual&amp;nbsp;synchronization.&amp;nbsp;This&amp;nbsp;is&amp;nbsp;usually&amp;nbsp;visible&amp;nbsp;only&amp;nbsp;via&amp;nbsp;documentation.&amp;nbsp;It&amp;nbsp;is&amp;nbsp;possible&amp;nbsp;to&amp;nbsp;add&amp;nbsp;an&amp;nbsp;@unchecked&amp;nbsp;Sendable&amp;nbsp;conformance&amp;nbsp;in&amp;nbsp;this&amp;nbsp;case&amp;nbsp;as&amp;nbsp;well.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730821013100&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension ColorComponents: @retroactive @unchecked Sendable {
}&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: 'Nanum Gothic';&quot;&gt;# &lt;a href=&quot;https://github.com/swiftlang/swift-evolution/blob/main/proposals/0364-retroactive-conformance-warning.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SE-0364 Warning&amp;nbsp;for&amp;nbsp;Retroactive&amp;nbsp;Conformances&amp;nbsp;of&amp;nbsp;External&amp;nbsp;Types&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;외부 모듈에서 가져온 타입을 extension 해서 쓰는 경우가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730820403459&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Not a great implementation, but I suppose it could be useful.
extension Date: Identifiable {
    public var id: TimeInterval { timeIntervalSince1970 }
}&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: 'Nanum Gothic';&quot;&gt;이는 여러 사이드이펙 가능성을 안고 있다. 예를들어 Foundation 이 나중에 id 를 제공하면 빌드가 실패할 것, Foundation 에서 제공하는 &amp;nbsp;id (다른 구현)을 선택했는데, &amp;nbsp;이미 DB 에 저장된 ID 가 있다면 ID 는 다른 값이 되는 등.. &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: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;부득이한 경우, 클라이언트는 @retroactive attribute 를 프로토콜에 추가하여 이 경고를 무시할 수 있다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd; font-family: 'Nanum Gothic';&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: 'Nanum Gothic';&quot;&gt;Q. 내부 모듈 extension 에 쓰면 에러나 워닝 발생하는가 ? -&amp;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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.40.09.png&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxkwu0/btsKyksqBkK/bgJsu8cLY2Bo8rpzfrRXlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxkwu0/btsKyksqBkK/bgJsu8cLY2Bo8rpzfrRXlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxkwu0/btsKyksqBkK/bgJsu8cLY2Bo8rpzfrRXlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxkwu0%2FbtsKyksqBkK%2FbgJsu8cLY2Bo8rpzfrRXlK%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;1430&quot; height=&quot;422&quot; data-filename=&quot;스크린샷 2024-11-06 오전 12.40.09.png&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /Swift</category>
      <author>eungding</author>
      <guid isPermaLink="true">https://eunjin3786.tistory.com/670</guid>
      <comments>https://eunjin3786.tistory.com/670#entry670comment</comments>
      <pubDate>Wed, 6 Nov 2024 01:04:55 +0900</pubDate>
    </item>
  </channel>
</rss>