<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>MINTraum</title>
    <link>https://mintraum.tistory.com/</link>
    <description>iOS Developer 
MINT_raum 
민트의 꿈</description>
    <language>ko</language>
    <pubDate>Sat, 25 Jul 2026 06:52:28 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>MINT09</managingEditor>
    <image>
      <title>MINTraum</title>
      <url>https://tistory1.daumcdn.net/tistory/6719226/attach/5aa9a300842c446db6373e684243381a</url>
      <link>https://mintraum.tistory.com</link>
    </image>
    <item>
      <title>OAuth - Kakao vs Apple</title>
      <link>https://mintraum.tistory.com/56</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;티스토리-블로그-003.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAfINw/btsPRzdi4Rg/GmQeNM8PmScGiWc637HdYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAfINw/btsPRzdi4Rg/GmQeNM8PmScGiWc637HdYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAfINw/btsPRzdi4Rg/GmQeNM8PmScGiWc637HdYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAfINw%2FbtsPRzdi4Rg%2FGmQeNM8PmScGiWc637HdYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;500&quot; data-filename=&quot;티스토리-블로그-003.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&lt;b&gt;누가 가입/탈퇴 절차의 책임을 지는가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트를 진행하면서 iOS는 카카오, 애플 소셜 로그인을 가져가고 있는데 이 둘의 차이점에 의해 백엔드와 길게 논의하는 시간이 있었다. 카카오는 앱에서 카카오 인증 서버에게 토큰을 발급받는데 애플은 그렇지 않아 생기는 논쟁이었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;서버 주도의 Apple&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플 로그인의 흐름은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;앱에서 Apple 로그인 요청 &amp;rarr; 사용자 동의&lt;/li&gt;
&lt;li&gt;Apple이 AuthorizationCode 전달&lt;/li&gt;
&lt;li&gt;앱이 AuthorizationCode를 서버로 전달&lt;/li&gt;
&lt;li&gt;서버가 Apple API에 Authorization Code를 제출해 AccessToken &amp;amp; RefreshToken 발급&lt;/li&gt;
&lt;li&gt;서버에서 가입 / 로그인 처리 및 토큰 관리&lt;/li&gt;
&lt;li&gt;탈퇴할때도 서버에서 RefreshToken을 활용해 Apple API 호출&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 AccessToken을 발급받거나, 탈퇴 처리를 할 때 서버에서만 관리하는 -그렇지 않으면 보안 이슈가 생길 수 있는- client_secret을 사용해야 한다. 책임의 주체는 서버로 토큰 발급, 갱신, 만료 관리, 가입 여부 판단 모두 서버에서 처리한다. 보안성이 높고 토큰 관리를 서버에서 온전히 책임진다는 장점이 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;앱 주도의 Kakao&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오의 경우는 조금 다르다. 카카오톡 앱에서 로그인을 할 수 있도록 하려면 SDK를 사용해야 하는데, 이를 사용하면 앱이 카카오 인증 서버에서 AccessToken을 발급 받게 된다. 즉 가입/로그인 할 때의 책임이 온전히 한쪽에 있지 않고 나뉘게 되는 것이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;앱에서 Kakao SDK 호출 &amp;rarr; 로그인 화면 표시 (앱으로)&lt;/li&gt;
&lt;li&gt;카카오 인증 서버에서 사용자 동의 &amp;rarr; AccessToken 발급&lt;/li&gt;
&lt;li&gt;앱이 AccessToken을 서버로 전달&lt;/li&gt;
&lt;li&gt;서버가 해당 토큰을 사용해 카카오 API를 호출하거나 가입 처리&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 발급 / 갱신 / 만료 감지는 앱의 책임이지만, 서버 유효성 검증 / 사용자 정보 조회 / 가입 / 로그인 처리 / 서비스 내 권한 관리는 서버의 책임으로 나뉘게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서버 주도의 Kakao?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 백엔드 개발자 분이 두 경우 동일하게 책임이 한쪽에 있었으면 좋겠다는 의견을 제시하셨다. 이에 동의하고 카카오에서 Authorization Code만 발급받으려 했으나 이 경우 &amp;ldquo;카카오톡 앱으로 로그인&amp;rdquo;은 할 수 없었다. 코드 &amp;rarr; 서버 교환 단계가 SDK 밖으로 노출되지 않고 사용자 동의 후 곧바로 OAuthToken을 앱에 반환해주기 때문이었다. 우리는 앱 서비스임에도 불구하고 Authorization Code를 전달로 인해 카카오 로그인을 하려면 웹이 열려서 사용자가 직접 본인의 카카오 아이디와 비밀번호를 쳐야 하는 상황이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이 경우 사용자 경험이 좋지 않을 것 같다는 판단에 다시 원래 상태로 돌아와 카카오와 애플은 서로 다른 책임과 로직을 가지게 되었다. 더 좋은 방법이 있다면 공유해주시면 좋겠다&amp;hellip;&lt;/p&gt;</description>
      <category>  iOS/  트러블 슈팅</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/56</guid>
      <comments>https://mintraum.tistory.com/56#entry56comment</comments>
      <pubDate>Wed, 13 Aug 2025 00:13:17 +0900</pubDate>
    </item>
    <item>
      <title>[iOS/SwiftUI] Life Cycle</title>
      <link>https://mintraum.tistory.com/55</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;티스토리-블로그-002.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EXUd5/btsPOsL4Eix/FEf3n2AxJ4X3AkaKCPEWiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EXUd5/btsPOsL4Eix/FEf3n2AxJ4X3AkaKCPEWiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EXUd5/btsPOsL4Eix/FEf3n2AxJ4X3AkaKCPEWiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEXUd5%2FbtsPOsL4Eix%2FFEf3n2AxJ4X3AkaKCPEWiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;500&quot; data-filename=&quot;티스토리-블로그-002.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SwiftUI는 선언적 UI 프레임워크로, 뷰 자체가 State에 반응하여 UI를 갱신한다. UIKit과는 달리 ViewController가 없고, State를 중심으로 UI가 업데이트 되기 때문에 명시적 LifeCycle Method가 존재하지 않는다. 때문에 LifeCycle hook 대신 &amp;lsquo;Modifier&amp;rsquo;를 제공하는데 이마저도 UIKit처럼 상세하게 나뉘지는 않고 onAppear, onDisappear로 종류가 한정적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;State가 변하면 이를 감지해 View 전체를 re-render하게 되는데, 이때 메모리에서 View는 값 타입으로 변경점이 있는 부분만 재구성된다. 비동기 작업 역시 UIKit은 callback, delegate, notification 등을 활용하지만 SwiftUI는 Combine, async/await과 자연스러운 연계가 가능하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SwiftUI Life Cycle 상세 흐름&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;앱 실행 &amp;rarr; @main App Struct의 body 내 Scene 구성이 실행&lt;/li&gt;
&lt;li&gt;뷰가 화면에 표시될 때 onAppear modifier 실행&lt;/li&gt;
&lt;li&gt;상태 변화가 감지되면 뷰 전체가 다시 계산되고 화면 갱신&lt;/li&gt;
&lt;li&gt;뷰가 사라질 때 onDisappear 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;비동기 이벤트 관리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SwiftUI는 State 변화를 중심으로 UI를 설계하기 때문에 비동기 작업 결과를 State에 반영하면 곧바로 화면에 반영할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Combine&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Combine의 Publisher - Subscriber 모델을 이용해 비동기 스트림을 처리하고, 이를 쉽게 UI 상태에 연결할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;import SwiftUI
import Combine

class ViewModel: ObservableObject {
    @Published var text = &quot;Loading...&quot;
    private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()
    
    func loadData() {
        fetchDataPublisher()
            .receive(on: DispatchQueue.main)
            .sink { completion in
                if case .failure(let error) = completion {
                    self.text = &quot;Error: \\(error.localizedDescription)&quot;
                }
            } receiveValue: { value in
                self.text = value
            }
            .store(in: &amp;amp;cancellables)
    }
    
    func fetchDataPublisher() -&amp;gt; AnyPublisher&amp;lt;String, Error&amp;gt; {
        Just(&quot;Hello from Combine!&quot;)
            .delay(for: .seconds(2), scheduler: DispatchQueue.global())
            .setFailureType(to: Error.self)
            .eraseToAnyPublisher()
    }
}

struct CombineView: View {
    @StateObject private var vm = ViewModel()
    
    var body: some View {
        Text(vm.text)
            .onAppear {
                vm.loadData()
            }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;async / await&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 SwiftUI는 async-await 문법을 직접 View에서 task modifier와 함께 사용할 수 있어, 비동기 코드를 깔끔하고 간결하게 작성할 수 있다. UIKit에서는 비동기 작업 이후 UI 업데이트를 위해 이벤트를 별도로 보내는 등의 관리가 필요하지만 SwiftUI는 상태 바인딩만 하면 된다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;import SwiftUI

struct AsyncView: View {
    @State private var text: String = &quot;Loading...&quot;
    
    var body: some View {
        Text(text)
            .task {
                await loadData()
            }
    }
    
    func loadData() async {
        do {
            let result = try await fetchData()
            text = result  // 상태 변경 &amp;rarr; UI 자동 갱신
        } catch {
            text = &quot;Error: \\(error.localizedDescription)&quot;
        }
    }
    
    func fetchData() async throws -&amp;gt; String {
        try await Task.sleep(nanoseconds: 2 * 1_000_000_000)  // 2초 지연
        return &quot;Hello from async/await!&quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;불명확성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 SwiftUI는 명시적 LifeCycle을 사용하지 않기에 rendering이 씹혀 화면이 갱신되지 않거나, UI 업데이트가 되지 않는 문제가 발생하기도 한다. 이는 SwiftUI에서 State에 따라 View를 &lt;b&gt;자동으로&lt;/b&gt; 그려주기에, 내부적으로 언제 뷰가 다시 re-render 되는지, 어떤 시점에 View의 LifeCycle 이벤트가 호출되는지 개발자가 예측하여 직접 제어하기 어렵기 때문이다. 즉, UIKit과는 달리 SwiftUI는 View의 LifeCycle 관리가 간접적이고 추상화되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 문제들은 아래와 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. State 변경 감지 실패&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SwiftUI는 @State, @StateObject 등의 State Property가 변경될 때만 뷰를 다시 그린다. (re-render) 때문에 상태가 변경되지 않거나, 상태가 변경되어도 SwiftUI가 이를 감지하지 못하면 랜더링 갱신이 일어나지 않는데 상태를 직접 변경하지 않고 내부 프로퍼티만 수정하거나, 참조 타입 내부 속성이 변했는데 @Published가 제대로 동작하지 않는 경우가 있다. 이때 모든 상태들이 State Property로 올바르게 관리되고 있는지에 대한 확인이 필요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. View의 Identity 충돌&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SwiftUI는 View를 값 타입으로 처리하며, 내부적으로 View의 id나 데이터가 바뀌었는지 확인하여 이를 다시 그릴지 말지 결정한다. id가 없거나, 동일한 id가 재사용 될 때, SwiftUI가 View를 갱신하지 않고 그대로 재사용하는 경우가 있는데 List나 ForEach에서 고유한 id를 지정하지 않아 누락이 일어나는 경우가 많다. 이는 보통 id: \.self 나 고유한 식별자를 명시해주면 해결된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 비동기 작업과 View State 불일치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 작업 중에 View가 사라졌다가 다시 나타나는 경우, 이전 작업 결과가 반영되지 않거나, 작업 완료 시점에 View가 이미 메모리에서 사라진 경우 UI 업데이트가 무시되는 현상이 생긴다. @State 변경 시 View의 유효성 확인이 필요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. View 재사용 &amp;amp; 메모리 최적화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SwiftUI는 성능 최적화를 위해 필요 없는 View는 메모리에서 해제하거나 다시 사용한다. 이때 onAppear, onDisappear modifier가 예상과 다르게 호출되는 경우가 있어 개발자가 직접 제어하기 어렵다. 이렇게 되면 View가 재생성되거나, 재사용되면서 예상하지 못하게 onAppear, onDisappear에 넣었던 로직이 여러번 호출되게 된다. 때문에 항상 상태를 추적해서 중복 호출을 방지해 주어야 한다.&lt;/p&gt;</description>
      <category>  iOS/  궁금증</category>
      <category>Lifecycle</category>
      <category>SwiftUI</category>
      <category>궁금증</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/55</guid>
      <comments>https://mintraum.tistory.com/55#entry55comment</comments>
      <pubDate>Mon, 11 Aug 2025 11:02:18 +0900</pubDate>
    </item>
    <item>
      <title>[SwiftUI] InteractiveUI (Gesture, Animation, Transition)</title>
      <link>https://mintraum.tistory.com/54</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하십니까, 민트입니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스터디 중 SwiftUI의 InteractiveUI들에 대해 정리해 보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;티스토리-블로그-006 (1).png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m768H/btsNt15kZa2/WtPrOkJJb2cct38MjBKiV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m768H/btsNt15kZa2/WtPrOkJJb2cct38MjBKiV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m768H/btsNt15kZa2/WtPrOkJJb2cct38MjBKiV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm768H%2FbtsNt15kZa2%2FWtPrOkJJb2cct38MjBKiV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;500&quot; data-filename=&quot;티스토리-블로그-006 (1).png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Gesture&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@GestureState&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제스처가 진행 중일 때만 &lt;b&gt;임시적으로 상태를 유지&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;제스처가 끝나면 자동으로 &lt;b&gt;초기값으로 되돌아가는 상태 래퍼 (일시적)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;상태에 따라 뷰 속성을 실시간으로 변화시킴&lt;/li&gt;
&lt;li&gt;@State 처럼 지속적인 변화가 아닌, 임시 상태 용도&lt;/li&gt;
&lt;li&gt;제스쳐 추적하는 동안 콜백을 통해 상태를 직접 갱신
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;updating: 제스쳐가 변경되는 순간, 일시적인 UI 상태&lt;/li&gt;
&lt;li&gt;onChanged: 제스쳐를 사용하는 동안의 UI 상태, 종료 후 재설정 X&lt;/li&gt;
&lt;li&gt;onEnded: 종료 후 상태 재설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Gesture Composition Type&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Simultaneous
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 제스쳐를 합쳐서 한 번에 인식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sequenced
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 제스쳐 상태를 순서대로 적용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Exclusive
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 제스쳐 중 처음 시도한 하나의 제스쳐만 활성화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Simulator Screen Recording - iPhone 16 Pro - 2025-04-21 at 20.30.36.gif&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1S5W4/btsNttA9Diy/uxF7O0YYW9Kx8AhazxL08k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1S5W4/btsNttA9Diy/uxF7O0YYW9Kx8AhazxL08k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1S5W4/btsNttA9Diy/uxF7O0YYW9Kx8AhazxL08k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/1S5W4/btsNttA9Diy/uxF7O0YYW9Kx8AhazxL08k/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;205&quot; height=&quot;446&quot; data-filename=&quot;Simulator Screen Recording - iPhone 16 Pro - 2025-04-21 at 20.30.36.gif&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import SwiftUI

struct GestureView: View {
    @GestureState private var isPressed = false
    @State private var offset = CGSize.zero

    var body: some View {
        Circle()
            .fill(isPressed ? Color.blue : Color.red)
            .frame(width: 100, height: 100)
            .offset(offset)
            .gesture(
                LongPressGesture(minimumDuration: 0.5)
                    .sequenced(before: DragGesture())
                    .updating($isPressed) { value, state, _ in
                        if case .first(true) = value {
                            state = true
                        }
                    }
                    .onEnded { value in
                        if case .second(true, let drag?) = value {
                            offset = drag.translation
                        }
                    }
            )
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Animation&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple이 SwiftUI를 만들게 된 중요한 계기 중 하나&lt;/li&gt;
&lt;li&gt;State의 변화를 기반으로 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;명시적 vs 암묵적&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명시적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자가 직접 withAnimation 블록 안에서 상태 변경 트리거&lt;/li&gt;
&lt;li&gt;제어 수준 높음 (정확한 위치에 애니메이션 적용)&lt;/li&gt;
&lt;li&gt;선언적이나 의도 명확&lt;/li&gt;
&lt;li&gt;사용자가 제어해야 할 상호작용, 전환에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;암시적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;뷰에 .animation modifier를 지정하면 특정 상태값의 변화에 따라 자동으로 적용&lt;/li&gt;
&lt;li&gt;제어 수준 낮음 (전체 뷰에 애니메이션 적용됨)&lt;/li&gt;
&lt;li&gt;선언적으로 간단하나 예측이 어려울 수도 있음.&lt;/li&gt;
&lt;li&gt;단순한 UI 상태 변화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;matchedGeometryEffect&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 개의 뷰가 서로 다른 위치나 크기라도 시스템이 이들을 동일한 뷰로 인식하게 해주는 기능&lt;/li&gt;
&lt;li&gt;뷰 간 전환 시 위치, 크기, 모양 등을 자연스럽게 애니메이션으로 연결할 수 있게 만들어줌.&lt;/li&gt;
&lt;li&gt;작은 뷰와 큰 뷰는 전혀 다른 뷰이지만, 하나의 뷰처럼 애니메이션이 이어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@Namespace&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Namespace private var animation
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;matchedGeometryEffect가 어떤 뷰들이 같은 뷰인지 인식할 수 있도록 공감(namespace)를 공유하는 변수&lt;/li&gt;
&lt;li&gt;뷰 간의 연결 고리&lt;/li&gt;
&lt;li&gt;id: 연결을 위한 키값&lt;/li&gt;
&lt;li&gt;animation: 같은 공간 안에 있다는 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로젝트 별 선택 기준&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;animation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순 상태 변화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;withAnimation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 액션에 따라 명확히 제어해야 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;matchedGeometryEffect + @Namespace
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;View 간에 자연스러운 전환이 필요할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZpS9D/btsNs6ZhdTj/z6kkb6TU6x2PvYnjod8UqK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZpS9D/btsNs6ZhdTj/z6kkb6TU6x2PvYnjod8UqK/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;808&quot; data-filename=&quot;animation.gif&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZpS9D/btsNs6ZhdTj/z6kkb6TU6x2PvYnjod8UqK/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZpS9D%2FbtsNs6ZhdTj%2Fz6kkb6TU6x2PvYnjod8UqK%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;406&quot; height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nVWie/btsNu0xeKAs/UMjj5DkE9GLspUPuvEtI6K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nVWie/btsNu0xeKAs/UMjj5DkE9GLspUPuvEtI6K/img.gif&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;808&quot; data-is-animation=&quot;true&quot; data-filename=&quot;ImplicitAnimation.gif&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nVWie/btsNu0xeKAs/UMjj5DkE9GLspUPuvEtI6K/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnVWie%2FbtsNu0xeKAs%2FUMjj5DkE9GLspUPuvEtI6K%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;406&quot; height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;명시적, 암시적&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;//명시적
struct AnimationView: View {
    @Namespace private var animation
    @State private var isExpanded = false

    var body: some View {
        VStack {
            if isExpanded {
                RoundedRectangle(cornerRadius: 25)
                    .fill(Color.green)
                    .matchedGeometryEffect(id: &quot;card&quot;, in: animation)
                    .frame(width: 300, height: 300)
                    .onTapGesture {
                        withAnimation(.spring()) {
                            isExpanded.toggle()
                        }
                    }
            } else {
                RoundedRectangle(cornerRadius: 25)
                    .fill(Color.green)
                    .matchedGeometryEffect(id: &quot;card&quot;, in: animation)
                    .frame(width: 100, height: 100)
                    .onTapGesture {
                        withAnimation(.spring()) {
                            isExpanded.toggle()
                        }
                    }
            }
        }
    }
}

//암묵적
struct ImplicitAnimationView: View {
    @State private var isOn = false

    var body: some View {
        Circle()
            .fill(isOn ? Color.green : Color.red)
            .frame(width: isOn ? 200 : 100, height: isOn ? 200 : 100)
            .onTapGesture {
                isOn.toggle()
            }
            .animation(.easeInOut, value: isOn)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Transition&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;insertion과 removal에 서로 다른 효과 적용 가능&lt;/li&gt;
&lt;li&gt;각각 별개의 identity를 적용 시키기에 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;AnyTransition.modifier&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커스텀 전환 효과를 구현할 때 사용하는 고급 도구&lt;/li&gt;
&lt;li&gt;기본 효과 외에 직접 만든 시각 효과를 사용하고 싶을 때 유용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.opacity, .slide, .scale&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;뷰가 삽입되거나 제거될 때, 원하는 방식으로 뷰를 수정해서 전환 애니메이션을 만들 수 있게 해주는 transition 생성자&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;AnyTransition.modifier(
    active: ViewModifier,   // 전환 중에 적용될 상태
    identity: ViewModifier  // 평소 상태
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/erzp32/btsNuwcjnJm/a9zJkTgIFSPX3buDvqzEU0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/erzp32/btsNuwcjnJm/a9zJkTgIFSPX3buDvqzEU0/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;808&quot; data-filename=&quot;transitionTwo.gif&quot; width=&quot;336&quot; height=&quot;669&quot; style=&quot;width: 51.602%; margin-right: 10px;&quot; data-widthpercent=&quot;52.21&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/erzp32/btsNuwcjnJm/a9zJkTgIFSPX3buDvqzEU0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ferzp32%2FbtsNuwcjnJm%2Fa9zJkTgIFSPX3buDvqzEU0%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;406&quot; height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dc5CO3/btsNuiL9QJb/FkaosBMCZ5IsAlpEVqYqO0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dc5CO3/btsNuiL9QJb/FkaosBMCZ5IsAlpEVqYqO0/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot; data-filename=&quot;blurAnimation.gif&quot; width=&quot;286&quot; height=&quot;622&quot; style=&quot;width: 47.2352%;&quot; data-widthpercent=&quot;47.79&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dc5CO3/btsNuiL9QJb/FkaosBMCZ5IsAlpEVqYqO0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdc5CO3%2FbtsNuiL9QJb%2FFkaosBMCZ5IsAlpEVqYqO0%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1206&quot; height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;//Modifier 정의
struct BlurModifier: ViewModifier {
    let amount: CGFloat

    func body(content: Content) -&amp;gt; some View {
        content
            .blur(radius: amount)
            .opacity(1 - Double(amount / 10))
    }
}

//Transition 정의
extension AnyTransition {
    static var blurFade: AnyTransition {
        .modifier(
            active: BlurModifier(amount: 10),
            identity: BlurModifier(amount: 0)
        )
    }
}

struct TransitionView: View {
    @State private var show = false

    var body: some View {
        VStack(spacing: 20) {
            Button(&quot;Toggle Box&quot;) {
                withAnimation {
                    show.toggle()
                }
            }

            if show {
                RoundedRectangle(cornerRadius: 20)
                    .fill(Color.purple)
                    .frame(width: 200, height: 200)
                    .transition(.blurFade)
            }
        }
    }
}
&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;a href=&quot;https://github.com/prography/10th-iOS-Study/tree/main/%EC%8B%AC%ED%99%94/3.%20%EA%B3%A0%EA%B8%89%20%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98%20%EC%A0%84%ED%99%98%20%EB%B0%8F%20%EC%A0%9C%EC%8A%A4%EC%B3%90%20%ED%99%9C%EC%9A%A9&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/prography/10th-iOS-Study/tree/main/%EC%8B%AC%ED%99%94/3.%20%EA%B3%A0%EA%B8%89%20%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98%20%EC%A0%84%ED%99%98%20%EB%B0%8F%20%EC%A0%9C%EC%8A%A4%EC%B3%90%20%ED%99%9C%EC%9A%A9&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745235738057&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;10th-iOS-Study/심화/3. 고급 애니메이션 전환 및 제스쳐 활용 at main &amp;middot; prography/10th-iOS-Study&quot; data-og-description=&quot;10기 iOS 파트원들의 스터디 공간입니다. Contribute to prography/10th-iOS-Study development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/prography/10th-iOS-Study/tree/main/%EC%8B%AC%ED%99%94/3.%20%EA%B3%A0%EA%B8%89%20%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98%20%EC%A0%84%ED%99%98%20%EB%B0%8F%20%EC%A0%9C%EC%8A%A4%EC%B3%90%20%ED%99%9C%EC%9A%A9&quot; data-og-url=&quot;https://github.com/prography/10th-iOS-Study/tree/main/%EC%8B%AC%ED%99%94/3.%20%EA%B3%A0%EA%B8%89%20%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98%20%EC%A0%84%ED%99%98%20%EB%B0%8F%20%EC%A0%9C%EC%8A%A4%EC%B3%90%20%ED%99%9C%EC%9A%A9&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cjGQEt/hyYIjy3RBY/3jMHBUWk7uQX5fnN08YKJ0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/BkaG0/hyYFxSitqA/4YX3uZ1EhFxTxXaypKAwHk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/prography/10th-iOS-Study/tree/main/%EC%8B%AC%ED%99%94/3.%20%EA%B3%A0%EA%B8%89%20%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98%20%EC%A0%84%ED%99%98%20%EB%B0%8F%20%EC%A0%9C%EC%8A%A4%EC%B3%90%20%ED%99%9C%EC%9A%A9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/prography/10th-iOS-Study/tree/main/%EC%8B%AC%ED%99%94/3.%20%EA%B3%A0%EA%B8%89%20%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98%20%EC%A0%84%ED%99%98%20%EB%B0%8F%20%EC%A0%9C%EC%8A%A4%EC%B3%90%20%ED%99%9C%EC%9A%A9&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cjGQEt/hyYIjy3RBY/3jMHBUWk7uQX5fnN08YKJ0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/BkaG0/hyYFxSitqA/4YX3uZ1EhFxTxXaypKAwHk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;10th-iOS-Study/심화/3. 고급 애니메이션 전환 및 제스쳐 활용 at main &amp;middot; prography/10th-iOS-Study&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;10기 iOS 파트원들의 스터디 공간입니다. Contribute to prography/10th-iOS-Study development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>  iOS/  Docs</category>
      <category>Animation</category>
      <category>ios</category>
      <category>SwiftUI</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/54</guid>
      <comments>https://mintraum.tistory.com/54#entry54comment</comments>
      <pubDate>Mon, 21 Apr 2025 20:56:53 +0900</pubDate>
    </item>
    <item>
      <title>[오브젝트] 4장. 설계 품질과 트레이드 오프</title>
      <link>https://mintraum.tistory.com/53</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  기억에 남는 문장&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체 지향 설계란 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체지향 설계의 핵심은 책임&lt;/li&gt;
&lt;li&gt;책임을 할당하는 작업이 응집도와 결합도 같은 설계 품질과 깊이 연관되어 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;설계는 변경을 위해 존재하고 변경에는 어떤 식으로든 비용이 발생한다. 훌륭한 설계란 합리적인 비용 안에서 변경을 수용할 수 있는 구조를 만드는 것이다.&lt;/li&gt;
&lt;li&gt;결합도와 응집도를 합리적인 수준으로 유지할 수 있는 중요한 원칙이 있다. 객체의 상태가 아니라 객체의 행동에 초점을 맞추는 것이다.&lt;/li&gt;
&lt;li&gt;객체지향 설계에서는 두 가지 방법을 이용해 시스템을 객체로 분할할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태(데이터)를 분할의 중심축으로 삼는 방법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체는 자신이 포함하고 있는 데이터를 조작하는데 필요한 오퍼레이션 정의&lt;/li&gt;
&lt;li&gt;객체는 독립된 데이터 덩어리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;책임을 분할의 중심축으로 삼는 방법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체는 다른 객체가 요청할 수 있는 오퍼레이션을 위해 필요한 상태를 보관&lt;/li&gt;
&lt;li&gt;객체는 협력하는 공동체의 일원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;훌륭한 객체지향 설계는 데이터가 아니라 책임에 초점을 맞춰야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는 변경과 관련이 있다.&lt;/li&gt;
&lt;li&gt;객체의 상태는 구현에 속한다. 구현은 불안정하기 때문에 변하기 쉽다. 상태를 객체 분할의 중심축으로 삼으면 구현에 관한 세부사항이 객체의 인터페이스에 스며들게 되어 캡슐화의 원칙이 무너진다. 결과적으로 상태 변경은 인터페이스의 변경을 초래하며 이 인터페이스에 의존하는 모든 객체에게 변경의 영향이 퍼지게 된다. 따라서 데이터에 초점을 맞추는 설계는 변경에 취약할 수 밖에 없다.&lt;/li&gt;
&lt;li&gt;그에 비해 객체의 책임은 인터페이스에 속한다. 객체는 책임을 드러내는 안정적인 인터페이스 뒤로 책임을 수행하는 데 필요한 상태를 캡슐화함으로써 구현 변경에 대한 파장이 외부로 퍼져나가는 것을 방지한다. 따라서 책임에 초점을 맞추면 상대적으로 변경에 안정적인 설계를 얻을 수 있게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;설계 트레이드 오프&lt;/b&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;/li&gt;
&lt;li&gt;내부 구현의 변경이 외부로 퍼져나가는 파급 효과는 캡슐화가 부족하다는 명백한 증거다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  3줄 소감&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터와 기능을 너무 일찍 분리해버려서 문제였다.&lt;/li&gt;
&lt;li&gt;코드를 어떻게 개선해 볼 수 있을지에 대해서 더 가닥이 잡히는 것 같다.&lt;/li&gt;
&lt;li&gt;상태 중심은 함수형 프로그래밍에서는 완전 필수였는데&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  Book Log</category>
      <category>오브젝트</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/53</guid>
      <comments>https://mintraum.tistory.com/53#entry53comment</comments>
      <pubDate>Sun, 13 Apr 2025 15:08:01 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] 원격 모듈 업그레이드 오류 (Could not compute dependency graph: unable to load transferred PIF: The workspace contains multiple references with the same GUID 'PACKAGE:1YM1U4L3KHXI6YAJKZPL0AW5PXP8KNX9R::MAINGROUP')</title>
      <link>https://mintraum.tistory.com/52</link>
      <description>&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;티스토리-블로그-002.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBS9er/btsNgB62Dzd/RdsmZljavatMFvmIXQO4ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBS9er/btsNgB62Dzd/RdsmZljavatMFvmIXQO4ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBS9er/btsNgB62Dzd/RdsmZljavatMFvmIXQO4ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBS9er%2FbtsNgB62Dzd%2FRdsmZljavatMFvmIXQO4ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;500&quot; data-filename=&quot;티스토리-블로그-002.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&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;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Showing Recent Issues&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;Failed to resolve dependencies Dependencies could not be resolved because no versions of 'keychainmodule' match the requirement 1.0.2..&amp;lt;2.0.0 and 'networkkit' depends on 'keychainmodule' 1.0.2..&amp;lt;2.0.0.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Resolving Package Graph Failed&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;4/10/25, 7:00 PM&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;1.8 seconds&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키체인 모듈을 적용하고 있던 프로젝트에서 업그레이드한 1.0.2를 읽지 못하는 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clean build를 해도 여전히 다음과 같은 오류가 떴습니다.&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Showing Recent Issues&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Prepare clean&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ComputePackagePrebuildTargetDependencyGraph&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;error: Could not compute dependency graph: unable to load transferred PIF: The workspace contains multiple references with the same GUID 'PACKAGE:1YM1U4L3KHXI6YAJKZPL0AW5PXP8KNX9R::MAINGROUP'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Could not compute dependency graph: unable to load transferred PIF: The workspace contains multiple references with the same GUID 'PACKAGE:1YM1U4L3KHXI6YAJKZPL0AW5PXP8KNX9R::MAINGROUP'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Clean failed&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;4/10/25, 6:38 PM&lt;span&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;0.2 seconds&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경 사항이 키체인 모듈 업그레이드 밖에 없었기에 확인해보니, 로컬에는 1.0.2 가 올라갔는데, 원격에서는 push가 되지 않아 생긴 문제였습니다. Xcode에서 push 했는데.. 읍읍&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NzYDt/btsNgpy46AR/ydAgXLZov2Xfor61AH6CI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NzYDt/btsNgpy46AR/ydAgXLZov2Xfor61AH6CI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NzYDt/btsNgpy46AR/ydAgXLZov2Xfor61AH6CI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNzYDt%2FbtsNgpy46AR%2FydAgXLZov2Xfor61AH6CI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;354&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 확인 후 변경해 주었더니, 제대로 동작했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JbXJr/btsNfIru0uZ/xg86EiRVXN8gJzZ0NiGkD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JbXJr/btsNfIru0uZ/xg86EiRVXN8gJzZ0NiGkD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JbXJr/btsNfIru0uZ/xg86EiRVXN8gJzZ0NiGkD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJbXJr%2FbtsNfIru0uZ%2Fxg86EiRVXN8gJzZ0NiGkD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;438&quot; height=&quot;51&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>  iOS/  트러블 슈팅</category>
      <category>ios</category>
      <category>swift</category>
      <category>트러블슈팅</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/52</guid>
      <comments>https://mintraum.tistory.com/52#entry52comment</comments>
      <pubDate>Fri, 11 Apr 2025 11:37:22 +0900</pubDate>
    </item>
    <item>
      <title>[오브젝트] 3장. 역할, 책임, 협력</title>
      <link>https://mintraum.tistory.com/51</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;span&gt; &lt;/span&gt; 기억에 남는 문장들&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체지향의 본질은 협력하는 객체들의 공동체를 창조하는 것이다.&lt;/li&gt;
&lt;li&gt;객체지향 설계의 핵심은 협력을 구성하기 위해 적절한 객체를 찾고 적절한 책임을 할당하는 과정에서 드러난다.&lt;/li&gt;
&lt;li&gt;객체지향에서 가장 중요한 것은 역할, 책임, 협력이다. 역할, 책임, 협력이 제자리를 찾지 못한 상태라면 응집도 높은 클래스와 중복 없는 상속 계층을 구현한다고 하더라도 여러분의 애플리케이션이 침몰하는 것을 구원하지 못할 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;협력&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체들이 애플리케이션의 기능을 구현하기 위해 수행하는 상호작용&lt;/li&gt;
&lt;li&gt;객체는 고립된 존재가 아니라 시스템의 기능이라는 더 큰 목표를 달성하기 위해 다른 객체와 협력하는 사회적인 존재다. 협력은 객체지향의 세계에서 기능을 구현할 수 있는 유일한 방법이다.&lt;/li&gt;
&lt;li&gt;어떤 객체가 다른 객체에게 무언가를 요청하는 것이다. 한 객체는 어떤 것이 필요할 때 다른 객체에게 전적으로 위임하거나 서로 협력한다. 즉, 두 객체가 상호작용을 통해 더 큰 책임을 수행하는 것이다.&lt;/li&gt;
&lt;li&gt;여기서 객체가 메시지를 처리할 방법을 스스로 선택한다는 점이 중요하다. 외부의 객체는 오직 메시지만 전송할 수 있을 뿐이며 메시지를 어떻게 처리할지는 메시지를 수신한 객체가 직접 결정한다. 이것은 객체가 자신의 일을 스스로 처리할 수 있는 자율적인 존재라는 것을 의미한다.&lt;/li&gt;
&lt;li&gt;객체를 자율적으로 만드는 가장 기본적인 방법은 내부 구현을 캡슐화하는 것이다. 캡슐화를 통해 변경에 대한 파급효과를 제한할 수 있기 때문에 자율적인 객체는 변경하기도 쉬워진다.&lt;/li&gt;
&lt;li&gt;자율적인 객체는 자신에게 할당된 책임을 수행하던 중에 필요한 정보를 알지 못하거나 외부의 도움이 필요한 경우 적절한 객체에게 메시지를 전송해서 협력을 요청한다.&lt;/li&gt;
&lt;li&gt;메시지를 수신한 객체 역시 메시지를 처리하던 중에 직접 처리할 수 없는 정보나 행동이 필요한 경우 또 다른 객체에게 도움을 요청한다.&lt;/li&gt;
&lt;li&gt;이처럼 객체들 사이의 협력을 구성하는 일련의 요청과 응답의 흐름을 통해 애플리케이션의 기능이 구현된다.&lt;/li&gt;
&lt;li&gt;애플리케이션 안에 어떤 객체가 필요하다면 그 이유는 단 하나여야 한다. 그 객체가 어떤 협력에 참여하고 있기 때문이다. 그리고 객체가 협력에 참여할 수 있는 이유는 협력에 필요한 적절한 행동을 보유하고 있기 때문이다.&lt;/li&gt;
&lt;li&gt;결론적으로 객체의 행동을 결정하는 것은 객체가 참여하고 있는 협력이다. 협력이 바뀌면 객체가 제공해야 하는 행동 역시 바뀌어야 한다. 협력은 객체가 필요한 이유와 객체가 수행하는 행동의 동기를 제공한다.&lt;/li&gt;
&lt;li&gt;객체의 행동을 결정하는 것이 협력이라면, 객체의 상태를 결정하는 것은 행동이다.&lt;/li&gt;
&lt;li&gt;객체의 상태는 그 객체가 행동을 수행하는 데 필요한 정보가 무엇인지로 결정된다.&lt;/li&gt;
&lt;li&gt;따라서 협력은 객체를 설계하는 데 필요한 일종의 문맥 context 을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;책임&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체가 협력에 참여하기 위해 수행하는 로직&lt;/li&gt;
&lt;li&gt;책임이란 객체에 의해 정의되는 응집도 있는 행위의 집합으로 객체가 유지해야 하는 정보와 수행할 수 있는 행동에 대해 개략적으로 서술한 문장이다.&lt;/li&gt;
&lt;li&gt;객체가 무엇을 알고 있는가&lt;/li&gt;
&lt;li&gt;객체가 무엇을 할 수 있는가&lt;/li&gt;
&lt;li&gt;하는 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체를 생성하거나 계산을 수행하는 등의 스스로 하는 것&lt;/li&gt;
&lt;li&gt;다른 객체의 행동을 시작시키는 것&lt;/li&gt;
&lt;li&gt;다른 객체의 활동을 제어하고 조절하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;아는 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사적인 정보에 관해 아는 것&lt;/li&gt;
&lt;li&gt;관련된 객체에 관해 아는 것&lt;/li&gt;
&lt;li&gt;자신이 유도하거나 계산할 수 있는 것에 관해 아는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;일반적으로 책임과 메시지의 크기는 다르다. 책임은 객체가 수행할 수 있는 행동을 종합적이고 간략하게 서술하기 때문에 메시지보다 추상적이고 개념적으로도 더 크다.&lt;/li&gt;
&lt;li&gt;객체는 자신이 맡은 책임을 수행하는 데 필요한 정보를 알고 있을 책임이 있다. 또한 객체는 자신이 할 수 없는 작업을 도와줄 객체를 알고 있을 책임이 있다.&lt;/li&gt;
&lt;li&gt;어떤 책임을 수행하기 위해서는 그 책임을 수행하는 데 필요한 정보도 함께 알아야 할 책임이 있는 것이다.&lt;/li&gt;
&lt;li&gt;적절한 협력이 적절한 책임을 제공하고, 적절한 책임을 적절한 객체에게 할당해야만 단순하고 유연한 설계를 창조할 수 있다.&lt;/li&gt;
&lt;li&gt;자율적인 객체를 만드는 가장 기본적인 방법은 책임을 수행하는 데 필요한 정보를 가장 잘 알고 있는 전문가에게 그 책임을 할당하는 것이다. 이를 책임 할당을 위한 &lt;b&gt;정보 전문가 패턴&lt;/b&gt;이라고 부른다.&lt;/li&gt;
&lt;li&gt;어떤 책임을 선택하느냐가 전체적인 설계의 방향과 흐름을 결정한다. 이처럼 책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 협력을 설계하는 방법을 책임 주도 설계라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메시지가 객체를 결정한다&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체에게 책임을 할당하는 데 필요한 메시지를 먼저 식별하고 메시지를 처리할 객체를 나중에 선택했다는 것이 중요하다.&lt;/li&gt;
&lt;li&gt;객체가 최소한의 인터페이스를 가질 수 있게 된다.&lt;/li&gt;
&lt;li&gt;객체는 충분히 추상적인 인터페이스를 가질 수 있게 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체의 인터페이스는 무엇을 하는지는 표현해야 하지만 어떻게 수행하는지를 노출해서는 안 된다.&lt;/li&gt;
&lt;li&gt;메시지는 외부의 객체가 요청하는 무언가를 의미하기 때문에 메시지를 먼저 식별하면 무엇을 수행할지에 초점을 맞추는 인터페이스를 얻을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;행동이 상태를 결정한다&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체를 객체답게 만드는 것은 객체의 상태가 아니라 객체가 다른 객체에게 제공하는 행동이다.&lt;/li&gt;
&lt;li&gt;객체가 협력에 적합한지를 결정하는 것은 그 객체의 상태가 아니라 행동이다.&lt;/li&gt;
&lt;li&gt;객체지향 패러다임에 갓 입문한 사람들이 가장 쉽게 빠지는 실수는 객체의 행동이 아니라 상태에 초점을 맞추는 것이다. 이런 방식은 객체의 내부 구현이 객체의 퍼블릭 인터페이스에 노출되도록 만들기 때문에 캡슐화를 저해한다.&lt;/li&gt;
&lt;li&gt;객체의 내부 구현을 변경하면 퍼블릭 인터페이스도 함께 변경되고, 결국 객체에 의존하는 클라이언트로 변경의 영향이 전파된다. 레베카 워프스브록은 이와 같이 객체의 내부 구현에 초점을 맞춘 설계 방법을 데이터 주도 설계 Data-Driven-Design 이라고 부르기도 했다.&lt;/li&gt;
&lt;li&gt;캡슐화를 위반하지 않도록 구현에 대한 결정을 뒤로 미루면서 객체의 행위를 고려하기 위해서는 항상 협력이라는 문맥 안에서 객체를 생각해야 한다.&lt;/li&gt;
&lt;li&gt;중요한 것은 객체의 상태가 아니라 행동이다.&lt;/li&gt;
&lt;li&gt;협력이 객체의 행동을 결정하고, 행동이 상태를 결정한다.&lt;/li&gt;
&lt;li&gt;그리고 그 행동이 바로 객체의 책임이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CRC 카드&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체들이 협력 안에서 수행하는 책임들의 모임, 집합&lt;/li&gt;
&lt;li&gt;역할이 중요한 이유는 역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있기 때문이다.&lt;/li&gt;
&lt;li&gt;역할은 다른 것으로 교체할 수 있는 책임의 집합이다.&lt;/li&gt;
&lt;li&gt;역할을 구현하는 가장 일반적인 방법은 추상 클래스와 인터페이스를 사용하는 것이다.&lt;/li&gt;
&lt;li&gt;협력의 관점에서 추상 클래스와 인터페이스는 구체 클래스들이 따라야 하는 책임의 집합을 서술한 것이다. 추상 클래스는 책임의 일부를 구현해 놓은 것이고 인터페이스는 일체의 구현 없이 책임의 집합만들 나열해 놓았다는 차이가 있지만 협력의 관점에서는 둘 모두 역할을 정의할 수 있는 구현 방법이라는 공통점을 공유한다.&lt;/li&gt;
&lt;li&gt;레베카 워프스브록의 말을 인용하자면 협력에 참여하는 후보가 여러 종류의 객체에 의해 수행될 필요가 있다면 그 후보는 역할이 되지만 단지 한 종류의 객체만이 협력에 참여할 필요가 있다면 후보는 객체가 된다.&lt;/li&gt;
&lt;li&gt;다시 말해 협력에 적합한 책임을 수행하는 대상이 한 종류라면 간단하게 객체로 간주한다. 만약 여러 종류의 객체들이 참여할 수 있다면 역할이라고 부르면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-04 오전 12.08.37.png&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LnbsO/btsM8Pwv3r0/KOUqY67xHwKZ8VpGItHQXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LnbsO/btsM8Pwv3r0/KOUqY67xHwKZ8VpGItHQXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LnbsO/btsM8Pwv3r0/KOUqY67xHwKZ8VpGItHQXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLnbsO%2FbtsM8Pwv3r0%2FKOUqY67xHwKZ8VpGItHQXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;144&quot; data-filename=&quot;스크린샷 2025-04-04 오전 12.08.37.png&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인 모델은 불완전한 사람들이 세상을 바라보는 모델에 기반하기 때문에 그 역시 동일한 불완전성을 가질 수밖에 없다.&lt;/li&gt;
&lt;li&gt;추상화의 장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세부 사항에 억눌리지 않고도 상위 수준의 정책을 쉽고 간단하게 표현할 수 있다.&lt;/li&gt;
&lt;li&gt;설계를 유연하게 만들 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;❓&lt;/span&gt;&lt;span data-token-index=&quot;1&quot;&gt;&amp;nbsp;질문&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;span&gt; &lt;/span&gt; 3줄 소감&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업을 어떻게 시킬지에 대한 체계화 같다.&lt;/li&gt;
&lt;li&gt;인간 관계 같다.&lt;/li&gt;
&lt;li&gt;이번 장에는 유난히 비슷한 말의 반복이 많았다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  Book Log</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/51</guid>
      <comments>https://mintraum.tistory.com/51#entry51comment</comments>
      <pubDate>Fri, 4 Apr 2025 00:17:31 +0900</pubDate>
    </item>
    <item>
      <title>[iOS/WWDC21] Explore structured concurrency in swift 1</title>
      <link>https://mintraum.tistory.com/50</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;안녕하십니까, 민트입니다.  &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;WWDC21 Explore structured concurrency in swift를 보고 내용을 정리해 보았습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;양이 많아서 Part1과 2로 분리합니다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;티스토리-블로그-006.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boJEFx/btsM5TZDHVf/CWnZZluuyVMFUgDuXDWKSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boJEFx/btsM5TZDHVf/CWnZZluuyVMFUgDuXDWKSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boJEFx/btsM5TZDHVf/CWnZZluuyVMFUgDuXDWKSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboJEFx%2FbtsM5TZDHVf%2FCWnZZluuyVMFUgDuXDWKSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;500&quot; data-filename=&quot;티스토리-블로그-006.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;구조적 프로그래밍&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;초기에 컴퓨터를 사용할 때는 명령어 시퀸스로 작성되어 제어 흐름을 관리하기 어려웠습니다. 하지만 요즘은 언어에서 구조화된 프로그래밍을 사용하여 제어 흐름을 균일하게 만들기 때문에 이러한 일은 거의 없어졌습니다.&lt;/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;예를 들어 if - else문은 구조화된 제어 흐름을 사용합니다. 중첩된 코드 블록이 위에서 아래로 이동하는 동안에만 조건부로 실행됩니다. 이처럼 Swift에서는 해당 블록이 정적 범위인 scoping을 따릅니다. 이에 해당 블록 내에서 정의된 변수들의 생명 주기는 블록을 벗어날 때 끝납니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이처럼 정적 범위를 갖춘 구조적 프로그래밍은 제어 흐름과 변수의 수명을 이해하기 쉽게 만들어 줍니다. 이를 통해 자연스럽게 순서가 생기고 중첩되어 코드 전체를 위에서 아래까지 수원하게 읽을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 비동기적이고 동시성 있는 코드를 작성 할 때, 이러한 구조적 프로그래밍이 제대로 사용되고 있지 않았습니다. 이전까지 사용하던 콜백 패턴의 경우 오류를 처리할 때 바텀 - 업 형식을 이용합니다. 이는 위에서 아래로 읽고자 하는 구조적 제어 흐름에서 벗어납니다. 때문에 Swift Concurrency, 즉 async/await에서는 탑-다운 형식으로 오류를 처리할 수 있도록 구현되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;동시성, Task&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;async/await을 사용할 때 처리해야하는 양이 많아진다면, 순서대로 하나씩 처리하는 방식은 더 복잡하고 성능에 문제가 생길 수 있습니다. 때문에 여러 작업이 병렬로 실행될 수 있도록 동시성을 추가해야 합니다. 이를 위해서는 추가적인 Task의 생성이 필요합니다.&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;Task는 비동기 코드를 실행할 수 있는 새로운 실행 context를 제공합니다. 각 Task는 다른 실행 context와 동시에 실행됩니다. 특히 Task는 안정하고 효율적으로 병렬 실행이 가능할 때 자동으로 실행됩니다.&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;4가지 작업이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Structured Concurrency (async - let)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 let은 =를 기준으로 왼쪽에는 변수명, 오른쪽에는 초기화 표현식을 작성합니다. let에 도달시, 초기화 프로그램을 평가하여 값을 생성하는 방식입니다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;let result = URLSession.shared.data()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL을 이용하기 때문에 시간이 많이 걸릴 수 있는데, 이는 데이터가 다운로드 된 후 다음으로 넘어가기 전, 해당 값을 result에 바인딩하기 때문입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blnXYV/btsM4Rao2Fi/oR9qWiRfoF5IL2rAovSB91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blnXYV/btsM4Rao2Fi/oR9qWiRfoF5IL2rAovSB91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blnXYV/btsM4Rao2Fi/oR9qWiRfoF5IL2rAovSB91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblnXYV%2FbtsM4Rao2Fi%2FoR9qWiRfoF5IL2rAovSB91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1138&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드에 오랜 시간이 걸릴 수 있으니 데이터 다운로드를 시작하고 데이터가 실제로 필요할 때까지 다른 작업을 계속하도록 설정하는 것으로 이 문제를 해결할 수 있습니다. 이는 let 앞에 async 키워드를 붙이면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;async let result = URLSession.shared.data()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 실제로 result를 만나기 전에 자식 Task를 생성합니다. 자식 Task는 본인을 생성한 Task의 하위 Task입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image1.png&quot; data-origin-width=&quot;2482&quot; data-origin-height=&quot;1648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KIODZ/btsM4eRsBNd/Phmglb9DZXU0mxtU73Z3KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KIODZ/btsM4eRsBNd/Phmglb9DZXU0mxtU73Z3KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KIODZ/btsM4eRsBNd/Phmglb9DZXU0mxtU73Z3KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKIODZ%2FbtsM4eRsBNd%2FPhmglb9DZXU0mxtU73Z3KK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2482&quot; height=&quot;1648&quot; data-filename=&quot;image1.png&quot; data-origin-width=&quot;2482&quot; data-origin-height=&quot;1648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 위의 앞선 그림과는 달리 화살표가 두개 나옵니다. 하나는 데이터를 다운로드하는 자식 Task를, 다른 하나는 변수 result를 자리 표시자 값에 바인딩하는 부모 Task를 위한 화살표입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 result의 실제 값이 필요한 표현식에 도달하면, 부모는 자식이 작업을 완료할 때까지 기다려야 합니다. 이후 자식이 작업을 완료하면 result에 대한 자리 표시자를 채울 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서 URLSession 호출은 오류를 던질 수도 있습니다. 즉, 결과를 기다릴 때 오류가 발생할 수 있다는 것입니다. 이를 처리하기 위해 try 작성이 필요합니다. 이때 result 값을 다시 읽어도 해당 값을 다시 계산하지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식 Task들은 실제로 작업 트리라고 불리는 계층 구조의 일부입니다. 이 트리는 구조화된 동시성의 중요한 부분으로, Task의 속성에 영향을 미칩니다. 취소, 우선순위, 작업 로컬 변수 등이 있습니다. 하나의 async 함수에서 다른 async 함수를 호출할 때마다 동일한 Task가 호출을 실행하는 데 사용됩니다. 이때 해당 함수는 그 Task의 모든 속성을 상속받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async let을 사용하여 새로운 구조화된 Task를 만들 때, 그것은 현재 함수가 실행되는 Task의 자식이 됩니다. 부모 Task와 자식 Task의 관계가 되는 것이지 특정 함수의 자식은 아니지만, 생명 주기는 함수에 범위가 지정될 수 있습니다. 이렇게 생기는 작업 트리는 각 부모 Task와 자식 Task 간의 연결로 구성되는데, 이 연결은 부모 Task는 자식 Task가 모두 끝난 후에만 작업을 마칠 수 있다는 규칙을 강제합니다. 이 규칙은 자식 Task가 기다려지지 않는 비정상적인 제어 흐름이 발생될 때도 적용됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-01 오후 10.14.39.png&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xKNtT/btsM4jFgXxC/LF5i5EiMRqSMOKAmilFsD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xKNtT/btsM4jFgXxC/LF5i5EiMRqSMOKAmilFsD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xKNtT/btsM4jFgXxC/LF5i5EiMRqSMOKAmilFsD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxKNtT%2FbtsM4jFgXxC%2FLF5i5EiMRqSMOKAmilFsD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;735&quot; height=&quot;382&quot; data-filename=&quot;스크린샷 2025-04-01 오후 10.14.39.png&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 위의 그림에서 metadata 작업을 기다린 후에 이미지 data Task를 기다리는데 첫 번째로 기다린 Task가 오류를 던지며 끝났다면 fetchOne 함수는 그 오류를 던지며 즉시 종료해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 두번째 다운로드를 수행하는 Task에서는 Swift가 자동으로 기다리지 않은 Task를 취소된 것으로 표시되고, 함수가 종료되기 전에 그 Task가 끝날 때까지 기다리게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의할 점이 Task를 취소된 것으로 표시된다고 해서 Task가 중지되는 것은 아닙니다. 그저 해당 Task에 대한 결과가 더 이상 필요하지 않다는 것을 Task에 알리는 것이고, 실제로 작업이 취소되면 그 작업의 모든 하위 작업도 자동으로 취소 상태가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 함수 fetchOne은 자신이 직접 또는 간접적으로 생성한 모든 구조화된 작업들이 완료된 후, 오류를 던지며 종료되는데 이 보장은 구조화된 동시성에서 매우 중요한 원칙입니다. 이는 작업의 생애 주기를 관리하는데 도움을 주어, 메모리의 생애 주기를 자동으로 관리하는 ARC와 마찬가지로 실수로 작업이 유출되는 것을 방지합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;취소&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 작업의 취소 여부 상태는 모든 함수에서 확인할 수 있고, 코드는 취소를 명시적으로 확인하고 적절한 방법으로 실행을 종료해야 합니다. 특히 긴 시간 동안 실행되는 계산을 포함하는 경우, 취소를 염두에 두고 API를 구현하라고 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;isCancelled를 Bool값으로 얻을 수 있고, 이를 통해 분기 처리를 하면 부분적인 결과를 얻게 될 수도 있습니다. 취소된 경우와 취소되지 않은 경우로 나뉘기 때문입니다. 하지만 취소 중에도 코드가 완전한 결과를 요구하기 때문에 부분적인 결과가 반환 될 수 있음을 명시하지 않는다면, 작업 취소가 사용자에게 치명적인 오류를 일으킬 수도 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Group Task&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 async let은 변수 바인딩과 같이 범위가 제한됩니다. async let이 붙은 작업만큼 시작되고 완료되기 때문입니다. 또한 이는 다음 루프 반복이 시작되기 전에 완료되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Task Group을 사용할 수 있습니다. 동적 동시성 양을 제공하도록 설계된 구조적 동시성의 한 형태로 withThrowingTaskGroup 함수를 호출해 사용할 수 있습니다. 이 함수는 오류를 던질 수 있는 자식 task를 생성할 수 있는 범위가 제한된 그룹 객체를 제공합니다. 그룹에 추가된 작업은 그룹이 정의된 블록 범위를 벗어나지 못합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그룹에 추가되면 자식 작업은 즉시 실행되며, 순서는 무관합니다. 그룹 객체가 범위를 벗어난다면, 그 안의 모든 작업이 완료될 때까지 암묵적으로 기다리게 되는데 이는 앞서 설명한 작업 트리 규칙의 결과입니다. 그룹 작업도 구조화 되어 있기 때문이죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-01 오후 10.59.16.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cy7OX5/btsM4qYrjej/KQ2RH7PhZrbqIuCWeXoHoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cy7OX5/btsM4qYrjej/KQ2RH7PhZrbqIuCWeXoHoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cy7OX5/btsM4qYrjej/KQ2RH7PhZrbqIuCWeXoHoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcy7OX5%2FbtsM4qYrjej%2FKQ2RH7PhZrbqIuCWeXoHoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1128&quot; height=&quot;566&quot; data-filename=&quot;스크린샷 2025-04-01 오후 10.59.16.png&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 프로그램에서 동시성 양을 증가시킬 때 발생할 수 있는 실수가 있습니다. 우연하게 발생되는 Data Race입니다. WWDC 내의 예제에서는 이를 피하기 위해서 각 자식 작업이 값을 반환하게 하고, 부모 작업이 결과를 처리하는 책임을 지도록 구현했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그룹의 결과를 반복하는 중, 오류가 발생한 자식 작업을 만나서 그 오류가 그룹 블록에서 던져지면 그룹 내의 모든 작업은 암묵적으로 취소되고 모두 기다리게 됩니다. 이는 async let과 동일하게 동작하지만, 그룹이 블록에서 정상적으로 종료되어 범위를 벗어날 때 차이점이 나타납니다. 작업들이 취소되지 않고 대기만 하기 때문에 작업을 처리해주어야 합니다. 블록을 종료하기 전에 그룹의 cancelAll 메서드를 사용하여 모든 작업을 수동으로 취소할 수도 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;다음 글에 계속...&lt;/p&gt;</description>
      <category>  iOS/  WWDC</category>
      <category>asyncawait</category>
      <category>ios</category>
      <category>swift</category>
      <category>Swift Concurrency</category>
      <category>wwdc</category>
      <category>WWDC21</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/50</guid>
      <comments>https://mintraum.tistory.com/50#entry50comment</comments>
      <pubDate>Tue, 1 Apr 2025 23:17:36 +0900</pubDate>
    </item>
    <item>
      <title>[혼자 공부하는 컴퓨터 구조 + 운영 체제] 2장 데이터</title>
      <link>https://mintraum.tistory.com/49</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  기억에 남는 문장들&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0과 1을 나타내는 가장 작은 정보 단위를 비트(bit)라고 합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;비트는 전구에 빗대어 생각해 보면 이해하기 쉽습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 41.2791%; height: 177px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.7019%;&quot;&gt;1 byte&lt;/td&gt;
&lt;td style=&quot;width: 50.2981%;&quot;&gt;8 bit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.7019%;&quot;&gt;1 kB&lt;/td&gt;
&lt;td style=&quot;width: 50.2981%;&quot;&gt;1,000 byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.7019%;&quot;&gt;1 MB&lt;/td&gt;
&lt;td style=&quot;width: 50.2981%;&quot;&gt;1,000 kB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.7019%;&quot;&gt;1 GB&lt;/td&gt;
&lt;td style=&quot;width: 50.2981%;&quot;&gt;1,000 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.7019%;&quot;&gt;1 TB&lt;/td&gt;
&lt;td style=&quot;width: 50.2981%;&quot;&gt;1,000 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;word란 CPU가 한 번에 처리할 수 있는 데이터 크기를 의미합니다. 만약 CPU가 한 번에 16 비트를 처리할 수 있다면 1워드는 16비트가 되고, 한 번에 32 비트를 처리할 수 있다면 1워드는 32 비트가 되는 것이죠.&lt;/li&gt;
&lt;li&gt;정의된 워드의 절반 크기를 하프 워드, 1배 크기를 풀 워드, 2배 크기를 더블 워드라고 부릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이진수의 음수 표현&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴퓨터는 0과 1만 이해할 수 있기 때문에 마이너스 부호를 사용하지 않고 0과 1만으로 음수를 표현해야 합니다.&lt;/li&gt;
&lt;li&gt;가장 널리 사용되는 방법은 2의 보수를 구해 이 값을 음수로 간주하는 방법입니다.&lt;/li&gt;
&lt;li&gt;어떤 수를 그보다 큰 2^n 에서 뺀 값&lt;/li&gt;
&lt;li&gt;모든 0과 1을 뒤집고 거기에 1을 더한 값&lt;/li&gt;
&lt;li&gt;음수 양수 구분 위해 플래그 사용&lt;/li&gt;
&lt;li&gt;2의 보수를 사용하는 방법은 완벽하지 않음. 2과 0의 경우 문제가 생긴다.&lt;/li&gt;
&lt;li&gt;16진법은 15를 넘어가는 시점에 자리 올림하여 수를 표현하는 방법입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문자 집합과 인코딩&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자 집합: 컴퓨터가 인식하고 표현할 수 있는 문자의 모음&lt;/li&gt;
&lt;li&gt;문자 인코딩: 문자를 0과 1로 변환하는 과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 포인트: 글자에 부여된 고유한 값&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;문자 디코딩: 0과 1로 이루어진 문자 코드를 사람이 이해할 수 있는 문자로 변환하는 과정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아스키 코드&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초창기 문자 집합 중 하나&lt;/li&gt;
&lt;li&gt;영어 알파벳과 아라비아 숫자, 일부 특수 문자&lt;/li&gt;
&lt;li&gt;7비트&lt;/li&gt;
&lt;li&gt;128개의 문자&lt;/li&gt;
&lt;li&gt;실제로는 하나의 아스키 문자를 나타내기 위해 8비트를 사용합니다. 하지만 8비트 중 1비트는 패리티 비트라고 불리는, 오류 검출을 위해 사용되는 비트이기 때문에 실질적으로 문자 표현을 위해 사용되는 비트는 7비트입니다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EUC-KR&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;알파벳을 쭉 이어 쓰면 단어가 되는 영어와는 달리, 한글은 각 음절 하나하나가 초성, 중성, 종성의 조합으로 이루어져 있습니다.&lt;/li&gt;
&lt;li&gt;그래서 한글 인코딩에는 두 가지 방식이 존재합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;완성형 인코딩
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초성, 중성, 종성의 조합으로 이루어진 완성된 하나의 글자에 고유한 코드를 부여하는 인코딩 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;조합형 인코딩
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초성을 위한 비트열, 중성을 위한 비트열, 종성을 위한 비트열을 할당하여 그것들의 조합으로 하나의 글자 코드를 완성하는 인코딩 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모든 한글을 표현할 수 없다. -&amp;gt; 마이크로 소프트의 CP949 등장 but 여전히 부족.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유니코드와 UTF-8&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 나라 언어의 문자 집합과 인코딩 방식이 통일되어 언어별로 인코딩하는 수고로움을 덜 수 있게 하는, 표준 인코딩 방식&lt;/li&gt;
&lt;li&gt;현대 문자를 표현할 때 가장 많이 사용되는 표준 문자 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;❓&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;질문&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  3줄 소감&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그림도 직관적이고, 내용도 쉽게 설명해준다.&lt;/li&gt;
&lt;li&gt;작은 단원이 끝날 때마다 요약 정리와 함께 간단한 문제를 풀 수 있어서 다시 한 번 개념 정리하는 것에 도움이 되낟.&lt;/li&gt;
&lt;li&gt;강의도 무료로 풀려 있어서 책을 읽은 후 강의를 들으면 복습하기 정말 좋다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  Book Log</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/49</guid>
      <comments>https://mintraum.tistory.com/49#entry49comment</comments>
      <pubDate>Fri, 28 Mar 2025 16:40:57 +0900</pubDate>
    </item>
    <item>
      <title>[혼자 공부하는 컴퓨터 구조 + 운영체제] 1장 컴퓨터 구조 시작하기</title>
      <link>https://mintraum.tistory.com/48</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  기억에 남는 문장들&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴퓨터 구조를 이해하고 있다면 문제 상황을 빠르게 진단할 수 있고, 문제 해결의 실마리를 다양하게 찾을 수 있습니다.&lt;/li&gt;
&lt;li&gt;성능, 용량, 비용 문제는 프로그래밍 언어의 문법만 알아서는 해결하기 어렵습니다.&lt;/li&gt;
&lt;li&gt;컴퓨터 구조를 이해하면 입력과 출력에만 집중하는 개발을 넘어 성능, 용량, 비용까지 고려하며 개발하는 개발자가 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴퓨터 구조 지식&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;컴퓨터가 이해하는 정보&lt;/b&gt;: 0과 1
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터&lt;/b&gt;: 숫자, 문자, 이미지, 동영상과 같은 정적인 정보&lt;/li&gt;
&lt;li&gt;&lt;b&gt;명령어&lt;/b&gt;: 데이터를 움직이고 컴퓨터를 작동시키는 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴퓨터의 네 가지 핵심 부품&lt;/b&gt;&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CPU(중앙처리장치)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ALU(산술논리연산장치)&lt;/b&gt;: 계산기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;제어장치&lt;/b&gt;: 제어 신호라는 전기 신호를 내보내고 명령어를 해석하는 장치&lt;/li&gt;
&lt;li&gt;&lt;b&gt;레지스터&lt;/b&gt;: CPU 내부의 작은 임시 저장 장치, 여러 개가 존재.&lt;/li&gt;
&lt;li&gt;메인보드 안에서 메인보드 내 시스템 버스와 연결됨&lt;/li&gt;
&lt;li&gt;컴퓨터의 두뇌&lt;/li&gt;
&lt;li&gt;메모리에 저장된 명령어를 읽어 들이고, 해석하고, 실행하는 부품&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메모리(주기억장치)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메인보드 안에서 메인보드 내 시스템 버스와 연결됨&lt;/li&gt;
&lt;li&gt;현재 실행되는 프로그램의 명령어와 데이터를 저장하는 부품&lt;/li&gt;
&lt;li&gt;빠르게 작동하기 위해서 저장된 명령어와 데이터의 위치는 정돈되어 있어야 함&lt;/li&gt;
&lt;li&gt;&lt;i&gt;주소&lt;/i&gt; 개념 사용&lt;/li&gt;
&lt;li&gt;가격이 비싸 저장 용량이 적다&lt;/li&gt;
&lt;li&gt;전원이 꺼지면 저장된 내용을 잃는다&lt;/li&gt;
&lt;li&gt;현재 실행되는 프로그램 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보조기억장치&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메인보드의 밖에서 메인보드 내 시스템 버스와 연결됨&lt;/li&gt;
&lt;li&gt;메모리보다 크기가 크고, 전원이 꺼져도 저장된 내용을 잃지 않는 메모리 보조 저장 장치&lt;/li&gt;
&lt;li&gt;하드 디스크, SSD, USB 메모리, DVD, CD-ROM&lt;/li&gt;
&lt;li&gt;보관할 프로그램 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;입출력장치&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메인보드의 밖에서 메인보드 내 시스템 버스와 연결됨.&lt;/li&gt;
&lt;li&gt;마이크, 스피커, 프린터, 마우스, 키보드&lt;/li&gt;
&lt;li&gt;컴퓨터 외부에 연결되어 컴퓨터 내부와 정보를 교환하는 장치&lt;/li&gt;
&lt;li&gt;관점에 따라 보조기억장치도 입출력장치의 일종으로 볼 수 있다&lt;/li&gt;
&lt;li&gt;둘을 합쳐 주변장치라 통칭&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주기억 장치에는 크게 RAM(Random Access Memory)과 ROM(Read Only Memory)가 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리라는 용어는 보통 RAM을 지칭합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메인보드&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마더보드&lt;/li&gt;
&lt;li&gt;지금까지 설명한 컴퓨터의 핵심 부품들은 모두 메인보드라는 판에 연결됩니다.&lt;/li&gt;
&lt;li&gt;메인보드에 연결된 부품들은 서로 정보를 주고 받을 수 있는데, 이는 메인보드 내부에 버스라는 통로가 있기 때문입니다.&lt;/li&gt;
&lt;li&gt;컴퓨터 내부에는 다양한 종류의 통로, 즉 버스가 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;하지만 가장 중요한 버스는 시스템 버스 입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시스템 버스&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주소 버스: 주소를 주고 받는 통로&lt;/li&gt;
&lt;li&gt;데이터 버스: 명령어와 데이터를 주고 받는 통로&lt;/li&gt;
&lt;li&gt;제어 버스: 제어 신호를 주고 받는 통로&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;❓&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;질문&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레지스터도 캐시라고 볼 수 있나?&lt;/li&gt;
&lt;li&gt;근데 캐시는 임시 저장 장치인데, 레지스터는 그런 개념이 아니라 캐시라고 보긴 어렵겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  3줄 소감&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그림도 직관적이고, 내용도 쉽게 설명해준다.&lt;/li&gt;
&lt;li&gt;작은 단원이 끝날 때마다 요약 정리와 함께 간단한 문제를 풀 수 있어서 다시 한 번 개념 정리하는 것에 도움이 되낟.&lt;/li&gt;
&lt;li&gt;강의도 무료로 풀려 있어서 책을 읽은 후 강의를 들으면 복습하기 정말 좋다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  Book Log</category>
      <category>독서</category>
      <category>책</category>
      <category>컴퓨터구조</category>
      <category>혼자공부하는컴퓨터구조와운영체제</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/48</guid>
      <comments>https://mintraum.tistory.com/48#entry48comment</comments>
      <pubDate>Thu, 27 Mar 2025 14:46:01 +0900</pubDate>
    </item>
    <item>
      <title>[오브젝트] 2장 객체지향 프로그래밍 (feat. POP와의 비교)</title>
      <link>https://mintraum.tistory.com/47</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  기억에 남는 문장들&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;진정한 객체지향 패러다임으로의 전환은 클래스가 아닌 객체에 초점을 맞출 때에만 얻을 수 있다. 이를 위해서는 프로그래밍 하는 동안 다음의 두 가지에 집중해야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 클래스가 필요한지를 고민하기 전에 어떤 객체들이 필요한지 고민하라.&lt;/li&gt;
&lt;li&gt;클래스는 공통적인 상태와 행동을 공유하는 객체들을 추상화한 것이다. 따라서 클래스의 윤곽을 잡기 위해서는 어떤 객체들이 어떤 상태와 행동을 가지는지를 먼저 결정해야 한다. 객체를 중심에 두는 접근 방법은 설계를 단순하고 깔끔하게 만든다.&lt;/li&gt;
&lt;li&gt;객체를 독립적인 존재가 아니라 기능을 구현하기 위해 협력하는 공동체의 일원으로 봐야 한다. 객체는 홀로 존재하는 것이 아니다. 다른 객체에게 도움을 주거나 의존하면서 살아가는 협력적인 존재다.&lt;/li&gt;
&lt;li&gt;훌륭한 협력이 훌륭한 객체를 낳고 훌륭한 객체가 훌륭한 클래스를 낳는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;소프트웨어는 사용자가 원하는 어떤 문제를 해결하기 위해 만들어진다.&lt;/li&gt;
&lt;li&gt;이처럼 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야를 도메인이라고 부른다.&lt;/li&gt;
&lt;li&gt;클래스의 내부와 외부를 구분해야 하는 이유는 무엇일까? 그 이유는 경계의 명확성이 객체의 자율성을 보장하기 때문이다. 그리고 더 중요한 이유는 프로그래머에게 구현의 자유를 제공하기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자율적인 객체&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체지향의 핵심은 스스로 상태를 관리하고, 판단하고, 행동하는 자율적인 객체들의 공동체를 구성하는 것이다.&lt;/li&gt;
&lt;li&gt;객체가 자율적인 존재로 우뚝 서기 위해서는 외부의 간섭을 최소화해야 한다.&lt;/li&gt;
&lt;li&gt;일반적으로 객체의 상태는 숨기고 행동만 외부에 공개해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로그래머의 자유&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설계가 필요한 이유는 변경을 관리하기 위해서라는 것을 기억하라.&lt;/li&gt;
&lt;li&gt;객체지향 언어는 객체 사이의 의존성을 적절히 관리함으로써 변경에 대한 파급효과를 제어할 수 있는 다양한 방법을 제공한다.&lt;/li&gt;
&lt;li&gt;객체지향의 장점은 객체를 이용해 도메인의 의미를 풍부하게 표현할 수 있다는 것이다.&lt;/li&gt;
&lt;li&gt;따라서 의미를 좀 더 명시적이고 분명하게 표현할 수 있다면 객체를 사용해서 해당 개념을 구현하라.&lt;/li&gt;
&lt;li&gt;그 개념이 비록 하나의 인스턴스 변수만 포함하더라도 개념을 명시적으로 표현하는 것은 전체적인 설계의 명확성과 유연성을 높이는 첫걸음이다.&lt;/li&gt;
&lt;li&gt;메시지를 수신한 객체는 스스로의 결정에 따라 자율적으로 메시지를 처리할 방법을 결정한다. 이처럼 수신된 메시지를 처리하기 위한 자신만의 방법을 메서드라고 부른다.&lt;/li&gt;
&lt;li&gt;메시지와 메서드를 구분하는 것은 매우 중요하다. 메시지와 메서드의 구분에서부터 다형성의 개념이 출발한다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오버라이딩과 오버로딩&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오버라이딩은 부모 클래스에 정의된 같은 이름, 같은 파라미터 목록을 가진 메서드를 자식 클래스에서 재정의하는 경우를 가리킨다. 자식 클래스의 메서드는 오버라이딩한 부모 클래스의 메서드를 가리기 때문에 외부에서는 부모 클래스의 메서드가 보이지 않는다.&lt;/li&gt;
&lt;li&gt;오버로딩은 메서드의 이름은 같지만 제공되는 파라미터의 목록이 다르다. 오버로딩한 메서드는 원래의 메서드를 가리지 않기 때문에 이 메서드들은 사이 좋게 공존한다.&lt;/li&gt;
&lt;li&gt;어떤 클래스가 다른 클래스에 접근할 수 있는 경로를 가지거나 해당 클래스의 객체의 메서드를 호출할 경우 두 클래스 사이에 의존성이 존재한다고 말한다.&lt;/li&gt;
&lt;li&gt;코드의 의존성과 실행 시점의 의존성이 서로 다를 수 있다. 다시 말해 클래스 사이의 의존성과 객체 사이의 의존성은 동일하지 않을 수 있다. 그리고 유연하고, 쉽게 재사용할 수 있으며, 확장 가능한 객체지향 설계가 가지는 특징은 코드의 의존성과 실행 시점의 의존성이 다르다는 것이다.&lt;/li&gt;
&lt;li&gt;한 가지 간과해서는 안되는 사실은 코드의 의존성과 실행 시점의 의존성이 다르면 다를수록 코드를 이해하기 어려워진다는 것이다. 코드를 이해하기 위해서는 코드뿐만 아니라 객체를 생성하고 연결하는 부분을 찾아야 하기 때문이다. 반면 코드의 의존성과 실행 시점의 의존성이 다르면 다를수록 코드는 더 유연해지고 확장 가능해진다.&lt;/li&gt;
&lt;li&gt;여러분이 훌륭한 객체지향 설계자로 성장하기 위해서는 항상 유연성과 가독성 사이에서 고민해야 한다. 무조건 유연한 설계도, 무조건 읽기 쉬운 코드도 정답이 아니다. 이것이 객체지향 설계가 어려우면서도 매력적인 이유다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;차이에 의한 프로그래밍&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부모 클래스와 다른 부분만을 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법을 차이에 의한 프로그래밍이라고 부른다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상속과 인터페이스&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속이 가치 있는 이유는 부모 클래스가 제공하는 모든 인터페이스를 자식 클래스가 물려받을 수 있기 때문이다.&lt;/li&gt;
&lt;li&gt;인터페이스는 객체가 이해할 수 있는 메시지의 목록을 정의한다는 것을 기억하라. 상속을 통해 자식 클래스는 자신의 인터페이스에 부모 클래스의 인터페이스를 포함하게 된다. 결과적으로 자식 클래스는 부모 클래스가 수신할 수 있는 모든 메시지를 수신할 수 있기 때문에 외부 객체는 자식 클래스를 부모 클래스와 동일한 타입으로 간주할 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;다형성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Movie는 동일한 메시지를 전송하지만 실제로 어떤 메서드가 실행될 것인지는 메시지를 수신하는 객체의 클래스가 무엇이냐에 따라 달라진다. 이를 다형성이라고 부른다.&lt;/li&gt;
&lt;li&gt;다형성이란 동일한 메시지를 수신했을 때 객체의 타입에 따라 응답할 수 있는 능력을 의미한다. 따라서 다형적인 협력에 참여하는 객체들은 모두 같은 메시지를 이해할 수 있어야 한다. 다시 말해 인터페이스가 동일해야 한다는 것이다.&lt;/li&gt;
&lt;li&gt;다형성을 구현하는 방법은 매우 다양하지만 메시지에 응답하기 위해 실행될 메서드를 컴파일 시점이 아닌 실행 시점에 결정한다는 공통점이 있다. 다시 말해 메시지와 메서드를 실행 시점에 바인딩한다는 것이다. 이를 지연 바인딩 또는 동적 바인딩이라고 부른다. 그에 반해 전통적인 함수 호출처럼 컴파일 시점에 실행될 함수나 프로시저를 결정하는 것을 초기 바인딩 또는 정적 바인딩이라고 부른다.&lt;/li&gt;
&lt;li&gt;객체지향이 컴파일 시점의 의존성과 실행 시점의 의존성을 분리하고, 하나의 메시지를 선택적으로 서로 다른 메서드에 연결할 수 있는 이유가 바로 지연 바인딩이라는 메커니즘을 사용하기 때문이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;하지만 Swift에서는 Protocol에 Generic을 활용하거나, extension에 기본 구현을 제공하는 경우 컴파일 시점에 메서드 호출이 결정되는 정적 바인딩이 발생한다. 이로 인해 런타임에 동적으로 메서드를 찾는 오버헤드를 줄일 수 있어 성능상 이점이 있을 수 있다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;다만 주의해야 할 순간이 있다.&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;protocol type으로 사용&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;프로토콜을 구체적인 타입 대신 프로토콜 자체로 다룰 때(protocol existential로 사용하는 경우)는 내부적으로 동적 바인딩이 사용될 수 있다. -&amp;gt; Generic 사용 시 성능 최적화.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;클래스 전용 프로토콜, @objc&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;만약 프로토콜이 클래스에 한정되거나, Objective-C와의 호환성을 위해 @objc를 사용하는 경우, 동적 바인딩이 발생하여 런타임 비용이 증가할 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;구현 상속과 인터페이스 상속&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속을 구현 상속과 인터페이스 상속으로 분류할 수 있다. 흔히 구현 상속을 서브클래싱이라고 부르고 인터페이스 상속을 서브타이핑이라고 부른다.&lt;/li&gt;
&lt;li&gt;구현 상속 = 순수하게 코드를 재사용하기 위한 목적으로 상속을 사용하는 것&lt;/li&gt;
&lt;li&gt;인터페이스 상속 = 다형적인 협력을 위해 부모 클래스와 자식 클래스가 인터페이스를 공유할 수 있도록 상속을 이용하는 것.&lt;/li&gt;
&lt;li&gt;상속은 구현 상속이 아니라 인터페이스 상속을 위해 사용해야 한다.&lt;/li&gt;
&lt;li&gt;인터페이스를 재사용할 목적이 아니라 구현을 재사용할 목적으로 상속을 사용하면 변경에 취약한 코드를 낳게 될 확률이 높다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인터페이스와 다형성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;종종 구현은 공유할 필요가 없고 순수하게 인터페이스만 공유하고 싶을 때가 있다. 이를 위해서 C#과 자바에서는 인터페이스라는 프로그래밍 요소를 제공한다.&lt;/li&gt;
&lt;li&gt;추상화를 이용해 상위 정책을 기술한다는 것은 기본적인 애플리케이션의 협력 흐름을 기술한다는 것을 의미한다.&lt;/li&gt;
&lt;li&gt;추상화가 유연한 설계를 가능하게 하는 이유는 설계가 구체적인 상황에 결합되는 것을 방지하기 때문이다.&lt;/li&gt;
&lt;li&gt;컨텍스트 독립성이라고 불리는 이 개념은 프레임워크와 같은 유연한 설계가 필수적인 분야에서 그 진가를 발휘한다.&lt;/li&gt;
&lt;li&gt;여러분이 작성하는 모든 코드에는 합당한 이유가 있어야 한다.&lt;/li&gt;
&lt;li&gt;비록 아주 사소한 결정이더라도 트레이드오프를 통해 얻어진 결론과 그렇지 않은 결론 사이의 차이는 크다. 고민하고 트레이드오프하라.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코드 재사용&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체지향 설계와 관련된 자료를 조금이라도 본 사람들은 코드 재사용을 위해서는 상속보다는 합성이 더 좋은 방법이라는 이야기를 많이 들었을 것이다.&lt;/li&gt;
&lt;li&gt;합성은 다른 객체의 인스턴스를 자신의 인스턴스 변수로 포함해서 재사용하는 방법을 말한다.&lt;/li&gt;
&lt;li&gt;왜?&lt;/li&gt;
&lt;li&gt;상속은 캡슐화를 위반하고 설계를 유연하지 못하게 만든다.&lt;/li&gt;
&lt;li&gt;인터페이스에 정의된 메시지를 통해서만 코드를 재사용하는 방법을 합성이라고 부른다.&lt;/li&gt;
&lt;li&gt;합성은 인터페이스에 정의된 메시지를 통해서만 재사용이 가능하기 때문에 구현을 효과적으로 캡슐화할 수 있다. 또한 의존하는 인스턴스를 교체하는 것이 비교적 쉽기 때문에 설계를 유연하게 만든다.&lt;/li&gt;
&lt;li&gt;코드를 재사용하는 경우에는 상속보다 합성을 선호하는 것이 옳지만 다형성을 위해 인터페이스를 재사용하는 경우에는 상속과 합성을 함께 조합해서 사용할 수밖에 없다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Swift의 경우 Protocol extension으로 기본 구현을 제공하기에 상속을 꼭 사용하지 않아도 인터페이스의 재사용 및 다형성을 충분히 달성할 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;❓&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;질문&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  3줄 소감&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아무래도 Swift를 주로 사용하다보니 POP도 생각하면서 보게 되었다.&lt;/li&gt;
&lt;li&gt;왜 Apple이 OOP보다도 POP를 권장하는지 알게 되는 과정이었다.&lt;/li&gt;
&lt;li&gt;Swift의 Protocol을 사용하면 설계 관점에서도, 성능상으로도 이점이 있다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  Book Log</category>
      <author>MINT09</author>
      <guid isPermaLink="true">https://mintraum.tistory.com/47</guid>
      <comments>https://mintraum.tistory.com/47#entry47comment</comments>
      <pubDate>Wed, 26 Mar 2025 23:43:38 +0900</pubDate>
    </item>
  </channel>
</rss>