<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>천원의 개발</title>
    <link>https://1000one.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 5 Apr 2026 15:45:44 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>천 원</managingEditor>
    <image>
      <title>천원의 개발</title>
      <url>https://tistory1.daumcdn.net/tistory/5307586/attach/06b087fbaf3442c4aa7c01c3c0597a75</url>
      <link>https://1000one.tistory.com</link>
    </image>
    <item>
      <title>Swift Combine을 활용하여 간단하게 tableView를 그려보자</title>
      <link>https://1000one.tistory.com/93</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요 천원입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 Combine을 사용하면서 RxCocoa의 bind 메서드처럼 간편하게 UITableView를 그릴 수 없을까 고민한 내용을 공유드립니다.&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;1. RxSwift TableView 그리기&lt;/h4&gt;
&lt;pre id=&quot;code_1744075299006&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let items = Observable.just([
             &quot;First Item&quot;,
             &quot;Second Item&quot;,
             &quot;Third Item&quot;
         ])

/// bind 메서드를 활용한 tableView  
items.bind(to: tableView.rx.items) { (tableView, row, element) in
         let cell = tableView.dequeueReusableCell(withIdentifier: &quot;Cell&quot;)!
         cell.textLabel?.text = &quot;\(element) @ row \(row)&quot;
         return cell
     }
     .disposed(by: disposeBag)&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;위의 코드와 같이 bind 메서드를 활용해서 간편히 tableView를 그릴 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;2. Combine TableView 그리기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 UITableViewDataSource를 활용하여 tableView를 구현하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744075589801&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Return the number of rows for the table.     
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&amp;gt; Int {
   return 0
}


// Provide a cell object for each row.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&amp;gt; UITableViewCell {
   // Fetch a cell of the appropriate type.
   let cell = tableView.dequeueReusableCell(withIdentifier: &quot;cellTypeIdentifier&quot;, for: indexPath)
   
   // Configure the cell&amp;rsquo;s contents.
   cell.textLabel!.text = &quot;Cell text&quot;
       
   return cell
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그러나 최근에는 Combine과 함께는 UITableViewDiffableDataSource, NSDiffableDataSourceSnapshot 를 활용하여 코드를 작성하고 있습니다. 자세한 내용은 &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/220/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/videos/play/wwdc2019/220&lt;/a&gt; 해당 wwdc 영상을 통하여 확인이 가능합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744075735672&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func setupDataSource() {
    dataSource = UITableViewDiffableDataSource&amp;lt;Int, String&amp;gt;(tableView: tableView) { tableView, indexPath, itemIdentifier in
        let cell = tableView.dequeueReusableCell(withIdentifier: &quot;cell&quot;, for: indexPath)
        cell.textLabel?.text = itemIdentifier
        return cell
    }
}

private func applySnapshot(with items: [String]) {
    var snapshot = NSDiffableDataSourceSnapshot&amp;lt;Int, String&amp;gt;()
    snapshot.appendSections([0])
    snapshot.appendItems(items)
    dataSource.apply(snapshot, animatingDifferences: true)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 매번 tableView를 구현 시 dataSource와 snapshot을 구현해 적용해 줘야하는 불편함이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&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;3. Combine + UITableViewDiffableDataSource&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복적으로 작성하는 dataSource와 snapShot을 한번에 만들어 줄 메서드를 구현해 봅시다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 DataSoucre 작성을 위해 AnyPublisher 타입의 데이터 리스트를 제네릭하게 받습니다(AnyPublisher&amp;lt;[T], Never&amp;gt;)&lt;br /&gt;또한 초기화 시 필요한 cellProvider 클로저 또한 받아서 DataSource를 구현해 줍니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1744076209258&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func bind&amp;lt;T: Hashable&amp;gt;(
    to publisher: AnyPublisher&amp;lt;[T], Never&amp;gt;,
    cellProvider: @escaping (UITableView, IndexPath, T) -&amp;gt; UITableViewCell
) {
    let dataSource = UITableViewDiffableDataSource&amp;lt;Int, T&amp;gt;(tableView: self) { tableView, indexPath, item in
        return cellProvider(tableView, indexPath, item)
    }
}&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;그런 후 SnapShot을 만들기 위해 item 리스트를 받아와 줘야하니 publisher의 sink 메서드를 통해서 [T] 리스트를 받아와 snapShot을 만들어 줍시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1744076580816&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func bind&amp;lt;T: Hashable&amp;gt;(
    to publisher: AnyPublisher&amp;lt;[T], Never&amp;gt;,
    cellProvider: @escaping (UITableView, IndexPath, T) -&amp;gt; UITableViewCell
) {
    let dataSource = UITableViewDiffableDataSource&amp;lt;Int, T&amp;gt;(tableView: self) { tableView, indexPath, item in
        return cellProvider(tableView, indexPath, item)
    }

    publisher
        .sink { items in
            var snapshot = NSDiffableDataSourceSnapshot&amp;lt;Int, T&amp;gt;()
            snapshot.appendSections([0])
            snapshot.appendItems(items)
            dataSource.apply(snapshot, animatingDifferences: true)
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;마지막으로 여러 곳에서 편하게 사용할 수 있도록 해당 메서드를 extension으로 적용해 두면,&lt;/p&gt;
&lt;pre id=&quot;code_1744076823018&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension UITableView {
    func bind&amp;lt;T: Hashable&amp;gt;(
        to publisher: AnyPublisher&amp;lt;[T], Never&amp;gt;,
        cellProvider: @escaping (UITableView, IndexPath, T) -&amp;gt; UITableViewCell
    ) -&amp;gt; AnyCancellable {
        let dataSource = UITableViewDiffableDataSource&amp;lt;Int, T&amp;gt;(tableView: self) { tableView, indexPath, item in
            return cellProvider(tableView, indexPath, item)
        }
        
        return publisher
            .sink { items in
                var snapshot = NSDiffableDataSourceSnapshot&amp;lt;Int, T&amp;gt;()
                snapshot.appendSections([0])
                snapshot.appendItems(items)
                dataSource.apply(snapshot, animatingDifferences: true)
            }
    }
}

/// 사용하는 쪽
tableView.bind(to: transaction.eraseToAnyPublisher()) { tableView, indexPath, item in
    let cell = tableView.dequeueReusableCell(withIdentifier: DespositCell.identifier, for: indexPath) as! DespositCell
    cell.bind(item)
    return cell
}
.store(in: &amp;amp;cancellables)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;간편하게 Combine을 활용한 간편한 테이블 뷰 구현이 완성됩니다.  &lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS&amp;amp;Swift /Combine</category>
      <category>Combine</category>
      <category>combinecocoa</category>
      <category>ios</category>
      <category>UITableView</category>
      <category>uitableviewdiffabledatasource</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/93</guid>
      <comments>https://1000one.tistory.com/93#entry93comment</comments>
      <pubDate>Tue, 8 Apr 2025 10:49:50 +0900</pubDate>
    </item>
    <item>
      <title>Swift 6 새로운 기능</title>
      <link>https://1000one.tistory.com/92</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;안녕하세요. 천원입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오늘은 Swift 6 의 변경점에 대해서 정리하고자 이렇게 글을 작성합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 2024년 10월 17일 Xcode 16과 함께 Swift 6가 배포가 되었습니다. Swift 5가 배포된 후 5년 만의 정기 업데이트라고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 Swift의 10주년 기념의 배포라고 하네요. 공식 문서에 작성된 글들을 보면 Swift 팀에서는 Swift를 APP 개발을 위한 언어가 아닌 다양한 Platform에 지원이 가능한 언어를 목표로 하는 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Concurrency&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Data&amp;nbsp;Race&amp;nbsp;안전성을&amp;nbsp;달성하기&amp;nbsp;위한&amp;nbsp;여정은&amp;nbsp;여러&amp;nbsp;Swift&amp;nbsp;버전에&amp;nbsp;걸쳐&amp;nbsp;진행되었습니다.&amp;nbsp;Swift&amp;nbsp;5.5에서는&amp;nbsp;async/await와&amp;nbsp;Actors가&amp;nbsp;도입되었고,&amp;nbsp;Swift&amp;nbsp;5.6에서는&amp;nbsp;전달&amp;nbsp;가능한&amp;nbsp;Sendable&amp;nbsp;Distributed&amp;nbsp;Actor,&amp;nbsp;Swift&amp;nbsp;5.9에서는&amp;nbsp;Custom&amp;nbsp;Executors&amp;nbsp;및&amp;nbsp;Assertions&amp;nbsp;for&amp;nbsp;Isolation이&amp;nbsp;추가되었습니다.&amp;nbsp;그리고&amp;nbsp;Swift&amp;nbsp;5.10에서는&amp;nbsp;Full&amp;nbsp;Data&amp;nbsp;Isolation과&amp;nbsp;Isolated&amp;nbsp;Globals가&amp;nbsp;도입되었습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이러한&amp;nbsp;모든&amp;nbsp;기능들이&amp;nbsp;결합되어,&amp;nbsp;데이터&amp;nbsp;레이스&amp;nbsp;안전성을&amp;nbsp;완전히&amp;nbsp;보장하는&amp;nbsp;새로운&amp;nbsp;옵트인(opt-in)&amp;nbsp;컴파일러&amp;nbsp;모드가&amp;nbsp;제공됩니다.&amp;nbsp;Swift&amp;nbsp;6가&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;설명했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;각 버전별 기능을 조금 더 자세히 살펴보면&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 5.5 - async/await와 Actors&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;async/await&lt;/b&gt;: 비동기 작업을 작성하고 제어하는 새로운 문법으로, 코드가 &lt;b&gt;비동기적으로 순차 실행되도록&lt;/b&gt; 만들어 코드의 가독성을 높입니다. 복잡한 콜백 구조 없이 비동기 함수 호출을 작성할 수 있어 비동기 코드의 가독성과 유지보수성이 개선됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Actors&lt;/b&gt;: &lt;b&gt;동시성 문제를 줄이기 위해 고유한 실행 컨텍스트&lt;/b&gt;를 가진 객체입니다. 액터는 특정 데이터에 대한 &lt;b&gt;직렬화된 접근을 보장&lt;/b&gt;하여 데이터 레이스를 방지합니다. 이를 통해 여러 스레드가 동시에 같은 데이터에 접근하거나 수정할 수 없도록 보호합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 5.6 - Sendable 및 Distributed Actor&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Sendable&lt;/b&gt;: 데이터가 다른 &lt;b&gt;스레드로 안전하게 전달될 수 있음을 나타내는 프로토콜&lt;/b&gt;입니다. 특정 객체가 스레드 간 전송될 수 있는지를 검사하여 안전하지 않은 데이터 공유를 방지하고, 데이터 무결성을 유지할 수 있습니다. @Sendable 표시를 통해 안전성을 보장합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Distributed Actor&lt;/b&gt;: &lt;b&gt;다른 시스템이나 네트워크를 통해 통신할 수 있는 액터 유형&lt;/b&gt;입니다. 이를 통해 각 액터가 서로 독립적인 노드처럼 분산된 환경에서 작업할 수 있습니다. 분산 액터는 스레드 안전성을 강화하여 동시성 안전성을 높입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 5.9 - Custom Executors 및 Assertions for Isolation&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Custom Executors&lt;/b&gt;: &lt;b&gt;사용자 정의 실행자&lt;/b&gt;를 통해 특정 동시성 작업이 어떤 실행 맥락에서 수행될지 정의할 수 있습니다. 이렇게 하면 성능을 최적화하거나 특정 리소스에 액세스할 때 보안을 강화할 수 있습니다. 이는 특히 &lt;b&gt;Actor 시스템의 동작을 제어&lt;/b&gt;하여 더욱 안전하고 효율적인 동시성 처리를 가능하게 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Assertions for Isolation&lt;/b&gt;: 코드가 &lt;b&gt;올바르게 격리되어 있는지 확인하는 검증 기능&lt;/b&gt;입니다. 특정 데이터가 적절히 격리되어 다른 스레드로부터 보호되는지 확인하여, 동시성 관련 버그를 사전에 방지할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 5.10 - Full Data Isolation 및 Isolated Globals&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Full Data Isolation&lt;/b&gt;: 데이터에 대해 &lt;b&gt;완전한 격리&lt;/b&gt;를 제공합니다. 즉, 특정 데이터에 대한 모든 접근이 오직 해당 데이터를 소유한 액터를 통해서만 이루어지도록 보장합니다. 이를 통해 다른 스레드나 액터가 데이터를 잘못 수정하지 않도록 강력하게 보호합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Isolated Globals&lt;/b&gt;: &lt;b&gt;전역 변수를 격리&lt;/b&gt;하여, 전역적으로 공유되는 데이터에 대한 안전한 접근을 제공합니다. Swift에서는 전역 상태를&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Typed throws&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 6에서는 Error를 던질 때 any Error가 아닌 타입의 지정이 가능합니다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;thorws 키워드 뒤의 (ParseError)를 통해서 던질 Error 타입의 지정이 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730337488560&quot; class=&quot;go&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;func parseRecord(from string: String) throws(ParseError) -&amp;gt; Record { 
  // ... 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;제네릭 타입 또한 사용이 가능합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730337922492&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension Sequence {
  func map&amp;lt;T, E&amp;gt;(_ body: (Element) throws(E) -&amp;gt; T) throws(E) -&amp;gt; [T] { 
    // ... 
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만약 map 함수를 호출 시 Error 핸들링 할 필요가 경우는 Never를 활용하여 에러를 던지지 않는 함수로 변경이 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;~Copyable&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 5.9에서 등장한 ~Copyable은 &lt;b&gt;복사할 수 없는 타입&lt;/b&gt;을 나타내며 이러한 인스턴스는 다른 곳으로 복사될 수 없습니다. 대신, 이러한 타입의 인스턴스는 참조 또는 transfer 방식으로 전달됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;~Copyable은 Struct, Enum 타입에 사용이 가능하며 인스턴스는 그냥 사용하면 컴파일 에러가 발생합니다. 이때 사용하는 키워드들은 consuming, borrowing, inout 이 있는데 관련해서는 &lt;a href=&quot;https://ios-development.tistory.com/1684&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;종권님 블로그&lt;/a&gt;에 잘 정리되어 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730339296498&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct UniqueResource: ~Copyable {
    var value: Int
}

func useResource(resource: consuming UniqueResource) {
    // resource는 복사되지 않고, 소유권이 이동합니다.
    print(resource.value)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;복사 오버헤드 제거&lt;/b&gt;&amp;nbsp;~Copyable을 사용하면 복사할 필요가 없으므로, 데이터가 큰 구조체나 복잡한 객체를 다룰 때 성능을 크게 향상시킬 수 있습니다. 복사 과정에서 발생할 수 있는 메모리 할당 및 초기화 비용을 줄일 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 6에서는 ~Copyable과 제네릭 타입을 결합하여 다양한 타입에 대해 보다 유연하고 안전한 코드를 작성할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730348554366&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protocol Drinkable: ~Copyable {
  consuming func use()
}

struct Coffee: Drinkable, ~Copyable { /* ... */ }
struct Water: Drinkable { /* ... */ }

func drink(item: consuming some Drinkable &amp;amp; ~Copyable) {
  item.use()
}

drink(item: Coffee())
drink(item: Water())&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;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-4o-mini&quot; data-message-id=&quot;a03adeb1-943e-4670-b1fe-7aaa9233ce9b&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;C++ 상호 운용성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 5.9는 C++와의 양방향 상호 운용성을 도입하여 기존 프로젝트에 Swift를 더 원활하게 통합할 수 있도록 했습니다. Swift 6에서는 C++의 이동 전용 타입, 가상 메서드, 기본 인자, 그리고 &lt;b&gt;map&lt;/b&gt;과 &lt;b&gt;optional&lt;/b&gt;과 같은 더 많은 표준 라이브러리 타입에 대한 상호 운용성 지원이 확대되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Embedded Swift&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 6에는 프로그래밍 마이크로컨트롤러와 같은 임베디드 소프트웨어 개발에 적합한 언어 하위 집합 및 컴파일 모드인 Embedded Swift의 미리보기가 포함되어 있습니다. 이 툴체인은 ARM 및 RISC-V 베어메탈 타겟을 지원합니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;128-bit Intergers&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 6에서는 저수준 정수 원시 타입의 집합을 완성하여, 부호 있는 128비트 정수 타입과 부호 없는 128비트 정수 타입을 추가했습니다. 이들은 모든 Swift 플랫폼에서 사용 가능하며, 표준 라이브러리의 다른 고정 폭 정수 타입과 동일한 API를 제공합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730350372785&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 부호 있는 128비트 정수
let signedInt: Int128 = Int128.max // 최대값
print(&quot;부호 있는 최대값: \(signedInt)&quot;)

// 부호 없는 128비트 정수
let unsignedInt: UInt128 = UInt128.max // 최대값
print(&quot;부호 없는 최대값: \(unsignedInt)&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;@DebugDescription&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해당 매크로를 통해서 LLDB Print 기능 사용 시 커스터마이징된 포맷을 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730351008546&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@DebugDescription
struct Organization: CustomDebugStringConvertible {
  var id: String
  var name: String
  var manager: Person
  // ... 더 많은 속성들

  var debugDescription: String {
    &quot;#\(id) \(name) [\(manager.name)]&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;LLDB로 출력해 보면&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730351025643&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(lldb) p myOrg
(Organization) myOrg = &quot;`#100 Worldwide Travel [Jonathan Swift]`&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift Testing&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 6에서는 새로운 테스트 라이브러리인 Swift Testing이 포함되었습니다. 강력한 API인 새로운 APIs는 쉽게 테스트 코드를 작성할 수 있습니다. &lt;b&gt;#expect&lt;/b&gt; 매크로를 활용하여 자세한 테스트 실패 원인을 제공할 수 있습니다. 다양한 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;arguments로&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;background-color: #ffffff; color: #000000; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;For example:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730351399370&quot; class=&quot;leaf&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;@Test(&quot;Continents mentioned in videos&quot;, arguments: [
  &quot;A Beach&quot;,
  &quot;By the Lake&quot;,
  &quot;Camping in the Woods&quot;
])
func mentionedContinents(videoName: String) async throws {
  let videoLibrary = try await VideoLibrary()
  let video = try #require(await videoLibrary.video(named: videoName))
  #expect(video.mentionedContinents.count &amp;lt;= 3)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;정리한 부분 외에도 여러 변경점이 있으니 공식 문서를 한번씩 참고해 주셔도 좋을 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://www.swift.org/blog/announcing-swift-6/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.swift.org/blog/announcing-swift-6/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1730351472139&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;Announcing Swift 6&quot; data-og-description=&quot;We&amp;rsquo;re delighted to announce the general availability of Swift 6. This is a major new release that expands Swift to more platforms and domains.&quot; data-og-host=&quot;www.swift.org&quot; data-og-source-url=&quot;https://www.swift.org/blog/announcing-swift-6/&quot; data-og-url=&quot;https://swift.org/blog/announcing-swift-6/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cr5Kax/hyXs0AaDMN/Q0hIKUz2FpshtjuZ9oJov1/img.png?width=180&amp;amp;height=180&amp;amp;face=0_0_180_180&quot;&gt;&lt;a href=&quot;https://www.swift.org/blog/announcing-swift-6/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.swift.org/blog/announcing-swift-6/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cr5Kax/hyXs0AaDMN/Q0hIKUz2FpshtjuZ9oJov1/img.png?width=180&amp;amp;height=180&amp;amp;face=0_0_180_180');&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;Announcing Swift 6&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;We&amp;rsquo;re delighted to announce the general availability of Swift 6. This is a major new release that expands Swift to more platforms and domains.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.swift.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여기까지 Swift 6 새로운 기능 정리였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고:&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://blog.swiftify.com/whats-new-in-swift-6-e875ca675a28&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.swiftify.com/whats-new-in-swift-6-e875ca675a28&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://www.swift.org/blog/announcing-swift-6/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.swift.org/blog/announcing-swift-6/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS&amp;amp;Swift /Swift</category>
      <category>swift 5.9</category>
      <category>swift 6</category>
      <category>Swift6</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/92</guid>
      <comments>https://1000one.tistory.com/92#entry92comment</comments>
      <pubDate>Thu, 31 Oct 2024 14:12:21 +0900</pubDate>
    </item>
    <item>
      <title>카카오뱅크 iOS 개발자 지원 후기</title>
      <link>https://1000one.tistory.com/91</link>
      <description>&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;지원서 작성&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-27 오전 10.55.38.png&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NsJM1/btsKk620iq2/ig0ZVAOkXAmgdWM6kHRxT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NsJM1/btsKk620iq2/ig0ZVAOkXAmgdWM6kHRxT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NsJM1/btsKk620iq2/ig0ZVAOkXAmgdWM6kHRxT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNsJM1%2FbtsKk620iq2%2Fig0ZVAOkXAmgdWM6kHRxT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1418&quot; height=&quot;480&quot; data-filename=&quot;스크린샷 2024-10-27 오전 10.55.38.png&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오뱅크에는 개인적으로 존경하는 개발자분들이 계셔서 iOS&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-27 오전 11.10.30.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v64NX/btsKldU1SXS/sgZeibL3gSs7HHkBsUle3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v64NX/btsKldU1SXS/sgZeibL3gSs7HHkBsUle3k/img.png&quot; data-alt=&quot;서류 합격&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v64NX/btsKldU1SXS/sgZeibL3gSs7HHkBsUle3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv64NX%2FbtsKldU1SXS%2FsgZeibL3gSs7HHkBsUle3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;619&quot; height=&quot;425&quot; data-filename=&quot;스크린샷 2024-10-27 오전 11.10.30.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서류 합격&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서류 합격을 받고 너무 기뻤습니다. 그러나 다가올 과제 전형에 최근 당근페이 과제 전형에서 미끄러진 경험이 있기에.. 걱정이 되기도 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;span style=&quot;color: #121212; text-align: start;&quot;&gt;앞서 말씀드린&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;것 처럼&lt;span style=&quot;color: #121212; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;당근페이&lt;span style=&quot;color: #121212; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;과제에서는 왜 떨어졌을까 많은 고민을 했습니다. 물론 부족한 부분도 많았겠지만 제가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;생각했을때는&lt;span style=&quot;color: #121212; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;과제 전형의 규모의 프로젝트에서 DI나 Coordinator 패턴, modular architecture 등의 기술들은 오버엔지니어링으로 보일 수 있을까? 라는 생각에 다른 부분들을 중점을 두고 코드를 작성했는데.. 제출&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;하고보니&lt;span style=&quot;color: #121212; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 무슨 오만한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;생각이었나&lt;span style=&quot;color: #121212; text-align: start;&quot;&gt;.. 해서 이번&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;카카오뱅크&lt;span style=&quot;color: #121212; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;과제에서는 내가 고민하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;알고 있는&lt;span style=&quot;color: #121212; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;모든 부분을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;녹여보자고&lt;span style=&quot;color: #121212; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;생각하고 준비하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #121212; text-align: start;&quot;&gt;결과는 두둥&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-27 오전 11.33.33.png&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqsJnn/btsKkjaGNT1/bFgY38tk7TAXpthQcLIMb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqsJnn/btsKkjaGNT1/bFgY38tk7TAXpthQcLIMb1/img.png&quot; data-alt=&quot;과제 합격&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqsJnn/btsKkjaGNT1/bFgY38tk7TAXpthQcLIMb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqsJnn%2FbtsKkjaGNT1%2FbFgY38tk7TAXpthQcLIMb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;620&quot; height=&quot;448&quot; data-filename=&quot;스크린샷 2024-10-27 오전 11.33.33.png&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;과제 합격&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 기뻤지만 한편으로는 카카오뱅크의 무시무시한 실무 면접 난이도에 걱정되기도 했습니다. &lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실무진 면접&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무 면접을 준비하면서 Swift, iOS 의 기본적인 개념들, CS 그리고 과제 전형을 진행하면서 했던 고민들을 어떻게 전달 드릴 수 있을까 많이 고민했던 것 같습니다.&amp;nbsp; 면접은 판교에서 대면으로 진행했습니다. 그날 따라 잠이 오지 않아서.. 새벽 6시 경에 출발해 회사 아래의 투썸에서 그동안 정리했던 개념들을 다시 한번 준비했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;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;IMG_3384.JPG&quot; data-origin-width=&quot;5712&quot; data-origin-height=&quot;4284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OXQJO/btsKlFQYqeQ/SmOSTYIgj4Kdm7wLDLSNUK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OXQJO/btsKlFQYqeQ/SmOSTYIgj4Kdm7wLDLSNUK/img.jpg&quot; data-alt=&quot;반겨주는 KAKAO FRIENDS 들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OXQJO/btsKlFQYqeQ/SmOSTYIgj4Kdm7wLDLSNUK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOXQJO%2FbtsKlFQYqeQ%2FSmOSTYIgj4Kdm7wLDLSNUK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;518&quot; data-filename=&quot;IMG_3384.JPG&quot; data-origin-width=&quot;5712&quot; data-origin-height=&quot;4284&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;반겨주는 KAKAO FRIENDS 들&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-27 오전 11.53.34.png&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sYM7Q/btsKmour1JH/0etkK0KYdTDqJDb5NKVQxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sYM7Q/btsKmour1JH/0etkK0KYdTDqJDb5NKVQxK/img.png&quot; data-alt=&quot;요동치는 심장 박동은 비밀&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sYM7Q/btsKmour1JH/0etkK0KYdTDqJDb5NKVQxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsYM7Q%2FbtsKmour1JH%2F0etkK0KYdTDqJDb5NKVQxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;214&quot; height=&quot;274&quot; data-filename=&quot;스크린샷 2024-10-27 오전 11.53.34.png&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요동치는 심장 박동은 비밀&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접은&amp;nbsp;1시간&amp;nbsp;40~50분&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;Zedd님,&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;&lt;/p&gt;
&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;스크린샷 2024-10-27 오후 4.11.21.png&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTGS2r/btsKldgqcRt/NLJ142WP1s2b4rEYEw4KTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTGS2r/btsKldgqcRt/NLJ142WP1s2b4rEYEw4KTk/img.png&quot; data-alt=&quot;실무진 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTGS2r/btsKldgqcRt/NLJ142WP1s2b4rEYEw4KTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTGS2r%2FbtsKldgqcRt%2FNLJ142WP1s2b4rEYEw4KTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;488&quot; data-filename=&quot;스크린샷 2024-10-27 오후 4.11.21.png&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;854&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실무진 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발표까지 일주일 가량이 소요가 되었는데... 메일함의 새로고침만 하루에 수백번은 한 것 같습니다.. 부족한 부분도 있었지만 제가 고민하고 적용했던 부분들을 좋게 봐주셨다고 생각하여 정말 기뻤습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;경영진&amp;nbsp;면접은&amp;nbsp;화상으로&amp;nbsp;60분간&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;면접에서&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;&lt;br /&gt;면접에 들어가서는 떨리는 가슴을 부여잡고 최대한 나의 가치관, 나의 경험들을 솔직하게 전달하고자 노력하였습니다.&amp;nbsp;&lt;/p&gt;
&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;그&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;스크린샷 2024-10-27 오후 3.10.41.png&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCtKWS/btsKkuiLKJQ/6JWQVmcD6Vkf4pen7gmlAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCtKWS/btsKkuiLKJQ/6JWQVmcD6Vkf4pen7gmlAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCtKWS/btsKkuiLKJQ/6JWQVmcD6Vkf4pen7gmlAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCtKWS%2FbtsKkuiLKJQ%2F6JWQVmcD6Vkf4pen7gmlAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;727&quot; height=&quot;501&quot; data-filename=&quot;스크린샷 2024-10-27 오후 3.10.41.png&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;776&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;과정인&amp;nbsp;만큼&amp;nbsp;절실했고&amp;nbsp;그만큼&amp;nbsp;아쉬움도&amp;nbsp;컸기에&amp;nbsp;그렇게&amp;nbsp;느꼈던&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;2달 반(8.8 ~ 10.24) 가량의 여정의 끝이 쓸쓸해서 당장이라도 집에 가서 펑펑 울고싶었지만 회사 서비스의 배포일정에 치여서 마음대로 울지도 못하였습니다. 사실 집에가서 펑펑 우는 것 보다. 바쁜 일정에 치이는게 오히려 낫다고 생각했습니다.&lt;br /&gt;&lt;br /&gt;이렇게 카카오뱅크와의 인연은 끝이 났지만 채용 과정 속에서 즐거웠고 힘들었고 덕분에 많이 성장할 수 있었습니다. 좋은 경험과 기회를 주셔서 감사했고, 이번 경험을 바탕으로 더 성장할 수 있도록 정진하겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;여담으로.. 탈락 후 이틀 뒤 KWDC2024 컨퍼런스에 참여하였는데.. 민소네님이 계셔서 정말 인사드리고 싶었는데... 탈락한 지원자가 인사를 건네면 부담스러울 까봐... 꾹 참았습니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 카카오뱅크 iOS 개발자 지원 후기였습니다.&amp;nbsp; 혹여나 문제 시 바로 삭제하도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다.&lt;/p&gt;</description>
      <category>ios</category>
      <category>지원 후기</category>
      <category>카카오뱅크</category>
      <category>카카오뱅크 ios</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/91</guid>
      <comments>https://1000one.tistory.com/91#entry91comment</comments>
      <pubDate>Sun, 27 Oct 2024 15:41:27 +0900</pubDate>
    </item>
    <item>
      <title>iOS SwiftData CRUD를 구현해보자</title>
      <link>https://1000one.tistory.com/90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우선 SwiftData는 데이터 모델링 및 관리를 위한 프레임워크로 iOS 17이상 부터 사용이 가능한 데이터베이스입니다.&lt;br /&gt;개발하면서&amp;nbsp;오류&amp;nbsp;로그들을&amp;nbsp;확인해&amp;nbsp;보면&amp;nbsp;CoreData를&amp;nbsp;기반으로&amp;nbsp;만들어져&amp;nbsp;발생하는&amp;nbsp;오류가&amp;nbsp;CoreData에서&amp;nbsp;발생하는&amp;nbsp;오류와&amp;nbsp;비슷한&amp;nbsp;경우가&amp;nbsp;많았습니다.&lt;br /&gt;&lt;br /&gt;기본적으로&amp;nbsp;SwiftUI와&amp;nbsp;친화적으로&amp;nbsp;만들어져있어서&amp;nbsp;SwiftUI와&amp;nbsp;함께&amp;nbsp;사용하는게&amp;nbsp;더&amp;nbsp;간편하지만,&amp;nbsp;UIKit과&amp;nbsp;함께&amp;nbsp;사용하는&amp;nbsp;자료는&amp;nbsp;적은&amp;nbsp;것&amp;nbsp;같아&amp;nbsp;UIKit을&amp;nbsp;기반으로&amp;nbsp;어떻게&amp;nbsp;사용하면&amp;nbsp;좋을지&amp;nbsp;고민한&amp;nbsp;내용을&amp;nbsp;공유하고자&amp;nbsp;해당&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;h3 data-ke-size=&quot;size23&quot;&gt;모델&amp;nbsp;생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Model atributte 키워드를 통해서 모델의 생성이 가능합니다. 저는 영화의 정보를 저장할 Movie 모델을 생성하고&amp;nbsp;&amp;nbsp;@Attribute(.unique)를 활용하여 모델의 기본키를 지정해 주었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1724284904682&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;import SwiftData

@Model
final public class Movie {
    @Attribute(.unique) public var id: String?
    public var title: String
    public var content: String
    public var image: [Data]
    public var date: Date
    public var rate: Int
    public var heart: Int
    
    public init(
        title: String,
        content: String,
        image: [Data],
        date: Date,
        rate: Int
    ) {
        self.id = UUID().uuidString
        self.title = title
         self.content = content
        self.image = image
        self.date = date
        self.rate = rate
        self.heart = 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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;PersistentModel&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 다양한 Model의 CURD를 제공해주기 위해서 제네릭형태로 Repository를 구현을 하였습니다. 그러기 위해서 모델들의 공통적으로 가지고 있는 프로토콜 제약을 찾아보았고 PersistentModel이 SwiftDate의 인터페이스를 제공함을 확인할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-22 오전 9.17.31.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tYjMS/btsJafUSz7w/3hPwvpbfUClclPBS67A1X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tYjMS/btsJafUSz7w/3hPwvpbfUClclPBS67A1X0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tYjMS/btsJafUSz7w/3hPwvpbfUClclPBS67A1X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtYjMS%2FbtsJafUSz7w%2F3hPwvpbfUClclPBS67A1X0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;206&quot; data-filename=&quot;스크린샷 2024-08-22 오전 9.17.31.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;392&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;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ModelContainer&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화 구문에서 모델의 CRUD에 필요한 ModelContainer 를 생성해 주었습니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1724286664379&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public final class SwiftDataRepository&amp;lt;T: PersistentModel&amp;gt;: SwiftDataRepositoryProtocol {
    
    public init() {
        let configure = ModelConfiguration(&quot;\(T.self)&quot;)
        do {
            container = try ModelContainer(for: T.self, configurations: configure)
        } catch {
            fatalError(error.localizedDescription)
        }
    }
    
    let container: ModelContainer
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ModelContainer를 생성하기 위해서는 모델의 메타타입과 ModelConfiguration이 필요한데 여기서 만났던 이슈는 ModelConfiguration의 초기화 구문을 살펴보면 name을 옵셔널 형태로 가지고 있는데&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-22 오전 9.36.16.png&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N4NfV/btsJaeIqakx/vqRU1hPEyZXvbGzoS18yZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N4NfV/btsJaeIqakx/vqRU1hPEyZXvbGzoS18yZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N4NfV/btsJaeIqakx/vqRU1hPEyZXvbGzoS18yZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN4NfV%2FbtsJaeIqakx%2FvqRU1hPEyZXvbGzoS18yZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;238&quot; data-filename=&quot;스크린샷 2024-08-22 오전 9.36.16.png&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;238&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;ModelConfiguration() 형태로 생성하게 되면 하나의 모델에서는 정상적으로 동작하지만 모델의 개수가 2개 이상이 되면 충돌이 발생하니ModelConfiguration 생성 시에는 항상 name을 지정해 주면 좋을 것 같습니다. 저는 모델의 메타타입을 사용하여 name을 지정해 주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Insert Data&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Repository의 인터페이스는 아래와 같이 두었습니다. 해당 프로젝트에서는 clean architecture 적용하여 인터페이스는 Domain 모듈에 두어 Usecase에서 접근이 가능하도록 하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1724288340804&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public protocol SwiftDataRepositoryProtocol {
    
    associatedtype T: PersistentModel
    
    func insertData(data: T) async
    func fetchData() async throws -&amp;gt; [T]
    func deleteData(data: T) async
    func deleteAllData() async
}&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;비동기 처리는 async await 스타일로 진행을 하였고 초기화 시점에서 만든 container를 활용하여 insert와 save를 진행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1724288111106&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public func insertData(data: T) {
    Task { @MainActor in
        let context = container.mainContext
        context.insert(data)

        do {
            try context.save()
        } catch {
            print(&quot;Error saving context: \(error.localizedDescription)&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;h3 data-ke-size=&quot;size23&quot;&gt;Read Data&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 모든 데이터를 가져오지만 FetchDescriptor 생성 시 predicate에 가져오려는 데이터의 조건을 추가해 준다면 원하는 데이터를 소팅해서 가져올 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1724289889092&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@MainActor
public func fetchData() async throws -&amp;gt; [T] {
    let descriptor = FetchDescriptor&amp;lt;T&amp;gt;(predicate: nil)

    let context = container.mainContext
    let data = try context.fetch(descriptor)
    return data
}&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;Predicate 예시 코드&lt;/p&gt;
&lt;pre id=&quot;code_1724290035115&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let moviePredicate = #Predicate&amp;lt;Movie&amp;gt; { 
    $0.rate &amp;gt; 4 &amp;amp;&amp;amp; 
    $0.title.contains(&quot;birthday&quot;) &amp;amp;&amp;amp; 
    $0.date &amp;gt; today
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetchData 시 매개변수로 받아서 사용해도 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Delete Data&lt;/h3&gt;
&lt;pre id=&quot;code_1724299738263&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public func deleteData(data: T) async {
    let context = await container.mainContext
    context.delete(data)

    do {
        try context.save()
    } catch {
        print(&quot;Error saving context: \(error.localizedDescription)&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;특정 데이터를 삭제하는 함수이고 해당 함수와 fetch함수를 활용하여 전체 삭제 함수를 구현하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1724299813001&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public func deleteAllData() async {
    do {
        let data = try await fetchData()
        for item in data {
            await deleteData(data: item)
        }
    } catch {
        print(&quot;Error fetching or deleting all data: \(error.localizedDescription)&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Repository 사용해보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Usecase에서 repository의 인스턴스를 생성해서 사용하는 sample code 입니다.&amp;nbsp; repository의 타입을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SwiftDataRepositoryProtocol 인터페이스에 두고 repository의 associatedtype 조건을 Movie로 설정하여 Movie 정보를 DB에서 fetch하는 함수를 구현해 주었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1724300191996&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public protocol FetchMovieUsecaseProtocol {
    func execute() async -&amp;gt; [Movie]
}

public final class FetchMovieUsecase&amp;lt;Repository: SwiftDataRepositoryProtocol&amp;gt;: FetchMovieUsecaseProtocol where Repository.T == Movie {
    
    private let repository: Repository
    
    public init(repository: Repository) {
        self.repository = repository
    }
    
    public func execute() async -&amp;gt; [Movie] {
        do {
            let data = try await repository.fetchData()
            return data
        } catch {
            fatalError(error.localizedDescription)
        }
    }
}&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;여기까지 간단히 정리한 SwiftData의 CRUD 였습니다.&lt;/p&gt;
&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/swiftdata&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/documentation/swiftdata&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2023/10196&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/videos/play/wwdc2023/10196&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2023/10187&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.apple.com/videos/play/wwdc2023/10187&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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_1724301422290&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public final class SwiftDataRepository&amp;lt;T: PersistentModel&amp;gt;: SwiftDataRepositoryProtocol {
    
    public init() {
        let configure = ModelConfiguration(&quot;\(T.self)&quot;)
        do {
            print(&quot;configure Init&quot;)
            container = try ModelContainer(for: T.self, configurations: configure)
        } catch {
            fatalError(error.localizedDescription)
        }
    }
    
    let container: ModelContainer
    
    
    public func insertData(data: T) {
        Task { @MainActor in
            let context = container.mainContext
            context.insert(data)
            
            do {
                try context.save()
            } catch {
                print(&quot;Error saving context: \(error.localizedDescription)&quot;)
            }
        }
    }
    
    @MainActor
    public func fetchData() async throws -&amp;gt; [T] {
        let descriptor = FetchDescriptor&amp;lt;T&amp;gt;(predicate: nil)
        
        let context = container.mainContext
        let data = try context.fetch(descriptor)
        return data
    }
    
    public func deleteData(data: T) async {
        let context = await container.mainContext
        context.delete(data)
        
        do {
            try context.save()
        } catch {
            print(&quot;Error saving context: \(error.localizedDescription)&quot;)
        }
    }
    
    public func deleteAllData() async {
        do {
            let data = try await fetchData()
            for item in data {
                await deleteData(data: item)
            }
        } catch {
            print(&quot;Error fetching or deleting all data: \(error.localizedDescription)&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>iOS&amp;amp;Swift /iOS</category>
      <category>ios database</category>
      <category>ios swiftdata</category>
      <category>swift database</category>
      <category>swift db</category>
      <category>swiftdata</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/90</guid>
      <comments>https://1000one.tistory.com/90#entry90comment</comments>
      <pubDate>Thu, 22 Aug 2024 13:28:39 +0900</pubDate>
    </item>
    <item>
      <title>Swift ReactorKit Pulse 기능을 학습하기 위한 과정</title>
      <link>https://1000one.tistory.com/89</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 천원입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 ReactorKit을 학습하는 도중 직면했던 경험 이야기를 작성해 보려고 합니다.&lt;/p&gt;
&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;Reactorkit의 예제 프로젝트를 확인해 보면 아래와 같이 propertyWrapper 기능을 활용하여 Pulse를 구현하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1719463387627&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  struct State {
    var value: Int
    var isLoading: Bool
    @Pulse var alertMessage: 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;propertyWrapper에 대해 간단하게 설명드리면 재사용될&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;예제를 작성해 보면 UseDefaults를 활용해 사용자의 정보를 저장하고 싶다면 아래와 같이 모든 프로퍼티에 동일한 get, set 구문을 작성해 주어야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1719464059045&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var phoneNumber: String {
    get {
        return UserDefaults.standard.object(forKey: &quot;phoneNumber&quot;) as! String
    }
    set {
        UserDefaults.standard.setValue(newValue, forKey: &quot;phoneNumber&quot;)
    }
}

var name: String {
    get {
        return UserDefaults.standard.object(forKey: &quot;name&quot;) as! String
    }
    set {
        UserDefaults.standard.setValue(newValue, forKey: &quot;name&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 되면 저장해야할 정보가 많아 질수록 보일러플레이트 코드가 생기게 되니까 우리는 PropertyWrapper를 활용하여 기능을 구현해 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1719463912045&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Foundation

@propertyWrapper
struct UserDefault&amp;lt;T&amp;gt; {
    let key: String
    
    init(key: String) {
        self.key = key
    }
    
    var wrappedValue: T {
        get {
            return UserDefaults.standard.object(forKey: key) as! T
        }
        set {
            UserDefaults.setValue(newValue, forKey: key)
        }
    }
}&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;우선 propertyWrapper에는 필수적으로 wrappedValue 프로퍼티를 구현해 주어야 합니다. 그러니 반복되는 코드를 구현해 주고 저장에 필요한 key값을 초기화 구문에서 지정해 줍니다. 그러면 아래와 같이 사용이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719464819460&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 선언
@UserDefault(key: &quot;phoneNumber&quot;) var phoneNumber: String?
@UserDefault(key: &quot;name&quot;) var name: String?

// 호출
name = &quot;page&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;자 이제 돌아와서 Pulse 구현부를 살펴보면&lt;/p&gt;
&lt;pre id=&quot;code_1719465225306&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@propertyWrapper
public struct Pulse&amp;lt;Value&amp;gt; {

  public var value: Value {
    didSet {
      riseValueUpdatedCount()
    }
  }

  public internal(set) var valueUpdatedCount = UInt.min

  public init(wrappedValue: Value) {
    self.value = wrappedValue
  }

  public var wrappedValue: Value {
    get { value }
    set { value = newValue }
  }

  public var projectedValue: Pulse&amp;lt;Value&amp;gt; {
    self
  }

  private mutating func riseValueUpdatedCount() {
    valueUpdatedCount &amp;amp;+= 1
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 모르겠지만 value에 newValue를 저장하고 value 값이 변경이 되면 riseValueUpdatedCount를 통해서 Count 값을 증가 시켜주네요. 이번에는 Pulse로 만들어진 alertMessage를 사용하는 부분을 확인해 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1719465945569&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// bind
reactor.pulse(\.$alertMessage)
  .compactMap { $0 }
  .subscribe(onNext: { [weak self] message in
    let alertController = UIAlertController(
      title: nil,
      message: message,
      preferredStyle: .alert
    )
    alertController.addAction(UIAlertAction(
      title: &quot;OK&quot;,
      style: .default,
      handler: nil
    ))
    self?.present(alertController, animated: true)
  })
  .disposed(by: disposeBag)&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;pulse라는 메서드에 매개변수로 \.$alertMessage가 들어가네요.. SwiftUI 학습할 당시에 종종 봤지만 오늘은 구체적으로 한번 살펴보겠습니다. 우선 $alertMessage는 무엇을 뜻할까요? 다시 Property Wrapper로 돌아와서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719472085257&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@propertyWrapper
struct UserDefault&amp;lt;T&amp;gt; {
    let key: String
    
    init(key: String) {
        self.key = key
    }
    
    var wrappedValue: T {
        get {
            return UserDefaults.standard.object(forKey: key) as! T
        }
        set {
            UserDefaults.setValue(newValue, forKey: key)
        }
    }
    
    func delete() {
        UserDefaults.standard.removeObject(forKey: key)
    }
}&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;아까 작성했던 UserDefault Property Wrapper에 delete라는 메서드를 추가하게 된다면 어떤식으로 호출할 수 있을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평범한 구조체라면 우리는 인스턴스를 생성 후 해당 인스턴스로 접근하겠지만&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719472182676&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@UserDefault(key: &quot;phoneNumber&quot;) var phoneNumber: String?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로퍼티로는 접근이 불가능하겠죠? phoneNumber는 String type일 뿐이니까요 우리가 delete를 호출하기 위해서는 UserDefault&amp;lt;T&amp;gt;타입이 필요합니다. 그럴때 사용하는게 &lt;b&gt;projectedValue &lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719472387737&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@propertyWrapper
struct UserDefault&amp;lt;T&amp;gt; {
    let key: String
    
    init(key: String) {
        self.key = key
    }
    
    var wrappedValue: T {
        get {
            return UserDefaults.standard.object(forKey: key) as! T
        }
        set {
            UserDefaults.setValue(newValue, forKey: key)
        }
    }
    
    var projectedValue: UserDefault&amp;lt;T&amp;gt; { // projectedValue 추가
        self
    } 
    
    func delete() {
        UserDefaults.standard.removeObject(forKey: key)
    }
}&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;이렇게 projectedValue를 만들면 우리는 $키워드를 통해서 projectedValue에 접근이 가능합니다. 그렇다면 delete 메서드도 호출이 가능하겠죠&lt;/p&gt;
&lt;pre id=&quot;code_1719472471569&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$phoneNumber.delete()&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;Pulse로 돌아와서 확인해 보면 projectedValue가 구현되어 있으니 $표기를 통해서 Pulse&amp;lt;Value&amp;gt; 타입으로 접근이 가능해 보입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1719472850366&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;@propertyWrapper
public struct Pulse&amp;lt;Value&amp;gt; {

  public var value: Value {
    didSet {
      riseValueUpdatedCount()
    }
  }

  public internal(set) var valueUpdatedCount = UInt.min

  public init(wrappedValue: Value) {
    self.value = wrappedValue
  }

  public var wrappedValue: Value {
    get { value }
    set { value = newValue }
  }

  public var projectedValue: Pulse&amp;lt;Value&amp;gt; {
    self
  }

  private mutating func riseValueUpdatedCount() {
    valueUpdatedCount &amp;amp;+= 1
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 우리는 \.$alertMessage에서 $alertMessage가 Pulse&amp;lt;Value&amp;gt;를 뜻하는걸 알았으니 \.에 대해서 확인해 봅시다! 우선 해당 문법은 KeyPath 방법으로 프로퍼티에 접근하는 방식으로 &lt;a href=&quot;https://1000one.tistory.com/85&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;KeyPath 포스팅 &lt;/a&gt;을 통해서 확인하실 수 있습니다. 자자 그렇다면 우리는 KeyPath방식으로 매개변수를 받으니 pulse 메서드의 매개변수를 추측해 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719473384052&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public func test&amp;lt;T&amp;gt;(_ keyPath: KeyPath&amp;lt;State, Pulse&amp;lt;T&amp;gt;&amp;gt;) {} // 구현

reactor.test(\.$alertMessage) // 호출&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;이런식으로 생겼으니까 test(\.$alertMessage) 로 호출이 가능하겠다! 라고 생각했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데.. 두둥.. 실제 pulse 메서드는 아래와 같이 생겼습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1719473466950&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  public func pulse&amp;lt;Result&amp;gt;(_ transformToPulse: @escaping (State) throws -&amp;gt; Pulse&amp;lt;Result&amp;gt;) -&amp;gt; Observable&amp;lt;Result&amp;gt; {
    state.map(transformToPulse).distinctUntilChanged(\.valueUpdatedCount).map(\.value)
  }&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;엥..? \.$alertMessage가 &lt;b&gt;(&lt;span&gt;State&lt;/span&gt;) &lt;span&gt;throws&lt;/span&gt; -&amp;gt; &lt;span&gt;Pulse&lt;/span&gt;&amp;lt;Result&amp;gt; 타입이라고?&lt;/b&gt; 이게 가능한거야???? 라고 열심히 찾아봤는데..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하네요! &lt;a href=&quot;https://forums.swift.org/t/key-path-expressions-as-functions/19587&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://forums.swift.org/t/key-path-expressions-as-functions/19587&lt;/a&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_1719473738445&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct User {
	let name: String
}

\User.name = (User) -&amp;gt; String // 똑같당

let a = \User.name as (User) -&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;&lt;b&gt;KeyPath는 클로저 형식&lt;/b&gt;으로 표현이 가능합니다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 ReactorKit의 Pulse 기능을 학습하기 위한 과정이였는데 개인적으로 많은 도움이 되었어서 공유하고자 이렇게 글로 남겨둡니당~~~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS&amp;amp;Swift /Swift</category>
      <category>KeyPath</category>
      <category>property wrapper</category>
      <category>Pulse</category>
      <category>ReactorKit</category>
      <category>reactorkit pulse</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/89</guid>
      <comments>https://1000one.tistory.com/89#entry89comment</comments>
      <pubDate>Thu, 27 Jun 2024 16:50:05 +0900</pubDate>
    </item>
    <item>
      <title>Swift Builder 패턴 정리</title>
      <link>https://1000one.tistory.com/88</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 천원입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 오픈소스들의 코드를 학습하면서 만난 Builder 패턴에 대해서 학습해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&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;Builder 패턴&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;h4 data-ke-size=&quot;size20&quot;&gt;예제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명으로 만 보면 감이 안 잡힐 테니 예제를 함께 보면, 아래와 같은 Student 클래스에 대한 객체를 생성하는 Builder를 만들어 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1717479679391&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final class Student {
    private var name: String
    private var age: String
    private var phoneNumber: Int
    
    init(name: String, age: String, phoneNumber: Int) {
        self.name = name
        self.age = age
        self.phoneNumber = phoneNumber
    }
    
    public func getInformation() -&amp;gt; String {
        return &quot;Name: \(name), Age: \(age), Phone Number: \(phoneNumber)&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 멤버들을 초기화 해주는 메서드와 해당 멤버들을 가지고 Student인스턴스를 리턴하는 Build 메서드를 만들어 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1717480208323&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final class StudentBuilder {
    private var name: String = &quot;&quot;
    private var age: String = &quot;&quot;
    private var phoneNumber: Int = 0
    
    public func setName(name: String) -&amp;gt; Self {
        self.name = name
        return self
    }
    
    public func setAge(age: String) -&amp;gt; Self {
        self.age = age
        return self
    }
    
    public func setPhoneNumber(phoneNumber: Int) -&amp;gt; Self {
        self.phoneNumber = phoneNumber
        return self
    }
    
    public func build() -&amp;gt; Student {
        return Student(name: name, age: age, phoneNumber: phoneNumber)
    }
}&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;이제 Builder를 통해서 인스턴스를 생성하면 아래와 같이 SwiftUI와 같은 선언적으로 프로그래밍 형태로 구현이 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1717483760190&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let jae = StudentBuilder()
    .setName(name: &quot;Jae&quot;)
    .setAge(age: &quot;24&quot;)
    .setPhoneNumber(phoneNumber: 1234567890)
    .build()

let yoon = StudentBuilder()
    .setName(name: &quot;Yoon&quot;)
    .setAge(age: &quot;25&quot;)
    .setPhoneNumber(phoneNumber: 9876543210)
    .build()
    
print(yoon.getInformation())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자를 통해서 인스턴스를 생성해도 될텐데 builder 패턴을 통해서 생성하는 이유가 무엇일까요?&lt;/p&gt;
&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;다음으로는 최초 인스턴스를 생성 시에만 내부 값들을 변경 후 build메서드를 통해서 인스턴스를 생성하기 때문에 인스턴스가 만들어진 후에는 외부에서 변경이 불가능하여 새로운 기능에 대해서도 수정이나 추가가 용이합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 customAlert과 같이 공통적으로 사용한는  Class들을 builder 패턴을 통해서 구현해두면 사용하는 쪽에서 set 메서들을 통하여 필요한 매개변수들을 초기화하여 재사용적으로 효율적이게 사용이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 Builder 패턴 정리였습니다.&lt;/p&gt;</description>
      <category>iOS&amp;amp;Swift </category>
      <category>builder</category>
      <category>ios builder 패턴</category>
      <category>swift builder pattern</category>
      <category>빌더 패턴</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/88</guid>
      <comments>https://1000one.tistory.com/88#entry88comment</comments>
      <pubDate>Tue, 4 Jun 2024 16:11:44 +0900</pubDate>
    </item>
    <item>
      <title>iOS Privacy Manifest</title>
      <link>https://1000one.tistory.com/87</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 천원입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 2024년 5월 부터 검수를 올리기 위해서는 반드시 필요한 Privacy Mainfest에 대해서 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Privacy Mainfest&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Privacy Manifest란 Required Reason API를 사용하는 이유를 명시하는 파일을 의미합니다. 여기서 Required Reason API란 기기 신호에 접근해 기기 혹은 사용자의 신원을 알 수 있도록 오용될 가능성이 있는 것들로, 구체적으로 활성화된 키보드(active keyboard), 디스크 공간(disk space), 파일 날짜(file timestamp), 시스템 부팅 시간(system boot time), 기본 사용자(user defaults) 등이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4월에 검수를 올렸을 때 전달받은 메일의 일부입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략 privacy manifest를 포함해서 다음 버전에는 검수를 올리라는 내용입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-24 오전 10.43.59.png&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ngegw/btsGRJKeFX4/nbyM0r25jU4Uw5iYYudKj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ngegw/btsGRJKeFX4/nbyM0r25jU4Uw5iYYudKj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ngegw/btsGRJKeFX4/nbyM0r25jU4Uw5iYYudKj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNgegw%2FbtsGRJKeFX4%2FnbyM0r25jU4Uw5iYYudKj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1498&quot; height=&quot;396&quot; data-filename=&quot;스크린샷 2024-04-24 오전 10.43.59.png&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;396&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;또한 앱뿐만 아니라 앱에 적용된 타사 sdk 또한 privacy manifest를 적용해줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sdk가 프레임워크 형태로 적용되어 있다면 privacy manifest 파일이 해당 프레임워크에 추가되어야 하고, static 라이브러리(.a파일) 형태로 들어가 있다면 프로젝트 단의 privacy manifest에 추가해 주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;Privacy Manifest File&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;먼저 App privacy 파일을 추가해 줍니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-29 오전 8.08.05.png&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;1154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wTy5L/btsGZA0y2Bs/kXyn6YFsmGcHvOIfDNWjhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wTy5L/btsGZA0y2Bs/kXyn6YFsmGcHvOIfDNWjhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wTy5L/btsGZA0y2Bs/kXyn6YFsmGcHvOIfDNWjhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwTy5L%2FbtsGZA0y2Bs%2FkXyn6YFsmGcHvOIfDNWjhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;526&quot; data-filename=&quot;스크린샷 2024-04-29 오전 8.08.05.png&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;1154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당하는 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;스크린샷 2024-04-29 오전 8.10.07.png&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vf0H7/btsG1y1Fhoy/AWGrggczpVhKhRCtLvwjsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vf0H7/btsG1y1Fhoy/AWGrggczpVhKhRCtLvwjsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vf0H7/btsG1y1Fhoy/AWGrggczpVhKhRCtLvwjsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvf0H7%2FbtsG1y1Fhoy%2FAWGrggczpVhKhRCtLvwjsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;463&quot; height=&quot;197&quot; data-filename=&quot;스크린샷 2024-04-29 오전 8.10.07.png&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PrivacyInfo 에는 총 4가지의 속성을 추가할 수 있습니다. 하나 하나 확인해 보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-29 오전 10.31.05.png&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d8YZE4/btsG1wQmv7e/fbUHQelMkiKFNC1nJ68qw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d8YZE4/btsG1wQmv7e/fbUHQelMkiKFNC1nJ68qw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d8YZE4/btsG1wQmv7e/fbUHQelMkiKFNC1nJ68qw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd8YZE4%2FbtsG1wQmv7e%2FfbUHQelMkiKFNC1nJ68qw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;225&quot; data-filename=&quot;스크린샷 2024-04-29 오전 10.31.05.png&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;312&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;b&gt;NSPrivacyTracking&lt;/b&gt;(Privacy Tracking Enabled): ATT(APP Tracking Transparency framework)에 대한 사용 여부를 나타내는 Bool 타입입니다.&lt;/p&gt;
&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;NSPrivacyTrackingDomains&lt;/b&gt;(Privacy Tracking Domains): 추적에 사용되는 도메인을 나열할 배열입니다. 만약 NSPrivacyTracking 가 NO 라면 도메인을 추가해 주지 않아도 됩니다.&lt;/p&gt;
&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;NSPrivacyCollectedDataTypes&lt;/b&gt;(Privacy Nutrition label Types): 수집하는 데이터 항목을 나타내며 명시할 데이터 항목은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests#4250555&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 링크&lt;/a&gt;를 참고하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #242424; text-align: left;&quot;&gt;NSPrivacyAccessedAPITypes&lt;/span&gt;&lt;/b&gt;(Privacy Accessed API Types): &lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: left;&quot;&gt;접근하는 API 타입을 나타냅니다. API 타입은 Required Reason API로 지정되어있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-29 오전 11.19.38.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eAkIqC/btsGZvewgRE/5MjfLkxOkD5oQIuXQkq8r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eAkIqC/btsGZvewgRE/5MjfLkxOkD5oQIuXQkq8r0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eAkIqC/btsGZvewgRE/5MjfLkxOkD5oQIuXQkq8r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeAkIqC%2FbtsGZvewgRE%2F5MjfLkxOkD5oQIuXQkq8r0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1212&quot; height=&quot;302&quot; data-filename=&quot;스크린샷 2024-04-29 오전 11.19.38.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;302&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;Item을 추가해 주면 4가지에 Key값이 나오는데 하나 씩 살펴보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Collected Data&lt;/b&gt; Type: 사용하는 데이터 종류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Linked to User&lt;/b&gt;: 사용자에 직접적으로 연결되는 데이터를 나타냅니다. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&gt;예를 들어, 사용자의 이름, 이메일 주소, 전화번호와 같은 개인 식별 정보가 포함될 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Used for Tracking&lt;/b&gt;: 이&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: left;&quot;&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;p data-ke-size=&quot;size16&quot;&gt;다음으로 &lt;b&gt;&lt;span style=&quot;color: #242424; text-align: left;&quot;&gt;NSPrivacyAccessedAPITypes&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #242424; text-align: left;&quot;&gt;을 추가해 보면&lt;/span&gt;&lt;span style=&quot;color: #242424; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-29 오후 3.24.45.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DyqFt/btsG105ow37/UTLuoxxVXK2Wu3wajQ0mXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DyqFt/btsG105ow37/UTLuoxxVXK2Wu3wajQ0mXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DyqFt/btsG105ow37/UTLuoxxVXK2Wu3wajQ0mXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDyqFt%2FbtsG105ow37%2FUTLuoxxVXK2Wu3wajQ0mXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1246&quot; height=&quot;412&quot; data-filename=&quot;스크린샷 2024-04-29 오후 3.24.45.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;412&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;b&gt;Privacy Accessed API Type&lt;/b&gt;에는 사용하는 Required Reason API를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Privacy Accessed API Reaseons&amp;nbsp;&lt;/b&gt;에는 해당 API를 사용하는 이유를 명시해 주면 됩니다. 자세한 Value 값은 &lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api&quot;&gt;요기&lt;/a&gt; 링크를 통해서 확인해 보시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&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;Privacy Report&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;privacy report는 작성한 &lt;span style=&quot;background-color: #ffffff; color: #5c5c5c; text-align: left;&quot;&gt;PrivacyInfo 파일을 바탕으로 요약하여 개인정보 리포트를 생성하는 기능입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-29 오후 4.02.35.png&quot; data-origin-width=&quot;2052&quot; data-origin-height=&quot;1284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b501h1/btsG1hTU69u/KPHkN7hsxFmwnSUKyveQR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b501h1/btsG1hTU69u/KPHkN7hsxFmwnSUKyveQR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b501h1/btsG1hTU69u/KPHkN7hsxFmwnSUKyveQR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb501h1%2FbtsG1hTU69u%2FKPHkN7hsxFmwnSUKyveQR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2052&quot; height=&quot;1284&quot; data-filename=&quot;스크린샷 2024-04-29 오후 4.02.35.png&quot; data-origin-width=&quot;2052&quot; data-origin-height=&quot;1284&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;먼저 앱을 Archive 해주고 해당 파일에 오른쪽 클릭하여 Generate Privacy Report 를 클릭해 주면 아래와 같이 우리가 작성한 &lt;span style=&quot;background-color: #ffffff; color: #5c5c5c; text-align: left;&quot;&gt;PrivacyInfo 파일이 pdf 형태로 다운로드 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-29 오후 4.04.08.png&quot; data-origin-width=&quot;2802&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqcAvt/btsGZ0SJvoT/b93WZmXEAdjBZBVFB4Pha1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqcAvt/btsGZ0SJvoT/b93WZmXEAdjBZBVFB4Pha1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqcAvt/btsGZ0SJvoT/b93WZmXEAdjBZBVFB4Pha1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqcAvt%2FbtsGZ0SJvoT%2Fb93WZmXEAdjBZBVFB4Pha1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2802&quot; height=&quot;250&quot; data-filename=&quot;스크린샷 2024-04-29 오후 4.04.08.png&quot; data-origin-width=&quot;2802&quot; data-origin-height=&quot;250&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;여기까지 Privacy Manifest 정리였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다.&lt;/p&gt;</description>
      <category>iOS&amp;amp;Swift </category>
      <category>privacy manifest</category>
      <category>privacy report</category>
      <category>privacyinfo</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/87</guid>
      <comments>https://1000one.tistory.com/87#entry87comment</comments>
      <pubDate>Mon, 29 Apr 2024 16:05:38 +0900</pubDate>
    </item>
    <item>
      <title>iOS Background Tasks(백그라운드에서 API 호출하기)</title>
      <link>https://1000one.tistory.com/86</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 천원입니다.&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Background Tasks 프레임워크&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS 13부터 추가된 프레임워크로 시스템과 앱간의 background Task 요청을 담당하는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프레임워크에는 &lt;a style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; href=&quot;https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask&quot;&gt;BGAppRefreshTask&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;amp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; href=&quot;https://developer.apple.com/documentation/backgroundtasks/bgprocessingtask&quot;&gt;BGProcessingTask&lt;/a&gt; 이렇게 두가지 타입이 존재하는데 BGAppRefreshTask는 30초 정도가 소요되는 작업에 사용되고 그 이상의 몇분이 소요되는 것들은 BGProcessingTask를 사용하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN49bB/btsEZnXbJB3/EzgQ4tKWT9sQw1skwLEyf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN49bB/btsEZnXbJB3/EzgQ4tKWT9sQw1skwLEyf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN49bB/btsEZnXbJB3/EzgQ4tKWT9sQw1skwLEyf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN49bB%2FbtsEZnXbJB3%2FEzgQ4tKWT9sQw1skwLEyf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;416&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WWDC 2019 자료를 참고해 보면 사용자가 Foreground로 진입하는 시점을 시스템이 분석해서 그전에 호출하게 되고 그 외에도 유저의 백그라운드 앱 새로고침 설정, 배터리 충전 상태, 네트워크 연결 등의 여부에 따라 호출하며 앱을 호출하는 정확한 시점은 파악할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&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;먼저 Signing&amp;amp;Capabilities 에서 Background Modes를 추가해 주고, Background fetch와 Background processing을 체크해 줍니다. 각각 BGAppRefreshTask와 BGProcessingTask에 대한 설정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-20 오전 8.28.05.png&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;1178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYQgS6/btsE6PYDXA6/S5Q0DdQ0G33c4v70iIz0q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYQgS6/btsE6PYDXA6/S5Q0DdQ0G33c4v70iIz0q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYQgS6/btsE6PYDXA6/S5Q0DdQ0G33c4v70iIz0q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYQgS6%2FbtsE6PYDXA6%2FS5Q0DdQ0G33c4v70iIz0q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;309&quot; data-filename=&quot;스크린샷 2024-02-20 오전 8.28.05.png&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;1178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 스케줄러에 사용할 identifier를 Info.plist에 추가해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Permitted background task scheduler identifiers &lt;/b&gt;을 추가해 주고 Item의 Value 에 String 타입으로 identifier를 작성해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-20 오전 8.32.25.png&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxQvVy/btsE6CSNM18/4z3PLWOZGNZYnz5p4ah5F0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxQvVy/btsE6CSNM18/4z3PLWOZGNZYnz5p4ah5F0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxQvVy/btsE6CSNM18/4z3PLWOZGNZYnz5p4ah5F0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxQvVy%2FbtsE6CSNM18%2F4z3PLWOZGNZYnz5p4ah5F0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;228&quot; data-filename=&quot;스크린샷 2024-02-20 오전 8.32.25.png&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 TasK를 AppDelegate의 didFinishLauchingWithOptions 함수에 등록해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 핸들러 내부에는 실제로 수행할 백그라운드 동작을 구현해 주는데 이때 파라미터로 task를 함께 호출해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708386248955&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&amp;gt; Bool {
    // Override point for customization after application launch.

    BGTaskScheduler.shared.register(forTaskWithIdentifier: &quot;example.com&quot;, using: nil) { task in
        self.handleAppRefresh(task: task as! BGAppRefreshTask) // 실제로 수행할 코드가 구현된 매서드
    }

    // 2. Processing Task
    BGTaskScheduler.shared.register(forTaskWithIdentifier: &quot;example2.com&quot;, using: nil) { task in
        self.handleProcessingTask(task: task as! BGProcessingTask)
    }

    return true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;setTaskCompleted(success: true)&lt;/b&gt;를 통해서 task의 성공여부를 반드시 호출해 주어야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708386446839&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func handleAppRefresh(task: BGAppRefreshTask) {

    self.writeLog(_writeValue: &quot;백그라운드 호출 기록&quot;)

    task.expirationHandler = {
        task.setTaskCompleted(success: false)
    }

    task.setTaskCompleted(success: true)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;저는 BGProcessing Task를 사용하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;handleProcessingTask 함수 또한 동일한 코드로 구성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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등록이 되었다면 우리는 Scene이 백그라운드에 진입할 때 해당 Task를 호출할 수 있도록 스케줄러를 등록해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록시에 timeInterval을 설정할 수 있지만 원하는대로 동작하지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708387145055&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func scheduleAppRefresh() {
    let request = BGAppRefreshTaskRequest(identifier: &quot;example.com&quot;) // 설정한 identifier
    request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60) // 호출할 timeInterval을 1시간으로 설정

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print(&quot;Could not schedule app refresh: \(error)&quot;)
    }
}

func scheduleProcessingTaskIfNeeded() {
    let request = BGProcessingTaskRequest(identifier: &quot;example2.com&quot;)
    request.requiresExternalPower = false // 에너지 소모량
    request.requiresNetworkConnectivity = false // 네트워크 사용여부 옵션

    request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60)

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print(&quot;\(Date()): Could not schedule processing task: \(error)&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러를 등록하는 함수를 sceneDidEnterBackground 에서 호출해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708387190753&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func sceneDidEnterBackground(_ scene: UIScene) {
    scheduleAppRefresh()
    scheduleProcessingTaskIfNeeded()
}&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;테스트해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 스케줄러까지 등록을 했으면 테스트를 진행해야 하지만 백그라운드에서 동작하기에 Xcode에서 테스트하기 쉽지 않습니다.&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;스크린샷 2024-02-20 오전 9.09.12.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6VT4c/btsEZP6YEiU/JHaRGMVZisdb85DtTF1tAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6VT4c/btsEZP6YEiU/JHaRGMVZisdb85DtTF1tAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6VT4c/btsEZP6YEiU/JHaRGMVZisdb85DtTF1tAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6VT4c%2FbtsEZP6YEiU%2FJHaRGMVZisdb85DtTF1tAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;422&quot; data-filename=&quot;스크린샷 2024-02-20 오전 9.09.12.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱을 실행 후 해당 지점에 브레이크 포인트를 걸어두고 백그라운드에 진입합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런다음 아래에 명령어를 lldb에 입력해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708389506270&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@&quot;Info.plist에 등록한 태스크 Identifier&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어가 실행되면 다시 포그라운드 진입 후 Continue 해주면 정상적으로 handleAppRefresh 함수를 호출하는 모습입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-20 오전 9.56.51.png&quot; data-origin-width=&quot;2798&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmncRs/btsE4Y9FlzK/dkksyatZ0TPbRjYSzK3ZhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmncRs/btsE4Y9FlzK/dkksyatZ0TPbRjYSzK3ZhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmncRs/btsE4Y9FlzK/dkksyatZ0TPbRjYSzK3ZhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmncRs%2FbtsE4Y9FlzK%2FdkksyatZ0TPbRjYSzK3ZhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2798&quot; height=&quot;964&quot; data-filename=&quot;스크린샷 2024-02-20 오전 9.56.51.png&quot; data-origin-width=&quot;2798&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 기록된 백그라운드 호출 로그입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I2XGs/btsE8AfEohY/dLVEmX5WuiKvKdSOiKUKVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I2XGs/btsE8AfEohY/dLVEmX5WuiKvKdSOiKUKVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I2XGs/btsE8AfEohY/dLVEmX5WuiKvKdSOiKUKVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI2XGs%2FbtsE8AfEohY%2FdLVEmX5WuiKvKdSOiKUKVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;227&quot; height=&quot;492&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&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://itnext.io/swift-ios-13-backgroundtasks-framework-background-app-refresh-in-4-steps-3da32e65bc3d&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://itnext.io/swift-ios-13-backgroundtasks-framework-background-app-refresh-in-4-steps-3da32e65bc3d&lt;/a&gt;&lt;/p&gt;</description>
      <category>iOS&amp;amp;Swift /iOS</category>
      <category>backgorund에서 API 호출</category>
      <category>Background Tasks</category>
      <category>ios</category>
      <category>swift</category>
      <category>백그라운드 API 호출</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/86</guid>
      <comments>https://1000one.tistory.com/86#entry86comment</comments>
      <pubDate>Tue, 20 Feb 2024 10:13:48 +0900</pubDate>
    </item>
    <item>
      <title>iOS KeyPath 정리</title>
      <link>https://1000one.tistory.com/85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 천원입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 TCA로 사이드 프로젝트를 진행하고 있는데 아래와 같이 \. 이런 문법이 종종 등장을 해서 정리하고자 이 글을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703548128723&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;AuthTextField(text: viewStore.binding(get: \.codeText, send: InViteCodeFeature.Action.textEditing), placeholder: &quot;초대코드&quot;, error: viewStore.validCode) // TCA Binding

@Dependency(\.apiService) var apiServiced // Custom Dependency&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;먼저 Object-C에서 등장한 Key의 개념을 살펴보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Key는 문자열을 의미하고 Key값을 통해서 인스턴스의 프로퍼티에 접근하게 해주는 Objective-C에서 나온 개념이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;KVC(Key Value Coding)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인스턴스의 프로퍼티에 접근할 때 Key의 문자열로 접근하는 방식&lt;/li&gt;
&lt;li&gt;KVC는 Objective-C 런타임에 의존하므로 프로퍼티 앞에 @objc 붙여서 사용&lt;/li&gt;
&lt;li&gt;Objective-C의 것이기 때문에 NSObject가 가지고 있으므로 NSObject의 서브클래스여야 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1703548423601&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person: NSObject {
    @objc var name: String = &quot;윤제&quot;
}

let person = Person()
person.value(forKey: &quot;name&quot;) // Optional(윤제)

person.setValue(&quot;천원&quot;, forKey: &quot;name&quot;)
person.value(forKey: &quot;name&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;코드를 확인해 보면 dot syntax로 프로퍼티에 접근하는게 아니라 &quot;name&quot;이라는 문자열을 key값으로 name 프로퍼티에 접근을 한 모습입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;KVO(Key Value Observing)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KVC와 동일하게 키값으로 프로퍼티에 접근하는데 Observing이 가능하다&lt;/li&gt;
&lt;li&gt;@objc dynamic&amp;nbsp;을 붙여서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1703548622568&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person2: NSObject { 
  @objc dynamic var name: String = &quot;윤제&quot;
}

person2.observe(\.name, options: [.old, .new]) { instance, change in
  print(change.oldValue, change.newValue)
}
person2.name = &quot;천원&quot; //(윤제, 천원)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;observe 매서드를 통해서 name의 변화를 관찰하고 변동 시에 작성해둔 print문이 실행되는 모습입니다. willSet 혹은 didSet과 유사한 역할을 수행할 수 있어 보입니다. observe 함수의 매개변수의 타입을 확인해 보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-12-26 오전 9.05.09.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L9wd9/btsCBKy6O5m/Bo8UvRu4Z1w0Z7xZnIVVz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L9wd9/btsCBKy6O5m/Bo8UvRu4Z1w0Z7xZnIVVz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L9wd9/btsCBKy6O5m/Bo8UvRu4Z1w0Z7xZnIVVz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL9wd9%2FbtsCBKy6O5m%2FBo8UvRu4Z1w0Z7xZnIVVz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;319&quot; data-filename=&quot;스크린샷 2023-12-26 오전 9.05.09.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KeyPath를 타입으로 \.name을 받는것을 알 수 있습니다. 이제 KeyPath는 무엇인지 확인해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;KeyPath&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-12-22 오후 3.14.46.png&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;918&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPjYJW/btsCzIIBrKs/WZDSNUP8k61NrChgVXvPC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPjYJW/btsCzIIBrKs/WZDSNUP8k61NrChgVXvPC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPjYJW/btsCzIIBrKs/WZDSNUP8k61NrChgVXvPC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPjYJW%2FbtsCzIIBrKs%2FWZDSNUP8k61NrChgVXvPC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;918&quot; data-filename=&quot;스크린샷 2023-12-22 오후 3.14.46.png&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;918&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;Key 값으로 프로퍼티에 접근이 가능한 것 처럼 KeyPath를 통해서도 접근이 가능하다&lt;/li&gt;
&lt;li&gt;KeyPath는 Root라는 타입(=Person타입)으로부터 구체적인 Value Type(프로퍼티타입)으로의 key의 경로를 의미&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 설명을 보태자면 Person이라는 타입의 프로퍼티인 name 까지의 경로를 의미하는게 KeyPath라고 합니다. 코드로 확인해 보면&lt;/p&gt;
&lt;pre id=&quot;code_1703549461819&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person {
    var name: String = &quot;윤제&quot;
}

let person = Person()
person[keyPath: \Person.name] // 윤제
person[keyPath: \.name] // Root 타입은 생략 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person -&amp;gt; name 의 KeyPath를 통해서 name 프로퍼티에 접근한 모습입니다. 그런데 우리가 앞서 궁금증을 가졌던 \. 형태는 이렇게 Dictionary 같은 형태가 아니라 매개변수로 \.을 받아서 사용을 했잖아요? 아래처럼!&lt;/p&gt;
&lt;pre id=&quot;code_1703549613151&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;AuthTextField(text: viewStore.binding(get: \.codeText, send: InViteCodeFeature.Action.textEditing), placeholder: &quot;초대코드&quot;, error: viewStore.validCode) // TCA Binding

@Dependency(\.apiService) var apiServiced // Custom Dependency&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 KeyPath를 활용한 함수를 작성해 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;KeyPath 활용&lt;/h3&gt;
&lt;pre id=&quot;code_1703549685021&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct PersonInfo {
  var name: String
  var age: Int
}

struct School {
  var kim: PersonInfo
  var han: PersonInfo
  
  func getKim() -&amp;gt; PersonInfo {
    return self.kim
  }
  func getHan() -&amp;gt; PersonInfo {
    return self.han
  }
}

let kim = PersonInfo(name: &quot;김감자&quot;, age: 12)
let han = PersonInfo(name: &quot;한고구마&quot;, age: 13)
let school = School(kim: kim, han: han)

print(school.getKim()) // PersonInfo(name: &quot;김감자&quot;, age: 12)
print(school.getHan()) // PersonInfo(name: &quot;한고구마&quot;, age: 13)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;School 구조체 내부에 각각에 프로퍼티를 return 하는 매서드가 존재하여 getKim, getHan을 통해서 프로퍼티를 가져온 모습이지만.. 학생의 수가 많아지면 그만큼 매서드의 수도 많아져야겠죠 이를 개선하기 위해서 우리는 KeyPath를 활용해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1703549855780&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;extension School {
  func getChild(keyPath: KeyPath&amp;lt;Self, PersonInfo&amp;gt;) -&amp;gt; PersonInfo {
    self[keyPath: keyPath]
  }
}

print(school.getChild(keyPath: \.kim)) // PersonInfo(name: &quot;김감자&quot;, age: 12)
print(school.getChild(keyPath: \.han)) // PersonInfo(name: &quot;한고구마&quot;, age: 13)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getChild 라는 매서드의 파라미터로 KeyPath를 받아 사용한 모습입니다. 각각의 매서드를 사용하는 것 보다 재사용성이 올라간 모습이네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 KeyPath 정리였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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/swift/keypath&quot;&gt;https://developer.apple.com/documentation/swift/keypath&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ios-development.tistory.com/982&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ios-development.tistory.com/982&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS&amp;amp;Swift /iOS</category>
      <category>KeyPath</category>
      <category>swift keyPath</category>
      <category>Swift \.</category>
      <category>\.</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/85</guid>
      <comments>https://1000one.tistory.com/85#entry85comment</comments>
      <pubDate>Tue, 26 Dec 2023 09:25:13 +0900</pubDate>
    </item>
    <item>
      <title>Xcode15 linker Bug Report (Thread 1: EXC_BAD_ACCESS (code=1, address=0x0))</title>
      <link>https://1000one.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 천원입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 Xcode15 에서 발생하는 버그 리포팅을 해보고자 이 글을 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Xcode15.0.1에서 unit test 타켓을 추가하고 iOS 14이하의 디바이스로 빌드하게 되면 아래와 같은 런타임 crash 가 발생하게 되는데&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-29 오전 10.28.07.png&quot; data-origin-width=&quot;4448&quot; data-origin-height=&quot;1072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nOUIG/btsA8MdyTBo/4zWvjHyqPdhuTkJSso9NxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nOUIG/btsA8MdyTBo/4zWvjHyqPdhuTkJSso9NxK/img.png&quot; data-alt=&quot;Thread 1: EXC_BAD_ACCESS (code=1, address=0x0&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nOUIG/btsA8MdyTBo/4zWvjHyqPdhuTkJSso9NxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnOUIG%2FbtsA8MdyTBo%2F4zWvjHyqPdhuTkJSso9NxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4448&quot; height=&quot;1072&quot; data-filename=&quot;스크린샷 2023-11-29 오전 10.28.07.png&quot; data-origin-width=&quot;4448&quot; data-origin-height=&quot;1072&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Thread 1: EXC_BAD_ACCESS (code=1, address=0x0&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;먼저 Xcode 15에 Release Note를 살펴보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-29 오후 2.38.17.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bx4I0/btsA9PnXKln/sAJ4kFkY6PIBCpkayCucYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bx4I0/btsA9PnXKln/sAJ4kFkY6PIBCpkayCucYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bx4I0/btsA9PnXKln/sAJ4kFkY6PIBCpkayCucYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBx4I0%2FbtsA9PnXKln%2FsAJ4kFkY6PIBCpkayCucYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;390&quot; data-filename=&quot;스크린샷 2023-11-29 오후 2.38.17.png&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static 라이브러리 linking 속도의 향상을 위한 새로운 linker가 나왔네요! 이녀석 때문에 문제가 발생한 것 같아 한번 찾아보니 아래와 같이&lt;/p&gt;
&lt;p data-ke-size=&quot;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;스크린샷 2023-11-29 오후 2.59.08.png&quot; data-origin-width=&quot;2056&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxyaFu/btsA8rHTiga/0GygzEqEqz8VA98830xTJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxyaFu/btsA8rHTiga/0GygzEqEqz8VA98830xTJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxyaFu/btsA8rHTiga/0GygzEqEqz8VA98830xTJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxyaFu%2FbtsA8rHTiga%2F0GygzEqEqz8VA98830xTJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1059&quot; height=&quot;149&quot; data-filename=&quot;스크린샷 2023-11-29 오후 2.59.08.png&quot; data-origin-width=&quot;2056&quot; data-origin-height=&quot;290&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;iOS 14 이하에서 약한 정의를 같는 심볼을 사용하는 바이너리 파일에서 런타임 crash 가 발생한다고 하네요. 회피책을 보니 &lt;b&gt;OTHER_LDFLAGS&lt;/b&gt; bulid setting에서 &lt;b&gt;-Wl, -ld_classic&lt;/b&gt; 을 추가해 주라고 하니 한번 해보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-29 오후 3.03.36.png&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cC6MV5/btsA6TyanKp/BrOKLOVWezO8m87y7wAj2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cC6MV5/btsA6TyanKp/BrOKLOVWezO8m87y7wAj2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cC6MV5/btsA6TyanKp/BrOKLOVWezO8m87y7wAj2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcC6MV5%2FbtsA6TyanKp%2FBrOKLOVWezO8m87y7wAj2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;756&quot; height=&quot;306&quot; data-filename=&quot;스크린샷 2023-11-29 오후 3.03.36.png&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;588&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;b&gt;-Wl, -ld_classic&amp;nbsp;&lt;/b&gt;플래그는 classic 링커를 사용하기 위한 플래그라고 합니다.&lt;br /&gt;&lt;br /&gt;이번에는 앞서 realase note에 명시되어있는 &lt;b&gt;-ld64 &lt;/b&gt;플래그를 통해서 classic linker를 사용해 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-29 오후 3.05.38.png&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BziGb/btsA7Ayl2sL/vFKztITp3KZjQOBMci3va1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BziGb/btsA7Ayl2sL/vFKztITp3KZjQOBMci3va1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BziGb/btsA7Ayl2sL/vFKztITp3KZjQOBMci3va1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBziGb%2FbtsA7Ayl2sL%2FvFKztITp3KZjQOBMci3va1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;797&quot; height=&quot;304&quot; data-filename=&quot;스크린샷 2023-11-29 오후 3.05.38.png&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 잘 동작하네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로&amp;nbsp; Xcode 15.1 Beta 버전에서는 해결되어 특별히 Linker Flags Setting 안해주어도 정상동작 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-29 오전 10.36.29.png&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjxzpK/btsBbObf5s2/3KjszGW1qFMsZ6OmjcChx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjxzpK/btsBbObf5s2/3KjszGW1qFMsZ6OmjcChx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjxzpK/btsBbObf5s2/3KjszGW1qFMsZ6OmjcChx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjxzpK%2FbtsBbObf5s2%2F3KjszGW1qFMsZ6OmjcChx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;450&quot; data-filename=&quot;스크린샷 2023-11-29 오전 10.36.29.png&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;450&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 Release Note의 known Issues 는 귀찮아도 챙겨보자!&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS&amp;amp;Swift /Xcode</category>
      <category>address=0x0</category>
      <category>linking</category>
      <category>Thread 1: EXC_BAD_ACCESS (code=1</category>
      <category>xcode</category>
      <category>Xcode 15</category>
      <category>Xcode 15 Thread</category>
      <category>Xcode15 runtime crash</category>
      <author>천 원</author>
      <guid isPermaLink="true">https://1000one.tistory.com/84</guid>
      <comments>https://1000one.tistory.com/84#entry84comment</comments>
      <pubDate>Wed, 29 Nov 2023 15:15:10 +0900</pubDate>
    </item>
  </channel>
</rss>