<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>lgvv98</title>
    <link>https://rldd.tistory.com/</link>
    <description>iOS, Swift, UIKit, SwiftUI</description>
    <language>ko</language>
    <pubDate>Sat, 20 Jun 2026 20:24:14 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>lgvv</managingEditor>
    <image>
      <title>lgvv98</title>
      <url>https://tistory1.daumcdn.net/tistory/4492386/attach/290c903c478048eca8cef02346d8b71b</url>
      <link>https://rldd.tistory.com</link>
    </image>
    <item>
      <title>SwiftUI의 새로운 기능 (Explore related documentation, sample code, and more) -wwdc25</title>
      <link>https://rldd.tistory.com/803</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;SwiftUI의 새로운 기능 (Explore related documentation, sample code, and more)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SwiftUI의 저성능 관련 개선사항부터 UI상의 버튼에 이르기까지 시스템 전반에 걸쳐 개선&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;스크린샷 2026-05-02 오후 4.31.12.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m7C1E/dJMcabcXurO/FpTVLMYnaGv7Aa663kqsWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m7C1E/dJMcabcXurO/FpTVLMYnaGv7Aa663kqsWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m7C1E/dJMcabcXurO/FpTVLMYnaGv7Aa663kqsWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm7C1E%2FdJMcabcXurO%2FFpTVLMYnaGv7Aa663kqsWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;732&quot; data-filename=&quot;스크린샷 2026-05-02 오후 4.31.12.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;목차&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Make the new design shine&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 모든 시스템 기능 리뷰&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Framework foundations
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성능과 애니메이션, 레이아웃 관련 개선사항 공유&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SwiftUI across the system
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple 플랫폼 전반에 걸쳐 SwiftUI를 사용할 수 있는 새로운 환경 소개&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Expand SwiftUI views
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 콘텐츠, 리치 텍스트 지원 등 SwiftUI 뷰의 새 기능에 대해 알아볼 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Make the new design shine&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네비게이션, 버튼, 탭바, macOS, PadOS 등 디자인이 변경되었음&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;스크린샷 2026-05-02 오후 10.41.47.png&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nfjtn/dJMcaib820l/yQJzkerOy3YhYAkssyWKKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nfjtn/dJMcaib820l/yQJzkerOy3YhYAkssyWKKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nfjtn/dJMcaib820l/yQJzkerOy3YhYAkssyWKKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNfjtn%2FdJMcaib820l%2FyQJzkerOy3YhYAkssyWKKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1850&quot; height=&quot;900&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.41.47.png&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;900&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;아이템 간 간격은 toolbar의 Spacer API를 사용해 조정.&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;스크린샷 2026-05-02 오후 10.43.53.png&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXpERZ/dJMcaaZryaX/mAWcOF59xV9kkt7lzgy7E0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXpERZ/dJMcaaZryaX/mAWcOF59xV9kkt7lzgy7E0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXpERZ/dJMcaaZryaX/mAWcOF59xV9kkt7lzgy7E0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXpERZ%2FdJMcaaZryaX%2FmAWcOF59xV9kkt7lzgy7E0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1808&quot; height=&quot;852&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.43.53.png&quot; data-origin-width=&quot;1808&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;Liquid Glass는 색조를 지원하기도 하는데 스타일과 색상을 적용하면 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;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/bokvKj/dJMcaciFRgv/IkyEvlKUKqgJQGZ0hBA1KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bokvKj/dJMcaciFRgv/IkyEvlKUKqgJQGZ0hBA1KK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;642&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.47.36.png&quot; style=&quot;width: 46.3836%; margin-right: 10px;&quot; data-widthpercent=&quot;46.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bokvKj/dJMcaciFRgv/IkyEvlKUKqgJQGZ0hBA1KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbokvKj%2FdJMcaciFRgv%2FIkyEvlKUKqgJQGZ0hBA1KK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1194&quot; height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RHGsM/dJMcad2TylY/B3OVZ1OkzPm3bDJK8in8O1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RHGsM/dJMcad2TylY/B3OVZ1OkzPm3bDJK8in8O1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1712&quot; data-origin-height=&quot;814&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.47.22.png&quot; data-widthpercent=&quot;53.07&quot; style=&quot;width: 52.4536%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RHGsM/dJMcad2TylY/B3OVZ1OkzPm3bDJK8in8O1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRHGsM%2FdJMcad2TylY%2FB3OVZ1OkzPm3bDJK8in8O1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1712&quot; height=&quot;814&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;/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/zKP0G/dJMcabD39sC/i8Kscgee5nOrT9qtpkGjs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zKP0G/dJMcabD39sC/i8Kscgee5nOrT9qtpkGjs1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;1048&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.49.02.png&quot; style=&quot;width: 41.7105%; margin-right: 10px;&quot; data-widthpercent=&quot;42.2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zKP0G/dJMcabD39sC/i8Kscgee5nOrT9qtpkGjs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzKP0G%2FdJMcabD39sC%2Fi8Kscgee5nOrT9qtpkGjs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1484&quot; height=&quot;1048&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8Pk6c/dJMcaiwotUg/43Z9L3B1K8xsKbB3IVsMD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8Pk6c/dJMcaiwotUg/43Z9L3B1K8xsKbB3IVsMD1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;924&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.48.28.png&quot; data-widthpercent=&quot;57.8&quot; style=&quot;width: 57.1267%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8Pk6c/dJMcaiwotUg/43Z9L3B1K8xsKbB3IVsMD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8Pk6c%2FdJMcaiwotUg%2F43Z9L3B1K8xsKbB3IVsMD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1792&quot; height=&quot;924&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;외관을 새롭게 하려면 role에 search를 두어 역할을 갖도록 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;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;스크린샷 2026-05-02 오후 10.50.33.png&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgo6ms/dJMcajaZn9G/iqL5KxxwSZjtlDMhubqhF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgo6ms/dJMcajaZn9G/iqL5KxxwSZjtlDMhubqhF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgo6ms/dJMcajaZn9G/iqL5KxxwSZjtlDMhubqhF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgo6ms%2FdJMcajaZn9G%2FiqL5KxxwSZjtlDMhubqhF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1832&quot; height=&quot;878&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.50.33.png&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;878&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;Liquid Glass는 커스텀하게도 사용할 수 있음.&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;스크린샷 2026-05-02 오후 10.52.35.png&quot; data-origin-width=&quot;1784&quot; data-origin-height=&quot;896&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkALr8/dJMcahxwH0c/vW8Pz8Mo7DW0FTMW9Zizf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkALr8/dJMcahxwH0c/vW8Pz8Mo7DW0FTMW9Zizf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkALr8/dJMcahxwH0c/vW8Pz8Mo7DW0FTMW9Zizf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkALr8%2FdJMcahxwH0c%2FvW8Pz8Mo7DW0FTMW9Zizf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1784&quot; height=&quot;896&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.52.35.png&quot; data-origin-width=&quot;1784&quot; data-origin-height=&quot;896&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;macOS에서 메뉴 막대 구축시 `.commands` API를 사용했었고, 아이패드에서도 같은 결과를 낼 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;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/bcIj1T/dJMcabqyfPn/8Y0pAoCz0xEw67u1unkEBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcIj1T/dJMcabqyfPn/8Y0pAoCz0xEw67u1unkEBK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1560&quot; data-origin-height=&quot;878&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.54.56.png&quot; style=&quot;width: 48.8351%; margin-right: 10px;&quot; data-widthpercent=&quot;49.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcIj1T/dJMcabqyfPn/8Y0pAoCz0xEw67u1unkEBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcIj1T%2FdJMcabqyfPn%2F8Y0pAoCz0xEw67u1unkEBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1560&quot; height=&quot;878&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl1hz8/dJMcadu2KmR/EpRHTJ80lOn3jlkuU2rdYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl1hz8/dJMcadu2KmR/EpRHTJ80lOn3jlkuU2rdYK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;874&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.54.42.png&quot; data-widthpercent=&quot;50.59&quot; style=&quot;width: 50.0021%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl1hz8/dJMcadu2KmR/EpRHTJ80lOn3jlkuU2rdYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl1hz8%2FdJMcadu2KmR%2FEpRHTJ80lOn3jlkuU2rdYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1590&quot; height=&quot;874&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SplitView를 사용하는 앱은 시스템이 가능한 공간에 따라 자동으로 열을 숨기거나 보임.&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;스크린샷 2026-05-02 오후 10.56.37.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;862&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8OtTF/dJMcabYnpJt/wNTZJuxk7mZJjmSgRvEPGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8OtTF/dJMcabYnpJt/wNTZJuxk7mZJjmSgRvEPGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8OtTF/dJMcabYnpJt/wNTZJuxk7mZJjmSgRvEPGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8OtTF%2FdJMcabYnpJt%2FwNTZJuxk7mZJjmSgRvEPGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1620&quot; height=&quot;862&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.56.37.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;862&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;앱 크기 조정 기능을 구현하려면 UIRequiresFullScreen과 같이 화면을 전체 크기로 고정하는 API를 외부로 마이그레이션.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UIRequiresFullScreen 이건 PadOS 26에서 더 이상 지원되지 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이패드 사이즈 조정에 대한 부분을 확인하려면 Elevate the design of your iPad app(WWDC25) 확인&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/efovzO/dJMcaakQwC2/uWvzebqKtLVuDxqO1CKUGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/efovzO/dJMcaakQwC2/uWvzebqKtLVuDxqO1CKUGk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;918&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.59.36.png&quot; style=&quot;width: 49.015%; margin-right: 10px;&quot; data-widthpercent=&quot;49.59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/efovzO/dJMcaakQwC2/uWvzebqKtLVuDxqO1CKUGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FefovzO%2FdJMcaakQwC2%2FuWvzebqKtLVuDxqO1CKUGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1840&quot; height=&quot;918&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgTURU/dJMcafzH9I2/hI2WoWhT8mNdarEeDUNEqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgTURU/dJMcafzH9I2/hI2WoWhT8mNdarEeDUNEqK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1854&quot; data-origin-height=&quot;910&quot; data-filename=&quot;스크린샷 2026-05-02 오후 10.59.47.png&quot; style=&quot;width: 49.8222%;&quot; data-widthpercent=&quot;50.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgTURU/dJMcafzH9I2/hI2WoWhT8mNdarEeDUNEqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgTURU%2FdJMcafzH9I2%2FhI2WoWhT8mNdarEeDUNEqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;910&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;macOS에서도 windowResizeAnchor를 이용해서 사이즈를 동적으로 대응할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&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;Framework foundations&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.00.53.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kJgGX/dJMcabYnpPh/xVLrIjdXOSk9WaHiwehSQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kJgGX/dJMcabYnpPh/xVLrIjdXOSk9WaHiwehSQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kJgGX/dJMcabYnpPh/xVLrIjdXOSk9WaHiwehSQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkJgGX%2FdJMcabYnpPh%2FxVLrIjdXOSk9WaHiwehSQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;696&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.00.53.png&quot; data-origin-width=&quot;1800&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;강력한 성능 부터 사용 편의성 개선 및 새로운 콘텐츠 레이아웃에 이르기까지 SwiftUI로 앱을 개발하기 좋은 시점.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레임워크 성능 개선 또한 여러 앱을 포함해 Apple 플랫폼 전반의 모든 앱에 이점을 가져다 줌.&lt;/p&gt;
&lt;p data-ke-size=&quot;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/y0VEO/dJMcaf0JLhs/ZhkMwRb0dYGxySDIkTpXaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y0VEO/dJMcaf0JLhs/ZhkMwRb0dYGxySDIkTpXaK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;608&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.03.35.png&quot; style=&quot;width: 25.1831%; margin-right: 10px;&quot; data-widthpercent=&quot;25.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y0VEO/dJMcaf0JLhs/ZhkMwRb0dYGxySDIkTpXaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy0VEO%2FdJMcaf0JLhs%2FZhkMwRb0dYGxySDIkTpXaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;844&quot; height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLKjVY/dJMcagSUM0q/OPp5w3apXCckZqWVUTRhJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLKjVY/dJMcagSUM0q/OPp5w3apXCckZqWVUTRhJk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;926&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.04.09.png&quot; style=&quot;width: 35.891%; margin-right: 10px;&quot; data-widthpercent=&quot;36.75&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLKjVY/dJMcagSUM0q/OPp5w3apXCckZqWVUTRhJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLKjVY%2FdJMcagSUM0q%2FOPp5w3apXCckZqWVUTRhJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1832&quot; height=&quot;926&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b39HFx/dJMcadPlWZD/TDh9MaQeyHuSMprV0ZFNBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b39HFx/dJMcadPlWZD/TDh9MaQeyHuSMprV0ZFNBk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;914&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.04.48.png&quot; style=&quot;width: 36.6004%;&quot; data-widthpercent=&quot;37.47&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b39HFx/dJMcadPlWZD/TDh9MaQeyHuSMprV0ZFNBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb39HFx%2FdJMcadPlWZD%2FTDh9MaQeyHuSMprV0ZFNBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1844&quot; height=&quot;914&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;SwiftUI에서 Lists, Scrolling, Profiling and Debugging이 개선되었음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS에서는 10만개 항목 리스트 로딩이 6배, 업데이트는 최대 16배 더 빠르게 업데이트 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트가 클수록 성능이 더 크게 향상되며, 플랫폼 전반에 적용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTvxPQ/dJMcad2TyK2/kKKqd7nm2UmumnRqVp1qaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTvxPQ/dJMcad2TyK2/kKKqd7nm2UmumnRqVp1qaK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1890&quot; data-origin-height=&quot;928&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.07.39.png&quot; style=&quot;width: 32.7402%; margin-right: 10px;&quot; data-widthpercent=&quot;33.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTvxPQ/dJMcad2TyK2/kKKqd7nm2UmumnRqVp1qaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTvxPQ%2FdJMcad2TyK2%2FkKKqd7nm2UmumnRqVp1qaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1890&quot; height=&quot;928&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FrQKz/dJMcaakQwMX/mrQcaC3XH5Zejfj612Y8X1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FrQKz/dJMcaakQwMX/mrQcaC3XH5Zejfj612Y8X1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;916&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.08.20.png&quot; style=&quot;width: 32.2916%; margin-right: 10px;&quot; data-widthpercent=&quot;33.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FrQKz/dJMcaakQwMX/mrQcaC3XH5Zejfj612Y8X1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFrQKz%2FdJMcaakQwMX%2FmrQcaC3XH5Zejfj612Y8X1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1840&quot; height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b175S2/dJMb990z765/yqaevSRBhY4oHg0xTrrxp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b175S2/dJMb990z765/yqaevSRBhY4oHg0xTrrxp1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;916&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.09.00.png&quot; style=&quot;width: 32.6426%;&quot; data-widthpercent=&quot;33.42&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b175S2/dJMb990z765/yqaevSRBhY4oHg0xTrrxp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb175S2%2FdJMb990z765%2FyqaevSRBhY4oHg0xTrrxp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1860&quot; height=&quot;916&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;SwiftUI가 다음 스크롤링하면 다음 프레임을 렌더링할 수 있도록 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Deadline까지 모든 작업이 완료되지 않으면 프레임이 떨어지게 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;iOS 및 macOS는 UI 업데이트를 통해 SwiftUI 스케줄링 경험이 개선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반응성을 향상시키고 SwiftUI가 다음 프레임에 대비해 더 많은 작업을 처리하도록 해줌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로 인해서 많은 스크롤을 하는 동안 앱이 중단될 가능성을 줄여줌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M3Dyq/dJMcai4cbUi/fyhP3wcBOnLKn8r0Y7KkA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M3Dyq/dJMcai4cbUi/fyhP3wcBOnLKn8r0Y7KkA0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;1002&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.10.52.png&quot; style=&quot;width: 48.5695%; margin-right: 10px;&quot; data-widthpercent=&quot;49.14&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M3Dyq/dJMcai4cbUi/fyhP3wcBOnLKn8r0Y7KkA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM3Dyq%2FdJMcai4cbUi%2FfyhP3wcBOnLKn8r0Y7KkA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1152&quot; height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AD90Z/dJMcabKR8Na/iTDkWyiWKZ1Xd0jfSN8TN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AD90Z/dJMcabKR8Na/iTDkWyiWKZ1Xd0jfSN8TN1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;990&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.11.06.png&quot; style=&quot;width: 50.2677%;&quot; data-widthpercent=&quot;50.86&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AD90Z/dJMcabKR8Na/iTDkWyiWKZ1Xd0jfSN8TN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAD90Z%2FdJMcabKR8Na%2FiTDkWyiWKZ1Xd0jfSN8TN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1178&quot; height=&quot;990&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;LazyVStack을 ScrollView 안에 넣을 때 SwiftUI가 뷰가 나타나기 직전까지 로딩을 지연시킴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩된 ScrollView도 이와 같이 동일하게 동작함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, 사진 캐러셀과 같은 뷰를 만드는데 좋음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.13.33.png&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dotbE5/dJMcadV8V2O/4YH0QikzjkyTeKxapkOM4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dotbE5/dJMcadV8V2O/4YH0QikzjkyTeKxapkOM4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dotbE5/dJMcadV8V2O/4YH0QikzjkyTeKxapkOM4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdotbE5%2FdJMcadV8V2O%2F4YH0QikzjkyTeKxapkOM4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1850&quot; height=&quot;814&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.13.33.png&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;814&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;앱에 어떤 성능 문제가 남아 있는지 이해하려면 Xcode에서 SwiftUI 성능 도구를 사용하면 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 body를 업데이트 또는 View 업데이트와 같은 다양한 성능 문제 영역에서 문제를 확인하고 조치할 여러가지 방법을 사용할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Optimize SwiftUI performance with instruments에서 상세한 정보를 확인할 수 있음.&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;스크린샷 2026-05-02 오후 11.15.54.png&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfLOfx/dJMcafGtHDC/L8ZmwUgV76j52J3CmKpIQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfLOfx/dJMcafGtHDC/L8ZmwUgV76j52J3CmKpIQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfLOfx/dJMcafGtHDC/L8ZmwUgV76j52J3CmKpIQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfLOfx%2FdJMcafGtHDC%2FL8ZmwUgV76j52J3CmKpIQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1808&quot; height=&quot;1002&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.15.54.png&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1002&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;/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;스크린샷 2026-05-02 오후 11.18.34.png&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;982&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QPdKA/dJMcab47XhA/FWU6rTYT14J00Gaa6luMcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QPdKA/dJMcab47XhA/FWU6rTYT14J00Gaa6luMcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QPdKA/dJMcab47XhA/FWU6rTYT14J00Gaa6luMcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQPdKA%2FdJMcab47XhA%2FFWU6rTYT14J00Gaa6luMcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1840&quot; height=&quot;982&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.18.34.png&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;982&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;애니메이션에는 AnimatableData이라는 프로토콜을 사용함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드로잉 방향을 제외한 모든 모앙야 데한 속성을 애니메이션화 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;전체 애니메이션 데이터 선언을 추가할 때 드로잉 방향을 제외하는 것만 해도 코드가 정말 많은데, 새로운 애니메이션 매크로(@Animatable)를 사용하면, 사용자 지정 애니메이션 데이터 속성을 삭제하고, SwiftUI로 하여금 자동으로 합성하도록 만들 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드로잉 방향처럼 애니메이션을 원하지 않는 속성의 경우 @AnimatableIgnored을 사용해서 제외.&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;스크린샷 2026-05-02 오후 11.21.35.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPmhcX/dJMcahRNjzP/LkMmE1scFFjhvO8KUC5dP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPmhcX/dJMcahRNjzP/LkMmE1scFFjhvO8KUC5dP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPmhcX/dJMcahRNjzP/LkMmE1scFFjhvO8KUC5dP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPmhcX%2FdJMcahRNjzP%2FLkMmE1scFFjhvO8KUC5dP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1842&quot; height=&quot;656&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.21.35.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;656&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;레이아웃은 이제 3차원으로 작업하는데 SwiftUI에는 이미 익숙한 수정자에 새로운 깊이 기반 변형이 도입되어, 직접적인 volumetic layout 없이도 더 많은 작업이 가능함.&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;스크린샷 2026-05-02 오후 11.24.25.png&quot; data-origin-width=&quot;1876&quot; data-origin-height=&quot;894&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NfuU2/dJMcahRNjAL/fOwCOOx8ILVq2bab5xMESK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NfuU2/dJMcahRNjAL/fOwCOOx8ILVq2bab5xMESK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NfuU2/dJMcahRNjAL/fOwCOOx8ILVq2bab5xMESK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNfuU2%2FdJMcahRNjAL%2FfOwCOOx8ILVq2bab5xMESK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;894&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.24.25.png&quot; data-origin-width=&quot;1876&quot; data-origin-height=&quot;894&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;VisionOS 앱에서 3D가 적합한데, 새로운 Alignment3D를 활용하면 시간에 따른 정렬을 조정할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 VisionOS 26의 공간 레이아웃 개선사항으로 Meet SwiftUI spatial layout (WWDC25)를 확인.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;SwiftUI across the system&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱은 시스템 전반에 걸쳐 SwiftUI의 이점을 활용할 수 있음.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.27.03.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLLDwQ/dJMcadaKbiD/TW07S5gY80Z76n7zsPgoZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLLDwQ/dJMcadaKbiD/TW07S5gY80Z76n7zsPgoZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLLDwQ/dJMcadaKbiD/TW07S5gY80Z76n7zsPgoZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLLDwQ%2FdJMcadaKbiD%2FTW07S5gY80Z76n7zsPgoZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1860&quot; height=&quot;702&quot; data-filename=&quot;스크린샷 2026-05-02 오후 11.27.03.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;702&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;Scene에 대한 개선사항과 Widget 및 Control과 관련한 새로운 API 덕분에 앱이 플랫폼과 정말 잘 어우러짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SwiftUI는 다른 프레임워크와는 훨씬 더 잘 작동함.&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;스크린샷 2026-05-06 오후 11.33.48.png&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfiqBC/dJMcabxjwxQ/lrTq2FkgBVna0tXp0SaDAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfiqBC/dJMcabxjwxQ/lrTq2FkgBVna0tXp0SaDAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfiqBC/dJMcabxjwxQ/lrTq2FkgBVna0tXp0SaDAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfiqBC%2FdJMcabxjwxQ%2FlrTq2FkgBVna0tXp0SaDAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1782&quot; height=&quot;660&quot; data-filename=&quot;스크린샷 2026-05-06 오후 11.33.48.png&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;660&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;Scene은 앱의 뷰에 대한 루트 컨테이너로 인터페이스의 분리된 부분을 나타냄.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱의 `body` 영역에 선언하여 Scene에 접근할 수 있음.&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;스크린샷 2026-05-06 오후 11.39.05.png&quot; data-origin-width=&quot;1786&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vvVGX/dJMcadPosXQ/dGj5kkPO85CIfN3DwZxFCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vvVGX/dJMcadPosXQ/dGj5kkPO85CIfN3DwZxFCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vvVGX/dJMcadPosXQ/dGj5kkPO85CIfN3DwZxFCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvvVGX%2FdJMcadPosXQ%2FdGj5kkPO85CIfN3DwZxFCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1786&quot; height=&quot;702&quot; data-filename=&quot;스크린샷 2026-05-06 오후 11.39.05.png&quot; data-origin-width=&quot;1786&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해부터는 Scene 브릿징을 통해 UIKit과 AppKit 라이프사이클 앱에서 SwiftUI의 Scene 요청이 가능함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scene bridging을 통해서 UIKit과 AppKit에서 라이프사이클 상호운용 가능함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해서 SwiftUI에 있는 Scene 타입을 열거나 UIKit 또는 AppKit에서 바로 SwiftUI 기능을 사용할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MenuBarExtra나 ImmersiveSpace 같은 Scene 타입 등을 사용할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WindowStyle 등 다양한 Modifier와도 잘 작동함.&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;스크린샷 2026-05-06 오후 11.41.25.png&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;1014&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVtk4F/dJMb997mcqJ/l8plV7feuV1pvwXOtXTpc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVtk4F/dJMb997mcqJ/l8plV7feuV1pvwXOtXTpc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVtk4F/dJMb997mcqJ/l8plV7feuV1pvwXOtXTpc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVtk4F%2FdJMb997mcqJ%2Fl8plV7feuV1pvwXOtXTpc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1748&quot; height=&quot;1014&quot; data-filename=&quot;스크린샷 2026-05-06 오후 11.41.25.png&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;1014&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;인지 장애가 있는 사용자를 위한 특수 모드인 AssistiveAcess가 존재.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;스크린샷 2026-05-06 오후 11.46.44.png&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A4frl/dJMcahEil9D/guwcdW8KFbHJqaqylduP40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A4frl/dJMcahEil9D/guwcdW8KFbHJqaqylduP40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A4frl/dJMcahEil9D/guwcdW8KFbHJqaqylduP40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA4frl%2FdJMcahEil9D%2FguwcdW8KFbHJqaqylduP40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1812&quot; height=&quot;712&quot; data-filename=&quot;스크린샷 2026-05-06 오후 11.46.44.png&quot; data-origin-width=&quot;1812&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;올해 SwiftUI에는 AppKit 작업에 몇가지 큰 개선사항이 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scene bridging 외에도 SwiftUI 뷰가 있는 시트도 표시할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AppKit 제스처도 SwiftUI로 연결할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;interface Builder에서 NSHostingView를 사용할 수 있음.&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;스크린샷 2026-05-06 오후 11.49.54.png&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yVNoH/dJMcacQxp14/VzeJNWiR8prODATeraLixK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yVNoH/dJMcacQxp14/VzeJNWiR8prODATeraLixK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yVNoH/dJMcacQxp14/VzeJNWiR8prODATeraLixK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyVNoH%2FdJMcacQxp14%2FVzeJNWiR8prODATeraLixK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1108&quot; height=&quot;984&quot; data-filename=&quot;스크린샷 2026-05-06 오후 11.49.54.png&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;984&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;watchOS 26과 macOS Tahoe는 커스텀 제어 기능을 제공함.&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/bdKL8x/dJMcad2V6TH/kLCZoK5cHcLyKM7Ocv5gr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdKL8x/dJMcad2V6TH/kLCZoK5cHcLyKM7Ocv5gr1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;726&quot; data-filename=&quot;스크린샷 2026-05-06 오후 11.56.01.png&quot; style=&quot;width: 55.957%; margin-right: 10px;&quot; data-widthpercent=&quot;56.62&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdKL8x/dJMcad2V6TH/kLCZoK5cHcLyKM7Ocv5gr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdKL8x%2FdJMcad2V6TH%2FkLCZoK5cHcLyKM7Ocv5gr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1732&quot; height=&quot;726&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9E5B2/dJMcaja1WZp/luXSO68NITvWDZjs7f8cx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9E5B2/dJMcaja1WZp/luXSO68NITvWDZjs7f8cx0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1766&quot; data-origin-height=&quot;966&quot; data-filename=&quot;스크린샷 2026-05-06 오후 11.58.29.png&quot; style=&quot;width: 42.8802%;&quot; data-widthpercent=&quot;43.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9E5B2/dJMcaja1WZp/luXSO68NITvWDZjs7f8cx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9E5B2%2FdJMcaja1WZp%2FluXSO68NITvWDZjs7f8cx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1766&quot; height=&quot;966&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;올해부터는 위젯을 VisionOS와 CarPlay에서 사용할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VisionOS는 새로운 레벨 환경 변수를 사용해서 공간을 사용자가 지정할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;푸시 기반 업데이트 API 등 올해 여러가지가 새롭게 나옴.&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;스크린샷 2026-05-07 오전 12.03.57.png&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I6XPx/dJMcahqNT8N/FqTSSYMYgxGhpkQozDP8y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I6XPx/dJMcahqNT8N/FqTSSYMYgxGhpkQozDP8y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I6XPx/dJMcahqNT8N/FqTSSYMYgxGhpkQozDP8y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI6XPx%2FdJMcahqNT8N%2FFqTSSYMYgxGhpkQozDP8y1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1746&quot; height=&quot;816&quot; data-filename=&quot;스크린샷 2026-05-07 오전 12.03.57.png&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;816&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;SwiftUI View 기능이 확장되었는데, 리치 텍스트 편집부터 3D 차트에 이르기까지 기존 SwiftUI의 기능과 외관이 새롭게 단장됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱에서 웹 콘텐츠를 곧바로 임베드 할 때 WebKit에서 WebView와 같은 SwiftUI API 전체를 활용할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebView는 앱에서 웹 콘텐츠를 표시하는 새로운 SwiftUI 뷰로 Safari와 동일하게 WebKit으로 구동.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지를 커스텀하고 이와 상호작용하려면 WebView로 새 observable 모델 유형인 WebPage를 표시할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebPage는 웹과 풍부한 상호작용을 가능하도록 하는데, 프로그래밍을 통해 페이지에서 탐색하고 페이지 속성에 접근함.&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;스크린샷 2026-05-07 오전 12.08.16.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;1004&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3Szmo/dJMcahRPPRW/4Of47V3X19qA8iX56HyAsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3Szmo/dJMcahRPPRW/4Of47V3X19qA8iX56HyAsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3Szmo/dJMcahRPPRW/4Of47V3X19qA8iX56HyAsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3Szmo%2FdJMcahRPPRW%2F4Of47V3X19qA8iX56HyAsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1004&quot; data-filename=&quot;스크린샷 2026-05-07 오전 12.08.16.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;1004&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;WebKit이 SwiftUI를 지원하는 새로운 방법으로 사용자 에이전트 커스텀이나 JavaScript 호출, 커스텀 URL 스킴 등이 생겼고 더 다양해짐.&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;스크린샷 2026-05-07 오전 12.10.04.png&quot; data-origin-width=&quot;1776&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciTplC/dJMcafNjcv4/7KLF0OGfLrejuTdpq4k421/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciTplC/dJMcafNjcv4/7KLF0OGfLrejuTdpq4k421/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciTplC/dJMcafNjcv4/7KLF0OGfLrejuTdpq4k421/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciTplC%2FdJMcafNjcv4%2F7KLF0OGfLrejuTdpq4k421%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1776&quot; height=&quot;990&quot; data-filename=&quot;스크린샷 2026-05-07 오전 12.10.04.png&quot; data-origin-width=&quot;1776&quot; data-origin-height=&quot;990&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;Chart3D를 통해서 3차원 플롯을 보여줄수도 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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/IEMuS/dJMcac33u9a/OlfW51UUEwh8R8kDe3Mf90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IEMuS/dJMcac33u9a/OlfW51UUEwh8R8kDe3Mf90/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;1034&quot; data-filename=&quot;스크린샷 2026-05-07 오전 12.13.48.png&quot; style=&quot;width: 43.7214%; margin-right: 10px;&quot; data-widthpercent=&quot;44.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IEMuS/dJMcac33u9a/OlfW51UUEwh8R8kDe3Mf90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIEMuS%2FdJMcac33u9a%2FOlfW51UUEwh8R8kDe3Mf90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1804&quot; height=&quot;1034&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pQJoE/dJMcaarF1Nq/nwqHMakH0PVYSr7ObUl0ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pQJoE/dJMcaarF1Nq/nwqHMakH0PVYSr7ObUl0ck/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;632&quot; data-filename=&quot;스크린샷 2026-05-07 오전 12.16.38.png&quot; style=&quot;width: 55.1158%;&quot; data-widthpercent=&quot;55.76&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pQJoE/dJMcaarF1Nq/nwqHMakH0PVYSr7ObUl0ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpQJoE%2FdJMcaarF1Nq%2FnwqHMakH0PVYSr7ObUl0ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1390&quot; height=&quot;632&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;올해는 mac에 드래그를 도입해서 여러 항목 주변으로 드래그를 할 수 있음. &lt;br /&gt;새로운 dragContainer도 macOS에서 사용할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 뷰가 드래그 항목을 담는 컨테이너와 동일해지며, 커스텀 선택 동작과도 함께 작동함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드래그 앤 드롭이 실행될 때 항목은 지연 드래그하도록 SwiftUI가 자동으로 요청함.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;스크린샷 2026-05-07 오전 12.17.31.png&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xk6wV/dJMcaiDdiNK/iGQ8m0kBYalA0Ntn0V8joK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xk6wV/dJMcaiDdiNK/iGQ8m0kBYalA0Ntn0V8joK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xk6wV/dJMcaiDdiNK/iGQ8m0kBYalA0Ntn0V8joK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxk6wV%2FdJMcaiDdiNK%2FiGQ8m0kBYalA0Ntn0V8joK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;864&quot; data-filename=&quot;스크린샷 2026-05-07 오전 12.17.31.png&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신규 `dragCofiguration` api로 드래그 작업을 사용자화 할 수도 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`onDragSessionUpdated`는 이벤트를 관찰 가능하도록 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`dragPreviewsFormation`은 드래그 할 때 미리보기가 어떻게 뜨는지 확인하기 위해서 사용&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/ccpQe8/dJMcaakS39h/sOnudWC0AC9rlnALsezpQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccpQe8/dJMcaakS39h/sOnudWC0AC9rlnALsezpQ0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1726&quot; data-origin-height=&quot;822&quot; data-filename=&quot;스크린샷 2026-05-07 오전 12.22.11.png&quot; style=&quot;width: 52.4487%; margin-right: 10px;&quot; data-widthpercent=&quot;53.07&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccpQe8/dJMcaakS39h/sOnudWC0AC9rlnALsezpQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccpQe8%2FdJMcaakS39h%2FsOnudWC0AC9rlnALsezpQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1726&quot; height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zsRrW/dJMb99M1l2m/BY9XLet2pkN3Qqam1neipK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zsRrW/dJMb99M1l2m/BY9XLet2pkN3Qqam1neipK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;966&quot; data-filename=&quot;스크린샷 2026-05-07 오전 12.22.51.png&quot; style=&quot;width: 46.3885%;&quot; data-widthpercent=&quot;46.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zsRrW/dJMb99M1l2m/BY9XLet2pkN3Qqam1neipK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzsRrW%2FdJMb99M1l2m%2FBY9XLet2pkN3Qqam1neipK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1794&quot; height=&quot;966&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;TextView에 이제 AttributedString이 지원됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TextEditor에서 AttributedString에 바인딩을 통해 내장형 텍스트 포맷팅 제어 기능을 사용할 수 있음.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=cETgTtu6atM&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=cETgTtu6atM&lt;/a&gt;&lt;/p&gt;</description>
      <category>Apple/WWDC</category>
      <category>ios</category>
      <category>SwiftUI</category>
      <category>wwdc25</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/803</guid>
      <comments>https://rldd.tistory.com/803#entry803comment</comments>
      <pubDate>Sat, 2 May 2026 16:21:35 +0900</pubDate>
    </item>
    <item>
      <title>defer로 인해 lock의 범위가 넓어져 발생한 문제 분석</title>
      <link>https://rldd.tistory.com/801</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;defer로&amp;nbsp;인해&amp;nbsp;lock의&amp;nbsp;범위가&amp;nbsp;넓어져&amp;nbsp;발생한&amp;nbsp;문제&amp;nbsp;분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시성 코드에서 lock은 가장 직관적인 장치 중 하나로 여러 스레드가 같은 상태를 동시에 읽고 쓰면 race condition이 발생할 수 있기 때문에 공유 자원을 보호하기 위해 lock을 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 아래의 코드 패턴은 락의 범위를 함수 끝까지 늘려서 이 경우&amp;nbsp;후속 로직에서 lock을 다시 잡아야 하는 코드가 존재면 문제가 발생할 수 있음.&lt;/p&gt;
&lt;pre id=&quot;code_1774826549425&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func foo() {
    lock.lock()
    defer { lock.unlock() }
    
    // do something
}&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;문제가 발생하는 코드 예시&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;락을 잡은 채 기다리는 구조가 생기면 장기 대기나 데드락으로 이어질 수 있음.&lt;br /&gt;특히 코드와 로직이 복잡할수록 원인이 코드 한가운데에 숨어 있어 발견하기 어려움.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774826356581&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func someFunction() {
    lock.lock()
    defer { lock.unlock() }

    // 상태 변경
    isCompleted = false
    
    // callback이 signal 해줘야 함
    finished.wait()
}

func callback() {
    lock.lock()
    defer { lock.unlock() }
    
    // 상태 변경
    isCompleted = true

    finished.signal()
}&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;h4 data-ke-size=&quot;size20&quot;&gt;lock은 상태만 보호하기&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;defer로 lock 범위가 넓어진 상태에서 오래 기다릴 수 있는 작업까지 임계구역에 들어갈 경우 lock이 상태 보호의 도구를 넘어 흐름을 차단하는 장치로 동작하게 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 lock은 상태를 보호하기 위해서 짧게 사용하고, 대기하는 작업은 반드시 lock 밖에서 사용하기.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/개발 업무</category>
      <category>defer</category>
      <category>lock</category>
      <category>ObjC</category>
      <category>signal</category>
      <category>wait</category>
      <category>withlock</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/801</guid>
      <comments>https://rldd.tistory.com/801#entry801comment</comments>
      <pubDate>Thu, 26 Mar 2026 17:02:00 +0900</pubDate>
    </item>
    <item>
      <title>기기별 RAM 용량에 따른 이미지 동적 캐시 사이즈 설계</title>
      <link>https://rldd.tistory.com/800</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;기기별 RAM 용량에 따른 이미지 동적 캐시 사이즈 설계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;글로벌 서비스를 운영하다 보면, 여전히 RAM 2GB~3GB 수준의 구형 기기 사용자를 무시할 수 없음.&lt;br /&gt;iPhone 8, iPhone SE 1세대, 일부 보급형 기기처럼 메모리 여유가 크지 않은 환경에서는, 캐시 전략 하나만으로도 앱 안정성이 크게 달라질 수 있음.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;이미지 캐시는 사용자 경험을 빠르게 만들기 위한 중요한 장치지만, 동시에 메모리를 많이 사용하는 기능이기도 함.&lt;br /&gt;그래서 캐시 크기를 고정값으로 두면 다음과 같은 문제가 생김&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저사양 기기에서는 메모리 압박으로 크래시 위험이 커짐&lt;/li&gt;
&lt;li&gt;고사양 기기에서는 더 많은 자원을 활용하지 못해 비효율이 발생함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기기의 물리적 RAM 용량을 기준으로 이미지 캐시 사이즈를 동적으로 결정하는 방식을 적용&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;왜 동적 설정이 필요할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS는 앱이 사용할 수 있는 메모리 한도를 초과하면 명시적인 에러 없이 프로세스를 종료할 수 있음.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;저사양 기기의 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RAM 2GB 기기에서 캐시를 1GB로 고정하는 것은 실제 사용 가능한 메모리 대비 과도한 점유가 될 수 있음.&lt;/li&gt;
&lt;li&gt;경우에 따라서는 결과적으로 메모리 부족으로 인한 크래시 확률이 높아짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;고사양 기기의 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 디스크 접근 증가&lt;/li&gt;
&lt;li&gt;네트워크 재요청 증가&lt;/li&gt;
&lt;li&gt;이미지 디코딩 비용 증가&lt;/li&gt;
&lt;li&gt;스크롤 체감 성능 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;즉, 캐시를 너무 작게 잡아도 손해가 커서 &lt;/span&gt;&lt;/span&gt;기기별 메모리 특성에 맞게끔 균형을 찾고자 함.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #0d0d0d;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전체 코드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 캐시 사이즈에 기기별 가용 가능한 메모리 크기를 기반으로 상한값을 다르게 설정.&lt;br /&gt;pyshicalMemory는 기기 스펙의 메모리 용량과 일치하지 않음.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래 코드에서 1/8 값을 사용하고 있으나 프로젝트 특성에 맞게 변경해도 됨.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;안드로이드 공식 문서에서 1/8 사용하고 있는 예제가 있어서 그대로 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;swift&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;private func configureImageCacheLimits() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 기기의 물리적 RAM 전체 용량 (단위: bytes)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 예) 6GB RAM &amp;rarr; 6,442,450,944 bytes
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let physicalMemory = ProcessInfo.processInfo.physicalMemory
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 이미지 캐시에 허용할 최소 메모리 한도 (단위: MB)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let minimumLimitMB: UInt64 = 100
&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;// 이미지 캐시에 허용할 최대 메모리 한도 (단위: MB)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let maximumLimitMB: UInt64 = 512
&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;// physicalMemory(bytes) &amp;rarr; MB 변환 과정:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp; &amp;divide; 8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: 전체 RAM의 1/8만 캐시에 사용 (나머지는 OS or 앱 등이 사용)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp; &amp;divide; 1024&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : bytes &amp;rarr; KB
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp; &amp;divide; 1024&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : KB &amp;rarr; MB
&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;// 예) 6GB RAM 기기:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp; 6,442,450,944 &amp;divide; 8 = 805,306,368 bytes (약 768MB, RAM의 1/8)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp; 805,306,368 &amp;divide; 1024 = 786,432 KB레
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp; 786,432 &amp;divide; 1024 = 768 MB
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp; &amp;rarr; min(512, 768) = 512 &amp;rarr; max(100, 512) = 512MB 적용
&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;// 예) 2GB RAM 기기:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp; 2,147,483,648 &amp;divide; 8 = 268,435,456 bytes (약 256MB)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp; &amp;rarr; 256MB &amp;rarr; max(100, 256) = 256MB 적용
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let computedLimitMB = max(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;minimumLimitMB,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min(maximumLimitMB, physicalMemory / 8 / 1024 / 1024)
&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;// 최종 캐시 한도를 다시 bytes로 변환 (SDImageCache에 bytes 단위로 전달해야 하므로)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 예) 512MB &amp;rarr; 512 &amp;times; 1024 &amp;times; 1024 = 536,870,912 bytes
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let physicalMemoryMB = physicalMemory / 1024 / 1024
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let physicalMemoryGB = physicalMemoryMB / 1024
&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;var systemInfo = utsname()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uname(&amp;amp;systemInfo)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let deviceModel = withUnsafePointer(to: &amp;amp;systemInfo.machine) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$0.withMemoryRebound(to: CChar.self, capacity: 1) {
&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;String(validatingUTF8: $0) ?? &quot;Unknown&quot;
&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;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;기기 모델: \(deviceModel)&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;물리 메모리: \(physicalMemoryGB) GB&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;캐시 한도: \(computedLimitMB) MB&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;아이폰 15 Pro&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/orZcs/dJMcaaragzC/gaoQ6KEYLPXAKVuyw3aNC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/orZcs/dJMcaaragzC/gaoQ6KEYLPXAKVuyw3aNC1/img.png&quot; data-alt=&quot;아이폰 15 Pro&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/orZcs/dJMcaaragzC/gaoQ6KEYLPXAKVuyw3aNC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2ForZcs%2FdJMcaaragzC%2FgaoQ6KEYLPXAKVuyw3aNC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1044&quot; height=&quot;122&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아이폰 15 Pro&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;아이폰 13 mini&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/otUih/dJMcaivVcTZ/Yio0p0oX522SkOpuNfqve1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/otUih/dJMcaivVcTZ/Yio0p0oX522SkOpuNfqve1/img.png&quot; data-alt=&quot;아이폰 13 미니&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/otUih/dJMcaivVcTZ/Yio0p0oX522SkOpuNfqve1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FotUih%2FdJMcaivVcTZ%2FYio0p0oX522SkOpuNfqve1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1032&quot; height=&quot;156&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아이폰 13 미니&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&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&gt;&lt;b&gt;기기&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;RAM&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;1/8&lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;b&gt;계산값&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;최종&lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;b&gt;적용&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iPhone SE (1st)&lt;/td&gt;
&lt;td&gt;2GB&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iPhone 11&lt;/td&gt;
&lt;td&gt;4GB&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iPhone 15 Pro&lt;/td&gt;
&lt;td&gt;6GB&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;512MB (상한&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;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;(참고)&lt;br /&gt;&lt;a href=&quot;https://developer.android.com/topic/performance/graphics/cache-bitmap?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;https://developer.android.com/topic/performance/graphics/cache-bitmap?hl=ko&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;비트맵 캐싱 &amp;nbsp;|&amp;nbsp; App quality &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;단일 비트맵을 사용자 인터페이스(UI)에 로드하는 것은 간단하지만 한 번에 더 큰 이미지의 집합을 로드해야 하면 더 복잡해집니다. 많은 경우(ListView, GridView 또는 LruCache 클래스와 같은 구성요소&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/topic/performance/graphics/cache-bitmap?hl=ko&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/cDWPdx/dJMb86O1qvR/AAAAAAAAAAAAAAAAAAAAAPn_S2ZS-qyLi-4fIY1DVUZyvtHtxkyhyep5XWYqP16C/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1774969199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=8ng3qjwc0%2FOZXzbbpgC%2BPPg57Ag%3D&quot; data-og-url=&quot;https://developer.android.com/topic/performance/graphics/cache-bitmap?hl=ko&quot;&gt;&lt;a href=&quot;https://developer.android.com/topic/performance/graphics/cache-bitmap?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/topic/performance/graphics/cache-bitmap?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/cDWPdx/dJMb86O1qvR/AAAAAAAAAAAAAAAAAAAAAPn_S2ZS-qyLi-4fIY1DVUZyvtHtxkyhyep5XWYqP16C/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1774969199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=8ng3qjwc0%2FOZXzbbpgC%2BPPg57Ag%3D');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;비트맵 캐싱 &amp;nbsp;|&amp;nbsp; App quality &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;단일 비트맵을 사용자 인터페이스(UI)에 로드하는 것은 간단하지만 한 번에 더 큰 이미지의 집합을 로드해야 하면 더 복잡해집니다. 많은 경우(ListView, GridView 또는 LruCache 클래스와 같은 구성요소&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.android.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/개발 업무</category>
      <category>Android</category>
      <category>ios</category>
      <category>swift</category>
      <category>동적캐시</category>
      <category>이미지캐시</category>
      <category>캐시</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/800</guid>
      <comments>https://rldd.tistory.com/800#entry800comment</comments>
      <pubDate>Wed, 25 Mar 2026 23:02:57 +0900</pubDate>
    </item>
    <item>
      <title>HTTPURLResponse.allHeaderFields 파싱 시 NSNumber 헤더 누락 문제</title>
      <link>https://rldd.tistory.com/797</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;HTTPURLResponse.allHeaderFields&amp;nbsp;파싱&amp;nbsp;시&amp;nbsp;NSNumber&amp;nbsp;헤더&amp;nbsp;누락&amp;nbsp;문제&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 응답 처리 레이어에서 `HTTPURLResponse.allHeaderFields`를 [String: String] 형태로 변환해 상위 모듈에 전달.&lt;br /&gt;&lt;br /&gt;기존 변환 사용 로직은 Any를 `as? String`으로 캐스팅하는 방식인데, 이 방식이 Swift 타입 시스템과 Foundation의 NSNumber 브리징 동작이 결합되어 일부 헤더를 조용히 누락시키는 현상&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 헤더에는 동일한 key가 중복을 허용하고 있어서 header는 동일 키 값에 대해서도 여러개 받을 수 있도록 해야함.&lt;/p&gt;
&lt;pre id=&quot;code_1770138797059&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// ❌ 기존 코드 
var responseHeader: [String: String] = [:]

for (headerKey, headerValue) in httpResponse.allHeaderFields {
    guard let key = headerKey as? String,
          let value = headerValue as? String   // &amp;larr; ❌ NSNumber면 nil &amp;rarr; continue
    else { continue }

    responseHeader[key] = value
}

/// ✅ 안전한 방식
var responseHeader: [String: String] = [:]

for (key, value) in httpResponse.allHeaderFields {
    guard let keyString = key as? String else { continue }
    responseHeader[keyString] = String(describing: value)
}

///   NSNumber가 데이터로 들어오는 경우 
Content-Length: 123&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;br /&gt;&lt;br /&gt;(참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundation/httpurlresponse/allheaderfields&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/foundation/httpurlresponse/allheaderfields&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778421973682&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;allHeaderFields | Apple Developer Documentation&quot; data-og-description=&quot;All HTTP header fields of the response.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/foundation/httpurlresponse/allheaderfields&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/foundation/httpurlresponse/allheaderfields&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/8gOkc/dJMb85WXP4m/H2ek8oaGBnCPtc0PoBsgwk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ihuwj/dJMb81G1QFV/nJc5hkniaIQsmHqhcWxy5k/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundation/httpurlresponse/allheaderfields&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/foundation/httpurlresponse/allheaderfields&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/8gOkc/dJMb85WXP4m/H2ek8oaGBnCPtc0PoBsgwk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ihuwj/dJMb81G1QFV/nJc5hkniaIQsmHqhcWxy5k/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;allHeaderFields | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;All HTTP header fields of the response.&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;&lt;a href=&quot;https://developer.apple.com/documentation/foundation/httpurlresponse/value(forhttpheaderfield:)&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/foundation/httpurlresponse/value(forhttpheaderfield:)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778422111033&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;HTTPURLResponse | Apple Developer Documentation&quot; data-og-description=&quot;The metadata associated with the response to an HTTP protocol URL load request.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/foundation/httpurlresponse&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/foundation/httpurlresponse&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bFowCV/dJMb81G1QF9/9YS8j20fPAl27K2LVVkxL1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/qKGwJ/dJMb88e49R4/jdOpsNW6j7URF3Wja2CbEk/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundation/httpurlresponse&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/foundation/httpurlresponse&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bFowCV/dJMb81G1QF9/9YS8j20fPAl27K2LVVkxL1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/qKGwJ/dJMb88e49R4/jdOpsNW6j7URF3Wja2CbEk/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;HTTPURLResponse | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The metadata associated with the response to an HTTP protocol URL load request.&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;&lt;a href=&quot;https://developer.apple.com/documentation/swift/string/init(describing:)-67ncf&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/swift/string/init(describing:)-67ncf&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778422120599&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;init(describing:) | Apple Developer Documentation&quot; data-og-description=&quot;Creates a string representing the given value.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/swift/string/init(describing:)-67ncf&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/swift/string/init(describing:)-67ncf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bFMD3f/dJMb8SpMq9V/cjNQZOE8gEx7kF4bAPnCo1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/A2UYD/dJMb9kmg9ET/6rwcIO7XnU5Fet74DTKOZ0/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/swift/string/init(describing:)-67ncf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/swift/string/init(describing:)-67ncf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bFMD3f/dJMb8SpMq9V/cjNQZOE8gEx7kF4bAPnCo1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/A2UYD/dJMb9kmg9ET/6rwcIO7XnU5Fet74DTKOZ0/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;init(describing:) | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Creates a string representing the given value.&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;&lt;a href=&quot;https://github.com/swiftlang/swift-corelibs-foundation/pull/287&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/swiftlang/swift-corelibs-foundation/pull/287&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778427074997&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;Implement NSHTTPURLResponse by danieleggert &amp;middot; Pull Request #287 &amp;middot; swiftlang/swift-corelibs-foundation&quot; data-og-description=&quot;This implements both the trivial attributes (statusCode and allHeaderFields), and also parses the headers in order to set the following attributes: public var mimeType: String? { get } public var e...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/swiftlang/swift-corelibs-foundation/pull/287&quot; data-og-url=&quot;https://github.com/swiftlang/swift-corelibs-foundation/pull/287&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxD1yo/dJMb9eTT33X/QDTytqstGsK0Q4s4hsVQJ1/img.png?width=1200&amp;amp;height=600&amp;amp;face=999_133_1042_181,https://scrap.kakaocdn.net/dn/hHuhJ/dJMb8Z3vPsJ/udUns4kTH5x0XdWU8Qa7sK/img.png?width=1200&amp;amp;height=600&amp;amp;face=999_133_1042_181&quot;&gt;&lt;a href=&quot;https://github.com/swiftlang/swift-corelibs-foundation/pull/287&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/swiftlang/swift-corelibs-foundation/pull/287&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxD1yo/dJMb9eTT33X/QDTytqstGsK0Q4s4hsVQJ1/img.png?width=1200&amp;amp;height=600&amp;amp;face=999_133_1042_181,https://scrap.kakaocdn.net/dn/hHuhJ/dJMb8Z3vPsJ/udUns4kTH5x0XdWU8Qa7sK/img.png?width=1200&amp;amp;height=600&amp;amp;face=999_133_1042_181');&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;Implement NSHTTPURLResponse by danieleggert &amp;middot; Pull Request #287 &amp;middot; swiftlang/swift-corelibs-foundation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This implements both the trivial attributes (statusCode and allHeaderFields), and also parses the headers in order to set the following attributes: public var mimeType: String? { get } public var e...&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;</description>
      <category>Project/개발 업무</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/797</guid>
      <comments>https://rldd.tistory.com/797#entry797comment</comments>
      <pubDate>Wed, 4 Feb 2026 02:13:19 +0900</pubDate>
    </item>
    <item>
      <title>빠르게 링크: 빌드 및 실행 시간 개선 (Link fast: Improve build and launch times) - WWDC22</title>
      <link>https://rldd.tistory.com/792</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;빠르게&amp;nbsp;링크:&amp;nbsp;빌드&amp;nbsp;및&amp;nbsp;실행&amp;nbsp;시간&amp;nbsp;개선&amp;nbsp;(Link&amp;nbsp;fast:&amp;nbsp;Improve&amp;nbsp;build&amp;nbsp;and&amp;nbsp;launch&amp;nbsp;times)&amp;nbsp;-&amp;nbsp;WWDC22&lt;/h2&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;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;What is static linking?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;몇 가지 예와 정적 링킹이 무엇인지 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Recent ld64 improvements
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플의 정적 링커인 ld64의 신기능&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sattic linking best practices
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 링킹에 대한 배경시직과 함께 모범 사례에 대해서 설명&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What is dynamic linking?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동적 링킹 과정에서 어떤 일이 발생하는지 다룸&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Recent dyld improvements
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;올해 dyld의 새로운 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dynamic linking best practices
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동적 링크 시간 성능 개선을 위해 할 수 있는것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;New tools
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;바이너리에 무엇이 있고, 동적 링킹 과정에서 어떤 일이 일어나는지 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;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/BgFpu/dJMcai9LRma/baAhOUPZr1iRryYCN3OtUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BgFpu/dJMcai9LRma/baAhOUPZr1iRryYCN3OtUk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1540&quot; data-origin-height=&quot;866&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.58.27.png&quot; style=&quot;width: 44.3416%; margin-right: 10px;&quot; data-widthpercent=&quot;44.86&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BgFpu/dJMcai9LRma/baAhOUPZr1iRryYCN3OtUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBgFpu%2FdJMcai9LRma%2FbaAhOUPZr1iRryYCN3OtUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1540&quot; height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWTWca/dJMcaaRupMA/ECVHsBjDi7GaOWxrEztqfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWTWca/dJMcaaRupMA/ECVHsBjDi7GaOWxrEztqfK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;690&quot; data-filename=&quot;스크린샷 2026-01-02 오후 5.02.18.png&quot; style=&quot;width: 54.4956%;&quot; data-widthpercent=&quot;55.14&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWTWca/dJMcaaRupMA/ECVHsBjDi7GaOWxrEztqfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWTWca%2FdJMcaaRupMA%2FECVHsBjDi7GaOWxrEztqfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;690&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링킹에는 두가지 유형이 존재&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 링킹 (static linking)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱을 빌드할 때 발생&amp;nbsp;&lt;/li&gt;
&lt;li&gt;앱을 빌드하는데 걸리는 시간과 앱의 최종 크기에 영향&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동적 링킹 (dynamic linking)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱이 실행될 때 발생&lt;/li&gt;
&lt;li&gt;앱이 작동할 때까지 사용자의 대기 시간에 영향을 줄 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;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;스크린샷 2026-01-02 오후 5.11.33.png&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmCs3e/dJMcafkXj3y/rrfblMWkfCoL6SfEDWxUk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmCs3e/dJMcafkXj3y/rrfblMWkfCoL6SfEDWxUk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmCs3e/dJMcafkXj3y/rrfblMWkfCoL6SfEDWxUk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmCs3e%2FdJMcafkXj3y%2FrrfblMWkfCoL6SfEDWxUk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1826&quot; height=&quot;820&quot; data-filename=&quot;스크린샷 2026-01-02 오후 5.11.33.png&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 프로그램들이 간단했고, 소스 파일도 하나 뿐이라서 단 하나의 소스 파일 에서 컴파일러를 실행하면 실행 가능한 프로그램을 생성하지만 하나의 소스파일만 가지고는 프로그램을 확장하기가 쉽지가 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 파일을 컴파일 하기 위해서 처음에는 컴파일러를 여러개로 나누는 방향으로 접근.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트 파일을 읽고 실행 가능한 파일로 연결하는 링커를 정적 링커 `ld`라고 부름.&lt;/p&gt;
&lt;p data-ke-size=&quot;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/ctUzeo/dJMcacBLO9W/8Ak2bFgpqNducQsHGJJTy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctUzeo/dJMcacBLO9W/8Ak2bFgpqNducQsHGJJTy1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;900&quot; data-filename=&quot;스크린샷 2026-01-02 오후 5.13.43.png&quot; style=&quot;width: 48.8633%; margin-right: 10px;&quot; data-widthpercent=&quot;49.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctUzeo/dJMcacBLO9W/8Ak2bFgpqNducQsHGJJTy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctUzeo%2FdJMcacBLO9W%2F8Ak2bFgpqNducQsHGJJTy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1804&quot; height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cStIfD/dJMcahJQNQz/MVDklWLtfqnqXJEEoyqDdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cStIfD/dJMcahJQNQz/MVDklWLtfqnqXJEEoyqDdk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;880&quot; data-filename=&quot;스크린샷 2026-01-02 오후 5.13.30.png&quot; data-widthpercent=&quot;50.56&quot; style=&quot;width: 49.9739%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cStIfD/dJMcahJQNQz/MVDklWLtfqnqXJEEoyqDdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcStIfD%2FdJMcahJQNQz%2FMVDklWLtfqnqXJEEoyqDdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1804&quot; height=&quot;880&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;소프트웨어가 더 많이 발전하면서 사람들은 .o 파일들을 주고 받게 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.o 파일이 늘어마녀서 사람들이 묶어서 제공하자고 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 파일 묶는 아카이브 도구 ('ar')을 사용 (백업 및 배포에 사용)&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;스크린샷 2026-01-02 오후 6.03.29.png&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;970&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbWgHh/dJMcaaKIzd5/6Uu6jyFQnpJN3iKyJZx94k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbWgHh/dJMcaaKIzd5/6Uu6jyFQnpJN3iKyJZx94k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbWgHh/dJMcaaKIzd5/6Uu6jyFQnpJN3iKyJZx94k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbWgHh%2FdJMcaaKIzd5%2F6Uu6jyFQnpJN3iKyJZx94k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1696&quot; height=&quot;970&quot; data-filename=&quot;스크린샷 2026-01-02 오후 6.03.29.png&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;970&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러개의 .o 파일을 하나의 아카이브로 'ar'화 하고 아카이브 파일에서 직접적으로 .o 파일들을 읽도록 링커가 향상됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시에는 라이브러리 또는 아카이브라고 불렀음. (오늘날에는 정적 라이브러리라고 부름)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 라이브러리의 수천개의 함수가 프로그램에 복사되었기 때문에 최종 프로그램이 무거워짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적화가 필요했고, 링커가 정적 라이브러리의 모든 .o 파일을 사용하는 대신 정의되지 않은 심볼 문제가 해결되는 경우의 .o 파일만 정적 라이브러리에서 가져옴.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 C 표준 라이브러리 함수를 포함하는 거대한 하나의 lib.a 정적 라이브리러를 구축할 수 있음을 의미.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 프로그램이 하나의 libc.a에 연결할 수 있지만, 각 프로그램은 실제로 필요로하는 libc의 일부만 가져옴.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘날에도 이 모델은 존재.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-02 오후 6.09.35.png&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnlzcJ/dJMcahwi0PD/y5YLMnEQmPAGVpKBVQKMg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnlzcJ/dJMcahwi0PD/y5YLMnEQmPAGVpKBVQKMg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnlzcJ/dJMcahwi0PD/y5YLMnEQmPAGVpKBVQKMg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnlzcJ%2FdJMcahwi0PD%2Fy5YLMnEQmPAGVpKBVQKMg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1804&quot; height=&quot;908&quot; data-filename=&quot;스크린샷 2026-01-02 오후 6.09.35.png&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;908&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각을 .o 파일을 컴파일하면, 위의 이미지처럼 생성되고 회색 박스가 아닌 부분은 정의되지 않은 함수이기 때문이며 심볼의 사용이지 정의를 의미하는 것이 아님.&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/XaSxL/dJMb99562ZT/CWBkXfzZmhcGwstenUsHq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XaSxL/dJMb99562ZT/CWBkXfzZmhcGwstenUsHq0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1798&quot; data-origin-height=&quot;1002&quot; data-filename=&quot;스크린샷 2026-01-02 오후 6.13.01.png&quot; style=&quot;width: 32.357%; margin-right: 10px;&quot; data-widthpercent=&quot;33.13&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XaSxL/dJMb99562ZT/CWBkXfzZmhcGwstenUsHq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXaSxL%2FdJMb99562ZT%2FCWBkXfzZmhcGwstenUsHq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1798&quot; height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w6YjK/dJMcafrJd7Q/nRzC9CX0hWhKIVBa6uLYGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w6YjK/dJMcafrJd7Q/nRzC9CX0hWhKIVBa6uLYGk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;986&quot; data-filename=&quot;스크린샷 2026-01-02 오후 6.14.56.png&quot; style=&quot;width: 32.1871%; margin-right: 10px;&quot; data-widthpercent=&quot;32.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w6YjK/dJMcafrJd7Q/nRzC9CX0hWhKIVBa6uLYGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw6YjK%2FdJMcafrJd7Q%2FnRzC9CX0hWhKIVBa6uLYGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1760&quot; height=&quot;986&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRAuLB/dJMcacu0mHQ/MAevfYO8DLrhqAEoaVC2zK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRAuLB/dJMcacu0mHQ/MAevfYO8DLrhqAEoaVC2zK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;1008&quot; data-filename=&quot;스크린샷 2026-01-03 오전 11.57.45.png&quot; style=&quot;width: 33.1304%;&quot; data-widthpercent=&quot;33.92&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRAuLB/dJMcacu0mHQ/MAevfYO8DLrhqAEoaVC2zK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRAuLB%2FdJMcacu0mHQ%2FMAevfYO8DLrhqAEoaVC2zK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1852&quot; height=&quot;1008&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;1, 2번의 플로우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;bar.o와 baz.o를 정적 라이브러리 하나로 합치며, 두개의 .o파일과 이 정적 라이브러리를 링크.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 일어나는 일을 순서대로 알아보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. 링커는 명령어 라인 순서대로 파일을 읽어나감.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 먼저 찾은건 main.o이고, main.o를 로드하고 심볼 테이블에 보이는 main대한 정의를 찾음.&lt;/li&gt;
&lt;li&gt;하지만 main 안에 정의되지 않은 foo가 있다는 것도 찾음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2. 그 다음 링커는 명령어 라인의 다음 파일 foo.o를 파싱.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 파일은 foo에 대한 정의를 추가하며 즉, 더 이상 정의되지 않은 foo가 아닌 것.&lt;/li&gt;
&lt;li&gt;하지만 foo.o의 로드는 정의되지 않은 새 심볼 bar또한 추가&lt;/li&gt;
&lt;li&gt;명령어 라인의 모든 .o파일이 로드되어서 링커는 정의되지 않은 심볼이 남아 있는지 확인&lt;/li&gt;
&lt;li&gt;이 경우 아직 'bar'가 정의되지 않았기 때문에 링커는 명령어 라인의 라이브러리들을 살펴보면서 정의되지 않은 'bar' 심볼을 충족하는 라이브러리를 찾음&lt;/li&gt;
&lt;li&gt;링커가 정적 라이브러리 bar.o에서 'bar' 심볼이 정의되는 것을 찾음&lt;/li&gt;
&lt;li&gt;따라서 링커는 아카이브(libbarbaz.a)로 부터 bar.o를 로드&lt;/li&gt;
&lt;li&gt;이 시점에서는 더 이상 정의되지 않은 심볼이 없어서 따라서 라이브러리에서 찾기를 중단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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/bHJHJX/dJMcahpyCXS/TIWM65KT2C2Q2Z9kGrlkk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHJHJX/dJMcahpyCXS/TIWM65KT2C2Q2Z9kGrlkk1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1742&quot; data-origin-height=&quot;1002&quot; data-filename=&quot;스크린샷 2026-01-03 오후 12.15.01.png&quot; style=&quot;width: 49.618%; margin-right: 10px;&quot; data-widthpercent=&quot;50.2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHJHJX/dJMcahpyCXS/TIWM65KT2C2Q2Z9kGrlkk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHJHJX%2FdJMcahpyCXS%2FTIWM65KT2C2Q2Z9kGrlkk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1002&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwLa9K/dJMcacaHxwY/FtgshQfeLaQeCwZII5Jr81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwLa9K/dJMcacaHxwY/FtgshQfeLaQeCwZII5Jr81/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1728&quot; data-origin-height=&quot;1002&quot; data-filename=&quot;스크린샷 2026-01-03 오후 12.15.39.png&quot; style=&quot;width: 49.2192%;&quot; data-widthpercent=&quot;49.8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwLa9K/dJMcacaHxwY/FtgshQfeLaQeCwZII5Jr81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwLa9K%2FdJMcacaHxwY%2FFtgshQfeLaQeCwZII5Jr81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1728&quot; height=&quot;1002&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3. 링커는 다음 단계로 이동해 프로그램에 포함 될 모든 함수와 데이터에 주소를 할당
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 함수와 데이터를 출력 파일에 복사하여 출력 프로그램을 완성&lt;/li&gt;
&lt;li&gt;baz.o는 정적 라이브러리에 있지만, 프로그램에는 로드되지 않음.&lt;/li&gt;
&lt;li&gt;링커가 정적 라이브러리에서 선택적으로 로드한 방식 때문에 로드되지 않음.&lt;/li&gt;
&lt;li&gt;이 방식은 분명하진 않아도 정적 라이브러리의 핵심적인 측면.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 정적 링킹 및 정적 라이브러리의 기본과 이해.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 12.19.37.png&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;880&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HdcmT/dJMcab3VMCT/zr6d6DHBf4c3KK435CejGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HdcmT/dJMcab3VMCT/zr6d6DHBf4c3KK435CejGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HdcmT/dJMcab3VMCT/zr6d6DHBf4c3KK435CejGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHdcmT%2FdJMcab3VMCT%2Fzr6d6DHBf4c3KK435CejGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1794&quot; height=&quot;880&quot; data-filename=&quot;스크린샷 2026-01-03 오후 12.19.37.png&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;880&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;ld64 최적화에 많은 시간을 들여서 링커는 많은 프로젝트에서 2배 더 빠름.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링커 작업을 병렬로 수행하도록 여러개의 코어를 사용할 수 있는 여러 영역을 찾음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 12.31.52.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eeTpJf/dJMcac9yTjP/l4wpyXj4LuRdkQ0L0y9jx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eeTpJf/dJMcac9yTjP/l4wpyXj4LuRdkQ0L0y9jx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eeTpJf/dJMcac9yTjP/l4wpyXj4LuRdkQ0L0y9jx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeeTpJf%2FdJMcac9yTjP%2Fl4wpyXj4LuRdkQ0L0y9jx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;992&quot; data-filename=&quot;스크린샷 2026-01-03 오후 12.31.52.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;992&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 파일의 내용을 출력 파일로 복사&lt;/li&gt;
&lt;li&gt;lINKEDIT의 부분을 병렬 구축&lt;/li&gt;
&lt;li&gt;UUID 계산을 변경하고 코드 서명 해시를 병렬로 수행하는 것도 포함&lt;/li&gt;
&lt;li&gt;여러 알고리즘을 개선
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 심볼의 문자열 슬라이스를 나타내는데 C++의 string_view 객체를 사용하게 되면 exports-trie builder가 굉장히 잘 작동하는 것을 볼 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;바이너리의 UUID를 계산할 때 최신 암호화 라이브러리를 사용해 하드웨어 가속을 활용&lt;/li&gt;
&lt;li&gt;다른 알고리즘 개선도 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 1.06.27.png&quot; data-origin-width=&quot;1788&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEPL7E/dJMcagKWN0a/xhMT1uQBIhROyXecCfnTuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEPL7E/dJMcagKWN0a/xhMT1uQBIhROyXecCfnTuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEPL7E/dJMcagKWN0a/xhMT1uQBIhROyXecCfnTuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEPL7E%2FdJMcagKWN0a%2FxhMT1uQBIhROyXecCfnTuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1788&quot; height=&quot;864&quot; data-filename=&quot;스크린샷 2026-01-03 오후 1.06.27.png&quot; data-origin-width=&quot;1788&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링커의 성능 개선 작업 동안 일부 앱에서 링크 시간에 영향을 미리는 구성 문제를 발견.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 프로젝트에서 링크 시간 개선을 위해 할 수 있는 다섯 가지 주제를 다룸.&lt;/p&gt;
&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;스크린샷 2026-01-03 오후 1.09.14.png&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqbBNs/dJMcajniWVQ/6mFVZxEFPmtybWuSArvuaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqbBNs/dJMcajniWVQ/6mFVZxEFPmtybWuSArvuaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqbBNs/dJMcajniWVQ/6mFVZxEFPmtybWuSArvuaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqbBNs%2FdJMcajniWVQ%2F6mFVZxEFPmtybWuSArvuaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1746&quot; height=&quot;836&quot; data-filename=&quot;스크린샷 2026-01-03 오후 1.09.14.png&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;836&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. 정적 라이브러리의 사용 여부&lt;/li&gt;
&lt;li&gt;2. 링크 시간에 큰 영향을 주는 세가지 옵션 (잘 알려지지 않음)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`-all_load` or `-force_load`&lt;/li&gt;
&lt;li&gt;`-no_exported_symbols`&lt;/li&gt;
&lt;li&gt;`-no_deduplicate`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;3. 정적 링크의 속성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 1.13.05.png&quot; data-origin-width=&quot;1806&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xmNz5/dJMcaiWfWe8/NIxGyVrqi78tk62UBmZH4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xmNz5/dJMcaiWfWe8/NIxGyVrqi78tk62UBmZH4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xmNz5/dJMcaiWfWe8/NIxGyVrqi78tk62UBmZH4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxmNz5%2FdJMcaiWfWe8%2FNIxGyVrqi78tk62UBmZH4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1806&quot; height=&quot;644&quot; data-filename=&quot;스크린샷 2026-01-03 오후 1.13.05.png&quot; data-origin-width=&quot;1806&quot; data-origin-height=&quot;644&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. 정적 라이브러리에 빌드되는 소스 파일에 열심히 작업해서 빌드 시간이 느려지는 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일이 컴파일 되고 나면 목차를 포함한 정적 라이브러리 전체를 다시 빌드해야 하기 때문에 이런 현상이 발생
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단지 추가적인 I/O가 많아지는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;안정적인 코드에는 정적 라이브러리그 가장 적합
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드가 활발하게 변경되지 않는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;빌드 시간을 줄이기 위해서는 현재 개발 중인 코드를 정적 라이브러리 밖으로 이동하는 것을 고려&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2. 아카이브에서 선택적으로 로딩하는 것을 확인했는데, 선택적 로딩의 단점은 링커가 느려진다는 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왜냐하면 빌드를 재현 가능하도록 하면서 전통적인 정적 라이브러리의 의미를 따르기 위해서는 링커가 라이브러리를 고정된 직렬 순서로 처리해야 하기 때문
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, ld64의 병렬화 이점을 정적 라이브러리와 함께 사용할 수 없다는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이런 기록하는 동작이 별로 필요하지 않은 경우라면 링커 옵션을 사용해 빌드 속도를 높일 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 1.48.47.png&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mUTcn/dJMcad1G71P/BTSpubWj8Uv6dYs3xIEVzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mUTcn/dJMcad1G71P/BTSpubWj8Uv6dYs3xIEVzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mUTcn/dJMcad1G71P/BTSpubWj8Uv6dYs3xIEVzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmUTcn%2FdJMcad1G71P%2FBTSpubWj8Uv6dYs3xIEVzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1826&quot; height=&quot;684&quot; data-filename=&quot;스크린샷 2026-01-03 오후 1.48.47.png&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;링커 옵션 `-all_load`
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 정적 라이브러리의 모든 .o 파일을 로드하도록 링커에 지시&lt;/li&gt;
&lt;li&gt;이는 앱이 선택적 로딩을 하지만 결국 모든 정적 라이브러로부터 대부분의 콘텐츠를 로드하는 경우라면 유용&lt;/li&gt;
&lt;li&gt;`-all_load`의 사용은 링커가 모든 정적 라이브러리와 해당 콘텐츠를 병렬로 파싱할 수 있도록 함.&lt;/li&gt;
&lt;li&gt;동일한 심볼을 구현하는 여러 정적 라이브러리가 있어서 명령어 라인의 정적 라이브러리 순서에 의존해 어떤 구현을 사용할지 정하는 방식이라면 이 옵션은 적합하지 않음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;링커가 모든 구현을 로드하기 때문에 일반 정적 링킹 모드에서 찾을 수 있는 심볼의 의미를 가져오리란 법이 없기 때문.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;`-all_load`의 또 다른 단점은 프로그램이 무거워 질 수 있다는 것 `unused` 코드가 계속 추가되기 때문.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이를 보완하기 위해서 `-dead_strip` 링커 옵션을 사용할 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 옵션으로 연결 불가한 코드와 데이터를 링커가 제거&lt;/li&gt;
&lt;li&gt;`-dead_strip` 알고리즘은 빠르게 출력 파일의 사이즈를 줄임으로써 자체적으로 크기를 절약&lt;/li&gt;
&lt;li&gt;`all_load`, `dead_strip`를 함께 사용한다면 이들 옵션의 유무에 따른 링커 시간을 측정해서 우리 케이스에 유리한 선택인지 확인해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;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;스크린샷 2026-01-03 오후 2.32.58.png&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z2Q7V/dJMcajt4Avw/KvgZu7whixKRhtoyv7juB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z2Q7V/dJMcajt4Avw/KvgZu7whixKRhtoyv7juB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z2Q7V/dJMcajt4Avw/KvgZu7whixKRhtoyv7juB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz2Q7V%2FdJMcajt4Avw%2FKvgZu7whixKRhtoyv7juB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;872&quot; data-filename=&quot;스크린샷 2026-01-03 오후 2.32.58.png&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;872&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;링커 옵션 `-no_exported_symbols`
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;링커가 생성하는 LINKEDIT 세그먼트의 한 부분은 하나의 exported-trie로 preix trie(접두사 트리)이며 내보낸 모든 심볼 이름, 주소 및 플래그를 인코딩 함.&lt;/li&gt;
&lt;li&gt;모든 dylib에는 내보낸 심볼이 있어야 하는 반면 메인 앱 바이너리는 일반적으로 내보낸 심볼이 필요하지 않음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 일반적으로 메인 실행 파일에서는 심볼을 조회하지 않음.&lt;/li&gt;
&lt;li&gt;이런 케이스에 `-no_exported_symbols`를 사용하여 앱 타겟에 대해 LINKEDIT에서 trie 데이터 구조 생성을 건너뛰도록 할 수 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 링크 시간이 개선됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메인 앱 실행 파일로 다시 연결되는 플러그인을 로드하거나 xctest 번들 실행을 위해 호스트 환경으로 앱과 xctest를 사용하면 앱에 exports가 있어야 함.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 해당 구성에서는 `-no_exported_symbols`를 사용할 수 없다는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사이즈가 큰 경우엔 exports trie의 억제가 합리적임.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dyld_info 명령을 실행하여 내보낸 심볼의 수를 계산할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;해당 옵션을 통해 링커가 해당 심볼 수에 대한 exports trie를 빌드하는데 시간을 단축할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 2.58.32.png&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLTIwg/dJMcafkXDgK/5A4kvMPsXkCiF6EgLGPze1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLTIwg/dJMcafkXDgK/5A4kvMPsXkCiF6EgLGPze1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLTIwg/dJMcafkXDgK/5A4kvMPsXkCiF6EgLGPze1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLTIwg%2FdJMcafkXDgK%2F5A4kvMPsXkCiF6EgLGPze1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1814&quot; height=&quot;816&quot; data-filename=&quot;스크린샷 2026-01-03 오후 2.58.32.png&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;816&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;링커 옵션 `-no_deduplicate`&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령은 같지만 이름이 다른 함수를 병합하기 위해서 새로운 패스를 추가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++의 템플릿 확장으로 그런 함수들을 많이 얻음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;알고리즘은 비싸며 중복을 찾기 위해서 링커는 모든 함수의 명령을 반복적으로 해시해야 하기 때문 
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비용 문제 때문에 링커가 weak-def된 심볼만 살펴보도록 알고리즘을 제한함&amp;nbsp;&lt;/li&gt;
&lt;li&gt;템플릿 확장으로 C++ 컴파일러가 내보내는 인라인 되지 않은 것들을 제한.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;de-dup 중복 제거는 사이즈 최적화로 디버그 빌드는 사이즈가 아닌 속도 빠른 빌드에 관함.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Xcode는 디버그 구성을 위해 링커에 `-no_deduplicate`를 전달해 de-dup 최적화를 비활성화 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;clang 링크 라인을 `-O0`으로 실행할 경우 no-dedup 옵션을 링커에 전달.&lt;/li&gt;
&lt;li&gt;요약하자면 C++를 사용하고 custom build system를 가진 경우 링크 시간 개선을 위해서 디버그 빌드가 `no_dedeuplcate`를 추가하도록 해야함.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Xcode에서 비표준 구성을 사용하거나 다른 빌드 시스템을 사용하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 3.05.54.png&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NTCwS/dJMb99LN0A6/GRRIBHhdoc3BHlqakmOtnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NTCwS/dJMb99LN0A6/GRRIBHhdoc3BHlqakmOtnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NTCwS/dJMb99LN0A6/GRRIBHhdoc3BHlqakmOtnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNTCwS%2FdJMb99LN0A6%2FGRRIBHhdoc3BHlqakmOtnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1352&quot; height=&quot;560&quot; data-filename=&quot;스크린샷 2026-01-03 오후 3.05.54.png&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;560&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;Xcode Build Settings &amp;gt; Other Linker Flags에서 변경할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`all_load`, `-Wl`, `-no_exported_symbols`, `no_deduplicate` 등이 존재&lt;/p&gt;
&lt;p data-ke-size=&quot;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;스크린샷 2026-01-03 오후 4.22.01.png&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;876&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doSPZZ/dJMcadtRC9B/Xjbo7ZewB7xUKt4yyrblHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doSPZZ/dJMcadtRC9B/Xjbo7ZewB7xUKt4yyrblHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doSPZZ/dJMcadtRC9B/Xjbo7ZewB7xUKt4yyrblHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoSPZZ%2FdJMcadtRC9B%2FXjbo7ZewB7xUKt4yyrblHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1852&quot; height=&quot;876&quot; data-filename=&quot;스크린샷 2026-01-03 오후 4.22.01.png&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;876&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 라이브러리에 소스 코드가 빌드되고 앱이 이 라이브러리에 연결되지만 해당 코드는 최종 앱에 포함되지 않음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 어떤 함수에 `__attribute__((used))`를 추가했다거나 Objective-C 카테고리가 있는 경우 링커가 선택적 로딩을 수행하기 때문에 정적 라이브러리에 있는 .o 파일이 링크 과정 중에 필요한 일부 심볼 또한 정의하지 않는다면 해당 .o 파일은 링커가 로드하지 않게 됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정적 라이브러리와 `Dead stripping`간의 상호작용도 흥미로운데, 정적 라이브러리의 문제를 숨길 수 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적으로 심볼의 누락이나 중복되는 심볼은 링커의 오류를 유발&lt;/li&gt;
&lt;li&gt;하지만 `Dead stripping`은 링커가 메인에서 시작해서 모든 코드와 데이터에 걸쳐 도달 가능한 패스를 실행하도록 해서 심볼의 누락이 연결할 수 없는 코드에서 온 것으로 판명되면 링커가 심볼 누락 오류를 억제하도록 만듦.&lt;/li&gt;
&lt;li&gt;유사하게 정적 라이브러리에 중복되는 심볼이 있는 경우에 링커는 첫번째 것을 선택하고 오류를 내뱉지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정적 라이브러리를 사용할 때 하나의 정적 라이브러리가 여러 프레임워크에 통합되는 경우 각각의 프레임워크는 개별적으로는 잘 실행되지만 어느 시점에서 일부 앱은 두 가지 프레임워크를 모두 사용하게 되고 이는 런타임에 문제를 발생시킴.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 흔한 경우는 Objective-C 런타임 경고이며 동일한 클래스 명의 여러 인스턴스에 대한 경고.&lt;/li&gt;
&lt;li&gt;종합적으로 정적 라이브러리는 강력하며 이러한 위험을 피하기 위해서는 정확한 이해가 필요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.03.24.png&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s8rC9/dJMcaa41qol/ajMjyYjQeMCstLwvP0Uk70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s8rC9/dJMcaa41qol/ajMjyYjQeMCstLwvP0Uk70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s8rC9/dJMcaa41qol/ajMjyYjQeMCstLwvP0Uk70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs8rC9%2FdJMcaa41qol%2FajMjyYjQeMCstLwvP0Uk70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1802&quot; height=&quot;856&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.03.24.png&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;856&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;/p&gt;
&lt;p data-ke-size=&quot;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;스크린샷 2026-01-03 오후 7.04.55.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpBBEc/dJMcagRHyT4/fHj9H2ZKxIoX3GvVVJta4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpBBEc/dJMcagRHyT4/fHj9H2ZKxIoX3GvVVJta4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpBBEc/dJMcagRHyT4/fHj9H2ZKxIoX3GvVVJta4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpBBEc%2FdJMcagRHyT4%2FfHj9H2ZKxIoX3GvVVJta4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;808&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.04.55.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;808&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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용할 수 있는 라이브러리가 점점 많아질수록 최종 프로그램의 크기가 커짐.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시간에 따라 해당 프로그램 빌드에 들어가는 정적 링크 시간도 증가.&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ea9Cr2/dJMcaaDXfj7/vfOALY1e1PGcUpNTPHIY3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ea9Cr2/dJMcaaDXfj7/vfOALY1e1PGcUpNTPHIY3K/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;900&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.06.22.png&quot; style=&quot;width: 49.6085%; margin-right: 10px;&quot; data-widthpercent=&quot;50.19&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ea9Cr2/dJMcaaDXfj7/vfOALY1e1PGcUpNTPHIY3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fea9Cr2%2FdJMcaaDXfj7%2FvfOALY1e1PGcUpNTPHIY3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1714&quot; height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dm3fsz/dJMcaiva4A2/m83WKDf8ULE8M0ywRkVaZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dm3fsz/dJMcaiva4A2/m83WKDf8ULE8M0ywRkVaZk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1716&quot; data-origin-height=&quot;908&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.06.33.png&quot; style=&quot;width: 49.2288%;&quot; data-widthpercent=&quot;49.81&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dm3fsz/dJMcaiva4A2/m83WKDf8ULE8M0ywRkVaZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdm3fsz%2FdJMcaiva4A2%2Fm83WKDf8ULE8M0ywRkVaZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1716&quot; height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;'ar'에서 'ld'로 변경&lt;/figcaption&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;'ar'에서 'ld'로 바꾼다면 출력 라이브러리는 이제 실행 가능한 바이너리가 됨.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;'ar'에서 'ld'로 바꾸는 것이 90년대 동적 라이브러리의 시작.&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;약칭으로 동적 라이브러리를 'dylibs'라고 함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다른 플랫폼에서는 'DSO'(Dynamic Shared Objects), 'DLL(Dynamic-Link Library)'이라고 부름.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.11.32.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dj9aMf/dJMcaiva4Gg/9p9CyAYwFvtnG3bLamgoDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dj9aMf/dJMcaiva4Gg/9p9CyAYwFvtnG3bLamgoDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dj9aMf/dJMcaiva4Gg/9p9CyAYwFvtnG3bLamgoDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdj9aMf%2FdJMcaiva4Gg%2F9p9CyAYwFvtnG3bLamgoDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;808&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.11.32.png&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;808&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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라이브러리에서 최종 프로그램으로 코드를 복사하는 것 대신 링커가 일종의 Promise를 기록함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동적 라이브러리에서 사용되는 심볼 및 런타임 시 라이브러리의 경로를 기록하는 것.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이것의 장점으로는 프로그램 파일 사이즈가 개발자의 통제 하에 있을 수 있게 됨.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 우리가 작성한 코드와 런타임에 필요한 동적 라이브러리 목록만 포함됨.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라이브러리 코드의 복사본을 더 이상 프로그램에 받지 않게 됨.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로그램의 정적 링크 시간은 이제 코드 크기에 비례하고 링크하는 dylib 수와는 무관함.&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;스크린샷 2026-01-03 오후 7.19.37.png&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRKSvU/dJMcaa41qzj/oKDRwlpM1kNA89oqfDEptk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRKSvU/dJMcaa41qzj/oKDRwlpM1kNA89oqfDEptk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRKSvU/dJMcaa41qzj/oKDRwlpM1kNA89oqfDEptk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRKSvU%2FdJMcaa41qzj%2FoKDRwlpM1kNA89oqfDEptk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1816&quot; height=&quot;826&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.19.37.png&quot; data-origin-width=&quot;1816&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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여러 프로세스에서 동일한 동적 라이브러리가 사용되는 경우 가상 시스템은 그 dylib를 사용하는 모든 프로세스에서 해당 dylib에 대해 동일한 물리적 RAM 페이지를 재사용함.&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;/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;스크린샷 2026-01-03 오후 7.26.59.png&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnDDzS/dJMcagYsO0E/N0kcWl1yKYKvAJw9v7G7N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnDDzS/dJMcagYsO0E/N0kcWl1yKYKvAJw9v7G7N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnDDzS/dJMcagYsO0E/N0kcWl1yKYKvAJw9v7G7N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnDDzS%2FdJMcagYsO0E%2FN0kcWl1yKYKvAJw9v7G7N0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1808&quot; height=&quot;622&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.26.59.png&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;622&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동적 라이브러리 사용의 이점은 빌드 시간 단축&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동적 라이브러리의 비용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. 앱 시작 속도의 둔화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱 시작시 프로그램 파일 하나만 로드하는 것이 아니기 때문&lt;/li&gt;
&lt;li&gt;모든 dylib들이 로드되고 함께 연결되어야 함.&lt;/li&gt;
&lt;li&gt;빌드 시간에서 시작 시간으로 링킹 비용의 일부를 전가.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2. 동적 라이브러리 기반의 프로그램은 더티 페이지가 더 많음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 라이브러리의 경우 모든 정적 라이브러리의 모든 부분들을 링커가 메인 실행 파일의 동일한 DATA 페이지에 다 같이 배치&lt;/li&gt;
&lt;li&gt;하지만 dylib를 사용하면 DATA페이지가 각 라이브러리에 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;3. dyld라는 동적 링커가 필요함.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 시에 실행 파일에 기록된다던 Promise 부분에서 라이브러리를 로드하기 위해 Promise를 처리할 무언가가 런타임에 필요하기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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/XQTK8/dJMcagKWTFm/BbkoJg9fISPeqso2hvseD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XQTK8/dJMcagKWTFm/BbkoJg9fISPeqso2hvseD1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;872&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.28.07.png&quot; style=&quot;width: 51.4531%; margin-right: 10px;&quot; data-widthpercent=&quot;52.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XQTK8/dJMcagKWTFm/BbkoJg9fISPeqso2hvseD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXQTK8%2FdJMcagKWTFm%2FBbkoJg9fISPeqso2hvseD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;872&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bckPi5/dJMcabCRbZU/KLyGCenocvLkM7vkU2VeG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bckPi5/dJMcabCRbZU/KLyGCenocvLkM7vkU2VeG1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;950&quot; data-filename=&quot;스크린샷 2026-01-03 오후 7.35.59.png&quot; style=&quot;width: 47.3841%;&quot; data-widthpercent=&quot;47.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bckPi5/dJMcabCRbZU/KLyGCenocvLkM7vkU2VeG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbckPi5%2FdJMcabCRbZU%2FKLyGCenocvLkM7vkU2VeG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1828&quot; height=&quot;950&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;이것이 바로 dyld 동적 링커의 용도임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 링킹이 런타임에서의 동작&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. 실행 가능한 바이너리는 세그먼트로 분할
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적으로 최소한 TEXT, DATA, LINKEDIT 으로 분할.&lt;/li&gt;
&lt;li&gt;세그먼트는 항상 OS 사이즈의 배수로 각 세그먼트는 다른 권한을 가짐&lt;/li&gt;
&lt;li&gt;예를 들어 TEXT는 '실행' 권한을 가짐&lt;/li&gt;
&lt;li&gt;CPU가 페이지의 바이트를 기계 명령어로 처리 가능함을 의미.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2. 런타임 시 dyld는 여기 보이는 것처럼 각 세그먼트의 권한을 받아 실행 파일들을 메모리에 mmap() 해야 함.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세그먼트들은 페이지 사이즈이고 페이지 정렬되기 때문에 가상 메모리 시스템이 프로그램이나 dylib 파일을 VM 범위의 백업 저장소로 설정하는 것이 간단함.&lt;/li&gt;
&lt;li&gt;즉, 해당 페이지에 메모리 접근이 있을때 까지 RAM에 아무것도 로드되지 않으며 이로 인해 페이지 폴트가 발생하고 이는 VM 시스템이 파일의 적절한 하위 범위를 읽고 그 콘텐츠로 필요한 RAM 페이지를 채우도록 만듦.&lt;/li&gt;
&lt;li&gt;하지만 매핑만으로는 충분하지 않고 어떻게든 프로그램은 연결(link)되거나 dylib에 바인딩되어야 하는데, 이를 위해서 'fix ups'이란 개념이 존재함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 9.22.42.png&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdKr2V/dJMcah39gnB/vcsCtZjjO6rbD7dYXFnw9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdKr2V/dJMcah39gnB/vcsCtZjjO6rbD7dYXFnw9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdKr2V/dJMcah39gnB/vcsCtZjjO6rbD7dYXFnw9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdKr2V%2FdJMcah39gnB%2FvcsCtZjjO6rbD7dYXFnw9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1802&quot; height=&quot;814&quot; data-filename=&quot;스크린샷 2026-01-03 오후 9.22.42.png&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;814&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;TEXT, DATA, LINKEDIT으로 구성된 mach-o 파일을 보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TEXT는 불변이며, 코드 서명을 기반으로 하는 시스템 안에 있어야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;malloc의 상대주소는 프로그램이 빌드될 때 알 수 없고, 일어나는 일은 malloc이 dylib에 있는 것을 보고 정적 링커가 호출 사이트를 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 동일한 TEXT 세그먼트의 링커에 의해서 호출 사이트는 스텁에 대한 호출이 됨.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 빌드시에 상대 주소가 알려지고 이는 bl 명령어가 올바르게 형성될 수 있음을 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로인해서 스텁이 DATA로 부터 포인터를 로드하고 해당 위치로 점프함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 런타임에서는 TEXT에 대한 변경이 필요하지 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dyld에 의해 DATA만 변경됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dyld를 이해하는데 중요한 것은 dyld에 의한 모든 수정이 단지 DATA에 대한 dyld의 포인터 설정 뿐이라는 것.&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/bulG9U/dJMcajni4Ji/f8RBSjKQWlvhi8owX5qR11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bulG9U/dJMcajni4Ji/f8RBSjKQWlvhi8owX5qR11/img.png&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;890&quot; data-filename=&quot;스크린샷 2026-01-03 오후 9.39.12.png&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.7639%; margin-right: 10px;&quot; data-widthpercent=&quot;49.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bulG9U/dJMcajni4Ji/f8RBSjKQWlvhi8owX5qR11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbulG9U%2FdJMcajni4Ji%2Ff8RBSjKQWlvhi8owX5qR11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1816&quot; height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9PYKG/dJMcah39gQg/Ml6G9L4oAAtTvQJtVspioK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9PYKG/dJMcah39gQg/Ml6G9L4oAAtTvQJtVspioK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;882&quot; data-filename=&quot;스크린샷 2026-01-03 오후 9.50.27.png&quot; style=&quot;width: 50.0733%;&quot; data-widthpercent=&quot;50.66&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9PYKG/dJMcah39gQg/Ml6G9L4oAAtTvQJtVspioK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9PYKG%2FdJMcah39gQg%2FMl6G9L4oAAtTvQJtVspioK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1848&quot; height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Rebases, Binds&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;dyld가 수행하는 수정사항에 대해 더 자세히 보자면 LINKEDIT 어딘가에 수정 작업에 써야하는 정보가 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'fixups'에는 두가지 유형이 존재.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫번째 유형은 Rebases
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dylib 또는 app 자체 내에서 가리키는 포인터를 갖는 경우.&lt;/li&gt;
&lt;li&gt;ASLR이라는 보안기능이 존재하는데 dyld가 임의의 주소에서 dylib를 로드하도록 만듦.&lt;/li&gt;
&lt;li&gt;따라서 내부 포인터는 빌드 시에 설정할 수 없으며, 이런 포인터들은 시작 시에 dyld가 조정하거나 'Rebases'를 해야 함.&lt;/li&gt;
&lt;li&gt;dylib가 주소 0에서 로드되는 경우에 디스크에서 이런 포인터들은 target address들을 포함함.&lt;/li&gt;
&lt;li&gt;따라서 LINKEDIT이 기록해야 하는건 각 'Rebases'의 위치가 전부임.&lt;/li&gt;
&lt;li&gt;dyld는 dylib의 실제 로드 주소를 각 'Rebases' 위치에 추가해 올바르게 수정할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;두번째 유형은 Binds
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'Binds'는 symbolic reference로 target이 숫자가 아닌 symbol 이름.&lt;/li&gt;
&lt;li&gt;'malloc' 함수에 대한 포인터를 예로 하자면, 문자열 '_malloc'는 실제로 LINKEDIT에 저장되며, dyld는 해당 문자열을 사용해 libSystem.dylib의 exports trie에서 mallo의 실제 주소를 조회&lt;/li&gt;
&lt;li&gt;그 다음 dyld는 해당 값을 'Binds'가 지정한 위치에 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-03 오후 9.58.23.png&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1010&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PASCO/dJMcahb0rv7/MNg39Ks3Sp9TMfAsesXS71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PASCO/dJMcahb0rv7/MNg39Ks3Sp9TMfAsesXS71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PASCO/dJMcahb0rv7/MNg39Ks3Sp9TMfAsesXS71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPASCO%2FdJMcahb0rv7%2FMNg39Ks3Sp9TMfAsesXS71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1808&quot; height=&quot;1010&quot; data-filename=&quot;스크린샷 2026-01-03 오후 9.58.23.png&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1010&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;WWDC22에서는 fixups을 인코딩하는 새로운 방법으로 'chained fixups'을 새롭게 발표.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'chained fixups'의 첫번째 장점은 LINKEDIT이 더 작아짐.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 수정 위치를 저장하는 대신 새 형식은 각 DATA 페이지의 첫번째 수정 위치와 가져온 심볼 목록만을 저장하기 때문.&lt;/li&gt;
&lt;li&gt;나머지 정보는 최종적으로 'fixups'이 고정될 위치인 DATA 세그먼트 자체에 인코딩.&lt;/li&gt;
&lt;li&gt;'fixups' 위치가 서로 연결되어 있기 때문에 'chained fixups'이라고 명명함&lt;/li&gt;
&lt;li&gt;LINKEDIT은 첫번째 수정이 있었던 위치만 알려주고 DATA의 64bit 포인터 위치에 있는 일부 비트들은 다음 'fixups' 위치에 대한 오프셋을 포함함&lt;/li&gt;
&lt;li&gt;이 안에 fixups이 'Binds'인지 'Rebases'인지 알려주는 bit도 들어있음.&lt;/li&gt;
&lt;li&gt;'Binds'의 경우 나머지 bit는 심볼의 인덱스가 됨.&lt;/li&gt;
&lt;li&gt;'Rebases'의 경우 나머지 bit는 이미지 내 target에 대한 오프셋이 됨.&lt;/li&gt;
&lt;li&gt;'chained fixups'에 대한 런타임 지원은 iOS 13.4이상부터 존재.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chained fixups을 제대로 이해하기 위해서는 dyld가 작동하는 방식에 대해서 알아야 함.&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;스크린샷 2026-01-03 오후 10.07.02.png&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkE8CY/dJMcabv6kpu/EhoLRUCS0lhmOkZf4FnCH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkE8CY/dJMcabv6kpu/EhoLRUCS0lhmOkZf4FnCH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkE8CY/dJMcabv6kpu/EhoLRUCS0lhmOkZf4FnCH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkE8CY%2FdJMcabv6kpu%2FEhoLRUCS0lhmOkZf4FnCH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1804&quot; height=&quot;864&quot; data-filename=&quot;스크린샷 2026-01-03 오후 10.07.02.png&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dyld는 메인 실행 파일에서 시작.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱이라고 가정하면 mach-o를 파싱하여 종속 dylib들을 찾는데, 필요한 동적 라이브러리를 찾는 것.&lt;/li&gt;
&lt;li&gt;해당 dylib들을 찾아 mmap()을 수행.&lt;/li&gt;
&lt;li&gt;그 다음 이들 각각에 대해 재귀적으로 mach-o 구조를 파싱하는데 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;필요에 따라 추가적으로 dylib를 로드하면서 진행함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;모든 것이 로딩되면 dyld는 필요한 모든 'Binds' 심볼을 조회하고 fixups을 수행할 때 해당 주소를 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;모든 fixups이 완료되면 dyld는 이니셜라이저를 상향식으로 실행.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;애플은 5년전에 이를 발표했으며 프로그램과 앱이 실행될 때마다 녹색 단계들이 매번 동일.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;dylib들이 변경되지 않는 한 모든 녹색 단계는 첫번째 실행에서 캐시되고 후속 실행에서는 재사용 될 수 있음.&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;스크린샷 2026-01-03 오후 11.58.25.png&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1018&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kWnnW/dJMcahXo9za/kTkPb4bdk33yudZmhyq8S1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kWnnW/dJMcahXo9za/kTkPb4bdk33yudZmhyq8S1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kWnnW/dJMcahXo9za/kTkPb4bdk33yudZmhyq8S1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkWnnW%2FdJMcahXo9za%2FkTkPb4bdk33yudZmhyq8S1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1814&quot; height=&quot;1018&quot; data-filename=&quot;스크린샷 2026-01-03 오후 11.58.25.png&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1018&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;WWDC22에서는 dyld의 새로운 속성으로 Page-in linking을 발표.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dyld가 모든 fixups을 실행시에 모든 dylib에 적용하는 대신 커널이 fixups을 DATA 페이지에 lazy하게 적용할 수 있음.&lt;/li&gt;
&lt;li&gt;mmap() 처리된 영역의 일부 페이지에서 일부 주소를 처음 사용하면 커널이 해당 페이지를 읽도록 유발하는 경우가 항상 있었음.&lt;/li&gt;
&lt;li&gt;그러나 이제 DATA 페이지의 경우 커널은 해당 페이지에 필요한 fixup도 적용함.&lt;/li&gt;
&lt;li&gt;10년이 넘게 dyld 공유 캐시에 있는 OS dylib에 대한 page-in linking 특수 케이스가 존재했었음.&lt;/li&gt;
&lt;li&gt;WWDC22부터는 이를 일반화하여 모든 사람들이 사용할 수 있도록 만듦
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 매커니즘은 더티 메모리와 실행 시간을 줄임.&lt;/li&gt;
&lt;li&gt;이는 DATA_CONST 페이지가 깨끗하다는 의미이고 TEXT 페이지처럼 축출과 재생성이 가능해서 메모리 압박이 줄어듦&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Page-in linking은 chained-fixups로 빌드된 바이너리에만 작동.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왜냐하면 chained-fixups을 사용하면 대부분의 fixups 섲정보가 디스크의 DATA 세그먼트에 인코딩되고 Page-in linking 중에 커널에서 사용할 수 있기 때문&lt;/li&gt;
&lt;li&gt;주의할 것은 dyld가 이 매커니즘을 launch 중에만 사용한다는 것.&lt;/li&gt;
&lt;li&gt;나중에 dlopen()된 dylib들은 Page-in linking되지 못하는데, 이 경우에는 dyld는 전통적인 경로를 사용하고 fixups을 dlopen 호출 중에 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.01.39.png&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t6KVh/dJMcagEcaVV/tA3jfpy8tdNiVsaq1X1zIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t6KVh/dJMcagEcaVV/tA3jfpy8tdNiVsaq1X1zIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t6KVh/dJMcagEcaVV/tA3jfpy8tdNiVsaq1X1zIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft6KVh%2FdJMcagEcaVV%2FtA3jfpy8tdNiVsaq1X1zIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1794&quot; height=&quot;872&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.01.39.png&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;872&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;기존 dyld 워크플로우 다이어그램으로 돌아가자면 첫 실행에서 작업을 캐싱하고 후행 실행에서 이를 재사용함으로써 최적화하는 방향으로 진행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 이후에는 apply fixups 단계에서 fixups를 직접 수행하지 않고 lazy하게 Page-in 단계에서 커널이 하도록 만들어서 최적화 할 수 있음&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;스크린샷 2026-01-04 오전 12.02.06.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;850&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmyjKR/dJMb99ZmgY7/zNOjNJzY0mKumNGSZvfIF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmyjKR/dJMb99ZmgY7/zNOjNJzY0mKumNGSZvfIF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmyjKR/dJMb99ZmgY7/zNOjNJzY0mKumNGSZvfIF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmyjKR%2FdJMb99ZmgY7%2FzNOjNJzY0mKumNGSZvfIF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;850&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.02.06.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;850&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;동적 링크의 성능 개선을 위해 가능한 것 중에서는 기존에 dyld는 이미 동적 링킹의 많은 부분을 최적화 함.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;따라서 우리가 제어할 수 있는건 dylib의 수임.&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;스크린샷 2026-01-04 오전 12.15.10.png&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6aqt0/dJMcahiMoSE/kOs32wtlByYKgmZDdDyzYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6aqt0/dJMcahiMoSE/kOs32wtlByYKgmZDdDyzYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6aqt0/dJMcahiMoSE/kOs32wtlByYKgmZDdDyzYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6aqt0%2FdJMcahiMoSE%2FkOs32wtlByYKgmZDdDyzYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1812&quot; height=&quot;760&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.15.10.png&quot; data-origin-width=&quot;1812&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더 많은 dylib가 있을수록 dyld가 더 많은 로드 작업을 수행해야 하므로 더 적은 dylibs를 사용하면 더 적은 로드 작업을 수행할 수 있음.&lt;/li&gt;
&lt;li&gt;정적 이니셜라이저를 사용하여 최적화 할 수 있는데, 정적 이니셜라이저에서는 I/O 또는 네트워킹을 수행하지 말 것.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;몇 밀리초 이상 소요될 수 있는 작업은 이니셜라이저에서 수행되어서는 안됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;세상이 점점 복잡지고 요구사항이 늘어나면서 코드를 라이브러리로 관리하는게 합리적.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목표는 동적 및 정적 라이브러리 사이의 최적의 지점을 찾는 것.&lt;/li&gt;
&lt;li&gt;너무 많은 정적 라이브러리와 반복적인 빌드/디버그 사이클은 늦어짐.&lt;/li&gt;
&lt;li&gt;반면에 너무 많은 동적 라이브러리와 느린 시작 시간은 사용자가 알아차릴 수 있음.&lt;/li&gt;
&lt;li&gt;WWDC22 이후로는 ld64의 속도를 높였기 때문에 최적의 지점이 변경되었을 수도 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;더 많은 정적 라이브러리 또는 더 많은 소스 파일을 앱에서 직접 사용하면서 여전히 동일한 시간에 빌드할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.15.35.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nThYh/dJMcagc63bf/Pjx0klue2kQ2Pu1VgCG7Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nThYh/dJMcagc63bf/Pjx0klue2kQ2Pu1VgCG7Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nThYh/dJMcagc63bf/Pjx0klue2kQ2Pu1VgCG7Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnThYh%2FdJMcagc63bf%2FPjx0klue2kQ2Pu1VgCG7Uk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;828&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.15.35.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;828&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;링킹 과정을 들여다보는 데 도움이 될 새로운 도구 2가지 발표.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&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/YfeMM/dJMb9957v3O/kn61e8cSk2R49A5TkkDPJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YfeMM/dJMb9957v3O/kn61e8cSk2R49A5TkkDPJ1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;648&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.19.53.png&quot; style=&quot;width: 51.5548%; margin-right: 10px;&quot; data-widthpercent=&quot;52.16&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YfeMM/dJMb9957v3O/kn61e8cSk2R49A5TkkDPJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYfeMM%2FdJMb9957v3O%2Fkn61e8cSk2R49A5TkkDPJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1816&quot; height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCmVlx/dJMcacV4sVN/xvX3c2KsRheUko7YcbiQfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCmVlx/dJMcacV4sVN/xvX3c2KsRheUko7YcbiQfk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;712&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.22.36.png&quot; style=&quot;width: 47.2824%;&quot; data-widthpercent=&quot;47.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCmVlx/dJMcacV4sVN/xvX3c2KsRheUko7YcbiQfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCmVlx%2FdJMcacV4sVN%2FxvX3c2KsRheUko7YcbiQfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;712&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dyld_usage
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dyld가 하는 일을 추적할 수 있음.&lt;/li&gt;
&lt;li&gt;macOS만 실행할 수 있거나 시뮬레이터에서 앱 실행을 추적하거나 앱이 Mac Catalyst 용으로 빌드된 경우에 사용할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&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/nquod/dJMcaiBWpU3/fCnwUH4Pd2yFM92K7UVlbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nquod/dJMcaiBWpU3/fCnwUH4Pd2yFM92K7UVlbK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;684&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.29.40.png&quot; style=&quot;width: 53.4816%; margin-right: 10px;&quot; data-widthpercent=&quot;54.11&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nquod/dJMcaiBWpU3/fCnwUH4Pd2yFM92K7UVlbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnquod%2FdJMcaiBWpU3%2FfCnwUH4Pd2yFM92K7UVlbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1818&quot; height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OYl2j/dJMcafkXLMr/fgE3DI8qL9dVJiEs8QYJVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OYl2j/dJMcafkXLMr/fgE3DI8qL9dVJiEs8QYJVK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;740&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.29.27.png&quot; data-widthpercent=&quot;45.89&quot; style=&quot;width: 45.3556%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OYl2j/dJMcafkXLMr/fgE3DI8qL9dVJiEs8QYJVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOYl2j%2FdJMcafkXLMr%2FfgE3DI8qL9dVJiEs8QYJVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1668&quot; height=&quot;740&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dyld_info
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이를 사용해 디스크와 현재 dyld 캐시 모두에서 바이너리를 검사할 수 있음.&lt;/li&gt;
&lt;li&gt;이 도구에는 많은 옵션이 있지만 export 및 fixups을 보는 방법이 있음.&lt;/li&gt;
&lt;li&gt;-fixups 옵션은 dyld가 처리할 모든 fixups 위치와 그 타겟을 표시함.&lt;/li&gt;
&lt;li&gt;파일이 이전 스타일의 'fixups'이든 새로운 'chained-fixups'이든 관계없이 출력은 동일함.&lt;/li&gt;
&lt;li&gt;-exports 옵션은 dylib에 있는 모든 내보내진 심볼 및 dylib의 시작 부분의 각 심볼에 대한 오프셋을&amp;nbsp; 표시함.&lt;/li&gt;
&lt;li&gt;이 경우 dyld 캐시에 있는 dylib인 Foundation.framework에 대한 정보를 보여줌.&lt;/li&gt;
&lt;li&gt;디스크에 파일이 없지만 dyld_info 도구는 dyld와 동일한 코드를 사용하므로 찾을 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.33.54.png&quot; data-origin-width=&quot;1810&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHQUib/dJMcaaxb1Dp/1Wr9uiRWU3PkZvn23HniQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHQUib/dJMcaaxb1Dp/1Wr9uiRWU3PkZvn23HniQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHQUib/dJMcaaxb1Dp/1Wr9uiRWU3PkZvn23HniQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHQUib%2FdJMcaaxb1Dp%2F1Wr9uiRWU3PkZvn23HniQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1810&quot; height=&quot;964&quot; data-filename=&quot;스크린샷 2026-01-04 오전 12.33.54.png&quot; data-origin-width=&quot;1810&quot; data-origin-height=&quot;964&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱이 대형인데 빌드가 링크하는데 시간이 꽤 오래 소요된다면 더 빠른 링커가 있는 Xcode를 사용하거나&lt;/li&gt;
&lt;li&gt;여전히 정적 링크의 속도도 높이고 싶다면 세가지 링커 옵션을 살펴보고 우리 앱 빌드에서 유의미한 링크 시간 개선이 있는지 확인해보기.&lt;/li&gt;
&lt;li&gt;iOS 13.4 이상에서는 `chained-fixups`를 활성화하여 앱 및 임베디드 프레임워크 빌드해 볼 수 있음.&lt;/li&gt;
&lt;li&gt;그 다음이 iOS 16에서 더 작고 더 빠르게 실행되는지 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=yLXGtXNYhdg&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=yLXGtXNYhdg&lt;/a&gt;&lt;/p&gt;</description>
      <category>Apple/WWDC</category>
      <category>Build</category>
      <category>complie</category>
      <category>Dynamic Library</category>
      <category>Linker</category>
      <category>Static Library</category>
      <category>swift</category>
      <category>컴파일</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/792</guid>
      <comments>https://rldd.tistory.com/792#entry792comment</comments>
      <pubDate>Fri, 2 Jan 2026 17:06:50 +0900</pubDate>
    </item>
    <item>
      <title>Xcode 빌드에서의 병렬 처리에 대한 오해 해소(Demystify parallelization in Xcode builds) - WWDC22</title>
      <link>https://rldd.tistory.com/791</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Xcode&amp;nbsp;빌드에서의&amp;nbsp;병렬&amp;nbsp;처리에&amp;nbsp;대한&amp;nbsp;오해&amp;nbsp;해소(Demystify&amp;nbsp;parallelization&amp;nbsp;in&amp;nbsp;Xcode&amp;nbsp;builds)&amp;nbsp;-&amp;nbsp;WWDC22&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Xcode의 빌드 프로세스에 대해 자세히 정리&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;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Core concepts
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드의 핵심 개념 및 빌드 성능 문제를 검토하는데 도움이 되는 Xcode 도구 탐색&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Build phase
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타겟 빌드 시 Xcode의 병렬화 증가 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Corss-Target builds
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Xcode가 어떻게 많은 타겟으로 구성된 프로젝트를 구축하면서 빌드를 전체적으로 병렬로 처리하는지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;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-12-30 오후 9.23.42.png&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nbzd6/dJMcah37Omm/QePK8qoaZX2VrrGZq9HawK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nbzd6/dJMcah37Omm/QePK8qoaZX2VrrGZq9HawK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nbzd6/dJMcah37Omm/QePK8qoaZX2VrrGZq9HawK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNbzd6%2FdJMcah37Omm%2FQePK8qoaZX2VrrGZq9HawK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1816&quot; height=&quot;788&quot; data-filename=&quot;스크린샷 2025-12-30 오후 9.23.42.png&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;788&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;Xcode의 빌드 시스템은 전체 프로젝트의 표현으로 호출.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 source file, assets, build configuration 등 실행 타겟과 같은 기타 구성을 포함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 시스템은 앱 빌드 방법에 대한 단일 정보 소스로 어떤 설정을 사용하여 어떤 도구를 호출할 지 최종적으로 앱을 만들기 위해 어떤 중간 파일을 생성할 지 알고 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계로 빌드 시스템은 컴파일러와 같은 프로젝트의 입력 파일을 처리하는 도구를 호출.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&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/cileS3/dJMcabW9Cyp/MdMOMN8kcds7QDLTKB5sVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cileS3/dJMcabW9Cyp/MdMOMN8kcds7QDLTKB5sVk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1846&quot; data-origin-height=&quot;826&quot; data-filename=&quot;스크린샷 2025-12-31 오후 5.47.58.png&quot; style=&quot;width: 32.4692%; margin-right: 10px;&quot; data-widthpercent=&quot;33.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cileS3/dJMcabW9Cyp/MdMOMN8kcds7QDLTKB5sVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcileS3%2FdJMcabW9Cyp%2FMdMOMN8kcds7QDLTKB5sVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1846&quot; height=&quot;826&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwTLKQ/dJMcaioqukO/OJt0Ch3ViFc2U0rlBjzaoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwTLKQ/dJMcaioqukO/OJt0Ch3ViFc2U0rlBjzaoK/img.png&quot; data-filename=&quot;스크린샷 2025-12-31 오후 5.50.13.png&quot; data-origin-height=&quot;828&quot; data-origin-width=&quot;1868&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.7768%; margin-right: 10px;&quot; data-widthpercent=&quot;33.56&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwTLKQ/dJMcaioqukO/OJt0Ch3ViFc2U0rlBjzaoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwTLKQ%2FdJMcaioqukO%2FOJt0Ch3ViFc2U0rlBjzaoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1868&quot; height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7vVVg/dJMcaiaUeWt/IquCTMSOjGIsAnQW4QN8EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7vVVg/dJMcaiaUeWt/IquCTMSOjGIsAnQW4QN8EK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;836&quot; data-filename=&quot;스크린샷 2025-12-31 오후 5.49.52.png&quot; style=&quot;width: 32.4284%;&quot; data-widthpercent=&quot;33.2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7vVVg/dJMcaiaUeWt/IquCTMSOjGIsAnQW4QN8EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7vVVg%2FdJMcaiaUeWt%2FIquCTMSOjGIsAnQW4QN8EK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1866&quot; height=&quot;836&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;Clang과 Swift 두 컴파일러 모두 링커가 앱을 나타내는 실행 프로그램을 연결하는 데 필요한 오브젝트(.o) 파일을 생성.&lt;/p&gt;
&lt;p data-ke-size=&quot;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-12-31 오후 6.04.09.png&quot; data-origin-width=&quot;1872&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mzLcz/dJMcaihFLqb/lzk6OGteHVKdugqSvbkfx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mzLcz/dJMcaihFLqb/lzk6OGteHVKdugqSvbkfx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mzLcz/dJMcaihFLqb/lzk6OGteHVKdugqSvbkfx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmzLcz%2FdJMcaihFLqb%2Flzk6OGteHVKdugqSvbkfx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1872&quot; height=&quot;734&quot; data-filename=&quot;스크린샷 2025-12-31 오후 6.04.09.png&quot; data-origin-width=&quot;1872&quot; data-origin-height=&quot;734&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Swift 컴파일러
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;입력 소스 파일을 사용해서 사용자의 의도를 캡처하고 실행 가능한 바이너리(기계어)로 변환하여 오류가 있는지 소스 코드를 확인.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이 프로세스는 실패할 수 있고 실패 시 빌드가 취소되지만, 성공할 경우 각 입력에 대한 오브젝트(.o) 파일을 생성&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;오브젝트 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 과정에서 오브젝트 파일은 링커를 호출하는데 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;링커
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오브젝트 파일들을 결합하고 실행 파일을 생성하기 위해 외부에 연결된 라이브러리에 대한 참조를 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 작업은 소비와 생산을 기반으로 종속성을 가짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일러에 의해 생성된 오브젝트 파일은 링커에 의해 소비되며, 이로 인해서 빌드 시스템 그래프에 종속성이 만들어짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 콘텐츠 자체는 빌드 시스템에 중요하지 않지만 작업 간의 종속성은 중요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드를 실행하는 동안에는 다른 작업의 입력을 생성하는 작업이 완료됐다는 확인이 되어야 해당 작업을 시작할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 핵심 개념은 모든 종류의 작업에 유효&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 6.35.59.png&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBvezy/dJMcagRGtIG/jeMLxRrqrZK89syuh1lbH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBvezy/dJMcagRGtIG/jeMLxRrqrZK89syuh1lbH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBvezy/dJMcagRGtIG/jeMLxRrqrZK89syuh1lbH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBvezy%2FdJMcagRGtIG%2FjeMLxRrqrZK89syuh1lbH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1116&quot; height=&quot;552&quot; data-filename=&quot;스크린샷 2025-12-31 오후 6.35.59.png&quot; data-origin-width=&quot;1116&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;&lt;br /&gt;작업 A와 작업 B사이의 종속성을 보여주는 보다 일반적인 시각화 예시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 케이스의 경우에는 A는 B의 입력 중 일부 또는 전체를 생성.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일링과 링킹은 전체 타겟을 빌드하기 위해 실행해야 하는 많은 작업 타입 중 일부일 뿐이므로 그래프에 좀 더 일반적인 작업을 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에셋 컴파일링, 파일 복사 또는 코드 서명 같은 다른 타입의 작업(이미지의 Task C, Task D, Task E 등) 이들은 다함께 프레임워크 타겟의 빌드를 나타냄.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 작업은 입력 및 출력을 기반으로 종속성을 정의.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Task A를 완료하면 Task B, C의 차단이 해제되고 Task B를 완료하면 Task D, E의 차단이 해제됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차단이 해제된 작업을 'donwstream'이라고 하며, 차단하는 작업을 'upstream'이라고 함.&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-12-31 오후 6.47.08.png&quot; data-origin-width=&quot;1784&quot; data-origin-height=&quot;986&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2tgX7/dJMcagYrMub/1igkTymfCIxkQSsoPgYnT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2tgX7/dJMcagYrMub/1igkTymfCIxkQSsoPgYnT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2tgX7/dJMcagYrMub/1igkTymfCIxkQSsoPgYnT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2tgX7%2FdJMcagYrMub%2F1igkTymfCIxkQSsoPgYnT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1784&quot; height=&quot;986&quot; data-filename=&quot;스크린샷 2025-12-31 오후 6.47.08.png&quot; data-origin-width=&quot;1784&quot; data-origin-height=&quot;986&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;많은 프로젝트에는 둘 이상의 프레임워크 타겟이 포함되므로 App과 App Extension을 나타내는 타겟으로도 확인해 보고자 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타겟은 프로젝트에서 서로 간의 종속성을 명시적 혹은 암시적 종속성을 통해 정의.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'Link Binary with Libraries' 빌드 단계에 추가되어 종속성을 정의하는 것과 같이 App은 Framework에 링크됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, App Extension은 Framework와 종속 관계가 없음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 그래프를 실행할 때 서로 다른 작업엔 다른 시간이 걸리는데, 작업을 완료하는데 필요한 복잡성 수준으로 귀결됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력의 크기와 필요한 계산에 따라 달라짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.05.38.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;1038&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y9bn1/dJMcajt3zx9/WSKwhKXrFkaGbZKPtYDpC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y9bn1/dJMcajt3zx9/WSKwhKXrFkaGbZKPtYDpC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y9bn1/dJMcajt3zx9/WSKwhKXrFkaGbZKPtYDpC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy9bn1%2FdJMcajt3zx9%2FWSKwhKXrFkaGbZKPtYDpC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1860&quot; height=&quot;1038&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.05.38.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;1038&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 고려하면 위와 같은 이미지이며, 빌드 시스템이 같은 빌드를 실행할 때 종속성이 없는 작업을 먼저 실행하며 시작.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 Task를 실행하면서 다운스트림 차단을 해제하면서 진행됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계획된 모든 작업이 완료될 때까지 이 프로세스를 따름.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.07.15.png&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nUVow/dJMcaa40i7q/s8hhYGkSvNxvmtlJzTeNzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nUVow/dJMcaa40i7q/s8hhYGkSvNxvmtlJzTeNzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nUVow/dJMcaa40i7q/s8hhYGkSvNxvmtlJzTeNzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnUVow%2FdJMcaa40i7q%2Fs8hhYGkSvNxvmtlJzTeNzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1884&quot; height=&quot;906&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.07.15.png&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;906&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 입력으로 인해 Task를 다시 실행해야 하는 경우 다운스트림 작업도 재실행 해야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 실행의 종속성과 소요 시간은 첫 다운스트림 작업을 시작할 수 있는 시간을 말함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 정보를 사용하여 임계 경로를 계산할 수 있음.&lt;/p&gt;
&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-12-31 오후 7.17.06.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsnfmW/dJMcahC3rRd/GcsDTJiKTjeP5zQ6BTt60K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsnfmW/dJMcahC3rRd/GcsDTJiKTjeP5zQ6BTt60K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsnfmW/dJMcahC3rRd/GcsDTJiKTjeP5zQ6BTt60K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsnfmW%2FdJMcahC3rRd%2FGcsDTJiKTjeP5zQ6BTt60K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1870&quot; height=&quot;888&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.17.06.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;888&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 세션에서는 이 경로를 최적화해서 병렬화 및 확장성이 뛰어난 빌드 그래프를 생성하는 것을 알아봄.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 짧은 임계 경로가 반드시 전체 빌드 시간을 단축하는건 아니지만, 빌드가 하드웨어와 함께 확장되도록 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 임계 경로는 빌드 속도를 제한하는 요소들을 정의하며, 하드웨어에서 허용할지라도 빌드를 더 빨리 완료할 수 없음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임계 경로 단축은 그 안에 있는 종속성을 분해하여 수행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.25.09.png&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/erITAR/dJMcacuZpus/To34qE5i2Nj6uYC4qsazOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/erITAR/dJMcacuZpus/To34qE5i2Nj6uYC4qsazOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/erITAR/dJMcacuZpus/To34qE5i2Nj6uYC4qsazOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FerITAR%2FdJMcacuZpus%2FTo34qE5i2Nj6uYC4qsazOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1552&quot; height=&quot;848&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.25.09.png&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;848&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;너비
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업의 길이를 나타냄.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;높이
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병렬로 처리되는 작업의 수를 나타냄. CPU와 메모리 사용률과 직결되진 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;빈 공간
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다운스트림 작업을 막는 작업들로 인해 발생함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;색상
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연관된 타겟&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.27.38.png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yqGux/dJMcabW9EkN/0shF2Jug5Rq5IhVY2ZdoJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yqGux/dJMcabW9EkN/0shF2Jug5Rq5IhVY2ZdoJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yqGux/dJMcabW9EkN/0shF2Jug5Rq5IhVY2ZdoJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyqGux%2FdJMcabW9EkN%2F0shF2Jug5Rq5IhVY2ZdoJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1828&quot; height=&quot;832&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.27.38.png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;832&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히, 이 빌드 중에 실행될 것으로 예상되지 않았을 수 있는 것들을 발견할 수 있도록 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.31.50.png&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2aKex/dJMcahXn0kT/nMsaPH27AK9qkjQNbA0w4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2aKex/dJMcahXn0kT/nMsaPH27AK9qkjQNbA0w4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2aKex/dJMcahXn0kT/nMsaPH27AK9qkjQNbA0w4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2aKex%2FdJMcahXn0kT%2FnMsaPH27AK9qkjQNbA0w4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1462&quot; height=&quot;852&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.31.50.png&quot; data-origin-width=&quot;1462&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;해당 타겟의 실행 파일을 위해 성공적으로 빌드하기 위해 Xcode는 이 노드의 자식이 나타낸 모든 작업을 실행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 빌드 로그에서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;All로 되어 있으면&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;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;하지만 Recent로 변경할 경우 실제로 실행된 작업만 표시되며, 건너 뛴 작업은 모두 숨겨짐.&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-12-31 오후 7.54.04.png&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brxjVx/dJMcahwihlT/dBkscoE713v2Z92rJITjp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brxjVx/dJMcahwihlT/dBkscoE713v2Z92rJITjp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brxjVx/dJMcahwihlT/dBkscoE713v2Z92rJITjp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrxjVx%2FdJMcahwihlT%2FdBkscoE713v2Z92rJITjp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1908&quot; height=&quot;934&quot; data-filename=&quot;스크린샷 2025-12-31 오후 7.54.04.png&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;934&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&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;color: #333333; text-align: start;&quot;&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.05.36.png&quot; data-origin-width=&quot;1846&quot; data-origin-height=&quot;978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZj6IV/dJMcacPhXwB/dWFi6KWMwgG8pf8bA8yAk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZj6IV/dJMcacPhXwB/dWFi6KWMwgG8pf8bA8yAk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZj6IV/dJMcacPhXwB/dWFi6KWMwgG8pf8bA8yAk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZj6IV%2FdJMcacPhXwB%2FdWFi6KWMwgG8pf8bA8yAk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1846&quot; height=&quot;978&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.05.36.png&quot; data-origin-width=&quot;1846&quot; data-origin-height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Xcode가 개별 타겟을 정의하고 빌드하는 방법 및 병렬화를 증가시키는 방법에 대해서 살펴보고자 함.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;타겟을 구성할 때, Build Phases는 해당 타겟의 제품 생산을 위해 수행해야 하는 작업을 설명.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&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;color: #333333; text-align: start;&quot;&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.10.02.png&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V1uIS/dJMcahiLgeG/VxCql2q7Jm0dbrVk92Jgek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V1uIS/dJMcahiLgeG/VxCql2q7Jm0dbrVk92Jgek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V1uIS/dJMcahiLgeG/VxCql2q7Jm0dbrVk92Jgek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV1uIS%2FdJMcahiLgeG%2FVxCql2q7Jm0dbrVk92Jgek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1526&quot; height=&quot;906&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.10.02.png&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;906&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&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;color: #333333; text-align: start;&quot;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&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;color: #333333; text-align: start;&quot;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;하지만, 링킹은 컴파일을 따라야 하는데, 왜냐하면 컴파일 단계에서 생성된 오브젝트(.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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.11.50.png&quot; data-origin-width=&quot;1820&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpgsso/dJMcacV3i96/PPtaFYpG3jPZ6g7FU1n5u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpgsso/dJMcacV3i96/PPtaFYpG3jPZ6g7FU1n5u0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpgsso/dJMcacV3i96/PPtaFYpG3jPZ6g7FU1n5u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcpgsso%2FdJMcacV3i96%2FPPtaFYpG3jPZ6g7FU1n5u0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;866&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.11.50.png&quot; data-origin-width=&quot;1820&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;이번에는 'Run Script' 빌드 단계가 포함된 타겟을 확인.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;다른 빌드 단계와 달리 스크립트 단계의 입출력은 Target Editor에서 수동으로 구성해야 함.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.14.12.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;1018&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFd86k/dJMcaiaUg6I/881GJeH6nBIVTuSJIRFg00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFd86k/dJMcaiaUg6I/881GJeH6nBIVTuSJIRFg00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFd86k/dJMcaiaUg6I/881GJeH6nBIVTuSJIRFg00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFd86k%2FdJMcaiaUg6I%2F881GJeH6nBIVTuSJIRFg00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1842&quot; height=&quot;1018&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.14.12.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;1018&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;`FUSE_BUILD_SCRIPT_PHASES`를 YES로 설정하여 빌드 시스템이 병렬로 실행 시도를 해야함을 나타낼 수 있음.&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-12-31 오후 8.15.57.png&quot; data-origin-width=&quot;1864&quot; data-origin-height=&quot;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJOr0N/dJMcaaDWcJK/EQHb22X6JB9R3v6yoMdkT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJOr0N/dJMcaaDWcJK/EQHb22X6JB9R3v6yoMdkT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJOr0N/dJMcaaDWcJK/EQHb22X6JB9R3v6yoMdkT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJOr0N%2FdJMcaaDWcJK%2FEQHb22X6JB9R3v6yoMdkT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1864&quot; height=&quot;884&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.15.57.png&quot; data-origin-width=&quot;1864&quot; data-origin-height=&quot;884&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실행 단계에서 불완전한 입력 또는 출력 목록은 디버깅하기 어려운 매우 문제로 데이터 경쟁 문제로 나타날 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.31.02.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;1034&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r4P7b/dJMcafL0XVl/UCxlQok3keyjTZKkanwB0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r4P7b/dJMcafL0XVl/UCxlQok3keyjTZKkanwB0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r4P7b/dJMcafL0XVl/UCxlQok3keyjTZKkanwB0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr4P7b%2FdJMcafL0XVl%2FUCxlQok3keyjTZKkanwB0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1034&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.31.02.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;1034&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Xcode는 사용자 스크립트 샌드박싱을 지원해 각 스크립트 단계의 종속성을 정확하게 선언할 수 있도록 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샌드박싱은 Shell Script가 실수로 소스파일 및 중간 빌드 객체에 접근하는 것을 차단하는 선택적인 기능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 항목이 명시적으로 선언되지 않는 한 읽고 및 쓰기를 제한하며, 스크립트가 샌드박스를 위반하면 0이 아닌 종료 코드와 함께 빌드에 실패하게 됨&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-12-31 오후 8.23.33.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB7zsr/dJMcai9Laqv/w7Prp4KaGbLc1oA5DhWLPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB7zsr/dJMcai9Laqv/w7Prp4KaGbLc1oA5DhWLPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB7zsr/dJMcai9Laqv/w7Prp4KaGbLc1oA5DhWLPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB7zsr%2FdJMcai9Laqv%2Fw7Prp4KaGbLc1oA5DhWLPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1272&quot; height=&quot;584&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.23.33.png&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;584&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;/p&gt;
&lt;p data-ke-size=&quot;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-12-31 오후 8.30.08.png&quot; data-origin-width=&quot;1864&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCAaDc/dJMcahC3tgq/VNnKdDuHjxerJ73rV2SWj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCAaDc/dJMcahC3tgq/VNnKdDuHjxerJ73rV2SWj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCAaDc/dJMcahC3tgq/VNnKdDuHjxerJ73rV2SWj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCAaDc%2FdJMcahC3tgq%2FVNnKdDuHjxerJ73rV2SWj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1864&quot; height=&quot;846&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.30.08.png&quot; data-origin-width=&quot;1864&quot; data-origin-height=&quot;846&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;스크립트 A (체크섬 계산): source.txt 파일의 내용을 읽어 checksum.txt 파일을 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 B (HTML 생성): checksum.txt 파일을 읽어 HTML 파일에 내용을 삽입&lt;/p&gt;
&lt;div data-start-index=&quot;2182&quot;&gt;&amp;nbsp;&lt;/div&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/dreu2l/dJMcaioqxfD/1Uw2a7bK4bHNAkfDKmspUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dreu2l/dJMcaioqxfD/1Uw2a7bK4bHNAkfDKmspUK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1864&quot; data-origin-height=&quot;1018&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.32.44.png&quot; style=&quot;width: 49.3877%; margin-right: 10px;&quot; data-widthpercent=&quot;49.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dreu2l/dJMcaioqxfD/1Uw2a7bK4bHNAkfDKmspUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdreu2l%2FdJMcaioqxfD%2F1Uw2a7bK4bHNAkfDKmspUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1864&quot; height=&quot;1018&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ek4IRK/dJMcab3UPUI/tC3jJ1PEXKtdEnrQz2DgHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ek4IRK/dJMcab3UPUI/tC3jJ1PEXKtdEnrQz2DgHK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;984&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.33.18.png&quot; style=&quot;width: 49.4495%;&quot; data-widthpercent=&quot;50.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ek4IRK/dJMcab3UPUI/tC3jJ1PEXKtdEnrQz2DgHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fek4IRK%2FdJMcab3UPUI%2FtC3jJ1PEXKtdEnrQz2DgHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1804&quot; height=&quot;984&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;`FUSE_BUILD_SCRIPT_PHASES`가 켜져있을 때 병렬로 실행하는데, Xcode가 두 종속성 관계를 제대로 추론하지 못함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트&amp;nbsp;B가&amp;nbsp;A보다&amp;nbsp;먼저&amp;nbsp;실행되면&amp;nbsp;checksum.txt&amp;nbsp;파일이&amp;nbsp;없어&amp;nbsp;빌드가&amp;nbsp;실패하거나,&amp;nbsp;A가&amp;nbsp;실행&amp;nbsp;중일&amp;nbsp;때&amp;nbsp;B가&amp;nbsp;오래된&amp;nbsp;checksum.txt&amp;nbsp;파일을&amp;nbsp;읽어&amp;nbsp;조용히&amp;nbsp;버그를&amp;nbsp;만들어낼&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.34.18.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;982&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qRK0t/dJMcag5ePpU/f5EQV7M7txFI7grq8iDYH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qRK0t/dJMcag5ePpU/f5EQV7M7txFI7grq8iDYH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qRK0t/dJMcag5ePpU/f5EQV7M7txFI7grq8iDYH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqRK0t%2FdJMcag5ePpU%2Ff5EQV7M7txFI7grq8iDYH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1844&quot; height=&quot;982&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.34.18.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;982&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;ENABLE_USER_SCRIPT_SANDBOXING를 YES로 설정한 상태에서는 읽으려는 단계에서 실패하게 되고, 명시적인 실패를 통해 누락된 종속성을 명확하게 찾아낼 수 있음.&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-12-31 오후 8.37.21.png&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;598&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uFXQJ/dJMcahQBrdv/ImC07KaPR7Gk8qq2kHCyQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uFXQJ/dJMcahQBrdv/ImC07KaPR7Gk8qq2kHCyQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uFXQJ/dJMcahQBrdv/ImC07KaPR7Gk8qq2kHCyQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuFXQJ%2FdJMcahQBrdv%2FImC07KaPR7Gk8qq2kHCyQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1704&quot; height=&quot;598&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.37.21.png&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;598&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;Xcode의 Build Settings 혹은 xcconfig를 통해서 설정을 YES로 줄 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;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-12-31 오후 8.41.05.png&quot; data-origin-width=&quot;1846&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lfYKg/dJMcadHoAfe/V75R375aLHvKlx2fXGbbf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lfYKg/dJMcadHoAfe/V75R375aLHvKlx2fXGbbf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lfYKg/dJMcadHoAfe/V75R375aLHvKlx2fXGbbf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlfYKg%2FdJMcadHoAfe%2FV75R375aLHvKlx2fXGbbf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1846&quot; height=&quot;724&quot; data-filename=&quot;스크린샷 2025-12-31 오후 8.41.05.png&quot; data-origin-width=&quot;1846&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sandbox shell scripts를 활용하면 올바른 종속성 정보를 통해 증분빌드를 가능하게 하는 올바른 종속성 정보를 가질 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면, 입출력 파일의 변화가 없다는 확신을 통해 빌드 과정을 건너뛸 수 있기 때문이며, 그렇지 않은 경우에는 Xcode는 스크립트를 다시 실행함.&amp;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;샌드박싱을 통해 올바르게 정의된 종속성 정보를 통하여 `FUSE_BUILD_SCRIPT_PHASES`와 함께 병렬로 실행할 수도 있음.&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;스크린샷 2026-01-01 오후 8.29.26.png&quot; data-origin-width=&quot;1790&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0FkGq/dJMcadACBjB/I4TX7mblDlOmP5NJn5PgDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0FkGq/dJMcadACBjB/I4TX7mblDlOmP5NJn5PgDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0FkGq/dJMcadACBjB/I4TX7mblDlOmP5NJn5PgDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0FkGq%2FdJMcadACBjB%2FI4TX7mblDlOmP5NJn5PgDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1790&quot; height=&quot;690&quot; data-filename=&quot;스크린샷 2026-01-01 오후 8.29.26.png&quot; data-origin-width=&quot;1790&quot; data-origin-height=&quot;690&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Xcode가 Swift Target 간의 종속성을 사용하여 빌드에서 최대 병렬 처리량을 추출하는 방법을 살펴보고 프로젝트 구조 및 구성이 빌드 시간에 미치는 영향도 알아보고자 함.&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;스크린샷 2026-01-01 오후 8.29.04.png&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c29wB6/dJMcaiBVEdk/jwz3mwCimfaBweKksjor4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c29wB6/dJMcaiBVEdk/jwz3mwCimfaBweKksjor4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c29wB6/dJMcaiBVEdk/jwz3mwCimfaBweKksjor4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc29wB6%2FdJMcaiBVEdk%2Fjwz3mwCimfaBweKksjor4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1528&quot; height=&quot;706&quot; data-filename=&quot;스크린샷 2026-01-01 오후 8.29.04.png&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;706&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 로컬 라이브러리 모음에 의존하는 App Target은 의미론적 경계를 다라 여러 타겟 및 여러 프레임워크로 분할됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 타겟은 다양한 빌드 단계와 절차들을 포함하여 다른 타겟 빌드 단계로 그리고 빌드 단계로부터 파일 의존성을 생성하고 사용함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 커지면서 작업 그래프의 크기와 복잡성도 커짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;스크린샷 2026-01-01 오후 8.28.40.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yoZeA/dJMcai2ZERX/cjxjfbwQRCFvqKpSm6jwz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yoZeA/dJMcai2ZERX/cjxjfbwQRCFvqKpSm6jwz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yoZeA/dJMcai2ZERX/cjxjfbwQRCFvqKpSm6jwz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyoZeA%2FdJMcai2ZERX%2FcjxjfbwQRCFvqKpSm6jwz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1844&quot; height=&quot;858&quot; data-filename=&quot;스크린샷 2026-01-01 오후 8.28.40.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;858&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;Xcode 빌드 시스템이 이런 계층 구조를 평면화하면서 빌드를 여러 작업들로 분할하고, 이런 작업들은 모든 타겟의 빌드 단계에 해당함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift 타겟에 특화된 한 종류의 작업은 컴파일이고, Swift 타겟의 소스 코드를 바이너리 제품으로 빌드하는 것은 일반적으로 빌드 계획이나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 및 연결을 위한 많은 하위 작업을 포함하는 복잡한 작업으로 이러한 작업의 Swift Driver라는 Xcode toolchain에 위임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드라이버는 타겟 소스 코드에 필요한 컴파일러 및 링커 호출을 구성하는 시기와 방법에 대한 정보를 가지고 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift 코드를 포함하는 모든 타겟은 배포 단위인 모듈에도 해당됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 타겟의 공개 인터페이스를 캡처하는 바이너리 모듈 파일은 다운스트림 타겟이 컴파일을 시작하는데 필요한 빌드 product임.&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;스크린샷 2026-01-01 오후 9.25.46.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl5oFz/dJMcafytUqt/FFJdELyytCNsDx23SGxfv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl5oFz/dJMcafytUqt/FFJdELyytCNsDx23SGxfv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl5oFz/dJMcafytUqt/FFJdELyytCNsDx23SGxfv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl5oFz%2FdJMcafytUqt%2FFFJdELyytCNsDx23SGxfv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1844&quot; height=&quot;694&quot; data-filename=&quot;스크린샷 2026-01-01 오후 9.25.46.png&quot; data-origin-width=&quot;1844&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;Swift Driver는 최적화 기회를 최대화하도록 하는 하나의 컴파일러 작업을 예약.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 컴파일 노력을 병렬로 실행할 수 있는 더 작은 하위 작업으로 나눔.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 일부는 증분 빌드에서 다시 실행할 필요가 없음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift 모듈을 생성하려면 각 컴파일 작업의 부분적인 중간 product를 병합하는 추가 단계가 필요함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타겟에 있는 소스 파일의 수가 많다면 개별 파일은 일괄 컴파일 하위 작업에 할당될 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-02 오후 3.48.56.png&quot; data-origin-width=&quot;1754&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gnu2K/dJMcaf6kpgm/EhR6bJgd62iCgaoqYQEnbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gnu2K/dJMcaf6kpgm/EhR6bJgd62iCgaoqYQEnbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gnu2K/dJMcaf6kpgm/EhR6bJgd62iCgaoqYQEnbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGnu2K%2FdJMcaf6kpgm%2FEhR6bJgd62iCgaoqYQEnbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1754&quot; height=&quot;814&quot; data-filename=&quot;스크린샷 2026-01-02 오후 3.48.56.png&quot; data-origin-width=&quot;1754&quot; data-origin-height=&quot;814&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 소스 파일에 걸쳐 타겟의 빌드를 병렬화할 수 있는 것은 더 빠르고 작은 증분빌드에 중요하기 때문에 디버그 빌드가 증분 컴파일 모드 설정을 사용하는지 확인.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-02 오후 3.57.32.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;1030&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be9lnZ/dJMcacPiEVP/mQkKFfi2IYxTooU3U4xtE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be9lnZ/dJMcacPiEVP/mQkKFfi2IYxTooU3U4xtE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be9lnZ/dJMcacPiEVP/mQkKFfi2IYxTooU3U4xtE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe9lnZ%2FdJMcacPiEVP%2FmQkKFfi2IYxTooU3U4xtE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1842&quot; height=&quot;1030&quot; data-filename=&quot;스크린샷 2026-01-02 오후 3.57.32.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;1030&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;Swift의 타겟 의존성은 퍼블릭 인터페이스를 캡처하는 바이너리 모듈 파일을 제공함으로써 해결.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 의존성 관계를 해결하면 위와 같은 순서로 이어짐. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 타겟에 대한 최상위 Swift Driver 작업 및 각 타겟의 개별 하위 작업이 타임라인에 나타남.&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;스크린샷 2026-01-02 오후 4.03.58.png&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E2mvL/dJMcaiaUYSq/UKj8kEA7JCgzLS88P7Kd4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E2mvL/dJMcaiaUYSq/UKj8kEA7JCgzLS88P7Kd4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E2mvL/dJMcaiaUYSq/UKj8kEA7JCgzLS88P7Kd4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE2mvL%2FdJMcaiaUYSq%2FUKj8kEA7JCgzLS88P7Kd4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1834&quot; height=&quot;1000&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.03.58.png&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;1000&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;Xcode 14 이전의 빌드 시스템은 이원화 된 구조.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Xcode의 빌드 시스템은 전체적인 빌드 단계를 관리했고, Swift 코드 컴파일은 Swift 드라이버라는 별도의 도구에서 독립적으로 관리.&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;스크린샷 2026-01-02 오후 4.08.15.png&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;876&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EY3zT/dJMcadm5iVV/xSkEZN0PsWFdY33pitxE41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EY3zT/dJMcadm5iVV/xSkEZN0PsWFdY33pitxE41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EY3zT/dJMcadm5iVV/xSkEZN0PsWFdY33pitxE41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEY3zT%2FdJMcadm5iVV%2FxSkEZN0PsWFdY33pitxE41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1818&quot; height=&quot;876&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.08.15.png&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;876&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&gt;기존에는 두 스케줄러가 각자 시스템 CPU 코어를 최대한 활용하려다 보니 서로 경쟁하며 리소스를 비효율적으로 사용하는 CPU 수를 초과해서 사용하는 문제가 발생&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Xcode 14에서는 빌드 시스템과 컴파일러가 완전히 통합되어서 중앙 관리자가 통합 관리함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;중앙 관리자는 사용 가능한 리소스 만큼만 사용하도록 보장.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;스크린샷 2026-01-02 오후 4.10.53.png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;1018&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0EFy7/dJMcagxoieV/3t3GNKXVziBGV2LeR0W7sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0EFy7/dJMcagxoieV/3t3GNKXVziBGV2LeR0W7sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0EFy7/dJMcagxoieV/3t3GNKXVziBGV2LeR0W7sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0EFy7%2FdJMcagxoieV%2F3t3GNKXVziBGV2LeR0W7sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1828&quot; height=&quot;1018&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.10.53.png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;1018&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;br /&gt;Xcode14 이후부터 스케줄러 기본 값은 사용 가능한 작업 즉, 종속성이 충족되어서 이동할 준비가 된 작업은 8개의 사용 가능한 실행 슬롯 중 하나에 할당.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코어 수가 많을수록 많은 시스템에서는 더 많은 동시 작업을 수행할 수 있음.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.13.03.png&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CE4UQ/dJMcabXamvc/z5d5BCeqyj1kCvR4efola0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CE4UQ/dJMcabXamvc/z5d5BCeqyj1kCvR4efola0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CE4UQ/dJMcabXamvc/z5d5BCeqyj1kCvR4efola0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCE4UQ%2FdJMcabXamvc%2Fz5d5BCeqyj1kCvR4efola0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1838&quot; height=&quot;984&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.13.03.png&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;984&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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;제품이 사용 가능하게 되면, 다운스트림 타겟이 컴파일을 시작할 수 있음.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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/pN5u7/dJMcabbMXEk/VkZElcdj4X5cFcR6IgAkik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pN5u7/dJMcabbMXEk/VkZElcdj4X5cFcR6IgAkik/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;974&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.15.07.png&quot; style=&quot;width: 50.0738%; margin-right: 10px;&quot; data-widthpercent=&quot;50.66&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pN5u7/dJMcabbMXEk/VkZElcdj4X5cFcR6IgAkik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpN5u7%2FdJMcabbMXEk%2FVkZElcdj4X5cFcR6IgAkik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1842&quot; height=&quot;974&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yr3DX/dJMcadUVq8i/xpNjEK4ZqjZDtPbUEknOXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yr3DX/dJMcadUVq8i/xpNjEK4ZqjZDtPbUEknOXK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;998&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.15.28.png&quot; style=&quot;width: 48.7635%;&quot; data-widthpercent=&quot;49.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yr3DX/dJMcadUVq8i/xpNjEK4ZqjZDtPbUEknOXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyr3DX%2FdJMcadUVq8i%2FxpNjEK4ZqjZDtPbUEknOXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1838&quot; height=&quot;998&quot;/&gt;&lt;/span&gt;&lt;/div&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;Xcode 14 및 Swift 5.7에서는 새롭게 타겟 모듈의 구성이 모든 프로그램 소스 파일에서 직접 별도의 모듈 emit-module 작업에서 수행.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이는 타겟 종속성이 emit-module 작업이 완료되는 즉시 다른 컴파일러 작업을 기다리지 않고 컴파일을 시작할 수 있음을 의미&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D4SFI/dJMcac9yB4G/NwZKFuCfQKR52npT4083r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D4SFI/dJMcac9yB4G/NwZKFuCfQKR52npT4083r0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;850&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.16.53.png&quot; style=&quot;width: 50.6302%; margin-right: 10px;&quot; data-widthpercent=&quot;51.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D4SFI/dJMcac9yB4G/NwZKFuCfQKR52npT4083r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD4SFI%2FdJMcac9yB4G%2FNwZKFuCfQKR52npT4083r0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;850&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mwuLP/dJMcagEbHXf/isc6sLvTcRMilKYBrlYokK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mwuLP/dJMcagEbHXf/isc6sLvTcRMilKYBrlYokK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;886&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.17.26.png&quot; style=&quot;width: 48.207%;&quot; data-widthpercent=&quot;48.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mwuLP/dJMcagEbHXf/isc6sLvTcRMilKYBrlYokK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmwuLP%2FdJMcagEbHXf%2Fisc6sLvTcRMilKYBrlYokK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1844&quot; height=&quot;886&quot;/&gt;&lt;/span&gt;&lt;/div&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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 종종 더 빌드를 더 빠르게 마무리할 수 있음.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;빌드 시스템이 Swift 빌드 시에 수행할 수 있는 두 번째 Cross-Target 최적화 기능인 즉시 링킹(&lt;span style=&quot;background-color: #ffffff; color: #303030; text-align: start;&quot;&gt;Eager Linking)이 존재.&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;background-color: #ffffff; color: #303030; text-align: start;&quot;&gt;링킹 과정에도 유사한 최적화가 적용되는데 다운스트림 타겟의 링커가 업스트림 타겟의 최종 링크 Product를 기다려야 했음.&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;background-color: #ffffff; color: #303030; text-align: start;&quot;&gt;먼저 생성되는 심볼 목록이 담긴 텍스트 기반 stub 파일에만 의존하면 되므로 링킹을 즉시 시작할 수 있음.&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;background-color: #ffffff; color: #303030; text-align: start;&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.29.44.png&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPKsGI/dJMcafyubEO/PKWtOyf1BWY82fRuHa8F30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPKsGI/dJMcafyubEO/PKWtOyf1BWY82fRuHa8F30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPKsGI/dJMcafyubEO/PKWtOyf1BWY82fRuHa8F30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPKsGI%2FdJMcafyubEO%2FPKWtOyf1BWY82fRuHa8F30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1866&quot; height=&quot;702&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.29.44.png&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;702&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;background-color: #ffffff; color: #303030; text-align: start;&quot;&gt;이 경우 타겟 B가 타겟 A를 연결하기 때문에 타겟 B의 링크 작업은 타겟 A의 링크된 출력이 생성되고 자체 컴파일 작업이 완료될 때까지 기다려야 함.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.32.39.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hd3pW/dJMcacV3ZlG/F2HkeIJOTuSxqWbn2m2lGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hd3pW/dJMcacV3ZlG/F2HkeIJOTuSxqWbn2m2lGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hd3pW/dJMcacV3ZlG/F2HkeIJOTuSxqWbn2m2lGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHd3pW%2FdJMcacV3ZlG%2FF2HkeIJOTuSxqWbn2m2lGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1860&quot; height=&quot;738&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.32.39.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;738&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;background-color: #ffffff; color: #303030; text-align: start;&quot;&gt;즉시 링킹을 사용하면, 타겟 B의 링크 작업이 타겟 A의 emit-module 작업에 의존할 수 있고, 그 결과 타겟 B는 링킹을 빌드 초기에 시작할 수 있음.&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;background-color: #ffffff; color: #303030; text-align: start;&quot;&gt;타겟 A와 병렬로 실행하며 임계 경로(Critical Path)를 단축할 수 있음.&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;스크린샷 2026-01-02 오후 4.41.32.png&quot; data-origin-width=&quot;1862&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o5gf5/dJMcabiyXcn/qjn6e9idivBpoSCJhfZYPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o5gf5/dJMcabiyXcn/qjn6e9idivBpoSCJhfZYPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o5gf5/dJMcabiyXcn/qjn6e9idivBpoSCJhfZYPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo5gf5%2FdJMcabiyXcn%2Fqjn6e9idivBpoSCJhfZYPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1862&quot; height=&quot;864&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.41.32.png&quot; data-origin-width=&quot;1862&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종속성이 링크된 제품에 의존하는 대신 이전 빌드 프로세스에서 emit-module 작업에 의해 생성된 텍스트 기반 동적 라이브러리 스텁에 의존.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스텁에는 링크된 제품에 심볼 목록이 포함되어 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.45.44.png&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bovwVD/dJMcagRHcNf/6BfsHzuk72V61DNNvtK10K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bovwVD/dJMcagRHcNf/6BfsHzuk72V61DNNvtK10K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bovwVD/dJMcagRHcNf/6BfsHzuk72V61DNNvtK10K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbovwVD%2FdJMcagRHcNf%2F6BfsHzuk72V61DNNvtK10K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1818&quot; height=&quot;846&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.45.44.png&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;846&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;Xcode 빌드 설정을 사용해 이 최적화를 활성화할 수 있음. Xcode 빌드 설정을 사용해 이 최적화를 활성화 할 수 있고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉시 링킹은 의존성 항목에 의해 동적으로 링킹된 모든 순수 Swift 타겟에 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.47.04.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/orDNZ/dJMcabQn8j6/Vk0vFXLbfeUJXWMsbK4Hl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/orDNZ/dJMcabQn8j6/Vk0vFXLbfeUJXWMsbK4Hl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/orDNZ/dJMcabQn8j6/Vk0vFXLbfeUJXWMsbK4Hl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2ForDNZ%2FdJMcabQn8j6%2FVk0vFXLbfeUJXWMsbK4Hl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;770&quot; data-filename=&quot;스크린샷 2026-01-02 오후 4.47.04.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;770&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;&lt;span style=&quot;background-color: #ffffff; color: #303030; text-align: start;&quot;&gt;&lt;br /&gt;Xcode 14의 빌드 시스템 개선은 단순한 속도 향상이 아니라, 병렬화를 극대화하고, 개발자에게 빌드 과정을 투명하게 보여주며, 리소스를 가장 효율적으로 사용하도록 설계된 근본적인 구조 변화.&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;background-color: #ffffff; color: #303030; text-align: start;&quot;&gt; 이제 우리는 빌드 시간을 막연히 기다리는 대신, 새로운 도구와 지식을 활용해 직접 분석하고 개선할 수 있게 되었고, &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #303030; text-align: start;&quot;&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(링크)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=yo8_luxkSXY&amp;amp;t=4s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=yo8_luxkSXY&amp;amp;t=4s&lt;/a&gt;&lt;/p&gt;</description>
      <category>Apple/WWDC</category>
      <category>Build</category>
      <category>swift</category>
      <category>Xcode</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/791</guid>
      <comments>https://rldd.tistory.com/791#entry791comment</comments>
      <pubDate>Tue, 30 Dec 2025 21:18:18 +0900</pubDate>
    </item>
    <item>
      <title>swift rethrows</title>
      <link>https://rldd.tistory.com/789</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;swift&amp;nbsp;rethrows&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;함수나&amp;nbsp;메서드는&amp;nbsp;rethrows&amp;nbsp;키워드를&amp;nbsp;사용하여&amp;nbsp;선언할&amp;nbsp;수&amp;nbsp;있음.&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;의미.&lt;br&gt;이러한&amp;nbsp;함수와&amp;nbsp;메서드를&amp;nbsp;rethrowing&amp;nbsp;함수(rethrowing&amp;nbsp;functions)&amp;nbsp;또는&amp;nbsp;rethrowing&amp;nbsp;메서드(rethrowing&amp;nbsp;methods)라고&amp;nbsp;부름.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;func someFunction(callback: () throws -&amp;gt; Void) throws {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try callback()
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;br&gt;rethrowing&amp;nbsp;함수와&amp;nbsp;메서드는&amp;nbsp;적어도&amp;nbsp;하나의&amp;nbsp;throwing&amp;nbsp;함수&amp;nbsp;매개변수를&amp;nbsp;가져야&amp;nbsp;함.&lt;br&gt;rethrowing&amp;nbsp;함수나&amp;nbsp;메서드는&amp;nbsp;오직&amp;nbsp;catch&amp;nbsp;블록&amp;nbsp;안에서만&amp;nbsp;throw&amp;nbsp;문을&amp;nbsp;포함할&amp;nbsp;수&amp;nbsp;있음.&lt;br&gt;&amp;nbsp;&lt;br&gt;이&amp;nbsp;규칙을&amp;nbsp;통해&amp;nbsp;do-catch&amp;nbsp;구문&amp;nbsp;안에서&amp;nbsp;throwing&amp;nbsp;함수를&amp;nbsp;호출하고,&amp;nbsp;catch&amp;nbsp;블록에서&amp;nbsp;다른&amp;nbsp;에러를&amp;nbsp;던지며&amp;nbsp;에러를&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있음.&lt;br&gt;또한&amp;nbsp;catch&amp;nbsp;블록은&amp;nbsp;반드시&amp;nbsp;rethrowing&amp;nbsp;함수의&amp;nbsp;throwing&amp;nbsp;매개변수&amp;nbsp;중&amp;nbsp;하나가&amp;nbsp;던진&amp;nbsp;에러만&amp;nbsp;처리해야&amp;nbsp;함.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;예를 들어, 다음 코드는 잘못된 코드입&lt;br&gt;왜냐하면 catch 블록에서 alwaysThrows()가 던진 에러를 처리하려고 하기 때문&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;func alwaysThrows() throws {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw SomeError.error
}

func someFunction(callback: () throws -&amp;gt; Void) rethrows {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try callback()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try alwaysThrows()&amp;nbsp;&amp;nbsp;// Invalid, alwaysThrows() isn't a throwing parameter
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw AnotherError.error
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;throwing&amp;nbsp;메서드는&amp;nbsp;rethrowing&amp;nbsp;메서드를&amp;nbsp;오버라이드할&amp;nbsp;수&amp;nbsp;없음.&lt;br&gt;throwing&amp;nbsp;메서드는&amp;nbsp;rethrowing&amp;nbsp;메서드에&amp;nbsp;대한&amp;nbsp;프로토콜&amp;nbsp;요구사항을&amp;nbsp;충족할&amp;nbsp;수&amp;nbsp;없음.&lt;br&gt;&amp;nbsp;&lt;br&gt;반대로,&amp;nbsp;rethrowing&amp;nbsp;메서드는&amp;nbsp;throwing&amp;nbsp;메서드를&amp;nbsp;오버라이드할&amp;nbsp;수&amp;nbsp;있음.&lt;br&gt;rethrowing&amp;nbsp;메서드는&amp;nbsp;throwing&amp;nbsp;메서드에&amp;nbsp;대한&amp;nbsp;프로토콜&amp;nbsp;요구사항을&amp;nbsp;충족할&amp;nbsp;수&amp;nbsp;있음.&lt;br&gt;&lt;br&gt;또한,&amp;nbsp;rethrowing&amp;nbsp;대신&amp;nbsp;제네릭&amp;nbsp;코드에서&amp;nbsp;특정&amp;nbsp;에러&amp;nbsp;타입을&amp;nbsp;throw하는&amp;nbsp;방법을&amp;nbsp;사용할&amp;nbsp;수도&amp;nbsp;있음.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;func someFunction&amp;lt;E: Error&amp;gt;(callback: () throws(E) -&amp;gt; Void) throws(E) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try callback()
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&amp;nbsp;&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;있음.&lt;br&gt;하지만,&amp;nbsp;rethrows로&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;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드 샘플&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;//
//&amp;nbsp;&amp;nbsp;RethrowsExample.swift
//&amp;nbsp;&amp;nbsp;ArchitetureExample
//
//&amp;nbsp;&amp;nbsp;Created by Geon Woo lee on 12/2/25.
//

import Foundation

fileprivate enum SomeError: Error {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case error
}

fileprivate enum AnotherError: Error {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case error
}

fileprivate final class SomeClass {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func alwaysThrows() throws {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw SomeError.error
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func someFunction(callback: () throws -&amp;gt; Void) throws {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try callback()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func someFunction(callback: () throws -&amp;gt; Void) rethrows {
//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do {
//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try callback()
//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try alwaysThrows()&amp;nbsp;&amp;nbsp;// Invalid, alwaysThrows() isn't a throwing parameter
//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch {
//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw AnotherError.error
//&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;func someFunction2(callback: () throws -&amp;gt; Void) rethrows {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try callback()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func someFunction3(callback: () throws -&amp;gt; Void) throws {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try callback()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try alwaysThrows()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw AnotherError.error
&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;func someFunction4(callback: () throws -&amp;gt; Void) throws {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try callback()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try alwaysThrows()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw AnotherError.error
&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;func someFunction5&amp;lt;E: Error&amp;gt;(callback: () throws(E) -&amp;gt; Void) throws(E) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try callback()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}

import SwiftUI

fileprivate struct PreviewView: View {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var body: some View {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Button {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;buttonTapped4()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} label: {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Text(&quot;Button&quot;)
&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;private func buttonTapped() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let someClass = SomeClass()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try someClass.someFunction(callback: someClass.alwaysThrows)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(type(of: error)) // SomeError
&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;private func buttonTapped2() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let someClass = SomeClass()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try someClass.someFunction2(callback: someClass.alwaysThrows)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(type(of: error))
&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;private func buttonTapped3() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let someClass = SomeClass()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try someClass.someFunction3 { }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(type(of: error)) // AnotherError
&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;private func buttonTapped4() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let someClass = SomeClass()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try someClass.someFunction4 { }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(type(of: error)) // AnotherError
&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;private func buttonTapped5() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let someClass = SomeClass()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;someClass.someFunction5 { }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}

#Preview {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PreviewView()
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;(참고)&lt;br&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/declarations&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/declarations&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>Apple/iOS, UIKit, Documentation</category>
      <category>ios</category>
      <category>Rethrows</category>
      <category>swift</category>
      <category>swift rethrows</category>
      <category>throw</category>
      <category>throwing</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/789</guid>
      <comments>https://rldd.tistory.com/789#entry789comment</comments>
      <pubDate>Wed, 3 Dec 2025 21:16:28 +0900</pubDate>
    </item>
    <item>
      <title>AVAudioEngine, AVAudioNode 정리</title>
      <link>https://rldd.tistory.com/788</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;AVAudioEngine,&amp;nbsp;AVAudioNode&amp;nbsp;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AVAudioEngine과 관련한 내용을 모아서 정리&lt;/p&gt;
&lt;p data-ke-size=&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;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AVAudioEngine
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오 렌더링이란?&lt;/li&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Create&amp;nbsp;an&amp;nbsp;Engine&amp;nbsp;for&amp;nbsp;Audio&amp;nbsp;File&amp;nbsp;Playback&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AVAudioNode
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Configuring&amp;nbsp;an&amp;nbsp;Input&amp;nbsp;Format&amp;nbsp;Bus
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AVAudioNodeBus&lt;/li&gt;
&lt;li&gt;inputFormat(forBus:)&lt;/li&gt;
&lt;li&gt;name(forInputBus:)&lt;/li&gt;
&lt;li&gt;numberOfInputs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Creating&amp;nbsp;an&amp;nbsp;Output&amp;nbsp;Format&amp;nbsp;Bus
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;outputFormat(forBus:)&lt;/li&gt;
&lt;li&gt;name(forOutputBus:)&lt;/li&gt;
&lt;li&gt;numberOfOutputs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Installing&amp;nbsp;and&amp;nbsp;Removing&amp;nbsp;an&amp;nbsp;Audio&amp;nbsp;Tap&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;installTap(onBus:bufferSize:format:block:)&lt;/li&gt;
&lt;li&gt;removeTap(onBus:)&lt;/li&gt;
&lt;li&gt;typealias&amp;nbsp;AVAudioNodeTapBlock&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Getting&amp;nbsp;the&amp;nbsp;Audio&amp;nbsp;Engine&amp;nbsp;for&amp;nbsp;the&amp;nbsp;Node
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;engine&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Getting&amp;nbsp;Audio&amp;nbsp;Node&amp;nbsp;Properties
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;auAudioUnit&lt;/li&gt;
&lt;li&gt;latency&lt;/li&gt;
&lt;li&gt;outputPresentationLatency&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Resetting&amp;nbsp;the&amp;nbsp;Audio&amp;nbsp;Node
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;reset()&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Constants
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;typealias&amp;nbsp;AVAudioNodeCompletionHandler&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AVAudioEngine&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;/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-11-25 오후 10.15.16.png&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chhJB2/dJMcagDW1jy/XK9X09WTn9iSfwtCaNyQHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chhJB2/dJMcagDW1jy/XK9X09WTn9iSfwtCaNyQHK/img.png&quot; data-alt=&quot;AVAudioEngine&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chhJB2/dJMcagDW1jy/XK9X09WTn9iSfwtCaNyQHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchhJB2%2FdJMcagDW1jy%2FXK9X09WTn9iSfwtCaNyQHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;262&quot; data-filename=&quot;스크린샷 2025-11-25 오후 10.15.16.png&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AVAudioEngine&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;br /&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;오디오 데이터를 처리해서 최종적인 PCM 샘플로 만들어내는 작업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Audio Node의 처리 결과를 합치고 이펙트(효과)를 적용하고 믹싱하고 최종적으로 출력할 수 있는 형태로 데이터를 계산해서 생산하는 과정을 의미.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 렌더링과 수동 렌더링이 존재&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 렌더링의 경우에는 AudioEngine이 자동으로 주기적으로 콜백을 받아 오디오 샘플을 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수동 렌더링의 경우에는 앱이 직접 오디오 엔진에 렌더링을 요청해서 오디오 샘플을 생성해서 다 빠르게 처리할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Overview&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;오디오 엔진 객체는 AVAudioNode 인스턴스를 포함하며 이를 서로 연결해 오디오 처리 체인을 구성&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-11-25 오후 10.16.57.png&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNzmPe/dJMcafruYEa/zfJgKyt62R35tb9fmfM8bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNzmPe/dJMcafruYEa/zfJgKyt62R35tb9fmfM8bK/img.png&quot; data-alt=&quot;Overview&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNzmPe/dJMcafruYEa/zfJgKyt62R35tb9fmfM8bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNzmPe%2FdJMcafruYEa%2FzfJgKyt62R35tb9fmfM8bK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;114&quot; data-filename=&quot;스크린샷 2025-11-25 오후 10.16.57.png&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Overview&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임 동안 오디오 노드를 연결하거나 연결 해제하고 제거할 수 있지만 몇 가지 제한이 존재.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널 수가 서로 다르거나 믹서(mixer)인 오디오 노드를 제거하면 그래프가 깨질 수 있으며, 믹서의 상위(upstream)에 있는 노드만 재연결해야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 Audio Engine은 연결된 오디오 기기로 실시간 렌더링을 수행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 또는 실시간보다 더 빠르게 렌더링해야 하는 경우, 엔진을 수동 렌더링 모드(manual rendering mode)로 설정할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모드에서 엔진은 오디오 기기와의 연결을 끊고 앱이 직접 렌더링을 제어함.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Create&amp;nbsp;an&amp;nbsp;Engine&amp;nbsp;for&amp;nbsp;Audio&amp;nbsp;File&amp;nbsp;Playback&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오디오 파일 재생을 위한 순서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. 오디오 파일을 재생하기 위해서는 우선 읽기 모드로 열 수 있는 파일을 기반으로&amp;nbsp;AVAudioFile&amp;nbsp;객체를 생성&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2. 오디오 엔진(AVAudioEngine)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;객체와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;AVAudioPlayerNode&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;인스턴스를 만들고 플레이어 노드를 엔진에 attach&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;3. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;플레이어 노드를 오디오 엔진의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;output node에 연결(connect)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;4. 오디오 엔진은 오디오 출력을 담당하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;output node&lt;/span&gt;를 통해 사운드를 재생&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;4-1. output node는 엔진에서 처음 접근할 때 자동으로 생성되는 싱글턴(singleton)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;5. 오디오 파일 전체를 재생하도록 스케줄하고, 재생이 완료되면 콜백을 통해 앱에 알려줌.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;6. 오디오를 재생하기 전에 AudioEngine을 시작&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;7. 재생이 완료되면 오디오 플레이어 노드랑 오디오 엔진을 정지.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플코드&lt;/p&gt;
&lt;pre id=&quot;code_1764080179927&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func execute() throws {
    let audioFile = try AVAudioFile(forReading: URL(string: &quot;&quot;)!)
    let audioEngine = AVAudioEngine()
    let playerNode = AVAudioPlayerNode()
    
    // AVAudioEngine 객체와 AVAudioPlayerNode 인스턴스를 만들고 플레이어 노드를 엔진에 attach
    audioEngine.attach(playerNode)
    
    
    // 플레이어 노드를 출력 노드에 연결
    audioEngine.connect(
        playerNode,
        to: audioEngine.outputNode,
        format: audioFile.processingFormat
    )
    
    // 오디오 파일 전체를 재생하도록 스케줄하고, 재생이 완료되면 콜백을 통해 앱에 알려줌.
    playerNode.scheduleFile(
        audioFile,
        at: nil,
        completionCallbackType: .dataPlayedBack
    ) { _ in
        /* Handle any work that's necessary after playback. */
    }
    
    // 오디오를 재생하기 전에 AudioEngine을 시작
    try audioEngine.start()
    playerNode.play()
    
    // 재생이 완료되면 오디오 플레이어 노드랑 오디오 엔진을 정지.
    playerNode.stop()
    audioEngine.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;h4 data-ke-size=&quot;size20&quot;&gt;AVAudioNode&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;오디오 생성, 처리 또는 입출력(I/O) 블록에 사용하는 객체&lt;/p&gt;
&lt;pre id=&quot;code_1764080384637&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;iOS 8.0+
iPadOS 8.0+
Mac Catalyst 13.1+
macOS 10.10+
tvOS 9.0+
visionOS 1.0+
watchOS 2.0+&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;Overview&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&gt;AVAudioEngine&lt;/span&gt; 객체는 오디오 노드 인스턴스를 포함하며, 이 기본 클래스는 공통 기능을 제공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AVAudioEngine 객체의 인스턴스는 엔진에 연결되지 않으면 실질적인 기능을 수행하지 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드는 입력(input)과 출력(output) 버스(bus)를 가지며, 이 버스들이 연결 지점 역할을 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이펙트(effect) 노드는 하나의 입력 버스와 하나의 출력 버스를 가짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;믹서(mixer) 노드는 여러 입력 버스와 하나의 출력 버스를 가짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 버스는 프레임워크가 샘플 레이트(sample rate)와 채널 수(channel count)로 표현하는 포맷(format)을 포함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드 간 연결 시 포맷은 정확히 일치해야 하고 AVAudioMixerNode와 AVAudioOutputNode는 예외&lt;/p&gt;
&lt;p data-ke-size=&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;Configuring&amp;nbsp;an&amp;nbsp;Input&amp;nbsp;Format&amp;nbsp;Bus&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;b&gt;AVAudioNodeBus&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AVAudioNode의 버스 인덱스&lt;/li&gt;
&lt;li&gt;AVAudioNode 객체는 여러 개의 입력 및 출력 버스를 가질 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt; &lt;/span&gt;AVAudioNodeBus는 버스를 0부터 시작하는 인덱스로 표현.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764083072177&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public typealias AVAudioNodeBus = 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;b&gt;inputFormat(forBus:)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정한 bus의 입력 포맷을 가져옴&lt;/li&gt;
&lt;li&gt;해당 버스의 입력 포맷을 나타내는 AVAudioFormat 인스턴스를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764084244787&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func inputFormat(forBus bus: AVAudioNodeBus) -&amp;gt; AVAudioFormat&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;name(forInputBus:)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정한 입력 버스의 이름을 가져옴&lt;/li&gt;
&lt;li&gt;오디오 노드의 입력 버스&lt;/li&gt;
&lt;li&gt;인풋 버스의 이름&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764084329993&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func name(forInputBus bus: AVAudioNodeBus) -&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;b&gt;numberOfInputs&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인풋 버스의 수&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764084364239&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var numberOfInputs: Int { get }&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;h4 data-ke-size=&quot;size20&quot;&gt;Creating&amp;nbsp;an&amp;nbsp;Output&amp;nbsp;Format&amp;nbsp;Bus&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;b&gt;outputFormat(forBus:)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정한 버스의 출력 포맷&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764084710241&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func outputFormat(forBus bus: AVAudioNodeBus) -&amp;gt; AVAudioFormat&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;b&gt;name(forOutputBus:)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정한 출력 버스의 이름을 가져옴&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764084766007&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func name(forOutputBus bus: AVAudioNodeBus) -&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;b&gt;numberOfOutputs&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드의 출력 버스 수&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764084896726&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var numberOfOutputs: Int { get }&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;Installing&amp;nbsp;and&amp;nbsp;Removing&amp;nbsp;an&amp;nbsp;Audio&amp;nbsp;Tap&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;b&gt;installTap(onBus:bufferSize:format:block:)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정한 버스에 오디오 탭(audio tap)을 설치해 노드의 출력을 녹음, 모니터링, 관찰 할 수 있음.&lt;/li&gt;
&lt;li&gt;엔진이 실행 중일 때도 탭을 설치하거나 제거할 수 있음.&lt;/li&gt;
&lt;li&gt;하나의 버스에는 한 개의 탭만 설치할 수 있음.&lt;/li&gt;
&lt;li&gt;bus
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;탭을 연결한 출력 버스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;bufferSize
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;들어오는 오디오 버퍼의 크기 (실제 구현에서는 다른 크기 선택 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;format
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nil이 아닌 경우에 지정한 출력 버스에 해당 포맷 지정&lt;/li&gt;
&lt;li&gt;이미 연결된 상태의 출력 버스에 탭을 연결하면 오류가 발생&lt;/li&gt;
&lt;li&gt;탭과 연결 포맷 둘 다 nil이 아닌 경우에 두 포맷을 동일해야 하며, 그렇지 않은 경우에는 나중에 적용된 포맷이 이전 포맷을 덮어씀.&lt;/li&gt;
&lt;li&gt;AVAudioOutputNode의 경우 탭 포맷은 반드시 nil로 지정해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;tapBlock
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오 버퍼를 처리하기 위해 프레임워크가 호출하는 블록&lt;/li&gt;
&lt;li&gt;&lt;span&gt;프레임워크는 &lt;/span&gt;&lt;span&gt;tapBlock&lt;/span&gt;&lt;span&gt;을 &lt;/span&gt;메인 스레드가 아닌 다른 스레드에서 호출&lt;span&gt;할 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1764085520497&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func installTap(
    onBus bus: AVAudioNodeBus,
    bufferSize: AVAudioFrameCount,
    format: AVAudioFormat?,
    block tapBlock: @escaping AVAudioNodeTapBlock
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;removeTap(onBus:)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정한 버스의 오디오 탭을 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764085703170&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func removeTap(onBus bus: AVAudioNodeBus)&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;b&gt;AVAudioNodeTapBlock&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오 노드의 출력을 복사하여 받는 블록(block)&lt;/li&gt;
&lt;li&gt;&lt;span&gt;프레임워크는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;tapBlock&lt;/span&gt;&lt;span&gt;을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;메인 스레드가 아닌 다른 스레드에서 호출&lt;span&gt;할 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;buffer
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템이 오디오 노드의 출력에서 캡처한 오디오 버퍼&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;when
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템이 해당 버퍼를 캡처한 시점&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764085860891&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typealias AVAudioNodeTapBlock = (AVAudioPCMBuffer, AVAudioTime) -&amp;gt; Void&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;Getting&amp;nbsp;the&amp;nbsp;Audio&amp;nbsp;Engine&amp;nbsp;for&amp;nbsp;the&amp;nbsp;Node&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;b&gt;engine&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 노드를 관리하는 Audio Engine 존재하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764086092921&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var engine: AVAudioEngine?&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;Getting&amp;nbsp;the&amp;nbsp;Latest&amp;nbsp;Node&amp;nbsp;Render&amp;nbsp;Time&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;b&gt;lastRenderTime&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 최근의 렌더링 시간&lt;/li&gt;
&lt;li&gt;엔진이 실행 중이 아니거나 노드를 입력(input) 혹은 출력(output) 노드에 연결하지 않은 경우 이 값은 nil&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764086201517&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var lastRenderTime: AVAudioTime? { get }&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;h4 data-ke-size=&quot;size20&quot;&gt;Getting&amp;nbsp;Audio&amp;nbsp;Node&amp;nbsp;Properties&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;b&gt;auAudioUnit&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Audio Unit 객체로 구현체의 오디오 유닛을 래핑하거나 기반&lt;/li&gt;
&lt;li&gt;앱이 오디오 유닛을 패키징하는 방식에 따라 구현체의 오디오 유닛을 &lt;span&gt;래핑(wrap)하거나 기반(underlie)으로 하는 AUAudioUnit&lt;/span&gt;을 제공&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔진 상태와 충돌할 수 있는 작업(초기화 상태 변경, 스트림 포맷 변경, 채널 레이아웃 변경, 다른 오디오 유닛과의 연결 변경 등)은 직접 수행하지 말것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;앱은 이를 통해 커스텀 속성을 제어하고, 프리셋을 선택하며, 파라미터를 변경할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764086560219&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var auAudioUnit: AUAudioUnit { get }&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;b&gt;latency&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드의 processing latency 시간&lt;/li&gt;
&lt;li&gt;이 latency는 신호 처리로 인한 딜레이를 나타내어 값이 0인 경우에 latency가 없거나 알 수 없는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764086548576&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var latency: TimeInterval { get }&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;b&gt;outputPresentationLatency&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 이후(render downstream)의 최대 렌더 파이프라인 지연 시간&lt;/li&gt;
&lt;li&gt;이 지연은 노드의 출력에서 오디오가 실제로 재생되기까지 걸리는 최대 시간을 나타냄&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764086668734&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var outputPresentationLatency: TimeInterval { get }&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;h4 data-ke-size=&quot;size20&quot;&gt;Resetting&amp;nbsp;the&amp;nbsp;Audio&amp;nbsp;Node&lt;/h4&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;reset()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오 유닛의 이전 처리 상태를 초기화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Constants&lt;/h4&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;AVAudioNodeCompletionHandler
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오 노드에서 사용하는 일반적인 콜백 핸들러&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;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;AVAudioNode에는 이와 별개로 다른 노드들도 존재&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-11-26 오전 1.09.14.png&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpTG9z/dJMcac2y7Ih/KHlArwf84trKwdmJKNk9B1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpTG9z/dJMcac2y7Ih/KHlArwf84trKwdmJKNk9B1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpTG9z/dJMcac2y7Ih/KHlArwf84trKwdmJKNk9B1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpTG9z%2FdJMcac2y7Ih%2FKHlArwf84trKwdmJKNk9B1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;267&quot; data-filename=&quot;스크린샷 2025-11-26 오전 1.09.14.png&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;720&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/avfaudio/avaudionode&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/avfaudio/avaudionode&lt;/a&gt;&lt;/p&gt;</description>
      <category>Apple/Video, Audio, DRM</category>
      <category>avaudioengine</category>
      <category>avaudionode</category>
      <category>AVAudioNodeBus</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/788</guid>
      <comments>https://rldd.tistory.com/788#entry788comment</comments>
      <pubDate>Wed, 26 Nov 2025 01:09:49 +0900</pubDate>
    </item>
    <item>
      <title>AVAudioFormat 정리</title>
      <link>https://rldd.tistory.com/786</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;AVAudioFormat 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AVAudioFormat에 대해서 정리하며 연관된 지식도 모두 함께 정리.&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;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AVAudioFormat&lt;/li&gt;
&lt;li&gt;AVAudioFormat의 프로퍼티들
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Getting&amp;nbsp;the&amp;nbsp;Audio&amp;nbsp;Stream&amp;nbsp;Description
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;streamDescription&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Getting&amp;nbsp;Audio&amp;nbsp;Format&amp;nbsp;Values
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sampleRate&lt;/li&gt;
&lt;li&gt;channelCount&lt;/li&gt;
&lt;li&gt;channelLayout&lt;/li&gt;
&lt;li&gt;formatDescription&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Determining&amp;nbsp;the&amp;nbsp;Audio&amp;nbsp;Format
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;isInterleaved&lt;/li&gt;
&lt;li&gt;isStandard&lt;/li&gt;
&lt;li&gt;commonFormat&lt;/li&gt;
&lt;li&gt;settings&lt;/li&gt;
&lt;li&gt;magicCookie&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AVAudioFormat과&amp;nbsp;AudioStreamBasicDescription의&amp;nbsp;비교&amp;nbsp;정리&lt;/li&gt;
&lt;li&gt;Channel과&amp;nbsp;ChannelLayout의&amp;nbsp;차이&lt;/li&gt;
&lt;li&gt;AVAudioCommonFormat 정리&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;h4 data-ke-size=&quot;size20&quot;&gt;AVAudioFormat&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플 레이트, 채널 수 등을 포함하는 속성들을 포함하여 오디오 데이터가 어떤 구조로 구성되어 있는지를 알려주는 객체.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763938496265&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;iOS 8.0+
iPadOS 8.0+
Mac Catalyst 13.1+
macOS 10.10+
tvOS 9.0+
visionOS 1.0+
watchOS 2.0+&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-24 오전 7.55.02.png&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LN4La/dJMcabbydvm/IKf0jpo7Tcs7vVbCFcL0fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LN4La/dJMcabbydvm/IKf0jpo7Tcs7vVbCFcL0fK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LN4La/dJMcabbydvm/IKf0jpo7Tcs7vVbCFcL0fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLN4La%2FdJMcabbydvm%2FIKf0jpo7Tcs7vVbCFcL0fK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;293&quot; data-filename=&quot;스크린샷 2025-11-24 오전 7.55.02.png&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;826&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;Overview&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AVAudioFormat 클래스는 Core Audio의 AudioStreamBasicDescription을 감싸는 래퍼.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Core Audio에서 표준으로 사용하는 디인터리브(Deinterleaved)된 32비트 부동소수점(Float32) 포맷을 포함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 오디오 포맷을 손쉽게 다룰 수 있도록 여러 초기화 메서드와 접근자를 제공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;해당 클래스의 인스턴스 프로퍼티를 초기화 후에는 변경할 수 없음 (Immutable)&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size20&quot;&gt;AVAudioFormat의 프로퍼티들&lt;/h4&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;streamDescription&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;오디오 데이터 스트림의 오디오 포맷 속성&lt;/li&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;하위&amp;nbsp;레벨&amp;nbsp;오디오&amp;nbsp;API에서&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;AudioStreamBasicDescription을&amp;nbsp;반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763960184721&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var streamDescription: UnsafePointer&amp;lt;AudioStreamBasicDescription&amp;gt; { get }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sampleRate&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-start=&quot;287&quot; data-end=&quot;323&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;오디오 포맷의 샘플링 레이트(Hz 단위)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763960835883&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var sampleRate: Double { get }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;channelCount&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;오디오 데이터의 채널 수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;AVAudioChannelCount는 UInt32로 이보다 큰 경우 런타임 에러가 발생할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763961055362&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var channelCount: AVAudioChannelCount { get }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-5-mini&quot; data-message-id=&quot;dc383c14-e132-400d-b20f-1be281fe1ade&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;17&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;17&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;287&quot; data-end=&quot;323&quot;&gt;&lt;b&gt;channelLayout&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-start=&quot;287&quot; data-end=&quot;323&quot;&gt;기본 오디오 채널 레이아웃&lt;/li&gt;
&lt;li data-start=&quot;287&quot; data-end=&quot;323&quot;&gt;2채널을 초과하는 포맷에서만 채널 레이아웃이 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763964215450&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var channelLayout: AVAudioChannelLayout? { get }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;17&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;formatDescription&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;Core Media API와 함께 사용할 오디오 포맷&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763964621026&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var formatDescription: CMAudioFormatDescription { get }&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;b&gt;isInterleaved&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-start=&quot;287&quot; data-end=&quot;323&quot;&gt;샘플이 하나의 스트림으로 믹스되는지 여부를 나타내는 불리언 값.&lt;/li&gt;
&lt;li data-start=&quot;287&quot; data-end=&quot;323&quot;&gt;이&amp;nbsp;값은&amp;nbsp;PCM&amp;nbsp;포맷에서만&amp;nbsp;유효.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763968650946&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var isInterleaved: Bool { get }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;isStandard&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;포맷이 디인터리브드(deinterleaved)된 네이티브 엔디안(float) 상태인지 여부를 나타내는 불리언 값
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;이 값은 포맷이 AVAudioCommonFormat.pcmFormatFloat32일 경우 true를 반환&lt;/li&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;그 외의 경우는 false를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763968146457&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var isStandard: Bool { get }&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;b&gt;commonFormat&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;AVAudioCommonFormat 식별자 인스턴스&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763995349607&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var commonFormat: AVAudioCommonFormat { get }&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;b&gt;settings&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;이 설정에 들어가는 딕셔너리는 AudioStreamBasicDescription이 표현하는 모든 형식을 지원하지 않음.&lt;/li&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;따라서 해당 포맷을 지원하지 않는 경우 이 속성은 nil을 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763995648715&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var settings: [String : Any] { get }&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;magicCookie&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;인코더와 디코더가 필요로 하는 메타데이터를 포함한 객체.&lt;/li&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;인코더는 &lt;span&gt;magicCookie&lt;/span&gt; 객체를 생성하며 일부 디코더는 이를 통해 올바르게 디코딩을 수행할 수 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;참고로 magicCookie는 AAC, ALAC 같은 일부 코덱은 압축 형식에 대한 추가 정보가 있어야 제대로 디코딩 할 수 있음.&lt;/li&gt;
&lt;li data-end=&quot;323&quot; data-start=&quot;287&quot;&gt;인코더는 이 정보를 생성해서 디코더에 전달해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763995755099&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var magicCookie: Data? { get set }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;287&quot; 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; data-start=&quot;287&quot; data-end=&quot;323&quot;&gt;AVAudioFormat과 AudioStreamBasicDescription의 비교 정리&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1074&quot; data-start=&quot;1052&quot; data-ke-size=&quot;size16&quot;&gt;AVAudioFormat&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1183&quot; data-start=&quot;1075&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1130&quot; data-start=&quot;1075&quot;&gt;AVFAduio 프레임워크에 속하며 오디오 엔진(API: AVAudioEngine, AVAudioPlayerNode 등)&lt;/li&gt;
&lt;li data-end=&quot;1158&quot; data-start=&quot;1131&quot;&gt;Swift 환경에서 오디오 포맷을 다룰 때&lt;/li&gt;
&lt;li data-end=&quot;1183&quot; data-start=&quot;1159&quot;&gt;안전한 오디오 처리 로직을 구성할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;목적:&amp;nbsp;&lt;/span&gt;&lt;b&gt;오디오 포맷을 쉽게 다룰 수 있게 하는 추상화된 인터페이스&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AudioStreamBasicDescription(ASBD)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Core Audio 프레임워크에 속하는 저수준 API을 위해서 사용&lt;/li&gt;
&lt;li&gt;직접 버퍼 관리, PCM 데이터 처리&lt;/li&gt;
&lt;li&gt;압축 및 비압축 오디오의 실제 패킷 구조를 다뤄야 하는 상황&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적&lt;span&gt;: &lt;/span&gt;&lt;b&gt;하드웨어&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;수준에서&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;오디오&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;데이터를&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;정확히&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;기술하기&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;위한&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;로우&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;레벨&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;데이터&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Channel과 ChannelLayout의 차이&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;채널(Channel): 오디오 트랙 하나를 의미&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;채널 레이아웃(Channel Layout): 채널이 물리적으로 어디에 배치되는지 나타냄&lt;/li&gt;
&lt;li&gt;5.1&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;채널&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이상이면&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;각&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;채널의&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;위치&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;정보가&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;필요합니다&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널(Channel): 오디오 트랙 하나를 의미&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: 모노 &amp;rarr; 1채널&lt;/li&gt;
&lt;li&gt;스테레오 &amp;rarr; 2채널 (왼쪽, 오른쪽)&lt;/li&gt;
&lt;li&gt;서라운드 5.1 &amp;rarr; 6채널 (앞왼, 앞오, 중앙, 서브우퍼, 뒤왼, 뒤오)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경우에 따라 다르지만 우퍼는 저음으로 방향이 존재하지 않아서 .1을 붙임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널 레이아웃(Channel Layout): 채널이 물리적으로 어디에 배치되는지 나타냄&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순히 &amp;ldquo;채널 수 = 6&amp;rdquo;이라고 해도, 어떤 채널이 앞, 뒤, 좌, 우인지를 정의하지 않으면 스피커 배치가 올바르지 않을 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그래서 2채널 이상일 때만 필요&lt;/li&gt;
&lt;li&gt;2채널 스테레오는 일반적으로 왼쪽과 오른쪽이 명확하기 때문에 레이아웃이 필수가 아니지만, 5.1채널 이상이면 각 채널의 위치 정보가 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AVAudioCommonFormat 정리&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;일반적인 오디오 포맷을 설명하는 포맷 옵션으로 Enum으로 설정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;otherFormat
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AVAudioCommonFormat에서 정의하고 있는 것 외에 다른 형식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pcmFormatFloat32
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;표준 형식을 네이티브 엔디언의 부동소수점(float) 형태로 표현한 형식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pcmFormatFloat64
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네이티브 엔디언(double) 형식으로 표현한 포맷&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pcmFormatInt16
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서명된 16비트 네이티브 엔디언 정수(signed 16-bit native-endian integer)로 표현한 포맷&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pcmFormatInt32
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서명된 32비트 네이티브 엔디언 정수(signed 32-bit native-endian integer)로 표현한 포맷&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Apple/Video, Audio, DRM</category>
      <category>AudioStreamBasicDescription</category>
      <category>AVAudioCommonFormat</category>
      <category>AVAudioFormat</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/786</guid>
      <comments>https://rldd.tistory.com/786#entry786comment</comments>
      <pubDate>Mon, 24 Nov 2025 23:58:17 +0900</pubDate>
    </item>
    <item>
      <title>AudioStreamBasicDescription 정리</title>
      <link>https://rldd.tistory.com/787</link>
      <description>&lt;h2 style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span data-v-0e759aa9=&quot;&quot; data-v-38feae48=&quot;&quot;&gt;AudioStreamBasicDescription 정리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CoreAudioType에 속하는 AudioStreamBasicDescription를 정리.&amp;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;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AudioStreamBasicDescription&lt;/li&gt;
&lt;li&gt;패킷의&amp;nbsp;지속&amp;nbsp;시간(Duration)&amp;nbsp;계산&lt;/li&gt;
&lt;li&gt;구조체의&amp;nbsp;모든&amp;nbsp;필드를&amp;nbsp;0으로&amp;nbsp;초기화하는&amp;nbsp;코드&lt;/li&gt;
&lt;li&gt;Core&amp;nbsp;Audio에서&amp;nbsp;사용하는&amp;nbsp;용어&amp;nbsp;정의&lt;/li&gt;
&lt;li&gt;AudioStreamBasicDescription의&amp;nbsp;Instance&amp;nbsp;Property
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mFormatIDmSampleRatemBitsPerChannel&lt;/li&gt;
&lt;li&gt;mBytesPerPacket&lt;/li&gt;
&lt;li&gt;mReserved&lt;/li&gt;
&lt;li&gt;mFramesPerPacket&lt;/li&gt;
&lt;li&gt;mChannelsPerFrame&lt;/li&gt;
&lt;li&gt;mBytesPerFrame&lt;/li&gt;
&lt;li&gt;mFormatFlags&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span data-v-38feae48=&quot;&quot; data-v-0e759aa9=&quot;&quot;&gt;AudioStreamBasicDescription&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 data-v-0e759aa9=&quot;&quot; data-v-38feae48=&quot;&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 data-v-0e759aa9=&quot;&quot; data-v-38feae48=&quot;&quot;&gt;오디오 데이터가 어떤 형식으로 구성되어 있는지를 정의하는 명세서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-v-0e759aa9=&quot;&quot; data-v-38feae48=&quot;&quot;&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_1763938783451&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;iOS 2.0+
iPadOS 2.0+
macOS 10.0+
tvOS 9.0+
visionOS 1.0+
watchOS 3.0+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-24 오전 7.59.47.png&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;746&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pWDiT/dJMcac9j5IB/hKnSX63Qyz9P3qU90yUbK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pWDiT/dJMcac9j5IB/hKnSX63Qyz9P3qU90yUbK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pWDiT/dJMcac9j5IB/hKnSX63Qyz9P3qU90yUbK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpWDiT%2FdJMcac9j5IB%2FhKnSX63Qyz9P3qU90yUbK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;277&quot; data-filename=&quot;스크린샷 2025-11-24 오전 7.59.47.png&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;746&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;Overview&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;오디오 스트림 기본 설명(Audio Stream Basic Description, ASBD)을 구성할 때 선형 PCM(Linear PCM) 포맷 또는 채널 크기가 동일한 CBR(Constant Bit Rate) 포맷을 지정할 수 있음.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-end=&quot;351&quot; data-start=&quot;217&quot; data-ke-size=&quot;size16&quot;&gt;만약 가변 비트레이트(VBR) 오디오거나 혹은 채널 크기가 서로 다른 CBR 오디오&lt;br /&gt;각 패킷의 정보를 추가로 기술하기 위해 AudioStreamPacketDescription 구조체도 함께 사용해야 함.&lt;/p&gt;
&lt;p data-end=&quot;351&quot; data-start=&quot;217&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;351&quot; data-start=&quot;217&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;만약 필드 값이 0이면, 해당 값이 알 수 없거나 혹은 format에 적용되지 않음을 의미&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;351&quot; data-start=&quot;217&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;482&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;또한, 새로운 ASBD 구조체를 생성할 때는 모든 필드를 명시적으로 0으로 초기화하는 것을 권장&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;구조체의 모든 필드를 0으로 초기화하는 코드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;공식 문서에는 C 언어로 작성되어 있으나 Swift 문법 예제 추가&lt;/p&gt;
&lt;pre id=&quot;code_1763939176912&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;// MARK: - C Language
AudioStreamBasicDescription myAudioDataFormat = {0};

// MARK: - Swift Language

var myAudioDataFormat = AudioStreamBasicDescription()
myAudioDataFormat.mSampleRate = 0
myAudioDataFormat.mFormatID = 0
myAudioDataFormat.mFormatFlags = 0
myAudioDataFormat.mBytesPerPacket = 0
myAudioDataFormat.mFramesPerPacket = 0
myAudioDataFormat.mBytesPerFrame = 0
myAudioDataFormat.mChannelsPerFrame = 0
myAudioDataFormat.mBitsPerChannel = 0
myAudioDataFormat.mReserved = 0&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;h4 data-ke-size=&quot;size20&quot;&gt;패킷의 지속 시간(Duration) 계산&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오디오 스트림에서 하나의 패킷이 재생되는 시간을 계산하려면 AudioStreamBasicDescription에 존재하는 2개의 필드를 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mSampleRate: 초당 샘플 수&lt;/li&gt;
&lt;li&gt;mFramesPerPacket: 패킷 당 프레임 수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763939314237&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MARK: - C Language
duration = (1 / mSampleRate) * mFramesPerPacket

// MARK: - Swift Language
let duration = (1.0 / sampleRate) * Double(framesPerPacket)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수식에 대한 설명&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;x = 1 / mSampleRate: 한 샘플의 재생 시간&amp;nbsp;&lt;/li&gt;
&lt;li&gt;y = mFramesPerPacket: 패킷 내 프레임의 수 &lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, x와 y를 곱하면 해당 패킷이 재생되는 총 시간을 구할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플코드&lt;/p&gt;
&lt;pre id=&quot;code_1763939703830&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let sampleRate: Double = 44100  // 1초에 44100 샘플
let framesPerPacket: UInt32 = 1024

let duration = (1.0 / sampleRate) * Double(framesPerPacket)
print(&quot;패킷 지속 시간: \(duration) 초&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;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-end=&quot;88&quot; data-start=&quot;69&quot; data-ke-size=&quot;size20&quot;&gt;Core Audio에서 사용하는 용어 정의&lt;/h4&gt;
&lt;p data-end=&quot;116&quot; data-start=&quot;90&quot; data-ke-size=&quot;size16&quot;&gt;Core Audio에서 용어를 아래와 같은 의미로 정의하여서 사용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;859&quot; data-start=&quot;118&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;213&quot; data-start=&quot;118&quot;&gt;오디오 스트림(Audio Stream)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;213&quot; data-start=&quot;118&quot;&gt;소리를 표현하는 연속적인 데이터 시퀀스를 의미&lt;/li&gt;
&lt;li data-end=&quot;213&quot; data-start=&quot;118&quot;&gt;에시) 노래 한 곡의 데이터가 오디오 스트림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;309&quot; data-start=&quot;215&quot;&gt;채널(Channel)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;309&quot; data-start=&quot;215&quot;&gt;모노 오디오의 독립적인 트랙을 의미
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;286&quot; data-start=&quot;268&quot;&gt;모노 스트림 : 1개 채널&lt;/li&gt;
&lt;li data-end=&quot;286&quot; data-start=&quot;268&quot;&gt;스테레오 스트림 : 2개 채널&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;369&quot; data-start=&quot;311&quot;&gt;샘플(Sample)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;369&quot; data-start=&quot;311&quot;&gt;오디오 스트림 내 단일 채널의 단일 수치 값을 의미.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;513&quot; data-start=&quot;371&quot;&gt;프레임(Frame)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;513&quot; data-start=&quot;371&quot;&gt;시간적으로 일치하는 샘플들의 집합&lt;/li&gt;
&lt;li data-end=&quot;513&quot; data-start=&quot;371&quot;&gt;예시) Linear PCM 스테레오 오디오 파일은 프레임당 2개의 샘플을 가짐&lt;/li&gt;
&lt;li data-end=&quot;513&quot; data-start=&quot;479&quot;&gt;왼쪽 채널 1샘플 + 오른쪽 채널 1샘플 = 1 프레임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;740&quot; data-start=&quot;515&quot;&gt;패킷(Packet)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;740&quot; data-start=&quot;515&quot;&gt;연속된 1개 이상의 프레임 집합을 의미&lt;/li&gt;
&lt;li data-end=&quot;621&quot; data-start=&quot;569&quot;&gt;패킷은 특정 오디오 데이터 포맷에서 시간을 측정할 수 있는 가장 작은 단위&lt;/li&gt;
&lt;li data-end=&quot;661&quot; data-start=&quot;624&quot;&gt;Linear PCM에서는 패킷이 1 프레임을 담음.&lt;/li&gt;
&lt;li data-end=&quot;699&quot; data-start=&quot;664&quot;&gt;압축 포맷에서는 일반적으로 여러 프레임 담음.&lt;/li&gt;
&lt;li data-end=&quot;740&quot; data-start=&quot;702&quot;&gt;일부 포맷에서는 패킷당 프레임 수가 가변적일 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;859&quot; data-start=&quot;742&quot;&gt;샘플 레이트(Sample Rate)
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;859&quot; data-start=&quot;772&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;816&quot; data-start=&quot;772&quot;&gt;비압축 오디오: 초당 프레임 수(frames per second)&lt;/li&gt;
&lt;li data-end=&quot;859&quot; data-start=&quot;819&quot;&gt;압축 오디오: 압축을 해제했을 때의 동등한 초당 프레임 수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AudioStreamBasicDescription의&amp;nbsp;Instance&amp;nbsp;Property&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 객체에 존재하는 인스턴스 프로퍼티 정리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;mFormatID&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스트림의&amp;nbsp;일반&amp;nbsp;오디오&amp;nbsp;데이터&amp;nbsp;형식을&amp;nbsp;지정하는&amp;nbsp;식별자&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;지원되는 오디오 형식을 나타내는 식별자&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;간단하게 코덱에 대한 정보&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763943585548&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var mFormatID: AudioFormatID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;mFormatFlags&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;형식별 세부 정보를 지정하기 위한 포맷 전용 플래그&lt;/li&gt;
&lt;li&gt;플래그가 없음을 나타내려면 0으로 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763943907437&quot; class=&quot;actionscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;var mFormatFlags: AudioFormatFlags&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;mSampleRate&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;압축 포맷의 경우 압축을 해제한 데이터 기준 초당 프레임 수를 나타냄&lt;/li&gt;
&lt;li&gt;mSampleRate&amp;nbsp;필드는&amp;nbsp;0이&amp;nbsp;될&amp;nbsp;수&amp;nbsp;없음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단, AudioStreamBasicDescription 구조체에서 지원되는 포맷 리스트를 나열할 때는 예외 (kAudioStreamAnyRate로 표현)&lt;/li&gt;
&lt;li&gt;kAudioStreamAnyRate은 오디오 스트림이 어떤 샘플 레이트라도 사용할 수 있음을 나타내는 값&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763943908936&quot; class=&quot;actionscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;var mFormatFlags: AudioFormatFlags&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;단, AudioStreamBasicDescription 구조체에서 지원되는 포맷 리스트를 나열할 때는 예외라는 의미는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;녹음할 때, 샘플 레이트, 채널 수 등을 정확하게 넣어주어야 하나, OS나 라이브러리가 단순히 어떤 포맷들을 지원하는지 목록으로 나열해줄 때는 샘플레이트가 정확히 무엇인지는 중요하지 않기 때문에 mSampleRate = kAudioStreamAnyRate와 같은 특수값으로 표현.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;b&gt;mBitsPerChannel&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오 샘플 하나를 표현하는 비트 수&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;압축 포맷의 경우에는 패킷 내 샘플 비트 수를 정확하게 알 수 없어서 mBitsPerChannel 값을 0으로 설정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;즉&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;비압축&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;오디오에서는&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;샘플당&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;비트&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;수를&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;직접&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;계산하고&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;압축&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;오디오에서는&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;의미&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;없는&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;값이므로&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;0&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;으로&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;초기화&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763943611668&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var mBitsPerChannel: UInt32&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;br /&gt;linear PCM에서 kAudioFormatFlagsCanonical를 사용하는 경우 비트수를 계산하는 방법&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763940928456&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MARK: - C Language
mBitsPerChannel = 8 * sizeof(AudioSampleType);

// MARK: - Swift Language
let mBitsPerChannel = 8 * MemoryLayout&amp;lt;AudioSampleType&amp;gt;.size // warning: 'AudioSampleType' is deprecated: The concept of canonical formats is deprecated&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;Xcode 26에서는 아래와 같은 경고가 발생.&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-11-24 오전 8.40.17.png&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cipakJ/dJMcahCPT1J/X2DSJ2qrMNVgkSyAyHDVLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cipakJ/dJMcahCPT1J/X2DSJ2qrMNVgkSyAyHDVLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cipakJ/dJMcahCPT1J/X2DSJ2qrMNVgkSyAyHDVLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcipakJ%2FdJMcahCPT1J%2FX2DSJ2qrMNVgkSyAyHDVLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;474&quot; data-filename=&quot;스크린샷 2025-11-24 오전 8.40.17.png&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1763941003404&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;warning: 'AudioSampleType' is deprecated: The concept of canonical formats is deprecated&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;Core Audio 프레임워크에서 AudioSampleType 타입이 더 이상 권장되지 않으며, 예전&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&amp;ldquo;canonical format&amp;rdquo; 개념 자체가 deprecated 되었음을 의미. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;모든 오디오 샘플은 명시적으로 포맷을 지정해야 하고 오래된 기본형 AudioSampleType을 사용하지 말라고 경고를 띄워줌.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;아래 샘플 코드처럼 Core Audio에서 AudioSampleType에 의존하기 보단, ASBD 등 명시적으로 포맷을 전환하는 것이 안전.&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_1763941121839&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var asbd = AudioStreamBasicDescription()
asbd.mSampleRate = 44100
asbd.mFormatID = kAudioFormatLinearPCM
asbd.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked
asbd.mBitsPerChannel = 32
asbd.mChannelsPerFrame = 2
asbd.mBytesPerFrame = asbd.mChannelsPerFrame * (asbd.mBitsPerChannel / 8)
asbd.mFramesPerPacket = 1
asbd.mBytesPerPacket = asbd.mBytesPerFrame * asbd.mFramesPerPacket&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;mBytesPerFrame&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오 버퍼에서 한 프레임의 시작 지점부터 다음 프레임 시작 지점까지의 바이트 수&lt;/li&gt;
&lt;li&gt;압축&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;포맷의&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;경우&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;이&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;필드는&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;0&lt;/span&gt;으로&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;설정&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763944825730&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var mBytesPerFrame: UInt32&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;n개의 채널을 가지는 인터리브드(interleaved) 데이터의 오디오 버퍼에서, 각 샘플이 AudioSampleType일 경우&lt;/p&gt;
&lt;pre id=&quot;code_1763944730733&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MARK: - C Language
mBytesPerFrame = n * sizeof(AudioSampleType)

// MARK: - Swift Language

// 인터리브드 데이터 (n개의 채널)
let nChannels = n // 채널 수
let bytesPerFrame = nChannels * MemoryLayout&amp;lt;AudioSampleType&amp;gt;.size&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;비인터리브드(noninterleaved, 모노포닉) 데이터의 오디오 버퍼에서, 각 샘플이 AudioSampleType일 경우&lt;/p&gt;
&lt;pre id=&quot;code_1763944738729&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MARK: - C Language
mBytesPerFrame = sizeof(AudioSampleType)

// MARK: - Swift Language

// 비인터리브드 데이터 (모노포닉)
let monoBytesPerFrame = MemoryLayout&amp;lt;AudioSampleType&amp;gt;.size&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;mChannelsPerFrame&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 오디오 프레임에 포함된 채널 수&lt;/li&gt;
&lt;li&gt;이 값은 0이 될 수 없음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;170&quot; data-start=&quot;81&quot;&gt;오디오에서 &lt;b&gt;채널이 0개&lt;/b&gt;라는 것은 의미가 없음.&lt;/li&gt;
&lt;li data-end=&quot;170&quot; data-start=&quot;81&quot;&gt;0채널이면 소리가 없는 것과 같아서, 프레임 단위 계산이나 버퍼 처리도 불가능&lt;/li&gt;
&lt;li data-end=&quot;269&quot; data-start=&quot;171&quot;&gt;따라서 Core Audio에서는 항상 1 이상이어야 함.
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;269&quot; data-start=&quot;215&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;225&quot; data-start=&quot;215&quot;&gt;1 &amp;rarr; 모노&lt;/li&gt;
&lt;li data-end=&quot;240&quot; data-start=&quot;228&quot;&gt;2 &amp;rarr; 스테레오&lt;/li&gt;
&lt;li data-end=&quot;269&quot; data-start=&quot;243&quot;&gt;5, 6, 7, 8 &amp;rarr; 서라운드 채널 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763944868667&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var mChannelsPerFrame: UInt32&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;b&gt;&lt;br /&gt;mBytesPerPacket&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오&amp;nbsp;데이터&amp;nbsp;패킷의&amp;nbsp;바이트&amp;nbsp;수&lt;/li&gt;
&lt;li&gt;가변&amp;nbsp;패킷&amp;nbsp;크기(variable&amp;nbsp;packet&amp;nbsp;size)를&amp;nbsp;나타내려면&amp;nbsp;이&amp;nbsp;필드를&amp;nbsp;0으로&amp;nbsp;설정&lt;/li&gt;
&lt;li&gt;가변&amp;nbsp;패킷&amp;nbsp;크기를&amp;nbsp;사용하는&amp;nbsp;포맷의&amp;nbsp;경우,&amp;nbsp;각&amp;nbsp;패킷의&amp;nbsp;크기는&amp;nbsp;AudioStreamPacketDescription&amp;nbsp;구조체를&amp;nbsp;사용하여&amp;nbsp;지정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-v-38feae48=&quot;&quot; data-v-0e759aa9=&quot;&quot;&gt;AudioStreamPacketDescription&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;/li&gt;
&lt;li&gt;mStartOffset
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버퍼의 시작 지점부터 해당 패킷의 시작 지점까지의 &lt;b&gt;바이트 수&lt;/b&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;mVariableFramesInPacket
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패킷 내 데이터의 샘플 프레임 수&lt;/li&gt;
&lt;li&gt;만약 패킷당 프레임 수가 일정한 포맷에서는 이 필드를 0으로 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;mDataByteSize
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패킷의&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;바이트&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763956418586&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var mBytesPerPacket: UInt32&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;mFramesPerPacket&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오 데이터 패킷에 포함된 프레임 수&lt;/li&gt;
&lt;li&gt;비압축 오디오의 경우 이 값은 1&lt;/li&gt;
&lt;li&gt;가변 비트레이트(VBR) 형식에서는 이 값이 1024(AAC)처럼 더 큰 고정된 숫자.&lt;/li&gt;
&lt;li&gt;Ogg Vorbis처럼 패킷마다 프레임 수가 달라지는 형식이라면, 이 필드를 0으로 설정.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763956401753&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var mFramesPerPacket: UInt32&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;b&gt;mReserved&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구조체를&amp;nbsp;8바이트&amp;nbsp;단위로&amp;nbsp;정렬하기&amp;nbsp;위해&amp;nbsp;추가하는&amp;nbsp;패딩&amp;nbsp;바이트&amp;nbsp;수&lt;/li&gt;
&lt;li&gt;이 값은 0으로 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763946391709&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var mReserved: UInt32&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고)&lt;br /&gt;&lt;a href=&quot;https://developer.apple.com/documentation/CoreAudioTypes/AudioStreamBasicDescription&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/CoreAudioTypes/AudioStreamBasicDescription&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763995187072&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;AudioStreamBasicDescription | Apple Developer Documentation&quot; data-og-description=&quot;A format specification for an audio stream.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/CoreAudioTypes/AudioStreamBasicDescription&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/CoreAudioTypes/AudioStreamBasicDescription&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/T2C8a/hyZOj5wS3g/5Qtj6FK6jy3v1tv1ePla6k/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/oHopX/hyZN6zWhiX/VSY03Dxs1QRu5cDmfUaqpK/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/CoreAudioTypes/AudioStreamBasicDescription&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/CoreAudioTypes/AudioStreamBasicDescription&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/T2C8a/hyZOj5wS3g/5Qtj6FK6jy3v1tv1ePla6k/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/oHopX/hyZN6zWhiX/VSY03Dxs1QRu5cDmfUaqpK/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;AudioStreamBasicDescription | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A format specification for an audio stream.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Apple/Video, Audio, DRM</category>
      <category>AudioStreamBasicDescription</category>
      <category>AudioStreamPacketDescription</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/787</guid>
      <comments>https://rldd.tistory.com/787#entry787comment</comments>
      <pubDate>Mon, 24 Nov 2025 23:40:30 +0900</pubDate>
    </item>
    <item>
      <title>AVAudioPCMBuffer 정리</title>
      <link>https://rldd.tistory.com/785</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;AVAudioPCMBuffer 정리&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AVAudioPCMBuffer를 학습하고 이와 관련한 개념들을 정리.&lt;/p&gt;
&lt;p data-ke-size=&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;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AVAudioPCMBuffer 선수 지식 정리&lt;/li&gt;
&lt;li&gt;AVAudioPCMBuffer&lt;/li&gt;
&lt;li&gt;AVAudioPCMBuffer의 인스턴스 프로퍼티
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AVAudioPCMBuffer().frameLength&lt;/li&gt;
&lt;li&gt;AVAudioPCMBuffer().frameCapacity&lt;/li&gt;
&lt;li&gt;AVAudioPCMBuffer().stride&lt;/li&gt;
&lt;li&gt;AVAudioPCMBuffer().floatChannelData&lt;/li&gt;
&lt;li&gt;AVAudioPCMBuffer().int16ChannelData, AVAudioPCMBuffer().int32ChannelData&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AVAudioPCMBuffer&amp;nbsp;선수&amp;nbsp;지식&amp;nbsp;정리&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;AVAudioPCMBuffer를 학습하기 전에 사전 개념으로 필요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 이론은 하단의 링크 참고.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PCM(Audio)란?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pulse Code Modulation로 디지털 오디오의 가장 기본적인 형태를 의미.&lt;/li&gt;
&lt;li&gt;아날로그 소리를 일정한 간격으로 샘플링하고, 각 샘플을 디지털(숫자 값)으로 변환한 것.&lt;/li&gt;
&lt;li&gt;즉, PCM 오디오는 실제 소리의 진폭을 숫자로 표현한 샘플들의 배열&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PCM buffer란?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PCM buffer는 PCM 오디오 샘플을 담고 있는 메모리 공간(버퍼)를 의미.&lt;/li&gt;
&lt;li&gt;즉, 오디오 데이터 배열을 담은 객체로 transcription, analyze, 재생 등 오디오 처리를 위해 사용&lt;/li&gt;
&lt;li&gt;iOS에서는 AnalyzerInput이나 AVAudioPCMBuffer 같은 객체가 PCM Buffer를 제공.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석, 전사, 신호 처리 등 연속된 샘플 데이터가 필요한데, PCM buffer는 이 샘플 데이터를 일정한 포맷으로 모아둔 컨테이너 역할.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플링 레이트(예: 16kHZ, 44.1kHz), 채널 수(에: 모노, 스트레오), 각 샘플의 비트 깊이(예: 16-bit, 24, bit) 등의 정보와 함께 PCM buffer를 분석 모듈에 제공하면 음성을 텍스트로 변환하거나 VAD(음성 감지) 등을 처리할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&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;AVAudioPCMBuffer&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;PCM 오디오 포맷과 함께 사용하는 오디오 버퍼를 나타내는 객체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763912818306&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;iOS 8.0+
iPadOS 8.0+
Mac Catalyst 13.1+
macOS 10.10+
tvOS 9.0+
visionOS 1.0+
watchOS 2.0+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-24 오전 12.33.47.png&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;718&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5ivT2/dJMcajtPQ3p/785KosAnOGnCGZwZxOwLJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5ivT2/dJMcajtPQ3p/785KosAnOGnCGZwZxOwLJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5ivT2/dJMcajtPQ3p/785KosAnOGnCGZwZxOwLJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5ivT2%2FdJMcajtPQ3p%2F785KosAnOGnCGZwZxOwLJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;280&quot; data-filename=&quot;스크린샷 2025-11-24 오전 12.33.47.png&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;718&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&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;AVAudioPCMBuffer().frameLength&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버퍼의 현재 유효 샘플 프레임 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763913163660&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var frameLength: AVAudioFrameCount { get set }&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&gt;frameLength&lt;/span&gt; 속성은 유용한 값을 가지지 않으므로, &lt;span&gt;버퍼를 사용하기 전에 반드시 이 값을 설정.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;frameLength&lt;span&gt; 값은 버퍼의 &lt;/span&gt;frameCapacity&lt;span&gt;보다 &lt;/span&gt;&lt;span&gt;작거나 같아야 함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;deinterleaved 포맷&lt;span&gt;에서는 &lt;/span&gt;&lt;span&gt;frameCapacity&lt;/span&gt;&lt;span&gt;가 &lt;/span&gt;각 채널의 오디오 샘플 수&lt;span&gt;를 의미.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;버퍼 내용 수정 작업 중에 &lt;span&gt;frameLength&lt;/span&gt;를 변경할 수 있음.&lt;/li&gt;
&lt;li&gt;frameLength&lt;span&gt;를 수정하면 내부 &lt;/span&gt;AudioBufferList&lt;span&gt; 구조체의 각 &lt;/span&gt;AudioBuffer&lt;span&gt; 속성에 있는 &lt;/span&gt;mDataByteSize&lt;span&gt; 값도 &lt;/span&gt;&lt;span&gt;자동으로 업데이트&lt;/span&gt;&lt;span&gt;되며 그 반대도 마찬가지.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;정리하자면, frameLength는 버퍼 안에서 실제로 사용 가능한 오디오 샘플의 길이를 나타내는 값.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;버퍼를 분석, 재생, 전사 등에 사용하기 전에 정확하게 설정해 주어야 함.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;AVAudioPCMBuffer().frameCapacity&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763913676910&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var frameCapacity: AVAudioFrameCount { get }&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;AVAudioPCMBuffer().stride&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763914319693&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var stride: Int { get }&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;AVAudioPCMBuffer().floatChannelData&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;b&gt;&lt;br /&gt;&lt;/b&gt;버퍼의 오디오 샘플을 부동소수점(floating point) 값으로 나타낸 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763913747623&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var floatChannelData: UnsafePointer&amp;lt;UnsafeMutablePointer&amp;lt;Float&amp;gt;&amp;gt;? { get }&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;floatChannelData&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;버퍼의 포맷이 32비트 부동소수점(float)일 경우 버퍼의 오디오 샘플을 가리키는 포인터를 반환&lt;/li&gt;
&lt;li&gt;버퍼가 다른 포맷이라면 &lt;span&gt;nil&lt;/span&gt;을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환되는 포인터 구조&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반환되는 포인터는 &lt;span&gt;format.channelCount&lt;/span&gt; 개수만큼의 &lt;span&gt;float 포인터&lt;/span&gt; 배열&lt;/li&gt;
&lt;li&gt;각 포인터는 &lt;span&gt;frameLength&lt;/span&gt;만큼의 &lt;span&gt;유효 샘플&lt;/span&gt;을 가리키며, 클래스는 샘플 간 &lt;span&gt;stride&lt;/span&gt; 값에 따라 메모리를 배치.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포맷별 동작 차이&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;비인터리브(non-interleaved) 포맷
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;표준 &lt;/span&gt;deinterleaved float 포맷&lt;span&gt;처럼 각 채널은 &lt;/span&gt;별도의 메모리 영역&lt;span&gt;을 가리킴.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;이 경우 &lt;span&gt;stride&lt;/span&gt; 값은 항상 &lt;span&gt;1&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인터리브(interleaved) 포맷
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;샘플이 &lt;/span&gt;채널별로 섞여(interleaved)&lt;span&gt; 저장&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;각 포인터는 동일한 버퍼를 가리키지만, &lt;span&gt;1프레임씩 오프셋(offset)&lt;/span&gt; 되어 있음.&lt;/li&gt;
&lt;li&gt;이 경우 &lt;span&gt;stride&lt;/span&gt; 값은 &lt;span&gt;인터리브된 채널 수&lt;/span&gt;를 나타냄.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/txJEA/dJMcaaqa3fw/F1QaaBTSHdLBPCFSysBAgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/txJEA/dJMcaaqa3fw/F1QaaBTSHdLBPCFSysBAgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/txJEA/dJMcaaqa3fw/F1QaaBTSHdLBPCFSysBAgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtxJEA%2FdJMcaaqa3fw%2FF1QaaBTSHdLBPCFSysBAgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;281&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;interleaved과 non-interleaved에 대한 정리는 별도로 진행.&lt;/p&gt;
&lt;p data-ke-size=&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;AVAudioPCMBuffer().int16ChannelData,&amp;nbsp;AVAudioPCMBuffer().int32ChannelData&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;int16ChannelData&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버퍼의 16비트 정수 오디오 샘플&lt;/li&gt;
&lt;li&gt;int16ChannelData 속성은 버퍼의 형식이 2바이트 정수 샘플인 경우 해당 오디오 샘플을 반환하며, 다른 형식의 경우에는 nil 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;int32ChannelData&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버퍼의 32비트 정수 오디오 샘플&lt;/li&gt;
&lt;li&gt;int16ChannelData 속성은 버퍼의 형식이 4바이트 정수 샘플인 경우 해당 오디오 샘플을 반환하며, 다른 형식의 경우에는 nil 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/AVFAudio/AVAudioPCMBuffer&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/AVFAudio/AVAudioPCMBuffer&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763911925133&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;AVAudioPCMBuffer | Apple Developer Documentation&quot; data-og-description=&quot;An object that represents an audio buffer you use with PCM audio formats.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/AVFAudio/AVAudioPCMBuffer&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/AVFAudio/AVAudioPCMBuffer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bendPW/hyZOyVIfdK/7kkW9lK0CM3UBdJZ5khv0k/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/163L8/hyZNGOrBC2/PPLW251drtxJnOORAhkXqk/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/AVFAudio/AVAudioPCMBuffer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/AVFAudio/AVAudioPCMBuffer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bendPW/hyZOyVIfdK/7kkW9lK0CM3UBdJZ5khv0k/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/163L8/hyZNGOrBC2/PPLW251drtxJnOORAhkXqk/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;AVAudioPCMBuffer | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An object that represents an audio buffer you use with PCM audio formats.&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;a href=&quot;https://m.blog.naver.com/vasurada0/220987206351&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://m.blog.naver.com/vasurada0/220987206351&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763912444028&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;누워서 읽는 디지털 오디오 포맷 1) PCM (오디오 CD)&quot; data-og-description=&quot;아날로그 세계를 디지털로 기록하는 방법우리가 귀로 듣는 소리는 기본적으로 공기를 타고 전파되는 아날로...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://m.blog.naver.com/vasurada0/220987206351&quot; data-og-url=&quot;https://blog.naver.com/vasurada0/220987206351&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/3jfZY/hyZOt1asY2/z23lnxw4SlXm0i6oK5EAS0/img.jpg?width=743&amp;amp;height=418&amp;amp;face=0_0_743_418&quot;&gt;&lt;a href=&quot;https://m.blog.naver.com/vasurada0/220987206351&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://m.blog.naver.com/vasurada0/220987206351&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/3jfZY/hyZOt1asY2/z23lnxw4SlXm0i6oK5EAS0/img.jpg?width=743&amp;amp;height=418&amp;amp;face=0_0_743_418');&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;누워서 읽는 디지털 오디오 포맷 1) PCM (오디오 CD)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;아날로그 세계를 디지털로 기록하는 방법우리가 귀로 듣는 소리는 기본적으로 공기를 타고 전파되는 아날로...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Apple/Video, Audio, DRM</category>
      <category>AVAudioPCMBuffer</category>
      <category>PCM Buffer란</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/785</guid>
      <comments>https://rldd.tistory.com/785#entry785comment</comments>
      <pubDate>Mon, 24 Nov 2025 00:32:15 +0900</pubDate>
    </item>
    <item>
      <title>AssetInventory 정리 + AssetInstallationRequest</title>
      <link>https://rldd.tistory.com/784</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;AssetInventory 정리 + AssetInstallationRequest&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AssetInvenotry에 대해서 정리하고 관련된 메서드들 하나씩 정리&lt;/p&gt;
&lt;p data-ke-size=&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;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AssetInventory
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Install Assets&lt;/li&gt;
&lt;li&gt;Manage Assets&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Downloading&amp;nbsp;and&amp;nbsp;installing&amp;nbsp;assets
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;assetInstallationRequest(supporting:)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Checking&amp;nbsp;asset&amp;nbsp;status
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AssetInventory.Status
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;downloading&lt;/li&gt;
&lt;li&gt;installed&lt;/li&gt;
&lt;li&gt;supported&lt;/li&gt;
&lt;li&gt;unsupported&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Type&amp;nbsp;Properties
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AssetInventory.maximumReservedLocales&lt;/li&gt;
&lt;li&gt;AssetInventory.reservedLocales&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Type Methods
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AssetInventory.release(reservedLocale:&amp;nbsp;Locale)&amp;nbsp;async&amp;nbsp;-&amp;gt;&amp;nbsp;Bool&lt;/li&gt;
&lt;li&gt;AssetInventory.reserve(locale:)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AssetInstallationRequest
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;AssetInstallationRequest의&amp;nbsp;downloadAndInstall()&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763809578729&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;iOS 26.0+
iPadOS 26.0+
Mac Catalyst 26.0+
macOS 26.0+
visionOS 26.0+&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;transcription(받아쓰기) 또는 여러 기타 분석에&amp;nbsp; 필요한 Asset 들을 관리함.&lt;/p&gt;
&lt;p data-ke-size=&quot;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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-22 오후 8.07.12.png&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lVviu/dJMcacuKTKC/4K7Kko4AK1j28K7WdvMoq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lVviu/dJMcacuKTKC/4K7Kko4AK1j28K7WdvMoq0/img.png&quot; data-alt=&quot;class로 구현되어 있음.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lVviu/dJMcacuKTKC/4K7Kko4AK1j28K7WdvMoq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlVviu%2FdJMcacuKTKC%2F4K7Kko4AK1j28K7WdvMoq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;273&quot; data-filename=&quot;스크린샷 2025-11-22 오후 8.07.12.png&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;class로 구현되어 있음.&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;Overview&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&gt;SpeechAnalyzer&lt;/span&gt; 클래스를 사용하기 전에, 사용할 모듈에 필요한 에셋을 설치.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 에셋들은 Apple 서버에서 다운로드되며 시스템이 관리하는 &lt;span&gt;ML 모델.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번 에셋을 다운로드 혹은 설치하거나 사용하면 시스템은 이를 자동으로 보관하고 업데이트하여 다른 앱과도 공유.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 시스템은 저장 공간과 네트워크 사용량을 제한하기 위해, 앱이 사용할 수 있는 Locale 기반 asset reservations 수를 제한적으로 제공.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱은 에셋을 직접 다루지 않음. 대신 앱은 &lt;span&gt;모듈 객체를 구성&lt;/span&gt;하고, 시스템은 그 구성 내용을 바탕으로 어떤 에셋이 필요한지 판단.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;asset reservations이란?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Speech 프레임워크가 사용하는 모델(음성 인식 및 언어 모델 등)은 기기에 다운로드되어 저장.&lt;/li&gt;
&lt;li&gt;모든 앱이 무제한으로 모델을 설치하도록 허용할 시 기기 저장 공간이 과도하게 사용되고, 네트워크 트래픽이 많아질 수 있기에 iOS 앱 별로 설치 가능한 모델 개수에 제한.&lt;/li&gt;
&lt;li&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;AssetInventory를 통해 설치 가능 여부를 확인하고, 필요 없는 언어 모델은 사용 중지하여 공간을 확보.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;Install assets (에셋 설치하기)&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&gt;4단계&lt;/span&gt;로 이루어짐.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용하려는 구성(configuration)으로 analyze 모듈 생성.&amp;nbsp;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시스템은 객체 자체가 아니라, 모듈의 구성 정보를 기반으로 필요한 에셋을 설치.&lt;/li&gt;
&lt;li&gt;모듈 객체 자체는 이후에 폐기(discard)해도 괜찮음.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;앱의 에셋 예약(asset reservations)을 해당 로케일(locale)에 할당
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;Locale 기반 에셋이 필요한 모듈&lt;/span&gt;(즉, &lt;span&gt;LocaleDependentSpeechModule&lt;/span&gt; 프로토콜을 준수하는 모듈)에만 필요.&lt;/li&gt;
&lt;li&gt;그 외 모듈은 이 단계를 건너뛰어도 상관 없음.&lt;/li&gt;
&lt;li&gt;클래스가 필요 시 자동으로 수행하지만, 직접 &lt;span&gt;reserve(locale:)&lt;/span&gt;를 호출해 수동으로 처리할 수도 있음.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;모듈 구성에서 필요한 에셋 다운로드를 시작.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;그 객체의 &lt;span&gt;downloadAndInstall()&lt;/span&gt; 메서드를 호출하여 다운로드 및 설치 시작&lt;/li&gt;
&lt;li&gt;assetInstallationRequest(supporting:)&lt;span&gt;를 호출해 &lt;/span&gt;AssetInstallationRequest&lt;span&gt; 인스턴스를 얻은 뒤, &lt;/span&gt;다운로드가 완료될 때까지 기다림.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;이미 시스템에 사전 설치되었거나, 다른 앱이 설치했거나, 이전에 동일한 모듈 구성을 사용했다면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;다운로드가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;즉시 완료될 수 있음.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(정리)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에셋이 한 번 다운로드되면, &lt;span&gt;앱을 재시작해도 유지&lt;/span&gt;되며 &lt;span&gt;다른 앱과도 공유&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템은 오랫동안 사용되지 않은 에셋에 대해 앱의 구독 상태를 해제할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Manage assets (에셋 관리하기)&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;앱에서 특정 Locale에 대한 에셋이 더 이상 필요하지 않은 경우, &lt;span&gt;release(reservedLocale:)&lt;/span&gt;를 호출하여 해당 예약(reservation)을 해제할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템은 이후 필요에 따라 해당 에셋을 삭제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Downloading&amp;nbsp;and&amp;nbsp;installing&amp;nbsp;assets&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;assetInstallationRequest(supporting:)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에셋 다운로드를 시작하고 진행 상태를 모니터링하는 데 사용되는 설치 요청 객체를 반환&lt;/li&gt;
&lt;li&gt;현재 상태가 .installed일 경우 더 이상 수행할 작업이 없다는 것을 의미하므로, nil을 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부&amp;nbsp;에셋이&amp;nbsp;예약되지&amp;nbsp;않은&amp;nbsp;Locale을&amp;nbsp;필요로&amp;nbsp;한&amp;nbsp;경우&amp;nbsp;필요한&amp;nbsp;Locale을&amp;nbsp;자동으로&amp;nbsp;예약&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단,&amp;nbsp;자동으로&amp;nbsp;예약하는&amp;nbsp;작업으로&amp;nbsp;인해&amp;nbsp;maximumReservedLocales을&amp;nbsp;초과하는&amp;nbsp;경우&amp;nbsp;throw로&amp;nbsp;에러를&amp;nbsp;던짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 상황에 대한 플로우 정리&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미 예약된 Locale: [&quot;en-US&quot;]&lt;/li&gt;
&lt;li&gt;새로 필요한 Locale: [&quot;en-US&quot;, &quot;ko-KR&quot;]&lt;/li&gt;
&lt;li&gt;이 경우에는 &quot;ko-KR&quot;이 아직 준비되지 않았아서, inventory에 asset을 준비하는 코드를 명시적으로 작성하지 않아도 installation 시점에 알아서 reserve 해줌.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출 메서드&lt;/p&gt;
&lt;pre id=&quot;code_1763906879042&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static func assetInstallationRequest(
    supporting modules: [any SpeechModule]
) async throws -&amp;gt; AssetInstallationRequest?&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;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AssetInventory.Status&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;AssetInventory.Status는 우선순위가 존재하여, unsuppoted(1), suppoted(2), downloading(3), installed(4) 순서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자가 높을수록 우선순위 높음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;case downloading
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템이 현재 에셋을 다운로드 중이거나 네트워크 혹은 배터리 등의 조건이 좋아지면 다운로드를 다시 시도할 예정&lt;/li&gt;
&lt;li&gt;다운로드 진행상황은 progress를 통해 확인할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;case installed
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요한 에셋이 이미 기기에 설치되어 있고 모듈을 바로 사용할 수 있는 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;case supported
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 구성(configuration)으로 모듈을 실행할 수는 있으나 필요한 에셋은 아직 다운로드해야 함.&lt;/li&gt;
&lt;li&gt;해당 기기에서 지원 가능해서 설치해서 쓰면 됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;case&amp;nbsp;unsupported
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이&amp;nbsp;설정(configuration)에서는&amp;nbsp;모듈을&amp;nbsp;전혀&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;없음.&lt;/li&gt;
&lt;li&gt;preset, configuration, locale 등 iOS 26 이상의 기기여도 해당 특정 조건을 만족하지 않는 경우에도 해담.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AssetInventory.status(forModules:)&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 메서드는 배열로 modules를 받는데, 모듈들은 각각의 preset을 달리 가질 수 있어서 상태로 다를 수 있음.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763908841663&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static func status(
    forModules modules: [any SpeechModule]
) async -&amp;gt; AssetInventory.Status&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;/p&gt;
&lt;p data-ke-size=&quot;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;AssetInventory.maximumReservedLocales&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;앱이 사용할 수 있는 Locale 개수로 해당 값은 reservedLocales가 가질 수 있는 최대 개수로, 기기의 저장공간 등에 따라 기기마다 다를 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&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;AssetInventory.reservedLocales&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;앱이 현재 보유하고 있는 asset locale 목록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈이 필요로 하는 에셋을 구독하기 전에, 먼저 해당 에셋의 로케일을 예약해야 하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 이 메서드가 반환하는 로케일은 &lt;span&gt;reserve(locale:)&lt;/span&gt; 메서드에 전달한 로케일의 변형(variant)일 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;variant란?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;reserve(locale: )에 en_US를 요청해도 시스템 내부에서 가장 적합한 Locale이 en_GB라고 판단하며, 실제 reserve 목록에서는 en_GB가 반환될 수 있다는 의미&lt;/li&gt;
&lt;li&gt;즉, 내가 요청한 Locale과 시스템이 실제로 사용하는 Locale이 정확히 같지 않아도, 같은 언어 계열이면 variant(가장 가까운 대체 Locale)로 선택해서 반환한다는 의미.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AssetInventory.release(reservedLocale:&amp;nbsp;Locale)&amp;nbsp;async&amp;nbsp;-&amp;gt;&amp;nbsp;Bool&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;Asset Locale의 reservation을 해제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Locale이 reserved되어 있지 않다면 false를 반환&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;AssetInventory.reserve(locale:)&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;이미 해당 Locale이 reserved 되었다면 false를 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력한 Locale을 지원하는 에셋이 존재한다면 해당 에셋의 Locale을 reservedLocales 목록에 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Throws (에러를 던지는 상황)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예약된 Locale 수가 maximumReservedLocales를 초과하게 되는 경우&lt;/li&gt;
&lt;li&gt;해당 Locale을 지우너하는 Asset이 존재하지 않는 경우&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;h4 data-ke-size=&quot;size20&quot;&gt;AssetInstallationRequest&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763910098987&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;iOS 26.0+
iPadOS 26.0+
Mac Catalyst 26.0+
macOS 26.0+
visionOS 26.0+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에셋 선택을 설명하고, 다운로드하며 설치하는 객체&lt;/p&gt;
&lt;p 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-11-24 오전 12.00.56.png&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yGhSZ/dJMcahCPNkR/cxDphldcD7RiCiPL3aKA6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yGhSZ/dJMcahCPNkR/cxDphldcD7RiCiPL3aKA6k/img.png&quot; data-alt=&quot;objc 유의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yGhSZ/dJMcahCPNkR/cxDphldcD7RiCiPL3aKA6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyGhSZ%2FdJMcahCPNkR%2FcxDphldcD7RiCiPL3aKA6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;255&quot; data-filename=&quot;스크린샷 2025-11-24 오전 12.00.56.png&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;objc 유의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AssetInstallationRequest의 구현부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;progress라는 프로퍼티를 가지고 있어서 진행 상황에 대해서 추적이 가능.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-24 오전 12.20.34.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzYge6/dJMcabJowuX/dbhM18LQaH6F5MUB21Grs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzYge6/dJMcabJowuX/dbhM18LQaH6F5MUB21Grs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzYge6/dJMcabJowuX/dbhM18LQaH6F5MUB21Grs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYge6%2FdJMcabJowuX%2FdbhM18LQaH6F5MUB21Grs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;475&quot; height=&quot;283&quot; data-filename=&quot;스크린샷 2025-11-24 오전 12.20.34.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;964&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;Overview&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;AssetInsllationRequest 타입의 인스턴스를 직접 생성하지 않고 &lt;br /&gt;AssetInventory.assetInstallationRequest(supporting:) 메서드를 통해 AssetInsllationRequest 객체를 얻음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템은 다운로드 및 설치 요청을 통합 관리하므로 여러 개의 인스턴스를 얻어 downloadAndInstall()을 여러번 호출해도 중복 다운로드가 발생하지 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;a style=&quot;color: #000000; text-align: left;&quot; data-v-16d388ce=&quot;&quot; data-v-fee2098e=&quot;&quot; data-v-18f3a3e9=&quot;&quot;&gt;&lt;span data-v-88c637be=&quot;&quot; data-v-18f3a3e9=&quot;&quot;&gt;AssetInstallationRequest의 downloadAndInstall()&lt;/span&gt;&lt;/a&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 data-v-88c637be=&quot;&quot; data-v-18f3a3e9=&quot;&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 data-v-88c637be=&quot;&quot; data-v-18f3a3e9=&quot;&quot;&gt;네트워크 문제나 기타 오류 등으로 인해 시스템이 즉시 에셋을 다운로드 할 수 없는 경우, 나중에 자동으로 다운로드를 시도&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-v-88c637be=&quot;&quot; data-v-18f3a3e9=&quot;&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 data-v-88c637be=&quot;&quot; data-v-18f3a3e9=&quot;&quot;&gt;즉, 이후에 진행 상황이나 성공 여부 등은 status(forModules: ) 또는 다른 설치 요청 객체를 사용하여 모니터링.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템은 다운로드 및 설치 요청을 통합 관리하므로 중복 호출해도 중복 다운로드가 발생하지 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763910524072&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func execute() async throws {
    let transcriber = SpeechTranscriber(locale: .current, preset: .progressiveTranscription)
    let requset = try await AssetInventory.assetInstallationRequest(supporting: [transcriber])
    try await requset?.downloadAndInstall()
}&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/speech/assetinventory&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/speech/assetinventory&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Apple/iOS, UIKit, Documentation</category>
      <category>AssetInstallationRequest</category>
      <category>AssetInventory</category>
      <category>AssetInventory.Status</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/784</guid>
      <comments>https://rldd.tistory.com/784#entry784comment</comments>
      <pubDate>Sat, 22 Nov 2025 20:03:18 +0900</pubDate>
    </item>
    <item>
      <title>SpeechAnalyzer</title>
      <link>https://rldd.tistory.com/783</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;SpeechAnalyzer&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음성으로 입력된 오디오 콘텐츠를 다양한 방식으로 분석하고, analysis session을 관리&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-22 오후 3.43.12.png&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J67vn/dJMcaawWfS3/DauoZQUFf8GonPOhkOkz40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J67vn/dJMcaawWfS3/DauoZQUFf8GonPOhkOkz40/img.png&quot; data-alt=&quot;Actor로 정의되어 있음&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J67vn/dJMcaawWfS3/DauoZQUFf8GonPOhkOkz40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ67vn%2FdJMcaawWfS3%2FDauoZQUFf8GonPOhkOkz40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;440&quot; height=&quot;221&quot; data-filename=&quot;스크린샷 2025-11-22 오후 3.43.12.png&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Actor로 정의되어 있음&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;Overview&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Speech 프레임워크는 특정 유형의 &lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;analysis&lt;/span&gt;(분석) 및 &lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;transcription&lt;/span&gt;(받아쓰기) 기능을 제공하기 위해 &lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;analyzer에&lt;/span&gt; 추가할 수 있는 여러 모듈을 제공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 사용 사례에서는 음성을 텍스트로 변환하는 &lt;span&gt;SpeechTranscriber&lt;/span&gt; 모듈만으로 충분.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SpeechAnalyzer 역할
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관련 모듈을 보관&lt;/li&gt;
&lt;li&gt;음성 오디오 입력을 수신&lt;/li&gt;
&lt;li&gt;전체 분석 흐름을 제어&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;각 모듈의 역할
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;허용되는 입력 형태에 대한 가이던스를 제공&lt;/li&gt;
&lt;li&gt;자체 분석 결과 또는 &lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;transcription 결과를 출력&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;비동기 기반으로 흐름 분석&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;Analysis는 비동기로 수행.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;입력, 출력, 세션 제어는 서로 분리되어 있고, 일반적으로 사용자가 생성하거나 세션을 생성하는 여러 작업을 통해 이루어짐.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;Objective-C API가 delegate 통해 결과를 전달하는 방식과 달리 Swift API는 AsnycSequence를 통해 결과를 제공&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;마찬가지로 음성 입력 역시도 데이터를 넣어주는 AsyncSequence 형태로 제공&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;입력 처리 제약사항&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;analyzer는 한번에 하나의 입력 시퀀스만 분석할 수 있음.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Perform&amp;nbsp;analysis&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;필요한 모듈을 생성하고 구성&lt;/li&gt;
&lt;li&gt;관련 에셋이 설치되어 있거나 이미 존재하는지 확인. (&lt;span&gt;AssetInventory)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;음성 오디오를 제공할 수 있는 입력 시퀀스를 생성&lt;/li&gt;
&lt;li&gt;생성한 모듈과 입력 시퀀스로 분석기를 생성하고 구성&lt;/li&gt;
&lt;li&gt;오디오를 공급.&lt;/li&gt;
&lt;li&gt;분석을 시작.&lt;/li&gt;
&lt;li&gt;결과를 처리.&lt;/li&gt;
&lt;li&gt;필요 시 분석을 종료.&lt;/li&gt;
&lt;/ol&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 스텝에 따라 진행되는 샘플 코드&lt;/p&gt;
&lt;pre id=&quot;code_1763794602820&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//
//  Transcriber.swift
//  TranscriberExample
//
//  Created by Geon Woo lee on 11/21/25.
//

import Foundation
import Speech

final class Transcriber {
    private var transcriber: SpeechTranscriber!
    private var inputSequence: AsyncStream&amp;lt;AnalyzerInput&amp;gt;!
    private var inputBuilder: AsyncStream&amp;lt;AnalyzerInput&amp;gt;.Continuation!
    
    private var audioFormat: AVAudioFormat!
    private var analyzer: SpeechAnalyzer!
    
    private var lastSampleTime: CMTime?
    
    /// Create and configure the necessary modules.
    func createModules() async {
        // Step 1: Modules
        guard let locale = await SpeechTranscriber.supportedLocale(
            equivalentTo: Locale.current
        ) else {
            return
        }
        let transcriber = SpeechTranscriber(
            locale: locale,
            preset: .transcription
        )
        self.transcriber = transcriber
    }

    /// Ensure the relevant assets are installed or already present. See AssetInventory.
    func ensureAssetsArePresent() async throws {
        // Step 2: Assets
        if let installationRequest = try await AssetInventory.assetInstallationRequest(
            supporting: [transcriber]
        ) {
            try await installationRequest.downloadAndInstall()
        }

    }

    /// Create an input sequence you can use to provide the spoken audio.
    func createInputSequence() {
        // Step 3: Input sequence
        let (inputSequence, inputBuilder) = AsyncStream.makeStream(of: AnalyzerInput.self)
        self.inputSequence = inputSequence
        self.inputBuilder = inputBuilder
    }
    
    /// Create and configure the analyzer with the modules and input sequence.
    func createAnalyzer(with inputSequence: SFSpeechAudioBufferRecognitionRequest) async {
        // Step 4: Analyzer
        let audioFormat = await SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith: [transcriber])
        let analyzer = SpeechAnalyzer(modules: [transcriber])
        
        self.audioFormat = audioFormat
        self.analyzer = analyzer
    }
    
    /// Supply audio.
    func supplyAudio(_ audio: Data, to analyzer: SFSpeechRecognizer) {
        // Step 5: Supply audio
        Task {
            
//            while /* audio remains */ {
//                /* Get some audio */
//                /* Convert to audioFormat */
//                let pcmBuffer = /* an AVAudioPCMBuffer containing some converted audio */
//                let input = AnalyzerInput(buffer: pcmBuffer)
//                inputBuilder.yield(input)
//            }
            
            inputBuilder.finish()
        }

    }

    /// Start analysis.
    func stratAnalysis(using recognizer: SFSpeechRecognizer) async throws {
        // Step 6: Perform analysis
        let lastSampleTime = try await analyzer.analyzeSequence(inputSequence)
        self.lastSampleTime = lastSampleTime
    }

    /// Act on results.
    func actOnResults(_ results: [SFSpeechRecognitionResult]) {
        // Step 7: Act on results
        Task {
            do {
                for try await result in transcriber.results {
                    let bestTranscription = result.text // an AttributedString
                    let plainTextBestTranscription = String(bestTranscription.characters) // a String
                    print(plainTextBestTranscription)
                }
            } catch {
                /* Handle error */
            }
        }
    }

    /// Finish analysis when desired.
    func finishAnalysis() async throws {
        // Step 8: Finish analysis
        if let lastSampleTime {
            try await analyzer.finalizeAndFinish(through: lastSampleTime)
        } else {
            await analyzer.cancelAndFinishNow()
        }
    }
}&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;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Analyze audio files (오디오 파일 분석)&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&gt;AVAudioFile&lt;/span&gt; 객체로 표현된 하나 이상의 오디오 파일을 분석하기 위해서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;analyzeSequence(from:)&lt;/span&gt; 또는 &lt;span&gt;start(inputAudioFile:finishAfterFile:)&lt;/span&gt;와 같은 메서드를 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 파일 파라미터를 갖는 초기화 메서드 중 하나를 사용해 analyzer를 통해 생성할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;analyzeSequence(from:)&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;start(inputAudioFile:finishAfterFile:) 메서드는 파일을 자동으로 지원되는 오디오 형식으로 변환하고, 파일 전체를 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 파일 분석 후 세션을 종료하려면 &lt;span&gt;finishAfterFile&lt;/span&gt; 매개변수에 &lt;span&gt;true&lt;/span&gt;를 전달하거나 제공된 &lt;span&gt;finish&lt;/span&gt; 메서드 중 하나를 호출.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지 않으면 기본적으로 분석기는 결과 스트림을 종료하지 않고, 추가 오디오 파일이나 버퍼를 기다림.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석 세션은 각 파일 후 오디오 타임라인을 초기화하지 않으며, 다음 오디오는 이전 파일이 끝난 직후에 이어진 것으로 간주.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;Analyze audio buffers (오디오 버퍼 분석)&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: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;변환은 즉시(on-the-fly) 또는 사전에 미리&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;bestAvailableAudioFormat(compatibleWith:)&lt;span&gt; 또는 개별 모듈의 &lt;/span&gt;availableCompatibleAudioFormats&lt;span&gt; 메서드를 사용하여 변환할 형식을 선택할 수 있음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 오디오 버퍼에 대해 &lt;span&gt;AnalyzerInput&lt;/span&gt; 객체를 생성하고, 이를 사용자가 만든 입력 시퀀스에 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성한 입력 시퀀스를 &lt;span&gt;analyzeSequence(_:)&lt;/span&gt;, &lt;span&gt;start(inputSequence:)&lt;/span&gt; 또는 분석기 초기화 메서드의 유사한 파라미터에 전달&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오디오 스트림의 일부를 건너뛰고 싶다면, 입력 시퀀스에서 건너뛰고자 하는 버퍼를 제외하면 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 나중에 분석을 재개할 때, 각 모듈의 결과가 건너뛴 오디오를 고려하도록 시간 코드를 맞출 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해, 이후 버퍼가 오디오 스트림 내에서 시작되는 시간 코드를 해당 &lt;span&gt;AnalyzerInput&lt;/span&gt; 객체의 &lt;span&gt;bufferStartTime&lt;/span&gt; 파라미터로 전달.&lt;/p&gt;
&lt;p data-ke-size=&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;Analyze autonomously (자율적으로 분석하기)&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&gt;analyzeSequence(_:)&lt;/span&gt; 또는 &lt;span&gt;analyzeSequence(from:)&lt;/span&gt; 메서드를 사용해 분석을 수행하는 것이 좋음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 메서드는 Swift의 구조적 동시성(Structured Concurrency)과 잘 어울림.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Analyzer가 자체적으로 관리하는 작업 내에서 새로운 오디오 입력이 도착하는 즉시 &lt;span&gt;자동으로 분석을 진행&lt;/span&gt;하도록 하고 싶을 수도 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능을 사용하려면 입력 시퀀스나 파일 파라미터가 포함된 초기화 메서드 중 하나를 사용해 Analyzer를 생성하거나 &lt;span&gt;start(inputSequence:)&lt;/span&gt; 또는 &lt;span&gt;start(inputAudioFile:finishAfterFile:)&lt;/span&gt; 메서드를 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;입력이 끝날 때 분석을 종료하려면 &lt;/span&gt;finalizeAndFinishThroughEndOfInput()&lt;span&gt;을 호출&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 입력 분석을 끝내고 &lt;span&gt;다른 입력에 대한 분석을 새로 시작&lt;/span&gt;하려면 &lt;span&gt;start&lt;/span&gt; 계열 메서드를 다시 호출하면 됨.&lt;/p&gt;
&lt;p data-ke-size=&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;결과 처리와 타이밍 제어 (Control processing and timing of results)&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&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;특정 시간 코드(time-code)까지의 결과를 전달하려면 &lt;span&gt;finalize(through:)&lt;/span&gt;를 호출.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 이상 필요하지 않은 시간 이전의 결과 처리를 취소하려면 &lt;span&gt;cancelAnalysis(before:)&lt;/span&gt;를 호출.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;반응성 향상하기 (Improve responsiveness)&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;기본적으로 analyzer 모듈들은 필요한 시스템 리소스를 지연 로드(lazy load하며 해제될 때 해당 리소스를 언로드(unload)함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈을 설정한 후 analyzer를 미리 준비(preheat)하여 시스템 리소스를 선제적으로 로드하려면 &lt;span&gt;prepareToAnalyze(in:)&lt;/span&gt;를 호출.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업은 모듈이 초기 결과를 반환하는 속도를 향상시킬 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 분석기 인스턴스가 나중에 재사용할 수 있도록 리소스 언로드를 지연하거나 방지하려면, &lt;span&gt;SpeechAnalyzer.Options.ModelRetention&lt;/span&gt; 옵션을 선택하고 해당 옵션이 포함된 &lt;span&gt;SpeechAnalyzer.Options&lt;/span&gt; 객체로 analyzer를 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석 작업의 우선순위를 설정하려면, 우선순위 값이 지정된 &lt;span&gt;SpeechAnalyzer.Options&lt;/span&gt; 객체를 사용해 analyzer를 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 일부 모듈에서는 반응성을 향상시키는 추가 옵션을 제공하기도 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;분석 종료하기 (Finish analysis)&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&gt;finish&lt;/span&gt; 계열 메서드나 관련 파라미터를 사용하거나, 분석기 인스턴스를 해제(deallocate)해야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석 세션이 &lt;span&gt;finished 상태&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&gt;분석기는 입력 시퀀스로부터 &lt;/span&gt;더 이상 새로운 입력을 받지 않음.&lt;/li&gt;
&lt;li&gt;대부분의 메서드는 더 이상 동작하지 않으며, 특히 &lt;span&gt;새로운 입력 시퀀스나 모듈을 받아들이지 않음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;모듈의 결과 스트림은 종료되며, 모듈은 추가 결과를 발행하지 않음.&lt;/li&gt;
&lt;li&gt;단, 앱은 이미 발행된 결과들에 대해서는 계속 반복(iterate)하여 읽을 수 있음.&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;h4 data-ke-size=&quot;size20&quot;&gt;참고 (Note)&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&gt;AsyncStream.Continuation.finish()&lt;/span&gt;&lt;span&gt;처럼 입력 시퀀스를 종료하는 메서드를 통해 입력 스트림을 끝낼 수는 있지만, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 시퀀스를 종료하는 것만으로는 분석 세션이 finished 상태가 되지 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;입력 시퀀스가 끝난 후에도, &lt;/span&gt;다른 입력 시퀀스를 제공하여 세션을 계속 진행할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 분석 세션의 종료와 스트림의 종료를 다르게 봐야 한다는 의미.&lt;/p&gt;
&lt;p data-ke-size=&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;에러 처리하기 (Respond to errors)&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&gt;finished 상태로 전환&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때 동일한 오류(또는 &lt;/span&gt;&lt;span&gt;CancellationError&lt;/span&gt;&lt;span&gt;)가 &lt;/span&gt;대기 중인 모든 메서드와 결과 스트림에서 다시 throw&amp;nbsp;&lt;/p&gt;</description>
      <category>Apple/iOS, UIKit, Documentation</category>
      <category>AVAudio</category>
      <category>AVAudioFile</category>
      <category>AVFoundation</category>
      <category>ios</category>
      <category>SpeechAnalyzer</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/783</guid>
      <comments>https://rldd.tistory.com/783#entry783comment</comments>
      <pubDate>Sat, 22 Nov 2025 15:43:54 +0900</pubDate>
    </item>
    <item>
      <title>SpeechTranscriber 정리 + DictationTranscriber</title>
      <link>https://rldd.tistory.com/782</link>
      <description>&lt;h2 style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;SpeechTranscriber&amp;nbsp;정리&amp;nbsp;+&amp;nbsp;DictationTranscriber&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-v-38feae48=&quot;&quot; data-v-0e759aa9=&quot;&quot;&gt;SpeechTranscriber, DictationTranscriber 각각에 대해서 정리하고, SpeechTranscriber의 속성과 구현을 하나씩 확인.&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 data-v-38feae48=&quot;&quot; data-v-0e759aa9=&quot;&quot;&gt;목차&lt;/span&gt;&lt;/h4&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;SpeechTranscriber
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Check&amp;nbsp;device&amp;nbsp;support&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DictationTranscriber
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Improve accuracy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SpeechTranscriber와 DictationTranscriber 비교 정리&lt;/li&gt;
&lt;li&gt;SpeechTranscriber 자세히 보기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;isAvailable과&amp;nbsp;supportedLocales&amp;nbsp;프로퍼티&amp;nbsp;정의&lt;/li&gt;
&lt;li&gt;Configuring&amp;nbsp;transcription
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SpeechTranscriber.ReportingOption&lt;/li&gt;
&lt;li&gt;SpeechTranscriber.ResultAttributeOption&lt;/li&gt;
&lt;li&gt;SpeechTranscriber.TranscriptionOption&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Checking&amp;nbsp;locale&amp;nbsp;support
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SpeechTranscriber.InstalledLocales&lt;/li&gt;
&lt;li&gt;SpeechTranscriber.supportedLocales&lt;/li&gt;
&lt;li&gt;SpeechTranscriber.supportedLocale(equivalentTo:)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SpeechTranscriber.Result&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Getting transcriptions
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;alternatives&lt;/li&gt;
&lt;li&gt;text&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Working&amp;nbsp;with&amp;nbsp;transcriptions
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AttributeScopes.SpeechAttributes.TimeRangeAttribute&lt;/li&gt;
&lt;li&gt;AttributeScopes.SpeechAttributes.ConfidenceAttribute&lt;/li&gt;
&lt;li&gt;rangeOfAudioTimeRangeAttributes(intersecting:)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SpeechTranscriber.Preset
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Standard&amp;nbsp;presets
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;transcription&lt;/li&gt;
&lt;li&gt;transcriptionWithAlternatives&lt;/li&gt;
&lt;li&gt;timeIndexedTranscriptionWithAlternatives&lt;/li&gt;
&lt;li&gt;progressiveTranscription&lt;/li&gt;
&lt;li&gt;timeIndexedProgressiveTranscription&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Getting&amp;nbsp;preset&amp;nbsp;properties
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;attributeOptions&lt;/li&gt;
&lt;li&gt;reportingOptions&lt;/li&gt;
&lt;li&gt;transcriptionOptions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SpeechTranscriber.Preset을 사용하는 이유&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;h2 style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span data-v-38feae48=&quot;&quot; data-v-0e759aa9=&quot;&quot;&gt;SpeechTranscriber&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763792843882&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;iOS 26.0+
iPadOS 26.0+
Mac Catalyst 26.0+
macOS 26.0+
visionOS 26.0+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일상적인 대화와 일반적인 용도에 적합한 음성 - 텍스트 변환 모듈&lt;/p&gt;
&lt;p data-ke-size=&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;SpeechTranscriber Overview&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;여러 개의 Transcriber 인스턴스는 특정 측면에서 비슷하게 구성되어 있다면 동일한 엔진 인스턴스와 모델 공유할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;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;SpeechTranscriber&lt;span&gt; &lt;/span&gt;Check&amp;nbsp;device&amp;nbsp;support&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;현재 사용하는 디바이스가 SpeechTranscriber에서 사용하는 음성-텍스트(speech-to-text) 모델을 지원하는지 확인하려면 isAvailable 또는 supportedLocales 속성을 통해 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지원하지 않는 경우, 해당 기능을 비활성화하거나 대신 DictationTranscriber를 사용하는 것을 고려.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DictationTranscriber&lt;/h2&gt;
&lt;pre id=&quot;code_1763792836955&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;iOS 26.0+
iPadOS 26.0+
Mac Catalyst 26.0+
macOS 26.0+
visionOS 26.0+&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;시스템 받아 쓰기 기능과 유사하여, 구형 기기와도 호환되는 음성-텍스트(speech-to-text) 모듈&lt;/p&gt;
&lt;p data-ke-size=&quot;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;DictationTranscriber&amp;nbsp;Overview&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;해당 Transcriber는 speech-to-text ML 모델, 혹은 SFSpeechRecognizer가 온디바이스(On-Device) 모드로 설정되었을 때 사용하는 모델과 동일한 모델을 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DictationTranscriber는 SFSpeechRecognizer가 &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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;DictationTranscriber Improve accuracy&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;특정 단어에 인식 편향을 주거나 사용자가 직접 정의한 어휘를 제공하거나 Transcriber의 알고리즘을 조정해 음성 인식 정확도를 향상시킬 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;To bias recognition towards certain words (특정 단어에 인식 편향)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AnalysisContext 객체를 생성한 뒤 특정 단어들을 contextualStrings 프로퍼티에 추가.&lt;/li&gt;
&lt;li&gt;그 이후로 SpeechAnalyzer 객체를 생성하거나 기존 Analyzer에 context 프로퍼티에 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;To supply custom vocabulary (직접 정의한 어휘 제공)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;SFSpeechLanguageModel 객체를 생성하고 Transciber에 customizedLanguage(modelConfiguration:)에 옵션으로 구성하여 해당 모델을 사용하도록 설정&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;To adjust the transcriber&amp;rsquo;s algorithm (알고리즘을 조정)&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;DictationTranscriber.ContentHint 파라미터로 Transcriber를 구성&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1d1d1f; text-align: start;&quot;&gt;예를 들어서 먼 거리에서 들리는 음성의 정확도를 높이고 싶다면 farField에 힌드를 사용할 수 있음.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;SpeechTranscriber와 DictationTranscriber 비교 정리&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;DictationTranscriber는 SFSpeechRecognizer(iOS 10.0+)와 동일한 ML 모델을 사용해서 성능도 이와 동일하고, SpeechTranscriber에서 아직 언어를 지원하지 않는 경우에 받아쓰기 형태로 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 서비스에서 사용하기엔 성능이 안좋다고 느낌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SFSpeechRecognizer를 사용할 때는 Siri 권한 요청부터 오디오 재생 시간을 1분으로 제한하는 등 여러가지 제약사항이 존재하는데, DictationTranscriber는 SFSpeechRecognizer를 래핑해 둔 정도.&lt;/p&gt;
&lt;p data-ke-size=&quot;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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;SpeechTranscriber 자세히 보기&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;SpeechTranscriber는 SpeechModule과 LocaleDependentSpeechModule을 채택하고 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpeechModule은 나중에 AssetInventory에서 필요한 에셋을 조회할 때 해당 프로토콜을 채택한 객체들로 조회&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-11-23 오전 12.12.37.png&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cT0FDY/dJMcagYdWNm/hIHAEdQ7IbWe5TEbmcN0U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cT0FDY/dJMcagYdWNm/hIHAEdQ7IbWe5TEbmcN0U0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cT0FDY/dJMcagYdWNm/hIHAEdQ7IbWe5TEbmcN0U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcT0FDY%2FdJMcagYdWNm%2FhIHAEdQ7IbWe5TEbmcN0U0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;277&quot; data-filename=&quot;스크린샷 2025-11-23 오전 12.12.37.png&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;isAvailable과 supportedLocales 프로퍼티 정의&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;isAvailable는 시뮬레이터에서도 하드웨어 및 기기 OS조건이 맞으면 true로 반환.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;supportedLocales는 시뮬레이터에서는 항상 빈 배열로 나타나서 SpeechTranscriber에서는 사용할 수 없는 것으로 보임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763824957274&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 모듈이 현재 기기의 하드웨어 및 기능 조건에서 사용 가능한지를 나타내는 Boolean 값입니다.
 */
public static var isAvailable: Bool { get }

/**
 변환기가 전사(transcribe)할 수 있는 로케일(locale) 목록입니다.  
 설치되어 있지 않지만 다운로드 가능한 로케일도 포함됩니다.
 
 만약 기기가 변환기를 지원하지 않으면 이 배열은 비어 있습니다.
 */
public static var supportedLocales: [Locale] { get async }&lt;/code&gt;&lt;/pre&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;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;SpeechTranscriber.ReportingOption&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transcriber의 결과 전달과 관련된 옵션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;case .alternativeTranscriptions&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 가능성이 높은 transcriptions 결과 외에도 대체 transcriptions를 포함.&lt;/li&gt;
&lt;li&gt;음성을 텍스트로 변환할 때 가장 정확하다고 판단한 한 가지 결과는 주전사 결과이고, 음성 입력에 대해 다른 가능성이 있는 해석들도 제공&lt;/li&gt;
&lt;li&gt;장점: 후처리에서 문맥이나 사용자의 의도에 맞춰 더 정확하게 선택 가능&lt;/li&gt;
&lt;li&gt;단점: 결과가 많아져 처리해야 할 데이터가 늘어남..&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;case .fastResults&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;transcriber가 더 빠르게 반응하도록 조정&lt;/li&gt;
&lt;li&gt;각 결과를 빠르게 내기 위해서 참고하는 이전 context의 양을 줄이며, 음성 스트림의 작은 구간(window 및 chunk)만을 사용해서 처리&lt;/li&gt;
&lt;li&gt;장점: 처리 속도가 더 빨라짐. (즉, 처음 결과가 더 빠르게 나옴)&lt;/li&gt;
&lt;li&gt;단점: 이전 context를 충분히 참고하지 않기 때문에 정확도가 낮아질 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;case .volatileResults&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최종 결과(finalized result) 외에도 잠정 결과(tentative result)를 제공.&lt;/li&gt;
&lt;li&gt;즉, 전사가 완료되기 전에도 중간 단계의 텍스트를 미리 받아볼 수 있으며, 같은 오디오 구간(audio range)에 대해 여러번 중간 결과를 전달해 점점 전사 정확도를 높이면서 최종 결과로 수렴.&lt;/li&gt;
&lt;li&gt;목적: 실시간 피드백을 제공가능하며 중간 결과를 활용한 UI 업데이트 가능.&lt;/li&gt;
&lt;li&gt;효과: 전사가 완료되기 전에 텍스트를 미리 보여줄 수 있음.&lt;/li&gt;
&lt;li&gt;단점: 중간 결과가 최종과 다를 수 있어서 최종 결과와 혼동하지 않도록 처리가 필요.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;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;SpeechTranscriber.ResultAttributeOption&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 옵션은 transcription 결과가 어떻게 표현할지를 제어하는 옵션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;case .audioTimeRange&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타임코드(time-code) 정보를 전사 결과 AttributedString에 포함시킬 것인지를 결정하는 설정&lt;/li&gt;
&lt;li&gt;Foundation/AttributeScopes/SpeechAttributes/TimeRangeAttribute 타입&lt;/li&gt;
&lt;li&gt;Usage
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자막(SRT, VTT) 생성&lt;/li&gt;
&lt;li&gt;먼저 만든 단어와 싱크 유지&lt;/li&gt;
&lt;li&gt;탭 하면 원하는 오디오 위치로 이동하는 인터랙션&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;case .transcriptionConfidence&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전사 결과에 신뢰도(confidence)를 포함시킬 것인지 결정&lt;/li&gt;
&lt;li&gt;These are Foundation/AttributeScopes/SpeechAttributes/ConfidenceAttribute 타입&lt;/li&gt;
&lt;li&gt;Usage
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신뢰도 기능 UX 제공 (신뢰도가 낮은 부분은 회색처리 하거나 정확하지 않을 수 있음 안내)&lt;/li&gt;
&lt;li&gt;후처리 자동화 (특정 신뢰도 이하 단어를 자동 보정 모델에 보내기)&lt;/li&gt;
&lt;li&gt;자막 품질 개선 (저신뢰 단어는 별도 처리)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;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;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;SpeechTranscriber.TranscriptionOption&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;전사된 문자열(plain text)를 어떤 형태로 출력될지를 설정하는 것들.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;case .etiquetteReplacements&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 단어와 구문을 편집된(가려진) 형태로 대체.&lt;/li&gt;
&lt;li&gt;해당 옵션이 활성화 될 경우에 욕설로 인식된 구문은 별표(*)로 대체되어 전사됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; 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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;SpeechTranscriber.InstalledLocales&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;현재 기기에 설치된 Locales을 기준으로 transcribe할 수 있는 목록 리스트&lt;/p&gt;
&lt;p data-ke-size=&quot;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;SpeechTranscriber.supportedLocales&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;기기에 설치되지 않았지만 다운로드 가능한 Locale을 포함하여 transcribe할 수 있는 목록 리스트&lt;/p&gt;
&lt;p data-ke-size=&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;SpeechTranscriber.supportedLocale(equivalentTo:)&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;모듈이 지원하는 Locale 중에서 지정한 Locale과 동등한 Locale을 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Parameter
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;임의의 Locale&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ReturnValue&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지원되는 Locale 목록에서 동등한 Locale을 반환하여, 해당하는 Locale이 없으면 nil을 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Locale.current와 같은 임의의 Locale이 있을 때, 해당 모듈이 지원하는 Locale 중에서 어떤 것이 그 Locale과 동등한지 확인할 때 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 일치하는 Locale이 없는 경우에는 해당 메서드는 근사치(near-equivalent)를 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 같은 Locale.LanguageCode는 같은 값을 가지지만 Locale.Region 값이 다른 Locale로 가능하면 이미 설치된 Locale이 우선됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 color를 colour처럼 예상하는 것과 다른 결과를 생성할 수도 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 메서드를 사용하더라도, 앱은 사용자가 지원되는 Locale 목록에서 직접 Locale을 선택하여 수정할 수 있는 기능을 제공하는 것이 바라직.&lt;/p&gt;
&lt;p data-ke-size=&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;SpeechTranscriber.Result&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전사된 구문이나 구절로, 구문은 순서대로 전달.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Overview
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전사기가 volatile results 옵션으로 구성된 경우, 각 구문은 최종 결과로 확정되기 전까지 전사 정확도가 점점 향상되면서, 한 번 이상 반복해서 전달될 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;alternatives
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오디오 구간에 대한 모든 가능한 대체 해석을 포함하여 가능성 순으로 내림차순으로 정렬&lt;/li&gt;
&lt;li&gt;배열은 비어 있지는 않으나, 빈 문자열이 포함될 수 있음.&lt;/li&gt;
&lt;li&gt;빈 문자열의 경우에는 해당 오디오 구간에 전사가 없는 대체 결과를 나타냄.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대체 결과를 받기 위해서는SpeechTranscriber.ReportingOption.alternativeTranscriptions를 설정해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;text
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 오디오 구간에서 가장 가능성이 높은 전사 결과 (most likley interpertation)&lt;/li&gt;
&lt;li&gt;여기서는 빈 문자열은 해당 오디오에 인식 가능한 음성이 없음을 나타내고, volatile range의 결과에서는 이 구간의 이전의 결과가 무효화 되었음을 의미&lt;/li&gt;
&lt;li&gt;이 값은 alternatives 배열의 첫 번째 요소(가장 가능성이 높은 결과)와 동일.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AttributeScopes.SpeechAttributes.TimeRangeAttribute&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연관된 전사 텍스트에 대응하는 원본 오디오의 시간 범위&lt;/li&gt;
&lt;li&gt;원본 오디오에 특정 시간 범위에 해당하는 텍스트를 찾으려면 rangeOfAudioTimeRangeAttributes(intersecting: )메서드 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AttributeScopes.SpeechAttributes.ConfidenceAttribute&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연관된 전사 텍스트의 신뢰도로 0에서 1사이의 값.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;func&amp;nbsp;rangeOfAudioTimeRangeAttributes(intersecting&amp;nbsp;timeRange:&amp;nbsp;CMTimeRange)&amp;nbsp;-&amp;gt;&amp;nbsp;Range&amp;lt;AttributedString.Index&amp;gt;?&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 메서드는 주어진 시간 범위를 수신 객체의AttributeScopes.SpeechAttributes.TimeRangeAttribute와 비교.&lt;/li&gt;
&lt;li&gt;해당 SpeechTranscriber또는 DictationTranscriber 모듈의 volatile 또는 finalized 결과를 추적하는 AttributedString을 업데이트할 때 유용하게 사용할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SpeechTranscriber.Preset&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;미리 정의된 transcriber의 구성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Overview
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;transcriber는 preset을 사용하여 구성할 수 있고, 해당 속성 값을 수정한 뒤 수정된 값으로 transcriber를 구성할 수 있음.&lt;/li&gt;
&lt;li&gt;이 타입을 확장하여 사용자 정의의 preset을 직접 만들 수도 있음.&lt;/li&gt;
&lt;li&gt;preset을 반드시 사용할 필요는 없어서 transcriber를 사용하여 커스터마이징할 수도 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Preset을 이용하여 SpeechTranscriber를 구성하는 샘플 코드&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;timeIndexedTranscriptionWithAlternatives 프리셋을 기반으로 transcriber를 구성하고 etiquette filtering을 추가하고 alternative transcription을 제거한 경우를 보여줌.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1763831947674&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let preset = SpeechTranscriber.Preset.timeIndexedTranscriptionWithAlternatives
let transcriber = SpeechTranscriber(
    locale: Locale.current,
    transcriptionOptions: preset.transcriptionOptions.union([.etiquetteReplacements])
    reportingOptions: preset.reportingOptions.subtracting([.alternativeTranscriptions])
    attributeOptions: preset.attributeOptions
)&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;transcription
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본적이고 정확한 전사를 위한 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;transcriptionWithAlternatives&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;편집 제안 기능이 포함된 전사를 위한 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;timeIndexedTranscriptionWithAlternatives
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 오디오와 교차 참조된 편집 제안 기능 전사를 위한 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;progressiveTranscription
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실시간 오디오 즉시 전사를 위한 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;timeIndexedProgressiveTranscription
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스트림 타임코드와 교차 참조된 실시간 오디오 즉시 전사를 위한 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 156px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;b&gt;Preset&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;b&gt;volatileResults&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;b&gt; &lt;span&gt;fastResults&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;b&gt; &lt;span&gt;alternativeTranscriptions&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;b&gt; &lt;span&gt;audioTimeRange&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;&lt;span&gt;transcription&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;NO&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;NO&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;NO&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;span&gt;transcriptionWithAlternatives&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;NO&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;NO&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Yes&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;span&gt;timeIndexedTranscriptionWithAlternatives&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;NO&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;NO&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Yes&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 35px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Yes&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;&lt;span&gt;progressiveTranscription&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt; Yes&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Yes&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;NO&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;timeIndexedProgressiveTranscription&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Yes&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Yes&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;NO&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Yes&lt;/span&gt;&lt;/b&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;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;attributeOptions&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이&amp;nbsp;프리셋에 적합한 transcriber 속성 관련 옵션&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;reportingOptions
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 프리셋에 적합한 transcriber 결과 전달 옵션&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;transcriptionOptions
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이&amp;nbsp;프리셋에&amp;nbsp;적합한&amp;nbsp;전사&amp;nbsp;텍스트&amp;nbsp;관련&amp;nbsp;옵션&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SpeechTranscriber.Preset을 사용하는 이유&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;SpeechTranscriber의 이니셜라이저 코드를 보면 아래와 같이 2개의 생성자가 존재&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-11-23 오전 2.45.04.png&quot; data-origin-width=&quot;2014&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQcdpe/dJMcaihrfM6/eoQeTMbCHu8xoRuBPyQn4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQcdpe/dJMcaihrfM6/eoQeTMbCHu8xoRuBPyQn4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQcdpe/dJMcaihrfM6/eoQeTMbCHu8xoRuBPyQn4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQcdpe%2FdJMcaihrfM6%2FeoQeTMbCHu8xoRuBPyQn4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;204&quot; data-filename=&quot;스크린샷 2025-11-23 오전 2.45.04.png&quot; data-origin-width=&quot;2014&quot; data-origin-height=&quot;690&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;Preset을 통해 여러 옵션들에 대한 구성을 미리 정해둔 구성 세트로 필요할 때마다 다양한 옵션을 묶어서 한번에 제공.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transcriber에는 iOS 26 최신을 기준으로 여러 옵션이 존재.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과 전달 방식: 즉시 전달할지, 최종 결과만 전달할지&lt;/li&gt;
&lt;li&gt;텍스트 옵션: 비속어 필터링&lt;/li&gt;
&lt;li&gt;속성 옵션: 타임스탬프, 단어별 신뢰도, 대체 전사 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 옵션들을 매번 하나씩 설정하는 대신에 프리셋을 선택하여 미리 세팅된 상태로 Transcriber를 사용할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/speech/speechtranscriber&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/speech/speechtranscriber&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763789866050&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;SpeechAnalyzer | Apple Developer Documentation&quot; data-og-description=&quot;Analyzes spoken audio content in various ways and manages the analysis session.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/speech/speechanalyzer#overview&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/speech/speechanalyzer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cww64C/hyZN7LKaq9/nqUl6TVwDsSe50wNpcKFq0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/h9Y9w/hyZOdFclyn/YMWoGy2mrBTkAw3uZNrF8k/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/speech/speechanalyzer#overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/speech/speechanalyzer#overview&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cww64C/hyZN7LKaq9/nqUl6TVwDsSe50wNpcKFq0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/h9Y9w/hyZOdFclyn/YMWoGy2mrBTkAw3uZNrF8k/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;SpeechAnalyzer | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Analyzes spoken audio content in various ways and manages the analysis session.&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;&lt;a href=&quot;https://developer.apple.com/documentation/speech/dictationtranscriber&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/speech/dictationtranscriber&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763792646573&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;DictationTranscriber | Apple Developer Documentation&quot; data-og-description=&quot;A speech-to-text transcription module that&amp;rsquo;s similar to system dictation features and compatible with older devices.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/speech/dictationtranscriber&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/speech/dictationtranscriber&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9NnIC/hyZOeD7pgo/1G85virxSFbn6pRzkg4YnK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/blOBn0/hyZNJqG6Mi/C96KNWXtik1kH8IFoV9DBK/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/speech/dictationtranscriber&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/speech/dictationtranscriber&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9NnIC/hyZOeD7pgo/1G85virxSFbn6pRzkg4YnK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/blOBn0/hyZNJqG6Mi/C96KNWXtik1kH8IFoV9DBK/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;DictationTranscriber | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A speech-to-text transcription module that&amp;rsquo;s similar to system dictation features and compatible with older devices.&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Apple/iOS, UIKit, Documentation</category>
      <category>AI</category>
      <category>ios</category>
      <category>locale</category>
      <category>PadOS</category>
      <category>SpeechAnalyzer</category>
      <category>SpeechTranscriber.Preset</category>
      <category>swift</category>
      <category>번역</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/782</guid>
      <comments>https://rldd.tistory.com/782#entry782comment</comments>
      <pubDate>Sat, 22 Nov 2025 15:40:24 +0900</pubDate>
    </item>
    <item>
      <title>적절한 하이퍼파라미터 값 찾기 및 구현 실습</title>
      <link>https://rldd.tistory.com/780</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;적절한&amp;nbsp;하이퍼파라미터&amp;nbsp;값&amp;nbsp;찾기&amp;nbsp;및&amp;nbsp;구현&amp;nbsp;실습&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신경망에는 하이퍼파라미터가 다수 등장.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 말하는 하이퍼파라미터는 뉴런 수, 배치 크기, 매개변수 갱신 시의 학습률, 가중치 감소 등이 존재.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적절한 값을 설정하지 못할 경우 모델 성능이 크게 떨어지기도 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼파라미터 값은 매우 중요하지만, 그 값을 결정하기까지는 일반적으로 많은 시행착오를 겪음.&lt;/p&gt;
&lt;p data-ke-size=&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터셋을 훈련 데이터와 시험 데이터로 분리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼파라미터의 성능을 평가할 때는 시험 데이터를 사용해서는 안됨.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시험 데이터를 사용하여 하이퍼파라미터를 조정하면 하이퍼파라미터 값이 시험 데이터에 오버피팅되기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼파라미터를 조정할 때는 하이퍼파라미터 전용 확인 데이터가 필요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하이퍼파라미터 조정 데이터를 일반적으로 검증 데이터라고 부름&lt;/li&gt;
&lt;li&gt;하이퍼파라미터의 적절성을 평가하는 데이터.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;용어 정리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;훈련 데이터: 매개변수 학습&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증 데이터: 하이퍼파라미터 성능 평가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험 데이터: 신경망의 범용 성능 평가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 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;/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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;범위를 조금씩 줄이려면 우선 대략적인 범위를 설정하고 그 범위에서 무작위로 하이퍼파라미터 값을 골라낸(샘플링)한 후 그 값으로 정확도 평가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼파라미터 최적화에서는 그리드 서치 같은 규치적인 탐색 보다는 무작위로&amp;nbsp; 샘플링해 탐색하는 편이 더 좋은 결과를 낸다고 알려져 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼파라미터 값을 설정할 때는 대략적으로 지정하는 것이 효과적으로 0.001에서 1.000 사이로 설정하며, 매우 오랜 시간이 걸림.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 나쁠 듯한 값은 일찍 포기하는 것이 좋음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습을 위한 에폭을 작게 유지해서 1회 평가에 걸리는 시간을 단축하는 것이 효과적.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0단계: 하이퍼파라미터 값 범위 설정&lt;/li&gt;
&lt;li&gt;1단계: 설정된 범위에서 하이퍼파라미터의 값을 무작위로 추출&lt;/li&gt;
&lt;li&gt;2단계: 1단계에서 샘플링한 하이퍼파라미터 값을 사용하여 학습하고, 검증 데이터로 정확도를 평가 (에폭을 작게)&lt;/li&gt;
&lt;li&gt;1단계와 2단계를 특정 횟수 반복하여 그 정확도의 결과를 보고 하이퍼파라미터의 범위를 좁힘.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼파라미터 최적화 방법은 실용적인 방법. 하지만 과학이기보단 직관과 지혜에 의존.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;베이즈 최적화는 베이즈 정리를 중심으로 한 수학 이론을 구사하여 더 엄밀하고 효율적으로 최적화를 수행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;하이퍼파라미터 최적화 샘플 코드&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;훈련 데이터: 매개변수 학습&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763479850028&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import numpy as np
import random

# 재현성을 위한 시드 설정
torch.manual_seed(42)
np.random.seed(42)
random.seed(42)

print(&quot;라이브러리 임포트 완료 및 시드 설정 완료.&quot;)

# 데이터 로드 (MNIST 사용)
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
testset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)

# 모델 정의
class SimpleMLP(nn.Module):
    def __init__(self):
        super(SimpleMLP, self).__init__()
        self.fc1 = nn.Linear(784, 128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 10)

    def forward(self, x):
        x = x.view(-1, 784)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

print(&quot;데이터셋 로드 및 모델 클래스 정의 완료.&quot;)

def train_and_evaluate(params):
    # 하이퍼파라미터 추출
    lr = params['learning_rate']
    batch_size = params['batch_size']
    optimizer_name = params['optimizer']
    num_epochs = 5 # 최적화 탐색은 짧은 Epoch으로 빠르게 수행

    # 데이터 로더 설정 (배치 크기 적용)
    trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True)
    testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False)
    
    # 모델 및 최적화 함수 설정
    model = SimpleMLP()
    criterion = nn.CrossEntropyLoss()
    
    if optimizer_name == 'Adam':
        optimizer = optim.Adam(model.parameters(), lr=lr)
    elif optimizer_name == 'SGD':
        optimizer = optim.SGD(model.parameters(), lr=lr)
    else:
        raise ValueError(&quot;Unknown optimizer&quot;)

    # 학습 루프
    for epoch in range(num_epochs):
        model.train()
        for data, targets in trainloader:
            scores = model(data)
            loss = criterion(scores, targets)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

    # 테스트 정확도 평가
    model.eval()
    num_correct = 0
    num_samples = 0
    with torch.no_grad():
        for x, y in testloader:
            scores = model(x)
            _, predictions = scores.max(1)
            num_correct += (predictions == y).sum()
            num_samples += predictions.size(0)
    
    accuracy = (num_correct / num_samples * 100).item()
    return accuracy

# 탐색할 하이퍼파라미터 공간 정의
hyperparameter_space = {
    'learning_rate': [0.01, 0.001, 0.0001],
    'batch_size': [32, 64, 128],
    'optimizer': ['Adam', 'SGD']
}

# 탐색 횟수 설정
NUM_TRIALS = 10
best_accuracy = 0
best_params = {}
results = []

print(f&quot;--- 하이퍼파라미터 랜덤 서치 시작 (시도 횟수: {NUM_TRIALS}) ---&quot;)

for i in range(NUM_TRIALS):
    # 무작위 조합 선택
    params = {
        'learning_rate': random.choice(hyperparameter_space['learning_rate']),
        'batch_size': random.choice(hyperparameter_space['batch_size']),
        'optimizer': random.choice(hyperparameter_space['optimizer'])
    }
    
    print(f&quot;\n시도 {i+1}/{NUM_TRIALS}: {params}&quot;)
    
    # 학습 및 평가 실행
    try:
        accuracy = train_and_evaluate(params)
        results.append({'params': params, 'accuracy': accuracy})
        print(f&quot;-&amp;gt; 정확도: {accuracy:.2f}%&quot;)
        
        # 최고 성능 갱신
        if accuracy &amp;gt; best_accuracy:
            best_accuracy = accuracy
            best_params = params
            
    except Exception as e:
        print(f&quot;오류 발생: {e}&quot;)
        continue

print(&quot;\n--- 탐색 완료 ---&quot;)
print(f&quot;최고 정확도: {best_accuracy:.2f}%&quot;)
print(f&quot;최적 파라미터: {best_params}&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763479883812&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;라이브러리 임포트 완료 및 시드 설정 완료.
데이터셋 로드 및 모델 클래스 정의 완료.
--- 하이퍼파라미터 랜덤 서치 시작 (시도 횟수: 10) ---

시도 1/10: {'learning_rate': 0.0001, 'batch_size': 32, 'optimizer': 'Adam'}
-&amp;gt; 정확도: 95.09%

시도 2/10: {'learning_rate': 0.0001, 'batch_size': 64, 'optimizer': 'Adam'}
-&amp;gt; 정확도: 94.30%

시도 3/10: {'learning_rate': 0.01, 'batch_size': 32, 'optimizer': 'Adam'}
-&amp;gt; 정확도: 92.79%

시도 4/10: {'learning_rate': 0.0001, 'batch_size': 128, 'optimizer': 'Adam'}
-&amp;gt; 정확도: 93.07%

시도 5/10: {'learning_rate': 0.0001, 'batch_size': 64, 'optimizer': 'Adam'}
-&amp;gt; 정확도: 93.72%

시도 6/10: {'learning_rate': 0.01, 'batch_size': 32, 'optimizer': 'Adam'}
-&amp;gt; 정확도: 93.64%

시도 7/10: {'learning_rate': 0.01, 'batch_size': 128, 'optimizer': 'Adam'}
-&amp;gt; 정확도: 94.84%
...

--- 탐색 완료 ---
최고 정확도: 95.09%
최적 파라미터: {'learning_rate': 0.0001, 'batch_size': 32, 'optimizer': 'Adam'}
Output is truncated. View as a scrollable element or open in a text editor. Adjust cell output settings...&lt;/code&gt;&lt;/pre&gt;</description>
      <category>AI/DeepLearning</category>
      <author>lgvv</author>
      <guid isPermaLink="true">https://rldd.tistory.com/780</guid>
      <comments>https://rldd.tistory.com/780#entry780comment</comments>
      <pubDate>Wed, 19 Nov 2025 00:32:12 +0900</pubDate>
    </item>
  </channel>
</rss>