<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>B_log</title>
    <link>https://jayb-log.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 13 Jul 2026 00:17:27 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>B_log</managingEditor>
    <image>
      <title>B_log</title>
      <url>https://tistory1.daumcdn.net/tistory/3876522/attach/86b8e97485474a9195a857d25dcbb79c</url>
      <link>https://jayb-log.tistory.com</link>
    </image>
    <item>
      <title>Swift로 배우는 전략 패턴 (Strategy Pattern) &amp;ndash; Head First 디자인 패턴 기반 설명</title>
      <link>https://jayb-log.tistory.com/365</link>
      <description>&lt;h1&gt;Swift로 배우는 전략 패턴 (Strategy Pattern) &amp;ndash; Head First 디자인 패턴 기반 설명&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;『Head First Design Patterns』는 디자인 패턴을 쉽고 직관적으로 설명하는 명저입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 그중에서도 &lt;b&gt;전략 패턴(Strategy Pattern)&lt;/b&gt;을 iOS 개발 환경에 맞게 Swift로 구현해보며 학습해보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  전략 패턴이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전략 패턴(Strategy Pattern)은 알고리즘(또는 행위)을 객체로 캡슐화하여 &lt;b&gt;상호 교환 가능하게 만드는 디자인 패턴&lt;/b&gt;입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 개념:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;행동을 클래스로 분리해서, 실행 중에 바꿀 수 있게 만든다.&quot;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 전략 패턴 요약 정리&lt;/h2&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;| 패턴명 | 전략(Strategy) 패턴 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 목적 | 알고리즘을 런타임에 자유롭게 교체 가능하게 만들기 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 특징 | 1개의 인터페이스 + 여러 전략 구현체 + Context 객체 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 장점 | OCP 만족, 중복 제거, 행동 교체 가능 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 자주 쓰이는 경우 | 로그인 방식, 정렬 기준, 렌더링 전략 등 |&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  책 속 예시: 오리 시뮬레이터&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Head First 디자인 패턴에서 소개된 전략 패턴의 대표 예시는 &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;code&gt;Duck&lt;/code&gt; 객체는 &lt;code&gt;fly()&lt;/code&gt;와 &lt;code&gt;quack()&lt;/code&gt; 같은 행동을 가짐&lt;/li&gt;
&lt;li&gt;행동은 상황에 따라 달라지므로, 서브클래싱보다 &lt;b&gt;구성(Composition)&lt;/b&gt;을 통해 캡슐화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FlyBehavior&lt;/code&gt;, &lt;code&gt;QuackBehavior&lt;/code&gt;라는 인터페이스로 행동을 외부에서 주입&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  iOS에서의 실제 예: 로그인 전략&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS 앱에서 다음과 같은 로그인 기능을 제공한다고 가정해봅시다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple ID 로그인&lt;/li&gt;
&lt;li&gt;Google 계정 로그인&lt;/li&gt;
&lt;li&gt;게스트 로그인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 3가지 로그인 방식은 서로 방식이 다르지만, &quot;로그인&quot;이라는 기능은 같기 때문에 전략 패턴이 유용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;code&gt;LoginStrategy&lt;/code&gt; 프로토콜 정의&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;
protocol LoginStrategy {

    func login(completion: @escaping (Bool) -&amp;gt; Void)

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 전략 클래스 구현&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;
struct AppleLoginStrategy: LoginStrategy {

    func login(completion: @escaping (Bool) -&amp;gt; Void) {

        print(&quot;  Apple ID로 로그인 시도&quot;)

        // Apple 로그인 로직

        completion(true)

    }

}



struct GoogleLoginStrategy: LoginStrategy {

    func login(completion: @escaping (Bool) -&amp;gt; Void) {

        print(&quot;  Google 계정으로 로그인 시도&quot;)

        // Google 로그인 로직

        completion(true)

    }

}



struct GuestLoginStrategy: LoginStrategy {

    func login(completion: @escaping (Bool) -&amp;gt; Void) {

        print(&quot;  게스트 로그인&quot;)

        completion(true)

    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Context 객체: &lt;code&gt;LoginManager&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;
final class LoginManager {

    private var strategy: LoginStrategy



    init(strategy: LoginStrategy) {

        self.strategy = strategy

    }



    func setStrategy(_ strategy: LoginStrategy) {

        self.strategy = strategy

    }



    func login(completion: @escaping (Bool) -&amp;gt; Void) {

        strategy.login(completion: completion)

    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. ViewController에서 사용&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;
class LoginViewController: UIViewController {

    let loginManager = LoginManager(strategy: GuestLoginStrategy())



    @IBAction func appleLoginTapped() {

        loginManager.setStrategy(AppleLoginStrategy())

        loginManager.login { success in

            print(&quot;로그인 성공 여부: \(success)&quot;)

        }

    }



    @IBAction func googleLoginTapped() {

        loginManager.setStrategy(GoogleLoginStrategy())

        loginManager.login { success in

            print(&quot;로그인 성공 여부: \(success)&quot;)

        }

    }



    @IBAction func guestLoginTapped() {

        loginManager.setStrategy(GuestLoginStrategy())

        loginManager.login { success in

            print(&quot;로그인 성공 여부: \(success)&quot;)

        }

    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  iOS에서 자주 쓰이는 전략 패턴 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 상황 | 전략이 필요한 이유 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| ------ | ------------------- |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 이미지 필터 처리 | 다양한 필터 적용 방식(흑백, 세피아 등)을 런타임에 변경 가능 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 정렬 기준 선택 | 리스트 정렬 기준을 이름순 / 날짜순 등으로 교체 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 광고 A/B 전략 | 유저 그룹별 광고 표시 전략 분리 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 데이터 저장 방식 | UserDefaults, Keychain, File 방식 전략적으로 교체 |&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  고급 팁: 클로저로도 전략을 표현할 수 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift에서는 꼭 클래스로만 전략을 구현하지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 전략은 &lt;b&gt;클로저&lt;/b&gt;로 대체할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;
typealias LoginClosure = (@escaping (Bool) -&amp;gt; Void) -&amp;gt; Void



let appleLogin: LoginClosure = { completion in

    print(&quot;  Apple로 로그인&quot;)

    completion(true)

}



let guestLogin: LoginClosure = { completion in

    print(&quot;  게스트로 로그인&quot;)

    completion(true)

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식은 간단한 상황에서는 더욱 가볍게 전략을 구성할 수 있어 유용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전략 패턴은 다음과 같은 경우에 아주 효과적입니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;동작을 바꿔야 하지만, 코드를 수정하고 싶지 않을 때&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;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift와 iOS 프로젝트에서 전략 패턴을 적극적으로 활용해보세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한층 더 유연하고 유지보수하기 쉬운 앱을 만들 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  참고&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;『Head First Design Patterns』, O&amp;rsquo;Reilly Media&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift 공식 문서, Apple Developer Guide&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>iOS 앱개발/iOS</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/365</guid>
      <comments>https://jayb-log.tistory.com/365#entry365comment</comments>
      <pubDate>Wed, 9 Jul 2025 15:22:33 +0900</pubDate>
    </item>
    <item>
      <title>  React + Vite 프로젝트 시작하기</title>
      <link>https://jayb-log.tistory.com/354</link>
      <description>&lt;h1&gt;  React + Vite 프로젝트 생성 방법&lt;/h1&gt;
&lt;p&gt;안녕하세요! 이번 글에서는 빠르고 가볍게 React 개발 환경을 구성할 수 있는 &lt;strong&gt;Vite&lt;/strong&gt;를 사용해 프로젝트를 시작하는 방법을 소개합니다.&lt;br&gt;Vite는 빠른 번들링 속도와 간편한 설정으로 최근 가장 주목받는 프론트엔드 도구입니다.&lt;br&gt;React를 처음 시작하거나, CRA(Create React App)의 느린 속도에 지쳤다면 이 글을 꼭 따라해보세요.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 1단계: Node.js 설치&lt;/h2&gt;
&lt;p&gt;Vite를 사용하려면 먼저 Node.js가 필요합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;공식 사이트: &lt;a href=&quot;https://nodejs.org&quot;&gt;https://nodejs.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;LTS(Long Term Support) 버전 다운로드 권장&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;설치 후, 터미널에서 아래 명령어로 정상 설치 여부를 확인합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;node -v
npm -v&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;버전 정보가 잘 출력되면 다음 단계로 넘어가세요.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 2단계: Vite + React 프로젝트 생성&lt;/h2&gt;
&lt;p&gt;Vite는 다양한 템플릿을 제공합니다. 우리는 React 템플릿을 선택해서 프로젝트를 생성할 것입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;npm create vite@latest my-app -- --template react&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;my-app&lt;/code&gt;은 원하는 폴더 이름으로 변경 가능합니다.&lt;/li&gt;
&lt;li&gt;실행 후, 몇 가지 선택지가 나오면 기본 옵션(React + JavaScript)으로 진행해도 충분합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 3단계: 의존성 설치 및 개발 서버 실행&lt;/h2&gt;
&lt;p&gt;프로젝트 디렉토리로 이동한 후 필요한 패키지를 설치하고, 개발 서버를 실행합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;cd my-app
npm install
npm run dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아래와 같은 주소가 터미널에 표시됩니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;  ➜  Local:   http://localhost:5173/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;브라우저에서 접속하면 Vite + React 초기 화면이 보이게 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 4단계: VSCode에서 프로젝트 열기 (선택 사항)&lt;/h2&gt;
&lt;p&gt;Visual Studio Code를 사용한다면 아래 명령어로 바로 프로젝트를 열 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;code .&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;단, &lt;code&gt;code&lt;/code&gt; 명령어가 동작하지 않는다면 VSCode에서 Command Palette (Cmd+Shift+P 또는 Ctrl+Shift+P)를 열고&lt;br&gt;&lt;code&gt;Shell Command: Install &amp;#39;code&amp;#39; command in PATH&lt;/code&gt; 를 실행해주세요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;  마무리&lt;/h2&gt;
&lt;p&gt;이제 React + Vite 기반의 개발 환경이 성공적으로 준비되었습니다!&lt;/p&gt;
&lt;p&gt;이 환경에서는 빠른 HMR(Hot Module Replacement), 간단한 설정 파일, 빠른 빌드 속도 등 다양한 장점을 누릴 수 있어요.&lt;br&gt;앞으로 컴포넌트를 만들고, 상태 관리를 추가하며 프론트엔드 개발을 본격적으로 시작해보세요.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  다음 글 예고&lt;/h2&gt;
&lt;p&gt;다음 포스팅에서는 &lt;code&gt;Tailwind CSS&lt;/code&gt;를 Vite + React 프로젝트에 적용하는 방법을 알아볼 예정입니다.&lt;br&gt;궁금한 점이나 오류가 있다면 댓글로 남겨주세요. 감사합니다  &lt;/p&gt;</description>
      <category>Web/개념 학습</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/354</guid>
      <comments>https://jayb-log.tistory.com/354#entry354comment</comments>
      <pubDate>Sat, 17 May 2025 22:00:51 +0900</pubDate>
    </item>
    <item>
      <title>Belkin 벨킨 썬더볼트 4 C 타입 초고속 케이블</title>
      <link>https://jayb-log.tistory.com/352</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6kaQP/btsISGFeriz/IRkKBCRwBYG511Kt3yYXT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6kaQP/btsISGFeriz/IRkKBCRwBYG511Kt3yYXT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6kaQP/btsISGFeriz/IRkKBCRwBYG511Kt3yYXT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6kaQP%2FbtsISGFeriz%2FIRkKBCRwBYG511Kt3yYXT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;533&quot; height=&quot;527&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구매처:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.coupang.com/vp/products/6360212290?itemId=13424049638&amp;amp;vendorItemId=80678659372&amp;amp;q=%EB%B2%A8%ED%82%A8+%EC%84%A0%EB%8D%94%EB%B3%BC%ED%8A%B8&amp;amp;itemsCount=36&amp;amp;searchId=1037b23e4c3c4048a9c3dde10154c15d&amp;amp;rank=17&amp;amp;isAddedCart=&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.coupang.com/vp/products/6360212290?itemId=13424049638&amp;amp;vendorItemId=80678659372&amp;amp;q=%EB%B2%A8%ED%82%A8+%EC%84%A0%EB%8D%94%EB%B3%BC%ED%8A%B8&amp;amp;itemsCount=36&amp;amp;searchId=1037b23e4c3c4048a9c3dde10154c15d&amp;amp;rank=17&amp;amp;isAddedCart=&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722751045699&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;벨킨 썬더볼트 4 C타입 PD 100W 40Gbps 초고속 케이블 INZ003bt1MBK - 휴대폰/액세서리 | 쿠팡&quot; data-og-description=&quot;현재 별점 4.9점, 리뷰 196개를 가진 벨킨 썬더볼트 4 C타입 PD 100W 40Gbps 초고속 케이블 INZ003bt1MBK! 지금 쿠팡에서 더 저렴하고 다양한 휴대폰/액세서리 제품들을 확인해보세요.&quot; data-og-host=&quot;www.coupang.com&quot; data-og-source-url=&quot;https://www.coupang.com/vp/products/6360212290?itemId=13424049638&amp;amp;vendorItemId=80678659372&amp;amp;q=%EB%B2%A8%ED%82%A8+%EC%84%A0%EB%8D%94%EB%B3%BC%ED%8A%B8&amp;amp;itemsCount=36&amp;amp;searchId=1037b23e4c3c4048a9c3dde10154c15d&amp;amp;rank=17&amp;amp;isAddedCart=&quot; data-og-url=&quot;https://www.coupang.com/vp/products/6360212290?vendorItemId=80678659372&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/h8EhG/hyWKvBnuO9/KkEB5YSXRzVGoKKxiQ9k81/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492,https://scrap.kakaocdn.net/dn/cupw68/hyWKJ7rjXs/rUfmpHWhlCIEunzaBsUDoK/img.jpg?width=230&amp;amp;height=230&amp;amp;face=0_0_230_230&quot;&gt;&lt;a href=&quot;https://www.coupang.com/vp/products/6360212290?itemId=13424049638&amp;amp;vendorItemId=80678659372&amp;amp;q=%EB%B2%A8%ED%82%A8+%EC%84%A0%EB%8D%94%EB%B3%BC%ED%8A%B8&amp;amp;itemsCount=36&amp;amp;searchId=1037b23e4c3c4048a9c3dde10154c15d&amp;amp;rank=17&amp;amp;isAddedCart=&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.coupang.com/vp/products/6360212290?itemId=13424049638&amp;amp;vendorItemId=80678659372&amp;amp;q=%EB%B2%A8%ED%82%A8+%EC%84%A0%EB%8D%94%EB%B3%BC%ED%8A%B8&amp;amp;itemsCount=36&amp;amp;searchId=1037b23e4c3c4048a9c3dde10154c15d&amp;amp;rank=17&amp;amp;isAddedCart=&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/h8EhG/hyWKvBnuO9/KkEB5YSXRzVGoKKxiQ9k81/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492,https://scrap.kakaocdn.net/dn/cupw68/hyWKJ7rjXs/rUfmpHWhlCIEunzaBsUDoK/img.jpg?width=230&amp;amp;height=230&amp;amp;face=0_0_230_230');&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;벨킨 썬더볼트 4 C타입 PD 100W 40Gbps 초고속 케이블 INZ003bt1MBK - 휴대폰/액세서리 | 쿠팡&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;현재 별점 4.9점, 리뷰 196개를 가진 벨킨 썬더볼트 4 C타입 PD 100W 40Gbps 초고속 케이블 INZ003bt1MBK! 지금 쿠팡에서 더 저렴하고 다양한 휴대폰/액세서리 제품들을 확인해보세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.coupang.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 C타입 케이블로 모니터 등을 연결하면 출력이 되지 않는 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(특히 4K 모니터&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-6234705958767260&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때에는 데이터 케이블을 사용 해야하는데, 벨킨 썬더볼트 제품 구매 후기입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;케이블 박스는 아래와 같이 패키징 되어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcem5H/btsITOh1zBC/fnI55k2AoFp6ugJFLf7P5K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcem5H/btsITOh1zBC/fnI55k2AoFp6ugJFLf7P5K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcem5H/btsITOh1zBC/fnI55k2AoFp6ugJFLf7P5K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcem5H%2FbtsITOh1zBC%2FfnI55k2AoFp6ugJFLf7P5K%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;3024&quot; height=&quot;4032&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒷면은 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjpg2a/btsISVPJXwb/NeTF3OyxYietKvQ3WqD4eK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjpg2a/btsISVPJXwb/NeTF3OyxYietKvQ3WqD4eK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjpg2a/btsISVPJXwb/NeTF3OyxYietKvQ3WqD4eK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjpg2a%2FbtsISVPJXwb%2FNeTF3OyxYietKvQ3WqD4eK%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;3024&quot; height=&quot;4032&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A7IoD/btsITvpo9Fd/c99mVY8SBX4KJFboyhT1e0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A7IoD/btsITvpo9Fd/c99mVY8SBX4KJFboyhT1e0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A7IoD/btsITvpo9Fd/c99mVY8SBX4KJFboyhT1e0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA7IoD%2FbtsITvpo9Fd%2Fc99mVY8SBX4KJFboyhT1e0%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;3024&quot; height=&quot;4032&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 케이블 스펙은 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;고속 데이터 전송&lt;/b&gt;: 최대 40Gbps의 속도로 데이터를 전송할 수 있어 대용량 파일 전송에 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;고해상도 디스플레이 지원&lt;/b&gt;: 4K 및 5K 해상도의 디스플레이를 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;전력 공급&lt;/b&gt;: 최대 100W의 전력 공급이 가능하여 빠른 충전이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;길이&lt;/b&gt;: 다양한 길이 옵션이 있어 필요에 맞게 선택할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;내구성&lt;/b&gt;: 벨킨의 높은 품질과 내구성을 자랑합니다.&lt;/p&gt;</description>
      <category>제품 구매 후기</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/352</guid>
      <comments>https://jayb-log.tistory.com/352#entry352comment</comments>
      <pubDate>Sun, 4 Aug 2024 15:00:47 +0900</pubDate>
    </item>
    <item>
      <title>터틀비치 리콘(Turtle Beach Recon) Xbox 컨트롤러 개봉기 및 리뷰</title>
      <link>https://jayb-log.tistory.com/350</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wXV5P/btsIGMrz9Fj/qnbfAHTpSeGFX83yaZmhZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wXV5P/btsIGMrz9Fj/qnbfAHTpSeGFX83yaZmhZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wXV5P/btsIGMrz9Fj/qnbfAHTpSeGFX83yaZmhZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwXV5P%2FbtsIGMrz9Fj%2FqnbfAHTpSeGFX83yaZmhZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;621&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-6234705958767260&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&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-origin-width=&quot;1776&quot; data-origin-height=&quot;1268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JWB1r/btsIH0oIlUv/pJNRFcox0EgwbugTfJvJNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JWB1r/btsIH0oIlUv/pJNRFcox0EgwbugTfJvJNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JWB1r/btsIH0oIlUv/pJNRFcox0EgwbugTfJvJNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJWB1r%2FbtsIH0oIlUv%2FpJNRFcox0EgwbugTfJvJNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;752&quot; height=&quot;537&quot; data-origin-width=&quot;1776&quot; data-origin-height=&quot;1268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;구매 당시 가격은 제품 약 39,000원, 배송비 포함 약 42,000원 에 구매했습니다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NLIsv/btsIGvp6ep3/6Y6SFd3xMXVyRYZCFgg85k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NLIsv/btsIGvp6ep3/6Y6SFd3xMXVyRYZCFgg85k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NLIsv/btsIGvp6ep3/6Y6SFd3xMXVyRYZCFgg85k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNLIsv%2FbtsIGvp6ep3%2F6Y6SFd3xMXVyRYZCFgg85k%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;695&quot; height=&quot;927&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;박스 열었을 때&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cobzmn/btsIIzRNdvQ/pIqyRuoZn5V7HvuGcRxWj0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cobzmn/btsIIzRNdvQ/pIqyRuoZn5V7HvuGcRxWj0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cobzmn/btsIIzRNdvQ/pIqyRuoZn5V7HvuGcRxWj0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcobzmn%2FbtsIIzRNdvQ%2FpIqyRuoZn5V7HvuGcRxWj0%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;722&quot; height=&quot;963&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;컨트롤러&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/792Y2/btsIIyL63E8/ytFDyJK3JbD8rn83YCvS50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/792Y2/btsIIyL63E8/ytFDyJK3JbD8rn83YCvS50/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/792Y2/btsIIyL63E8/ytFDyJK3JbD8rn83YCvS50/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F792Y2%2FbtsIIyL63E8%2FytFDyJK3JbD8rn83YCvS50%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;681&quot; height=&quot;908&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 컨트롤러를 살펴보겠습니다. 디자인은 매우 인체공학적으로 되어 있으며, 손에 잡히는 느낌이 아주 좋습니다.&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-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLQgPh/btsIHD1KxF9/sMFr4GCauR9Nm9u9d0flzK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLQgPh/btsIHD1KxF9/sMFr4GCauR9Nm9u9d0flzK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLQgPh/btsIHD1KxF9/sMFr4GCauR9Nm9u9d0flzK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLQgPh%2FbtsIHD1KxF9%2FsMFr4GCauR9Nm9u9d0flzK%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;748&quot; height=&quot;997&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성품으로는 컨트롤러 본체 외에도 USB 케이블, 사용 설명서 등이 포함되어 있습니다. 필요한 모든 것이 잘 갖추어져 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;터틀비치 리콘 컨트롤러의 장단점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터틀비치 리콘 컨트롤러는 인체공학적인 디자인 덕분에 장시간 사용해도 손목이나 손에 무리가 가지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 터틀비치의 독자적인 사운드 조절 기능이 내장되어 있어, 게임 도중에도 쉽게 음향을 조절할 수 있다는 점이 큰 장점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히, 고음질의 사운드를 제공해 게임 몰입도를 높여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 컨트롤러의 내구성도 뛰어나 오랜 시간 사용해도 문제없이 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 매핑 가능한 커스텀 키도 2개 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 몇 가지 단점도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 컨트롤러의 무게가 다소 무겁게 느껴질 수 있습니다. 장시간 사용 시 손목에 부담을 줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 무선이 아닌 유선 컨트롤러 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이러한 단점에도 불구하고, 전체적인 성능과 기능은 충분히 그 가치를 하고 있다고 생각합니다.&lt;/p&gt;</description>
      <category>제품 구매 후기</category>
      <category>터틀비치#리콘#xbox#controller</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/350</guid>
      <comments>https://jayb-log.tistory.com/350#entry350comment</comments>
      <pubDate>Sat, 20 Jul 2024 17:59:32 +0900</pubDate>
    </item>
    <item>
      <title>데이터 저장: Core Data vs. Realm</title>
      <link>https://jayb-log.tistory.com/348</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;개요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SwiftUI를 활용해 앱을 개발하면서, 데이터 저장 도구에 대한 선택지를 고민했습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;iOS 앱을 개발할 때 데이터 저장 솔루션을 선택하는 것은 중요한 결정입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터를 어떻게 저장하고 관리할지를 결정하는 것은 앱의 성능, 확장성 및 개발 경험에 큰 영향을 미칩니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 글에서는 Core Data와 Realm의 장단점을 비교하고 어떤 상황에서 어떤 것을 선택해야 하는지 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Core Data&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;장점:&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;공식적인&amp;nbsp;Apple&amp;nbsp;지원:&amp;nbsp;Core&amp;nbsp;Data는&amp;nbsp;Apple에서&amp;nbsp;제공하는&amp;nbsp;공식&amp;nbsp;데이터&amp;nbsp;저장&amp;nbsp;및&amp;nbsp;관리&amp;nbsp;프레임워크로서&amp;nbsp;iOS&amp;nbsp;개발&amp;nbsp;생태계와&amp;nbsp;밀접한&amp;nbsp;통합을&amp;nbsp;제공합니다.&amp;nbsp;SwiftUI와의&amp;nbsp;호환성이&amp;nbsp;뛰어나며,&amp;nbsp;Apple의&amp;nbsp;업데이트에&amp;nbsp;즉시&amp;nbsp;대응할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;간편한&amp;nbsp;통합:&amp;nbsp;SwiftUI에서&amp;nbsp;@FetchRequest를&amp;nbsp;사용하면&amp;nbsp;데이터를&amp;nbsp;가져오고&amp;nbsp;SwiftUI&amp;nbsp;뷰와&amp;nbsp;쉽게&amp;nbsp;바인딩할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;이는&amp;nbsp;간단한&amp;nbsp;데이터&amp;nbsp;바인딩과&amp;nbsp;데이터&amp;nbsp;변경&amp;nbsp;감지를&amp;nbsp;위한&amp;nbsp;편리한&amp;nbsp;방법을&amp;nbsp;제공합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;복잡한&amp;nbsp;데이터&amp;nbsp;모델&amp;nbsp;처리:&amp;nbsp;Core&amp;nbsp;Data는&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;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;단점:&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;러닝&amp;nbsp;커브:&amp;nbsp;Core&amp;nbsp;Data는&amp;nbsp;초기&amp;nbsp;설정과&amp;nbsp;개념&amp;nbsp;이해에&amp;nbsp;시간이&amp;nbsp;걸릴&amp;nbsp;수&amp;nbsp;있으며,&amp;nbsp;초보자에게는&amp;nbsp;조금&amp;nbsp;복잡할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;성능&amp;nbsp;문제:&amp;nbsp;대량의&amp;nbsp;데이터를&amp;nbsp;다루는&amp;nbsp;경우&amp;nbsp;성능&amp;nbsp;문제가&amp;nbsp;발생할&amp;nbsp;수&amp;nbsp;있으며,&amp;nbsp;신중한&amp;nbsp;최적화가&amp;nbsp;필요할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Realm&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Realm: https://github.com/realm/realm-swift&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;장점:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;빠른&amp;nbsp;성능:&amp;nbsp;Realm은&amp;nbsp;객체&amp;nbsp;그래프를&amp;nbsp;직접&amp;nbsp;다루므로&amp;nbsp;작업이&amp;nbsp;빠르며&amp;nbsp;효율적입니다.&amp;nbsp;대량의&amp;nbsp;데이터&amp;nbsp;처리&amp;nbsp;및&amp;nbsp;복잡한&amp;nbsp;쿼리에&amp;nbsp;강력합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;간단한&amp;nbsp;API:&amp;nbsp;Realm은&amp;nbsp;간단한&amp;nbsp;API를&amp;nbsp;제공하여&amp;nbsp;데이터를&amp;nbsp;쉽게&amp;nbsp;저장하고&amp;nbsp;검색할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;SwiftUI와&amp;nbsp;함께&amp;nbsp;사용하기&amp;nbsp;특히&amp;nbsp;편리합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;다중&amp;nbsp;플랫폼&amp;nbsp;호환성:&amp;nbsp;Realm은&amp;nbsp;iOS&amp;nbsp;뿐만&amp;nbsp;아니라&amp;nbsp;Android,&amp;nbsp;React&amp;nbsp;Native&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;단점:&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;비공식&amp;nbsp;솔루션:&amp;nbsp;Realm은&amp;nbsp;Apple에서&amp;nbsp;제공하는&amp;nbsp;공식&amp;nbsp;프레임워크가&amp;nbsp;아니므로,&amp;nbsp;Apple의&amp;nbsp;업데이트와&amp;nbsp;호환성을&amp;nbsp;유지하는&amp;nbsp;데&amp;nbsp;약간의&amp;nbsp;노력이&amp;nbsp;필요할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;상대적으로&amp;nbsp;작은&amp;nbsp;커뮤니티:&amp;nbsp;Core&amp;nbsp;Data에&amp;nbsp;비해&amp;nbsp;상대적으로&amp;nbsp;작은&amp;nbsp;커뮤니티를&amp;nbsp;가지고&amp;nbsp;있으므로&amp;nbsp;문제&amp;nbsp;해결과&amp;nbsp;지원을&amp;nbsp;찾는&amp;nbsp;것이&amp;nbsp;어려울&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;결론&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SwiftUI와&amp;nbsp;함께&amp;nbsp;데이터를&amp;nbsp;저장하기&amp;nbsp;위해&amp;nbsp;Core&amp;nbsp;Data와&amp;nbsp;Realm은&amp;nbsp;각각&amp;nbsp;장점과&amp;nbsp;단점을&amp;nbsp;가지고&amp;nbsp;있습니다.&amp;nbsp;Core&amp;nbsp;Data는&amp;nbsp;Apple의&amp;nbsp;지원과&amp;nbsp;통합성을&amp;nbsp;강조하며&amp;nbsp;복잡한&amp;nbsp;데이터&amp;nbsp;모델을&amp;nbsp;다루기에&amp;nbsp;적합합니다.&amp;nbsp;반면,&amp;nbsp;Realm은&amp;nbsp;빠른&amp;nbsp;성능과&amp;nbsp;간단한&amp;nbsp;API로&amp;nbsp;대량의&amp;nbsp;데이터&amp;nbsp;처리에&amp;nbsp;적합하며&amp;nbsp;다중&amp;nbsp;플랫폼&amp;nbsp;호환성을&amp;nbsp;제공합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;두 프레임워크 모두 강력하며 SwiftUI와 함께 원활하게 통합될 수 있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS 앱개발/iOS</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/348</guid>
      <comments>https://jayb-log.tistory.com/348#entry348comment</comments>
      <pubDate>Sat, 9 Sep 2023 16:31:02 +0900</pubDate>
    </item>
    <item>
      <title>[헤드퍼스트 디자인패턴 with Swift] - 2.옵저버 패턴(Observer Pattern)</title>
      <link>https://jayb-log.tistory.com/347</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctCYAC/btr4DrTqkgW/GkgprjuPXpTp5tQ1Yay7p0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctCYAC/btr4DrTqkgW/GkgprjuPXpTp5tQ1Yay7p0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctCYAC/btr4DrTqkgW/GkgprjuPXpTp5tQ1Yay7p0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctCYAC%2Fbtr4DrTqkgW%2FGkgprjuPXpTp5tQ1Yay7p0%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;453&quot; height=&quot;547&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;☀️ 기상 모니터링 애플리케이션 만들기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;현재 기상 조건(온도, 습도, 기압)&lt;/b&gt;을 추적해서 아래 결과를 표시하는 앱을 만들어 보려고 해요 :]&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 현재 조건&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. 기상 통계&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 간단한 기상 예보&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;기상 모니터링이므로 당연히 이 항목들은 모두 실시간으로 갱신돼야 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기에 더해 추가 요구 사항이 있어요..&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;다른 개발자가 직접 날씨 디스플레이를 만들어서 바로 넣을 수 있도록 확장 가능해야 합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;나중에 새로운 디스플레이를 손쉽게 추가할 수 있도록 해주세요.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  시스템 파악하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 시스템은 총 3가지로 구성되어 있어요.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #99cefa; font-family: 'Noto Serif KR';&quot;&gt;1. 기상 스테이션&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;➡️ 실제 기상 정보를 수집하는 물리 장비(데이터를 제공하는 부분)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #99cefa; font-family: 'Noto Serif KR';&quot;&gt;2. 기상 스테이션으로부터 오는 정보를 추적하는 객체&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;➡️ WeatherData 객체&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #99cefa; font-family: 'Noto Serif KR';&quot;&gt;3. (제공되는 데이터를 볼 수 있는) 디스플레이 장비&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;➡️ 원하는 조건을 선택해서 표시 가능&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;쉽게 생각하면 서버에서 오는 정보를 View로 띄워주는 것처럼&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;기상 스테이션 ➡️ 데이터 가공 ➡️ 디스플레이 장비&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;의 순서로 동작이 이루어져요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 직접 구현해야하는 부분은 아래 2가지입니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. Weather Data를 받아 통합하여 재가공&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. 디스플레이로 1의 결과 보여주기(옵션 선택 가능)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  WeatherData 클래스 살펴보기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;먼저 객체를 살펴볼게요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xNt0E/btr4v8tywd7/VcyNqB2KxnIsh9Dyh9rov1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xNt0E/btr4v8tywd7/VcyNqB2KxnIsh9Dyh9rov1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xNt0E/btr4v8tywd7/VcyNqB2KxnIsh9Dyh9rov1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxNt0E%2Fbtr4v8tywd7%2FVcyNqB2KxnIsh9Dyh9rov1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1374&quot; height=&quot;306&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Swift에서는 get으로 시작하는 메서드 네이밍을 지양하지만, 책에서 Java를 베이스로 하고 있기 때문에 이름을 그대로 가져왔어요.&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: 'Noto Serif KR';&quot;&gt;이제 해야 할 작업은 기상 관측값이 갱신될 때마다 호출될 메서드 &lt;b&gt;measurementsChanged()&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; &amp;zwj;♂️ measurementsChanged 구현하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 메서드는 결국 &lt;b&gt;새로 가져온 정보를 기준으로 디스플레이를 업데이트&lt;/b&gt;하는 작업을 해야 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;구현 목표를 조금 더 정리해 볼게요!&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. measurementsChanged() 메서드가 어떤 식으로 호출되는지 알 필요는 없음(호출된다는 사실만 앎)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. measurementsChanged()에서 디스플레이를 업데이트&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;+ 추가될 디스플레이를 대비해 확장성 있게 만들기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이것을 바탕으로 코드를 추가해 볼게요!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4zbgO/btr4vn5Vzg0/p7LzNXN7tg3sDWBS4MDSlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4zbgO/btr4vn5Vzg0/p7LzNXN7tg3sDWBS4MDSlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4zbgO/btr4vn5Vzg0/p7LzNXN7tg3sDWBS4MDSlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4zbgO%2Fbtr4vn5Vzg0%2Fp7LzNXN7tg3sDWBS4MDSlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1818&quot; height=&quot;780&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;780&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이름에서 유추할 수 있듯이 각 메서드는 우리가 출력해야 될 View의 결과물들을 업데이트하는 역할을 해요.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 현재 조건 업데이트 ➡️ currentConditionsDisplay.update&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. 기상 통계 업데이트 ➡️ statisticsDisplay.update&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 간단한 기상 예보 업데이트 ➡️ forecastDisplay.update&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;즉, 디스플레이를 갱신해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 문제는 무엇일까요?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  현재 구현 문제 이해하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 화면을 update해주는 메서드들을 각 Display 객체가 가지고 있고, 이 객체들은 구체적인 구상체예요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 이 클래스는 다른 구상체에 맞춰서 코딩했기 때문에(의존) 수정이 생길 경우 이 부분에 반드시 수정이 필요해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;➡️ 캡슐화 필요&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. 세 종류의 디스플레이가 모두 update 메서드를 가지며 같은 parameter들을 입력받고 있어요.&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: 'Noto Serif KR';&quot;&gt;3. 실행 중에 디스플레이를 더하거나 빼기가 어려운 구조예요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  옵저버 패턴 사용하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;사실 iOS 개발자들은 Notification Center의 존재 때문에 Observer Pattern에 익숙한 것 같아요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위의 구현을 옵저버 패턴을 사용해서 구현해 볼게요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 용어 정리가 필요한데,&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;관찰할 데이터 ➡️ Subject(주제)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 데이터를 관찰할 관찰자 ➡️ Observer&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;라고 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;즉, 옵저버 객체들은 대상 주제를 구독하고 있으며 주제 데이터가 바뀌면 갱신 내용을 전달받아요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;(저는 주제라는 말보다는 관찰 대상이라는 말이 조금 더 와닿네요)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  옵저버 패턴의 정의&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;옵저버 패턴&lt;/b&gt;은 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(1:N) 의존성을 정의합니다.&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 1은 당연히 Subject, N은 Observer가 될 거예요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1 : N의 의미는 결국 하나의 Subject를 여러 Observer들이 동시에 관찰할 수 있다는 의미예요.&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: 'Noto Serif KR';&quot;&gt;&lt;b&gt;옵저버 패턴에서는 주제가 상태를 저장하고 제어해요.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 상태가 들어있는 객체는 하나만 있을 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;반면에 &lt;b&gt;옵저버는&lt;/b&gt; 상태를 사용하지만 (반드시) &lt;b&gt;소유할 필요는 없어요&lt;/b&gt;(관찰한 결과물을 활용하면 되니까).&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 옵저버는 하나의 주제에 대해 여러 개가 있을 수 있으며, 주제에서 상태가 바뀌었다는 사실을 알려 주길 기다리는 &lt;b&gt;주제에 의존적인 성질&lt;/b&gt;을 가져요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;옵저버 패턴은 여러 방법으로 구현할 수 있는데, 보통은 주제 인터페이스와 옵저버 인터페이스가 들어있는 클래스 디자인으로 구현해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Subject(관찰 대상)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eiX8fa/btr4xh4Xrgw/92s1FLTgMRYEIKWUESK9Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eiX8fa/btr4xh4Xrgw/92s1FLTgMRYEIKWUESK9Dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eiX8fa/btr4xh4Xrgw/92s1FLTgMRYEIKWUESK9Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeiX8fa%2Fbtr4xh4Xrgw%2F92s1FLTgMRYEIKWUESK9Dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1232&quot; height=&quot;670&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Observer(옵저버)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1YnmS/btr4LHg1CQ5/HCo9CndmwbgXnZIYTqTae0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1YnmS/btr4LHg1CQ5/HCo9CndmwbgXnZIYTqTae0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1YnmS/btr4LHg1CQ5/HCo9CndmwbgXnZIYTqTae0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1YnmS%2Fbtr4LHg1CQ5%2FHCo9CndmwbgXnZIYTqTae0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1152&quot; height=&quot;406&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  느슨한 결합(Loose Coupling)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;느슨한 결합은 객체들이 상호작용할 수 있지만, 서로를 잘 모르는 관계를 의미해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 객체들의 관계를 느슨하게 설계하면, 유연한 구조를 만들 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;옵저버 패턴은 느슨한 결합의 대표적인 예예요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;1. 주제는 옵저버가 특정 인터페이스(Swift에서 프로토콜)를 구현한다는 사실만 알아요&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;주제가 옵저버의 구상 클래스가 무엇인지, 옵저버가 무엇을 하는지 알 필요가 없어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;2. 옵저버는 아무 때나 새로 추가하거나 제거할 수 있어요&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;주제는 Observer 인터페이스를 구현하는 객체의 목록에만 의존하기 때문에 이 목록을 변경함으로써 언제든지 새로운 옵저버를 추가하거나 기존 옵저버를 제거할 수 있어요.&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: 'Noto Serif KR';&quot;&gt;&lt;b&gt;3. 새로운 형식의 옵저버를 추가할 때도 주제를 변경할 필요가 없어요&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;새로운 구상 클래스가 생겼다고 해도 기존의 주제는 변경이 필요하지 않아요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;새로운 클래스에서 Observer 인터페이스(프로토콜)를 구현하고, 옵저버로 등록하기만 하면 돼요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;주제는 신경 쓸 필요가 없어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;4. 주제와 옵저버는 서로 독립적으로 재사용할 수 있어요&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;5. 주제나 옵저버가 달라져도 서로에게 영향을 미치지 않아요&lt;/b&gt;&lt;/span&gt;&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;&lt;span style=&quot;background-color: #99cefa; font-family: 'Noto Serif KR';&quot;&gt;  디자인 원칙: 상호작용하는 객체 사이에는 가능하면 느슨한 결합을 사용해야 한다.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  기상 스테이션 구현하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위에서 설명한 구조를 Observer 패턴에 맞게 다시 구성해 볼게요.&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: 'Noto Serif KR';&quot;&gt;책에서는 우선 update시에 측정치를 직접 전달하는 게 상태를 갱신하는 가장 간단한 방법이라고 생각했어요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;938&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nR6s2/btr4w5Kk6gc/kmPzke8Hcda1bu3YrC2VE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nR6s2/btr4w5Kk6gc/kmPzke8Hcda1bu3YrC2VE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nR6s2/btr4w5Kk6gc/kmPzke8Hcda1bu3YrC2VE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnR6s2%2Fbtr4w5Kk6gc%2FkmPzke8Hcda1bu3YrC2VE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1722&quot; height=&quot;938&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  Subject 프로토콜 구현하기&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1556&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CyiE3/btr4wFd7g1u/Swhei8Aul34lcYVVZdWr1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CyiE3/btr4wFd7g1u/Swhei8Aul34lcYVVZdWr1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CyiE3/btr4wFd7g1u/Swhei8Aul34lcYVVZdWr1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCyiE3%2Fbtr4wFd7g1u%2FSwhei8Aul34lcYVVZdWr1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1556&quot; height=&quot;836&quot; data-origin-width=&quot;1556&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nq1ZZ/btr4LHOTDHC/X0ygKsPpnha57OY01pLs01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nq1ZZ/btr4LHOTDHC/X0ygKsPpnha57OY01pLs01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nq1ZZ/btr4LHOTDHC/X0ygKsPpnha57OY01pLs01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnq1ZZ%2Fbtr4LHOTDHC%2FX0ygKsPpnha57OY01pLs01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1916&quot; height=&quot;738&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;738&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  디스플레이 요소 구현하기&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;1142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9PHaJ/btr4G8zgx37/cHiZLAEw5kH3KbiUkDhMy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9PHaJ/btr4G8zgx37/cHiZLAEw5kH3KbiUkDhMy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9PHaJ/btr4G8zgx37/cHiZLAEw5kH3KbiUkDhMy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9PHaJ%2Fbtr4G8zgx37%2FcHiZLAEw5kH3KbiUkDhMy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1384&quot; height=&quot;1142&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;1142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 구현 중 update 메서드 내에서 display 하는 부분이 상당히 마음에 걸렸어요... &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마침 책에서도 그 이야기를 다루고, 추후 다른 챕터에서 다룬다고 이야기해요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;☁️ Weather Station 만들기&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zk7mk/btr4w5p90bq/yDFK2TCKWg6Ty79I7NSIT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zk7mk/btr4w5p90bq/yDFK2TCKWg6Ty79I7NSIT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zk7mk/btr4w5p90bq/yDFK2TCKWg6Ty79I7NSIT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZk7mk%2Fbtr4w5p90bq%2FyDFK2TCKWg6Ty79I7NSIT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1746&quot; height=&quot;734&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;☕️ Cocoa의 Key-Value Observing을 통해 실제 옵저버 패턴 예시 보기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;책에서는 이러한 옵저버 패턴의 예시 중 하나로 Cocoa를 이야기해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;실제로는 어떻게 옵저버 패턴이 적용되는지 살펴볼게요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swift/using-key-value-observing-in-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.apple.com/documentation/swift/using-key-value-observing-in-swift&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679212988945&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Using Key-Value Observing in Swift | Apple Developer Documentation&quot; data-og-description=&quot;Notify objects about changes to the properties of other objects.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/swift/using-key-value-observing-in-swift&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/swift/using-key-value-observing-in-swift&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hqOFJ/hyRZbnjEhx/VZYaHIgSDPl0yDktSstIJ1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bemqV2/hyRXEknnMI/jzKhQLOJVYTQNgfMXCJ9K1/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swift/using-key-value-observing-in-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/swift/using-key-value-observing-in-swift&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hqOFJ/hyRZbnjEhx/VZYaHIgSDPl0yDktSstIJ1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bemqV2/hyRXEknnMI/jzKhQLOJVYTQNgfMXCJ9K1/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Using Key-Value Observing in Swift | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Notify objects about changes to the properties of other objects.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYmayI/btr4RA2Z9Ae/2upVtkl5B6TVypOnC0zYi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYmayI/btr4RA2Z9Ae/2upVtkl5B6TVypOnC0zYi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYmayI/btr4RA2Z9Ae/2upVtkl5B6TVypOnC0zYi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYmayI%2Fbtr4RA2Z9Ae%2F2upVtkl5B6TVypOnC0zYi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1516&quot; height=&quot;308&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 말하는 MyObjectToObserve는 책에서 이야기하는 Subject에요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;(이해하기 쉬운 네이밍이군요..)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&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-origin-width=&quot;1712&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7mbBZ/btr4FlS3Rrd/uspa7LcG5gYifERCXHOJV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7mbBZ/btr4FlS3Rrd/uspa7LcG5gYifERCXHOJV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7mbBZ/btr4FlS3Rrd/uspa7LcG5gYifERCXHOJV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7mbBZ%2Fbtr4FlS3Rrd%2Fuspa7LcG5gYifERCXHOJV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1712&quot; height=&quot;804&quot; data-origin-width=&quot;1712&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;MyObserver는 당연히 옵저빙 하는 객체예요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마찬가지로 관찰 대상(subject)을 저장하고, 그 정보를 기반으로 observation이라는 프로퍼티를 저장해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;메서드를 보면 저장 후에 결과를 print해주는 부분이 completion handler로 정의되어 있는 것 같아요.&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: 'Noto Serif KR';&quot;&gt;마찬가지로 아래와 같이 정의해 주고&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN5kJt/btr4JLjBpR1/KosDK5RikBFplGIVLbWVck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN5kJt/btr4JLjBpR1/KosDK5RikBFplGIVLbWVck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN5kJt/btr4JLjBpR1/KosDK5RikBFplGIVLbWVck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN5kJt%2Fbtr4JLjBpR1%2FKosDK5RikBFplGIVLbWVck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;110&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래와 같이 사용하면 돼요!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1756&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/10fJp/btr4xLZf3F9/zXuykgeJWJ4y6lvrGRGNuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/10fJp/btr4xLZf3F9/zXuykgeJWJ4y6lvrGRGNuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/10fJp/btr4xLZf3F9/zXuykgeJWJ4y6lvrGRGNuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F10fJp%2Fbtr4xLZf3F9%2FzXuykgeJWJ4y6lvrGRGNuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1756&quot; height=&quot;122&quot; data-origin-width=&quot;1756&quot; data-origin-height=&quot;122&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #99cefa; font-family: 'Noto Serif KR';&quot;&gt;  디자인 원칙: 상속보다는 구성(composition)을 사용한다&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;상속 ➡️ &lt;b&gt;is-a&lt;/b&gt; 관계&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;구성 ➡️ &lt;b&gt;has- a&lt;/b&gt; 관계&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;구성은&amp;nbsp;중복되는 로직들을 갖는 객체를 분리 구현하고, 이 객체를 주입받아 중복 로직을 호출함으로써 프로토콜을 재사용하는 방법이에요.&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;상속은 중복 코드를 제거하는 좋은 방식이지만 결합도가 높아지고 유연성이 떨어지며, 다중 상속의 문제 등을 유발할 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 중복 코드를 제거해야 할 때에는 구성을 활용하는 것이 더 좋아 보여요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2장은 여기까지입니다 :]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;정리하면, 옵저버 패턴은 관찰자와 관찰 대상을 나누어 구현하고, 1:N의 관계에서 관찰 대상에 변화가 생겼을 때 일괄적으로 관찰자에게 해당 변경을 적용하는 패턴이에요.&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: 'Noto Serif KR';&quot;&gt;결국 책임을 분리해서 결합도를 낮추고, &quot;변화의 trigger ➡️ 변화를 만드는 객체&quot;의 구조라고 이해했어요.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발 독서</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/347</guid>
      <comments>https://jayb-log.tistory.com/347#entry347comment</comments>
      <pubDate>Sun, 19 Mar 2023 17:25:10 +0900</pubDate>
    </item>
    <item>
      <title>[헤드퍼스트 디자인패턴 with Swift] - 1. 전략 패턴(Strategy Pattern)</title>
      <link>https://jayb-log.tistory.com/346</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFitKi/btr3dcdlv2u/WXBFTKxrtlQzWlSBHF8a2K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFitKi/btr3dcdlv2u/WXBFTKxrtlQzWlSBHF8a2K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFitKi/btr3dcdlv2u/WXBFTKxrtlQzWlSBHF8a2K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFitKi%2Fbtr3dcdlv2u%2FWXBFTKxrtlQzWlSBHF8a2K%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;453&quot; height=&quot;547&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  헤드퍼스트 디자인패턴 시작하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&quot;헤드퍼스트 디자인패턴&quot;은 Java를 기반으로 쓰인 책이지만, 여기서는 Swift로 구현하며 내용을 정리해 볼게요!&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;항상 그렇지만 이 글은 개인적인 정리의 목적으로 작성하는 것이기 때문에 책을 직접 읽어보기를 추천드려요 :]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  오리 시뮬레이션 게임 만들기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;SimUDuck이라는 오리 게임을 만들어볼게요 :]&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;방법 1. 일반적인 상속 구조 사용하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;일반적인 표준 객체지향 기법을 사용하여 Duck이라는 Super class를 만들고, 상속을 통해 여러 오리를 만들어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;(private, final 등 세세한 설정 등은 생략할게요)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfH1Tg/btr3dblbNXV/sOu2Y8G5Q8e1ixeQANkMoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfH1Tg/btr3dblbNXV/sOu2Y8G5Q8e1ixeQANkMoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfH1Tg/btr3dblbNXV/sOu2Y8G5Q8e1ixeQANkMoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfH1Tg%2Fbtr3dblbNXV%2FsOu2Y8G5Q8e1ixeQANkMoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;476&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;각 오리들은 부모 클래스(Duck)의 메서드를 override 하여 사용해요.&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: 'Noto Serif KR';&quot;&gt;  추가 요구 사항 발생&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;➡️ 오리는 날 수 있어야 합니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 Duck 클래스에 fly 메서드를 추가해요.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blX86I/btr3gNKn0FS/IzTWYSzEbvV70l9I5JuBp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blX86I/btr3gNKn0FS/IzTWYSzEbvV70l9I5JuBp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blX86I/btr3gNKn0FS/IzTWYSzEbvV70l9I5JuBp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblX86I%2Fbtr3gNKn0FS%2FIzTWYSzEbvV70l9I5JuBp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;97&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 되면 모든 오리에 하늘을 나는 기능이 추가돼요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;문제는 오리 중에 장난감 오리처럼 하늘을 날 수 없는 오리가 있다는 거예요  &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KxGHn/btr3dcYF0UZ/ZJgXLVvTSUmZDk3wWJxizK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KxGHn/btr3dcYF0UZ/ZJgXLVvTSUmZDk3wWJxizK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KxGHn/btr3dcYF0UZ/ZJgXLVvTSUmZDk3wWJxizK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKxGHn%2Fbtr3dcYF0UZ%2FZJgXLVvTSUmZDk3wWJxizK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;872&quot; height=&quot;490&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 문제를 단편적으로 해결하기 위해서는 날지 못하는 오리에서 fly를 override 하여 아무 동작도 하지 않게 만들 수 있어요.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCCMGY/btr3gO3ARPf/Qr09jHxydLY20wnGmKkNmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCCMGY/btr3gO3ARPf/Qr09jHxydLY20wnGmKkNmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCCMGY/btr3gO3ARPf/Qr09jHxydLY20wnGmKkNmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCCMGY%2Fbtr3gO3ARPf%2FQr09jHxydLY20wnGmKkNmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;412&quot; height=&quot;184&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;하지만 여기서 다른 문제가 발생해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  추가 요구 사항 발생&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;➡️ 앞으로 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: 'Noto Serif KR';&quot;&gt;이렇게 상속을 사용하는 상황에서 제품 스펙이 지속적으로 업데이트된다면 유지 보수에 큰 리소스가 들어가요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 Duck의 메서드들을 전부 살펴보면서 상황에 따라 변경해야 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;방법 2. 프로토콜(인터페이스) 사용하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위의 방법 1에서 구현한 fly() 메서드를 Duck 클래스에서 빼고 Flyable이라는 Protocol을 만들어서 채택하는 구조로 만들어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 하면 같은 Duck이라고 하더라도 &quot;하늘을 나는 능력을 가진 오리&quot;만 이 프로토콜을 채택시켜서 문제를 해결할 수 있어요.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJ39m2/btr3gOo0Dyg/PYujbQ5nMkSG1CJGhediF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJ39m2/btr3gOo0Dyg/PYujbQ5nMkSG1CJGhediF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJ39m2/btr3gOo0Dyg/PYujbQ5nMkSG1CJGhediF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJ39m2%2Fbtr3gOo0Dyg%2FPYujbQ5nMkSG1CJGhediF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;414&quot; height=&quot;266&quot; data-origin-width=&quot;722&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  책에서 말하는 이 방법의 단점&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;(자바 기준으로) 이렇게 구현하면 코드 중복이 많이 발생해요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;fly의 기능이 대부분 오리에서 같다고 했을 때, Flyable을 채택하는 오리들에서 같은 코드가 불필요하게 반복되어야 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;날아가는 동작을 조금 바꾸기 위해 Duck의 서브클래스에서 fly 메서드들을 전부 수정해야 할 수도 있다는 단점이 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369; font-family: 'Noto Serif KR';&quot;&gt;➡️ Swift에서는 사실 Protocol의 extension에서 func을 정의하는 기능이 있기 때문에 이 문제에서 벗어날 수 있어요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ef5369; font-family: 'Noto Serif KR';&quot;&gt;Java와 다르게 POP의 관점에서 코드를 구현할 수 있기 때문에 책에서 말하는 단점은 Swift에 적용되지 않는다고 생각해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #99cefa; font-family: 'Noto Serif KR';&quot;&gt;  디자인 원칙: 애플리케이션에서 달라지는 부분을 찾아내고, 달라지는 부분과 분리한다&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;달라지는 부분을 찾아서 나머지 코드에 영향을 주지 않도록 '캡슐화'해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그러면 코드를 변경하는 과정에서 의도치 않게 발생하는 일을 줄이면서 시스템의 유연성을 향상시킬 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 원칙을 적용하는데 많은 시간을 쏟아야 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;바뀌는 부분은 따로 뽑아서 캡슐화한다. 그러면 나중에 바뀌지 않는 부분에는 영향을 미치지 않고 그 부분만 고치거나 확장할 수 있다&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 개념은 다른 모든 디자인 패턴의 기반을 이루는 원칙이에요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이것을 기반으로 위의 오리 문제를 다시 살펴볼게요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Duck 클래스를 '변화하는 부분과 그대로 있는 부분'으로 분리하려면, 2개의 클래스 집합을 만들어야 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;하나는 하늘을 나는 것과 관련된 것, 다른 하나는 꽥꽥 거리는 것과 관련된 부분이에요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;각 클래스 집합에는 각각의 행동을 구현한 것을 전부 집어넣어요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;예를 들어, 꽥꽥거리는 행동을 구현하는 클래스, 삑삑거리는 행동을 구현하는 클래스, 그리고 아무 소리도 내지 않는 행동을 구현하는 클래스를 만들어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;fly()와 quack()은 오리 종류에 따라 달라져요.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;그래서 Duck 클래스로부터 이 두 메서드를 분리하려면 2개의 메서드 모두 Duck 클래스에서 끄집어내서 각 행동을 나타낼 클래스 집합을 새로 만들어야 해요.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그러면 이 행동들을 구현하는 클래스 집합은 어떻게 디자인해야 할까요?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;최대한 유연하게 만드는 것이 좋아요!&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;애초에 이러한 문제가 발생했던 이유가 이 부분에서 변경점이 생길 가능성이 높아서였으니까요.&lt;/span&gt;&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;&lt;span style=&quot;background-color: #99cefa; font-family: 'Noto Serif KR';&quot;&gt;  디자인 원칙: 구현보다는 인터페이스 맞춰서 프로그래밍한다.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;각 행동은 인터페이스로 표현하고, 이런 인터페이스를 사용해서 행동을 구현해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;이제 Duck 클래스에서 나는 행동과 꽥꽥거리는 행동을 구현하지 않아요. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;행동(Behavior) 인터페이스는 Duck 클래스가 아니라 행동 클래스에서 구현해요.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqgY4F/btr3oetS2pf/eoex0KHckcnWlfvrA0RK20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqgY4F/btr3oetS2pf/eoex0KHckcnWlfvrA0RK20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqgY4F/btr3oetS2pf/eoex0KHckcnWlfvrA0RK20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqgY4F%2Fbtr3oetS2pf%2Feoex0KHckcnWlfvrA0RK20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;409&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;796&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&quot;인터페이스에 맞춰서 프로그래밍한다&quot;라는 말은 사실 &quot;상위 형식에 맞춰서 프로그래밍한다&quot;라는 말입니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 말하는 인터페이스는 중의적으로 사용되었는데, 자바에서 말하는 인터페이스를 의미하기도 하고 인터페이스라는 개념을 지칭하기도 해요. 반드시 자바의 인터페이스(Swift의 프로토콜)를 사용하라는 뜻은 아니에요.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;핵심은 실제 실행 시에 쓰이는 객체가 코드에 고정되지 않도록 상위 형식에 맞춰 프로그래밍해서 다형성을 활용해야 한다는 것이에요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;또한 여기서 &lt;b&gt;&quot;상위 형식에 맞춰서 프로그래밍하라&quot;&lt;/b&gt;는 &lt;b&gt;&quot;변수를 선언할 때 보통 추상 클래스나 인터페이스 같은 상위 형식으로 선언해야 한다&quot;라는&lt;/b&gt; 의미예요. 객체를 변수에 대입할 때 상위 형식을 구체적으로 구현한 형식이라면 어떤 객체든 넣을 수 있는 유연성이 생기기 때문이에요. &quot;그러면 변수를 선언하는 클래스에서 실제 객체의 형식을 몰라도 된다&quot;는 의미로도 해석될 수 있어요.&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: 'Noto Serif KR';&quot;&gt;이렇게 구현한 결과물은 아래와 같아요.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J3Phd/btr3vPN25fp/sPiO54XzYOyXuaRkzCw7Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J3Phd/btr3vPN25fp/sPiO54XzYOyXuaRkzCw7Ok/img.png&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;800&quot; data-is-animation=&quot;false&quot; width=&quot;403&quot; height=&quot;425&quot; style=&quot;width: 58.0165%; margin-right: 10px;&quot; data-widthpercent=&quot;58.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J3Phd/btr3vPN25fp/sPiO54XzYOyXuaRkzCw7Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ3Phd%2Fbtr3vPN25fp%2FsPiO54XzYOyXuaRkzCw7Ok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;758&quot; height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pZg2l/btr3cCcoe36/qs1llu6OVnzaUemw74KbF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pZg2l/btr3cCcoe36/qs1llu6OVnzaUemw74KbF1/img.png&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;1074&quot; data-is-animation=&quot;false&quot; width=&quot;425&quot; height=&quot;638&quot; style=&quot;width: 40.8207%;&quot; data-widthpercent=&quot;41.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pZg2l/btr3cCcoe36/qs1llu6OVnzaUemw74KbF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpZg2l%2Fbtr3cCcoe36%2Fqs1llu6OVnzaUemw74KbF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;716&quot; height=&quot;1074&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 디자인하면 다른 형식의 객체(오리가 아닌 다른 객체)에서도 나는 행동과 꽥꽥 거리는 행동을 재사용할 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 행동 자체가 오리 안에 숨겨져있지 않으니까요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그리고 Duck 클래스를 건드리지 않고도 새로운 행동을 추가할 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;상속을 쓸 때 떠안게 되는 부담을 떨쳐 버리고 재사용의 장점을 가져갈 수 있어요 :]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  Q. 행동만 나타내는 클래스는 어색해요. 클래스는 원래 어떤 대상을 나타내는 것 아닌가요? 상태, 행동을 모두 포함해야 하는 것 아닌가요?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;객체지향 시스템에서는 질문한 내용이 맞습니다. 하지만 &quot;행동&quot;도 상태와 메서드를 가질 수 있어요! (최고 높이, 속도 등)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;객체라는 개념이 반드시 어떤 하나의 물체로 한정될 필요는 없어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그럼 위의 프로토콜을 가지고 Duck을 다시 구현해 볼게요!&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnMCkT/btr3pqA4pjD/kRbhfsTtKgvgS5S821TBQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnMCkT/btr3pqA4pjD/kRbhfsTtKgvgS5S821TBQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnMCkT/btr3pqA4pjD/kRbhfsTtKgvgS5S821TBQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnMCkT%2Fbtr3pqA4pjD%2FkRbhfsTtKgvgS5S821TBQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;444&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  동적으로 오리의 행동 지정하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;앞서서 특정 구현에 맞춰서 프로그래밍하면 안 된다고 했어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;현재는 앞에 있는 생성자를 보면 Quack라는 구상 클래스의 인스턴스로 선언되어 있기 때문에 Duck이 Quack의 구현에 맞춰져 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위에서는 프로퍼티의 생성 시점에 동작이 확정되지만, 이 부분을 유연하게 만들 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래와 같이 메서드를 추가하면 코드 진행 중에 동작의 변경이 가능해요.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwvhQM/btr3prUhrUU/x18gOLyJY3WHyhUSDLO3v1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwvhQM/btr3prUhrUU/x18gOLyJY3WHyhUSDLO3v1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwvhQM/btr3prUhrUU/x18gOLyJY3WHyhUSDLO3v1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwvhQM%2Fbtr3prUhrUU%2Fx18gOLyJY3WHyhUSDLO3v1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;714&quot; height=&quot;420&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  두 클래스의 관계&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&quot;A에는 B가 있다&quot;의 관계를 생각해 보면, 오리에는 FlyBehavior, QuackBehavior가 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;각각 나는 행동과 꽥꽥 거리는 행동을 위임해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이런 식으로 &lt;b&gt;&quot;두 클래스를 합치는 것을 구성(composition)을 이용한다&quot;라고&lt;/b&gt; 해요.&lt;/span&gt;&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;&lt;span style=&quot;background-color: #99cefa; font-family: 'Noto Serif KR';&quot;&gt;  디자인 원칙: 상속보다는 구성을 활용한다.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Composition을 활용하면 시스템의 유연성을 향상시킬 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  iOS에서 전략 패턴 구현해보기&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1684652314014&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protocol Theme {
    var backgroundColor: UIColor { get }
    var textColor: UIColor { get }
    // 추가적인 테마 관련 프로퍼티 및 메서드 정의
}

struct LightTheme: Theme {
    var backgroundColor: UIColor = .white
    var textColor: UIColor = .black
    // 추가적인 테마 관련 프로퍼티 및 메서드 구현
}

struct DarkTheme: Theme {
    var backgroundColor: UIColor = .black
    var textColor: UIColor = .white
    // 추가적인 테마 관련 프로퍼티 및 메서드 구현
}

class ViewController: UIViewController {
    var theme: Theme
    
    init(theme: Theme) {
        self.theme = theme
        super.init(nibName: nil, bundle: nil)
    }
    
    // 뷰 컨트롤러의 테마 관련 로직을 구현
    // 예: 배경색 설정, 텍스트 색상 설정 등
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위의&amp;nbsp;예시에서&amp;nbsp;Theme&amp;nbsp;프로토콜은&amp;nbsp;테마&amp;nbsp;관련&amp;nbsp;속성을&amp;nbsp;정의하고&amp;nbsp;있어요.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그리고 LightTheme과 DarkTheme은 각각 테마 프로토콜을 구현한 전략 클래스에요.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 구현된 전략 패턴을 통해 애플리케이션의 테마를 동적으로 변경할 수 있어요.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  정리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;전략 패턴(Strategy Pattern)은 알고리즘군을 정의하고 캡슐화해서 각각의 알고리즘군을 수정해서 쓸 수 있게 해 줘요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;전략 패턴을 사용하면 클라이언트로부터 알고리즘을 분리해서 독립적인 변경(관리)이 가능해요!&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발 독서</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/346</guid>
      <comments>https://jayb-log.tistory.com/346#entry346comment</comments>
      <pubDate>Sun, 12 Mar 2023 19:33:19 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] - Xcode 단축키 정리</title>
      <link>https://jayb-log.tistory.com/344</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKq11U/btrVkXH5KaO/CRV324m4CxDkZfqGO4rGbk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKq11U/btrVkXH5KaO/CRV324m4CxDkZfqGO4rGbk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKq11U/btrVkXH5KaO/CRV324m4CxDkZfqGO4rGbk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKq11U%2FbtrVkXH5KaO%2FCRV324m4CxDkZfqGO4rGbk%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;1200&quot; height=&quot;630&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;개요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;자주 쓰는 단축키는&amp;nbsp;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&amp;nbsp;초록색 &lt;/span&gt;으로 표시하고, 개인적으로 잘 외워지지 않는 단축키는&amp;nbsp;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt; 핑크색 &lt;/span&gt;으로 표시했어요(나중에 보려고 정리한 거라..)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;참고의 Apple 공식 문서에 들어가면 더 많은 단축키를 볼 수 있어요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;필요에 따라 지속적으로 업데이트될 예정이에요.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래 정보는 Xcode 14.1을 기준으로 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;혹시 잘못된 정보가 있다면 피드백 부탁드려요  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;  XCode 단축키&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Command(⌘) + Shift(⇧) + K&lt;/span&gt; : 프로젝트 클린&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Command(⌘) + B&lt;/span&gt; : 프로젝트 빌드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Command(⌘) + R&lt;/span&gt; : 프로젝트 실행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + I : 프로젝트 프로파일링 빌드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + T : 새로운 탭 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Command(⌘) + [&lt;/span&gt; : 해당 코드 왼쪽으로 indent 한 탭(4 spaces or 2 spaces) 이동&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + . : 실행 중인 앱 강제 종료&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + , : XCode 프로퍼티창 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Command(⌘) + Shift(⇧) + O&lt;/span&gt; : 프로젝트 내 파일 / 클래스 / 함수 검색&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Command(⌘) + Shift(⇧) + F&lt;/span&gt; : 파인드 창으로 캐럿 강제 이동&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Command(⌘) + Shift(⇧) + 숫자 0&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 해당 코드 관련 Document 창 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Command(⌘) + F&lt;/span&gt; : 현재 창 검색&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Command(⌘) + Shift(⇧) + J&lt;/span&gt; : 현재 보고 있는 파일 위치 확인&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + G : 현재 페이지에서 텍스트 에디트에 적힌 문구 순차 검색&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Shift(⇧) + G : 현재 페이지에서 텍스트 에디트에 적힌 문구 역순차 검색&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Option(⌥) + ,(Comma) : 현재창 이중 분할&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Option(⌥) + n : 새로운 폴더 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Enter : 현재창을 단일창으로 변경&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Command(⌘) + /&lt;/span&gt;&amp;nbsp;: 해당라인 주석 걸기 ( 다중 라인도 가능 )&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Control(⌃) + Command(⌘) + 좌우 화살표 : 이전/이후 페이지 이동&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Control(⌃) + I&lt;/span&gt; : 선택된 구간 정해진 설정으로 들여쓰기(Indentation)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Control(⌃) + Shift(⇧) + Click&lt;/span&gt;: 멀티 커서&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Command(⌘) + D&lt;/span&gt; : 줄 복사&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  Xcode 네비게이터(Navigator) 영역&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2710&quot; data-origin-height=&quot;1730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQQUbz/btrWdfgBLG5/0Om6mUlPXj8UWJMIE9BKs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQQUbz/btrWdfgBLG5/0Om6mUlPXj8UWJMIE9BKs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQQUbz/btrWdfgBLG5/0Om6mUlPXj8UWJMIE9BKs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQQUbz%2FbtrWdfgBLG5%2F0Om6mUlPXj8UWJMIE9BKs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2710&quot; height=&quot;1730&quot; data-origin-width=&quot;2710&quot; data-origin-height=&quot;1730&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;영역 : 프로젝트(1), 소스 Control(2), 심벌(3), 검색(4), 이슈(5), 테스트(6), 디버그(7), 브레이크 포인트(8), 리포트(9)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Command(⌘) + 0&lt;/span&gt; : 왼쪽에 네비게이터 영역 윈도우 숨기기 &amp;amp; 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + 1 : 프로젝트 파인더 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + 2 : 프로젝트 Hierarchy(계층도) 창 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + 3 : 프로젝트 검색 창 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + 4 : 프로젝트 warning 및 error 창 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + 6 : 테스트 타깃창 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + 7 : 디버그 세션 창 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + 8 : 모든 브레이크 포인트를 보여주는 창 열기(설정 및 해제 가능)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + 9 : 프로젝트 리포트 창 열기&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  인스펙터(Inspector) 영역&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2608&quot; data-origin-height=&quot;1662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H3HX6/btrWbT6q5KW/aYigROjKYrW8vXtzGnCEbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H3HX6/btrWbT6q5KW/aYigROjKYrW8vXtzGnCEbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H3HX6/btrWbT6q5KW/aYigROjKYrW8vXtzGnCEbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH3HX6%2FbtrWbT6q5KW%2FaYigROjKYrW8vXtzGnCEbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2608&quot; height=&quot;1662&quot; data-origin-width=&quot;2608&quot; data-origin-height=&quot;1662&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Command(⌘) + Option(⌥) + 0(숫자키)&lt;/span&gt; : 인스펙터(inspector) 영역 열기/닫기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Option(⌥) + 1~3(숫자키) : 인스펙터(inspector) 영역 내 이동&lt;/span&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;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  디버깅 관련&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: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Command(⌘) + \&lt;/span&gt;&amp;nbsp;: 브레이크 포인트 걸기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Command(⌘) + Y&lt;/span&gt;&amp;nbsp;: 브레이크 포인트 비활성화 / 활성화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Command(⌘) + Shift(⇧) + Y&lt;/span&gt; : 하단 디버그 세션 창 열기 / 닫기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + U: Unit Test 실행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;F6 : 다음 라인 ( Step Over )&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;F7 : 현재 라인 내부 진입 ( Step Into )&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;F8 : 현재 함수에서 나가기 ( Step Out )&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Shift(⇧) + Y : 실행 중 멈춤 ( Pause ) &amp;amp; 멈춤 상태에서 재 진행&lt;/span&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;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  Source Control 관련&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: 'Noto Serif KR';&quot;&gt;Command(⌘) + Shift(⇧) + C : 커밋창 열기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Shift(⇧) + X : 업데이트하기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  iOS Simulator&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: 'Noto Serif KR';&quot;&gt;Command(⌘) + S : 현재 시뮬레이터에 뜬 화면 캡처&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;기본적인 맥 단축키&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + A : 전체 선택&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Z : 되돌리기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + X : 자르기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + C : 복사&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + V : 붙여 넣기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + BackSpace : 해당 라인 지우기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + 방향키 : 해당 방향의 끝으로 이동&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Up: 해당 창 가장 위로(Home 키도 동일하게 작동)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Down : 해당 창 가장 아래로(End 키도 동일하게 작동)&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Left : 선택된 텍스트라인 가장 왼쪽으로&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Right : 선택된 텍스트라인 가장 오른쪽으로&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Control(⌃) + 좌/우 : 페이지 변경&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Shift(⇧) + 3 : 선택한 화면 영역 이미지 파일로 캡처&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Shift(⇧) + 4 : 선택한 화면 영역 클립보드 캡처&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Space : Spotlight 검색&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + W : 현재 창 닫기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + Q : 선택된 프로그램 종료&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Command(⌘) + H : 현재 창 숨기기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Control(⌃) + Space : 언어 변환&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;참고&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/xcode_help-command_shortcuts/MenuCommands/MenuCommands014.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/xcode_help-command_shortcuts/MenuCommands/MenuCommands014.html&lt;/a&gt;&lt;/span&gt;&lt;/blockquote&gt;</description>
      <category>iOS 앱개발/iOS</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/344</guid>
      <comments>https://jayb-log.tistory.com/344#entry344comment</comments>
      <pubDate>Sat, 14 Jan 2023 20:39:25 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] 앱 테스트를 위해 네트워크 속도 조절하기(Mac &amp;amp; iPhone)</title>
      <link>https://jayb-log.tistory.com/343</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biARsM/btrNYqbO34r/k6oRqSi5cpUrp8vCfvasRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biARsM/btrNYqbO34r/k6oRqSi5cpUrp8vCfvasRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biARsM/btrNYqbO34r/k6oRqSi5cpUrp8vCfvasRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiARsM%2FbtrNYqbO34r%2Fk6oRqSi5cpUrp8vCfvasRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;342&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;개요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;앱의 크래쉬를 분석하다 보면 네트워크 이슈를 의심하는 경우가 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그런 상황을 재현하기 위해 맥에서 네트워크 속도를 조절할 수 있는 툴을 소개합니다 :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Mac에서 네트워크 속도 조절하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Network Link Conditioner는 애플에서 공식적으로 제공해주는 도구예요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 안심하고 사용할 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;설치하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2022년 10월 기준 Network Link Conditioner는 Apple Developer 사이트에서 다운로드할 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;a href=&quot;https://developer.apple.com/download/all/?q=additional%20tools&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Apple Developer Downloads&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1665042550773&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;로그인 - Apple&quot; data-og-description=&quot;&quot; data-og-host=&quot;idmsa.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/download/all/?q=additional%20tools&quot; data-og-url=&quot;https://idmsa.apple.com/IDMSWebAuth/signin.html?path=%2Fdownload%2Fall%2F%3Fq%3Dadditional%2Btools&amp;amp;appIdKey=891bd3417a7776362562d2197f89480a8547b108fd934911bcbea0110d07f757&amp;amp;rv=0&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/download/all/?q=additional%20tools&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/download/all/?q=additional%20tools&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;로그인 - Apple&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;idmsa.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Additional Tools 혹은 Network Link Conditioner로 검색하면 아래와 같이 나와요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img 61.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnNBpa/btrNUF2Psba/xYKNzB8KT0FkslgXDR42I1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnNBpa/btrNUF2Psba/xYKNzB8KT0FkslgXDR42I1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnNBpa/btrNUF2Psba/xYKNzB8KT0FkslgXDR42I1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnNBpa%2FbtrNUF2Psba%2FxYKNzB8KT0FkslgXDR42I1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1844&quot; height=&quot;682&quot; data-filename=&quot;img 61.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Network Link Conditioner를 직접 받는 게 아니라 Additional Tools를 받아서 그 안에 있는 Network Link Conditioner를 사용할 수 있어요.&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: 'Noto Serif KR';&quot;&gt;현재 본인의 Xcode 버전에 맞는 툴을 받아 dmg 파일을 실행시키면 아래와 같이 폴더가 구분돼요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHFQ7h/btrNYpYhUO1/Tni0UVssyueYFTjVsvFBTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHFQ7h/btrNYpYhUO1/Tni0UVssyueYFTjVsvFBTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHFQ7h/btrNYpYhUO1/Tni0UVssyueYFTjVsvFBTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHFQ7h%2FbtrNYpYhUO1%2FTni0UVssyueYFTjVsvFBTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1684&quot; height=&quot;384&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그 안에 .prePane 파일이 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oMQGf/btrNVhnb5vv/pmgVSUWssnvUB0HIZNZ6wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oMQGf/btrNVhnb5vv/pmgVSUWssnvUB0HIZNZ6wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oMQGf/btrNVhnb5vv/pmgVSUWssnvUB0HIZNZ6wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoMQGf%2FbtrNVhnb5vv%2FpmgVSUWssnvUB0HIZNZ6wk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;288&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;실행시킵니다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 사용자를 위해 설치할거냐고 물어보는데 OK를 하면&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BA6St/btrNY1Jy2kJ/brPP0ppmU0kSCTuxjIBYS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BA6St/btrNY1Jy2kJ/brPP0ppmU0kSCTuxjIBYS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BA6St/btrNY1Jy2kJ/brPP0ppmU0kSCTuxjIBYS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBA6St%2FbtrNY1Jy2kJ%2FbrPP0ppmU0kSCTuxjIBYS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1278&quot; height=&quot;606&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;606&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;맥의 시스템 환경설정에서 하나가 추가됩니다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;사용하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;설명이 필요하지 않을 정도로 간단합니다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Profile에서 원하는 네트워크 환경을 설정해주고&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xBnnM/btrNWrXkIHQ/SvkYRtH16eapZzMhREp7Ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xBnnM/btrNWrXkIHQ/SvkYRtH16eapZzMhREp7Ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xBnnM/btrNWrXkIHQ/SvkYRtH16eapZzMhREp7Ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxBnnM%2FbtrNWrXkIHQ%2FSvkYRtH16eapZzMhREp7Ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;444&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;OFF를 ON으로 켜주면&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/raBk5/btrNWqxm5rc/KlFATZSzV0au9hTKWjkdo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/raBk5/btrNWqxm5rc/KlFATZSzV0au9hTKWjkdo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/raBk5/btrNWqxm5rc/KlFATZSzV0au9hTKWjkdo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FraBk5%2FbtrNWqxm5rc%2FKlFATZSzV0au9hTKWjkdo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;392&quot; height=&quot;430&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;테스트 환경이 완성됩니다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;iPhone에서 네트워크 속도 조절하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; iPhone은 추가적인 설치 없이도 손쉽게 네트워크 테스트를 할 수 있어요!&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: 'Noto Serif KR';&quot;&gt;설정 &amp;gt; 개발자 모드&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;에 들어가면 Network Link Conditioner가 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;들어가 보면&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_21BF993C2835-1.jpeg&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rCyJ2/btrNYpK3ta6/dJlklHzGRj79FTqb4Zr150/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rCyJ2/btrNYpK3ta6/dJlklHzGRj79FTqb4Zr150/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rCyJ2/btrNYpK3ta6/dJlklHzGRj79FTqb4Zr150/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrCyJ2%2FbtrNYpK3ta6%2FdJlklHzGRj79FTqb4Zr150%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;578&quot; height=&quot;1251&quot; data-filename=&quot;IMG_21BF993C2835-1.jpeg&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;똑같이 속도 조절이 가능합니다!&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS 앱개발/iOS</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/343</guid>
      <comments>https://jayb-log.tistory.com/343#entry343comment</comments>
      <pubDate>Thu, 6 Oct 2022 21:49:09 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] - XCTest 놓치고 있던 내용 가벼운 정리</title>
      <link>https://jayb-log.tistory.com/342</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IYGRA/btrLQmJ7oae/eGmEnPruUK4cQ8aNqEE1K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IYGRA/btrLQmJ7oae/eGmEnPruUK4cQ8aNqEE1K0/img.png&quot; data-alt=&quot;ref:&amp;amp;nbsp;https://www.agnosticdev.com/blog-entry/swift/swift-network-testing-automate-xctest-python&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IYGRA/btrLQmJ7oae/eGmEnPruUK4cQ8aNqEE1K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIYGRA%2FbtrLQmJ7oae%2FeGmEnPruUK4cQ8aNqEE1K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;644&quot; height=&quot;644&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ref:&amp;nbsp;https://www.agnosticdev.com/blog-entry/swift/swift-network-testing-automate-xctest-python&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;개요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이번 글은 공유의 목적이라기보다 XCTest를 사용하며 개인적으로 놓쳤던 세세한 부분을 기록하는데 목적이 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;가볍게 작성하는 내용이라 가볍게 읽으시면 좋을 것 같아요 :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 글은 Raywenderlich의 &lt;a href=&quot;https://www.raywenderlich.com/25842120-testing-in-ios&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Testing in iOS&lt;/a&gt;를 기반으로 합니다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위의 강의를 보면서 놓쳤던 부분을 기록한 연습장 정도로 보셔도 좋을 것 같아요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;XCTest INFO I missed&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. Test를 진행할 때 Scheme 설정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Test를 구현할 때(특히 TDD)를 할 때 중요한 것이 테스트의 속도예요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;테스트를 구현할 때 모든 구현이 끝난 후 마지막에만 실행시켜보는 것이 아니라 실패 테스트를 먼저 작성하고, 테스트를 구현하는 과정 중간중간에 지속적인 테스트가 필요해서 빠른 테스트 속도는 정말 중요해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;기본적으로 큰 차이를 가져오진 않지만, 테스트 전에 우선 빌드를 하기 때문에 Scheme 설정을 Simulator로 하는 것보단 Mac으로 설정하는 것이 더 빠르다고 하네요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;테스트해보진 못했지만 가능하면 저도 이렇게 세팅하고 진행하려고 해요 :)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3QwFq/btrLTWKO312/Ozud8CkQGmGXjQkknKvQbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3QwFq/btrLTWKO312/Ozud8CkQGmGXjQkknKvQbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3QwFq/btrLTWKO312/Ozud8CkQGmGXjQkknKvQbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3QwFq%2FbtrLTWKO312%2FOzud8CkQGmGXjQkknKvQbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;296&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. @testable annotation&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프로젝트 코드를 테스트를 위해서는 import 하는 과정이 필요해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;예를 들어, 테스트 대상의 프로젝트 이름이 AnimalFinder라고 해볼게요.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662963242858&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import AnimalFinder&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: 'Noto Serif KR';&quot;&gt;일반적으로 import 하듯이 테스트 코드에서 프로젝트를 import &amp;lt;Project&amp;gt; 형식으로 한다면 &amp;lt;Project&amp;gt; 내의 object를 초기화(initialize)할 수 없어요.&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: 'Noto Serif KR';&quot;&gt;Swift의 경우 Access Control을 선언해주지 않는다면 Internal로 설정이 되는데요, Test는 다른 module이기 때문에 init을 할 수 없어요.&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: 'Noto Serif KR';&quot;&gt;이를 해결하기 위해서는 외부 module이 접근 가능하도록 프로젝트 내부 객체를 public으로 선언해주던가, @testable 마킹을 해줘야 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;라이브러리를 구현하는 중이라면 전자의 방법도 상관이 없지만 일반적인 앱을 구현 중이라면 후자의 방법이 필요해요.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662963454696&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@testable import AnimalFinder&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. Failure Test&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;TDD에 관한 책이나 글을 읽어보면 가장 중요한 것 중 하나가 실패 테스트를 먼저 해보는 것인데요, 설계를 잘못할 경우 테스트가 의도치 않게 성공하는 경우가 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그러면 테스트로서의 가치가 전혀 없을 거예요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;XCode에서 단순 compile은 test pass를 띄우기 때문에 성공이 테스트의 성공인지, 테스트 자체가 진행되지 않은 것인지 확인하기 위해서 반드시 failure test를 먼저 띄워볼 필요가 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. test 실행 단축키&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이건 정말 기본적인 정보이지만 제가 놓치고 있었던 부분이기 때문에 필기했어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Control + Option + Command + U&lt;/b&gt;를 하면 커서가 놓인 테스트 메서드만 실행할 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 U는 unit test를 의미하며 저는 기존에 Command + U는 사용했었는데 전체 테스트를 실행해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. XCTFail 사용하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;특히 Bool 값을 테스트할 때 좋은 방법이에요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;일반적으로는 Bool 값이 true냐, false냐로 테스트를 구현하기 쉬운데 경우에 따라서 특정 상황에서는 테스트가 fail이 되도록 설계할 필요가 있어요.&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: 'Noto Serif KR';&quot;&gt;반드시 Bool과만 사용해야 하는 것은 아니지만 특정 조건에서 테스트가 Fail이 되도록 설계할 때 XCTFail은 좋은 도구가 될 수 있어요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. Optional 처리하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;XCTUnwrap을 통해 Optional을 처리할 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662963860986&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let unwrapedBool = try XCTUnwrap( someOptionalBool )
XCTAssertTrue(unwrapedBool) // unwrap 과정이 없이 이 테스트를 진행하면 문제 발생&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: 'Noto Serif KR';&quot;&gt;그런데 저는 그냥 NotNil 테스트를 하고, 값 자체를 바로 비교해요.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662963951525&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;XCTAssertEqual(someOptionalBool, true)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서는 unwrap이 중요한 개념이 아니고, 값 자체를 비교하는 로직이기 때문에 테스트에서는 이것으로 충분하다고 생각해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;7. XCTAssert()&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;XCTAssert는 XCTAssertTrue와 완전히 같다고 하네요 :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;8. Test method 구현하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;XCTest를 진행하기 위해서 테스트 메서드를 구현할 때 항상 메서드 이름의 시작을 test로 해야 해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래야 그 메서드가 테스트를 위한 메서드인지 XCode가 인지하기 때문이죠.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그동안 생각을 안 해본 부분인데 그 테스트 메서드에 argument(parameter)가 정의될 경우 이 역시 테스트 메서드로 인식하지 못해요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;생각해보면 당연한 것이 이 메서드를 정의하는 건 우리지만 직접 사용하는 것이 아닌데 컴파일러 입장에서 파라미터가 필요한 메서드일 경우 무엇을 넣어줄지 알 수 없어요.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cB7UXm/btrLVXQjnGM/u9Mu79eSGQkqnAOdKay0SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cB7UXm/btrLVXQjnGM/u9Mu79eSGQkqnAOdKay0SK/img.png&quot; data-alt=&quot;test로 시작해도 테스트 메서드로 인식이 안되어 실행 가능을 의미하는 다이아몬드 표시가 없음&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cB7UXm/btrLVXQjnGM/u9Mu79eSGQkqnAOdKay0SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcB7UXm%2FbtrLVXQjnGM%2Fu9Mu79eSGQkqnAOdKay0SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;708&quot; height=&quot;180&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;test로 시작해도 테스트 메서드로 인식이 안되어 실행 가능을 의미하는 다이아몬드 표시가 없음&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그동안 생각해보지 않은 부분이라 필기했어요 :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;9. Tuple 값 비교하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;튜플을 테스트할 경우 equal을 사용할 때 조심해야 할 것이 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFIAng/btrLR8kwIG0/KLzwoPDnNlLf3XQGn2qnxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFIAng/btrLR8kwIG0/KLzwoPDnNlLf3XQGn2qnxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFIAng/btrLR8kwIG0/KLzwoPDnNlLf3XQGn2qnxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFIAng%2FbtrLR8kwIG0%2FKLzwoPDnNlLf3XQGn2qnxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;166&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래 값은 두 튜플을 단순 비교하는 로직이기 때문에 사용 가능하지만, 위의 Equal 테스트는 사용할 수 없어요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J5m3C/btrLNwzQfQi/yKQAjjcD3YdESDL1pWzZwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J5m3C/btrLNwzQfQi/yKQAjjcD3YdESDL1pWzZwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J5m3C/btrLNwzQfQi/yKQAjjcD3YdESDL1pWzZwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ5m3C%2FbtrLNwzQfQi%2FyKQAjjcD3YdESDL1pWzZwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;936&quot; height=&quot;111&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Tuple은 named type이 아니라 compound type이기 때문인데, 혹시 두 type의 차이를 모르신다면 이번 기회에 알아보면 좋을 것 같아요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;10. Test를 추상화하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이건 내용 자체보다 목적이 중요한 부분이에요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;예를 들어, 어떤 로직이 Int 와 Double 모두에 적용되는지 확인해야하는 상황이 있다고 가정해볼게요.&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: 'Noto Serif KR';&quot;&gt;가장 단순한 방법은 두 타입의 테스트를 모두 만드는 것이지만 이럴 경우 상황에 따라 무의미한 반복이 계속 2배로 생길 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 두 타입의 상위 개념을 계속 타고 올라가다보면 Int, Double 모두 AdditiveArithmetic에서 나왔음을 알 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 이것을 활용하면 두 테스트를 한 번에 할 수 있는 테스트를 설계할 수 있죠.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662964667575&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public extension Sequence where Element: AdditiveArithmetic { // 공통점을 활용
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;11. 소수점 비교하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;정수 타입이 아닌 Double, CGFloat 등의 타입 값을 비교할 때 같은 값임에도 불구하고 다르게 표현될 때가 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;부동소수점 방식이기 때문에 발생할 수 있는 문제인데 XCTest에서는 이렇게 소수점 비교를 위해 설정을 할 수 있어요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CyGbS/btrLNpU7h32/xlx3iTqvxG3dR4QOpuYLIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CyGbS/btrLNpU7h32/xlx3iTqvxG3dR4QOpuYLIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CyGbS/btrLNpU7h32/xlx3iTqvxG3dR4QOpuYLIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCyGbS%2FbtrLNpU7h32%2Fxlx3iTqvxG3dR4QOpuYLIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;634&quot; height=&quot;217&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위처럼 accuracy를 등록하면 &lt;b&gt;&quot;소수점 12자리 까지만 같으면 같은 것으로 취급하여 테스트를 통과한다&quot;&lt;/b&gt; 라고 선언하게 돼요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;소수점 테스트에서 유용하게 사용할 수 있을 것 같아요 :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이것 외에도 Test Coverage 활용, TDD, Switch 문을 테스트에 더 잘 활용하기 위해 ~= 설정 등 여러 내용이 있지만 여기서는 너무 길어져서 작성하지 않도록 하겠습니다!&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: 'Noto Serif KR';&quot;&gt;혹시 더 자세한 내용이 궁금하시면 도입부에 언급한 자료를 학습해보는 것도 좋을 것 같아요 :)&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS 앱개발/iOS</category>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/342</guid>
      <comments>https://jayb-log.tistory.com/342#entry342comment</comments>
      <pubDate>Mon, 12 Sep 2022 15:44:48 +0900</pubDate>
    </item>
  </channel>
</rss>