<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>gooreum_99.log</title>
        <link>https://velog.io/</link>
        <description>하루하루 꾸준히</description>
        <lastBuildDate>Sat, 11 Dec 2021 17:00:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>gooreum_99.log</title>
            <url>https://images.velog.io/images/gooreum_90/profile/4f41392a-9cb2-49ae-980a-a728b94b4260/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. gooreum_99.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/gooreum_90" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[저자의 글]]></title>
            <link>https://velog.io/@gooreum_90/%EC%A0%80%EC%9E%90%EC%9D%98-%EA%B8%80</link>
            <guid>https://velog.io/@gooreum_90/%EC%A0%80%EC%9E%90%EC%9D%98-%EA%B8%80</guid>
            <pubDate>Sat, 11 Dec 2021 17:00:09 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>TDD란 프로그래밍 도중 내린 결정과 그 결정에 대한 피드백 사이의 간격을 인지하고, 또한 이 간격을 통제할 수 있게 해주는 기술을 말한다.</p>
<ul>
<li>일주일 간 종이에다 설계한 다음 코드를 테스트 주도로 개발한다고 하더라도 이것은 TDD이다.</li>
<li>결정과 그에 대한 피드백 사이의 간격을 인지하고, 또 의식적으로 제어했기 때문이다.</li>
</ul>
</li>
<li><p>이 책의 구성은 크게 세 가지이다.</p>
<ul>
<li>1부. Money 예제<ul>
<li>코딩하기 전에 먼저 테스트를 만드는 법과 설계를 유기적으로 키워나가는 방법을 배우게 될 것</li>
</ul>
</li>
<li>2부.xUnit 예제<ul>
<li>자동화된 테스트를 위한 프레임워크를 만들고, 그 과정에서 리플랙션이나 예외 등을 포함하는 더 복잡한 노직을 테스트 하는 예제. 그 이외에도 이 예제는 많은 프로그래머 중심적인 테스팅 툴의 진수라 할 수 있는 xUnit의 아키텍처를 소개한다.</li>
<li>두번째 예에서는 첫 번째 예보다 훨씬 더 작은 단계로 작업하는 방식을 배우게 될 것이며, 컴퓨터 과학자들이 사랑하는 자기 참조(self-referential)의 일종도 접하게 된다.</li>
</ul>
</li>
<li>3부. 테스트 주도 개발을 위한 패턴들<ul>
<li>어떤 테스트를 작성해야 할 것인가, xUnit에서 어떻게 테스트할 것인가 등에 대한 패턴과, 예제에서 사용된 디자인 패턴과 리팩토링을 소개한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[타락한 객체]]></title>
            <link>https://velog.io/@gooreum_90/%ED%83%80%EB%9D%BD%ED%95%9C-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@gooreum_90/%ED%83%80%EB%9D%BD%ED%95%9C-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Sat, 11 Dec 2021 16:57:41 GMT</pubDate>
            <description><![CDATA[<h3 id="💡일반적인-tdd-주기">💡일반적인 TDD 주기</h3>
<hr>
<ol>
<li>테스트를 작성한다. <ul>
<li>마음속에 있는 오퍼레이션이 코드에 어떤 식으로 나타나길 원하는지 생각해보라.</li>
<li>이야기를 써내려가는 것이다.</li>
<li>원하는 인터페이스를 개발하라.</li>
<li>올바른 답을 얻기 위해 필요한 이야기의 모든 요소를 포함시켜라.</li>
</ul>
</li>
<li>실행 가능하게 만든다.<ul>
<li>다른 무엇보다도 중요한 것은 빨리 초록 막대를 보는 것이다.</li>
<li>깔끔하고 단순한 해법이 명백히 보인다면 그것을 입력하라.</li>
<li>만약 깔끔하고 단순한 해법이 있지만 구현하는 데 몇 분 정도 걸릴 것 같으면 일단 적어 놓은 뒤에 원래 문제(초록 막대를 보는 것)로 돌아오자.</li>
</ul>
</li>
<li>올바르게 만든다.<ul>
<li>이제 시스템이 작동하므로 직전에 저질렀던 죄악을 수습하자.</li>
<li>좁고 올곧은 소프트웨어 정의의 길로 되돌아와서 중복을 제거하고 초록 막대로 되돌리자.</li>
</ul>
</li>
</ol>
<br />

<h3 id="💡깔끔한-코드를-얻는-것이-목적">💡깔끔한 코드를 얻는 것이 목적</h3>
<hr>
<ul>
<li><strong>나누어 정복하기(divide and conquer)</strong><ul>
<li>처음부터 &#39;<strong>작동하는 깔금한 코드</strong>&#39;를 얻을 순 없다.</li>
<li>처음엔 &#39;<strong>작동하는</strong>&#39;에 해당하는 부분을 먼저 해결하고 나서 &#39;<strong>깔끔한 코드</strong>&#39; 부분을 해결한다.</li>
<li>이러한 접근 방식은 &#39;깔끔한 코드&#39; 부분을 먼저 해결한 후에, &#39;작동하는&#39; 부분을 해결해 가면서 배운 것들을 설계에 반영하는 &#39;아키텍처 주도 개발(architecture-driven development)&#39;과 정반대다.</li>
</ul>
</li>
</ul>
<br />

<h3 id="💡할일목록---dollar-부작용-해결하기">💡할일목록 - Dollar 부작용 해결하기</h3>
<hr>
<pre><code class="language-swift">$5 + 10CHF = $10(환율이 2:1일 경우)
$5 X 2 = $10(해결)
amount를 private으로 만들기
Dollar 부작용(진행)
Money 반올림?</code></pre>
<ul>
<li><p><strong>Dollar 부작용 해결하기</strong></p>
<ul>
<li>1장에서 2번째 할일목록 테스트를 통과하긴 했지만, Dollar 부작용 문제가 있다.</li>
<li>Dollar에 대한 연산을 수행한 후에 해당 Dollar 값이 바뀐다.</li>
</ul>
</li>
<li><p>테스트 코드를 추가해보자.</p>
<pre><code class="language-swift">  func testMultiplication() {
          let five = Dollar(5)
          five.times(2)
          XCTAssertEqual(10, five.amount)
          five.times(3)
          XCTAssertEqual(15, five.amount) -&gt; 테스트 실패
  }</code></pre>
<ul>
<li><p><code>times()</code> 를 처음 호출한 이후에 five는 더 이상 5가 아니다.</p>
</li>
<li><p><code>times()</code> 에서 새로운 객체를 반환하는 방식으로 문제를 해결해보자.</p>
<ul>
<li>Dollar 인터페이스를 수정해야 하고, 테스트도 수정해야 한다.</li>
<li>그러나 문제될 것은 없다. 어떤 구현이 올바른가에 대한 우리 추측이 완벽하지 못한 것과 마찬가지로 올바른 인터페이스에 대한 추측 역시 절대 완벽하지 못하다.</li>
</ul>
<pre><code class="language-swift">func testMultiplication() {
      let five = Dollar(5)
      var product = five.times(2)
      XCTAssertEqual(10, product.amount)
      product = five.times(3)
      XCTAssertEqual(15, product.amount)
}</code></pre>
</li>
<li><p>Dollar의 <code>times()</code> 타입이 Dollar가 아니므로 컴파일이 되지 않는다.</p>
<pre><code class="language-swift">class Dollar {
  var amount: Int!
  init(_ amount: Int) {
      self.amount = amount
  }
  func times(_ multiplier: Int) -&gt; Dollar? {
              amount *= multiplier
      return nil
  }
}</code></pre>
</li>
<li><p>컴파일 되지 않는 문제를 해결하기 위해 Dollar 인터페이스를 수정했다.</p>
</li>
<li><p>하지만 실행되지 않는다. 그래도 한 걸음 나아간 것이다!</p>
</li>
<li><p>테스트를 통과하기 위해서는 올바른 금액을 갖는 새 Dollar를 반환해야 한다.</p>
<pre><code class="language-swift">class Dollar {
  var amount: Int!
  init(_ amount: Int) {
      self.amount = amount
  }
  func times(_ multiplier: Int) -&gt; Dollar {
      return Dollar(amount * multiplier)
  }
}</code></pre>
</li>
</ul>
</li>
<li><p><strong>할일 목록 삭제하기</strong></p>
<pre><code class="language-swift">  $5 + 10CHF = $10(환율이 2:1일 경우)
  $5 X 2 = $10(완료)
  amount를 private으로 만들기
  Dollar 부작용(완료)
  Money 반올림?</code></pre>
</li>
</ul>
<br />

<h3 id="💡빠른-초록-막대-보기-위한-세-가지-전략">💡빠른 초록 막대 보기 위한 세 가지 전략</h3>
<hr>
<ol>
<li><strong>가짜로 구현하기</strong><ul>
<li>상수를 반환하게 만들고 진짜 코드를 얻을 때까지 단계적으로 상수를 변수로 바꾸어 간다.</li>
<li>1장에서는 테스트를 통과하기 위해 일단 가짜 구현으로 시작해서 점차 실제 구현을 만들어갔다.</li>
</ul>
</li>
<li><strong>명백한 구현 사용하기</strong><ul>
<li>실제 구현을 입력한다.</li>
<li>2장에서는 올바른 구현이라고 생각한 내용을 입력한 후 테스트를 실행했다.</li>
</ul>
</li>
<li><strong>삼각측량(triangulation)</strong><ul>
<li>3장에서 보고 다시 정리하자.</li>
</ul>
</li>
</ol>
<ul>
<li><strong>위 전략을 실무에서 TDD 적용</strong><ul>
<li>저자는 1,2 번째 방법을 실무에서 번걸아가며 사용한다고 한다고 한다.</li>
<li>모든 일이 자연스럽게 잘 진행되고 내가 뭘 입력해야 할지 알 때는 명백한 구현을 계속 더해 나간다.</li>
<li>예상치 못한 빨간 막대를 만나게 되면 가짜로 구현하기 방법을 사용하면서 올바른 코드로 리팩토링한다.</li>
<li>그러다 자신감을 찾으면 명백한 구현 사용하기 모드로 돌아온다.</li>
</ul>
</li>
</ul>
<br />

<h3 id="💡지금까지-배운-3가지-내용-정리">💡<strong>지금까지 배운 3가지 내용 정리</strong></h3>
<hr>
<ol>
<li>설계상의 결함(Dollar 부작용)을 그 결함으로 인해 실패하는 테스트로 변환했다.</li>
<li>스텁 구현으로 빠르게 컴파일을 통과하도록 만들었다.</li>
<li>올바르다고 생각하는 코드를 입력하여 테스트를 통과했다.</li>
</ol>
<ul>
<li>느낌(부작용에 대한 혐오감)을 테스트(하나의 Dollar 객체에 곱하기를 두 번수행하는 것)로 변환하는 것은 TDD의 일반적 주제다.</li>
<li>이런 작업을 오래 할수록 미적 판단을 테스트로 담아내는 것에 점점 익숙해지게 된다.</li>
<li>이걸 할 수 있을 때, 설계 논의는 훨씬 더 흥미로워진다.</li>
<li>우선 시스템이 이런 식으로 동작해야 하는지 저런 식으로 동작해야 하는지 논의할 수 있다.</li>
<li>일단 올바른 행위에 대해 결정을 내린 후에, 그 행위를 얻어낼 수 있는 최상의 방법에 대해 이야기할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[다중통화를 지원하는 Money 객체]]></title>
            <link>https://velog.io/@gooreum_90/%EB%8B%A4%EC%A4%91%ED%86%B5%ED%99%94%EB%A5%BC-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94-Money-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@gooreum_90/%EB%8B%A4%EC%A4%91%ED%86%B5%ED%99%94%EB%A5%BC-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94-Money-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Sat, 11 Dec 2021 15:49:09 GMT</pubDate>
            <description><![CDATA[<h3 id="💡화폐-예제">💡화폐 예제</h3>
<hr>
<ul>
<li>1부에서는 완전히 테스트에 의해 주도되는 전형적 모델 코드를 개발할 것이다.</li>
<li>1부의 목표는 <strong>테스트 주도 개발(TDD)의 주기</strong>를 보도록 하는 것이다.<ol>
<li><strong>재빨리 테스트를 하나 추가한다.</strong></li>
<li><strong>모든 테스트를 실행하고 새로 추가한 것이 실패하는지 확인한다.</strong></li>
<li><strong>코드를 조금 바꾼다.</strong></li>
<li><strong>모든 테스트를 실행하고 전부 성공하는지 확인한다.</strong></li>
<li><strong>리팩토링을 통해 중복을 제거한다.</strong></li>
</ol>
</li>
<li>이 리듬을 느끼면 다음 내용들을 확인할 수 있다.<ul>
<li>각각의 테스트가 기능의 작은 증가분을 어떻게 커버하는지</li>
<li>새 테스트를 돌아가게 하기 위해 얼마나 작고 못생긴 변화가 가능한지</li>
<li>얼마나 자주 테스트를 실행하는지</li>
<li>얼마나 수 없이 작은 단계를 통해 리팩토링이 되어가는지</li>
</ul>
</li>
</ul>
<br />

<h3 id="💡money-객체-구성하기">💡Money 객체 구성하기</h3>
<hr>
<ul>
<li><strong>다중 통화를 지원하는 보고서</strong></li>
</ul>
<pre><code>| 종목 | 주 | 가격 | 합계 |
| --- | --- | --- | --- |
| IBM | 1000 | 25USD | 25000USD |
| Novartis | 400 | 150CHF | 60000CHF |
|  |  | 합계 | 65000USD |</code></pre><ul>
<li><strong>환율</strong></li>
</ul>
<pre><code>| 기준 | 변환 | 환율 |
| --- | --- | --- |
| CHF | USD | 1.5 |</code></pre><ul>
<li><p><strong>문제</strong></p>
<ul>
<li><p>다중 통화를 지원하는 보고서를 만들어야 하는데,</p>
<p>  <strong>어떤 테스트</strong>들이 있어야 보고서에 제대로 계산 되도록 하는 코드가 완성되었는지 확실할 수 있는가.</p>
<ul>
<li>통화가 다른 두 금액을 더해서 주어진 환율에 맞게 변한 금액을 결과로 얻을 수 있어야 한다.</li>
<li>어떤 금액(주가)을 어떤 수(주식의 수)에 곱한 금액을 결과로 얻을 수 있어야 한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<br />

<h3 id="💡작업진행-방식">💡작업진행 방식</h3>
<hr>
<p><strong>1.할일 목록을 작성한다.</strong></p>
<ul>
<li>앞으로 어떤 일을 해야 하는지 알려주고,</li>
<li>지금 하는 일에 집중할 수 있도록 도와주며,</li>
<li>언제 일이 다 끝나는지 알려줄 수 있다.</li>
</ul>
<p><strong>2.작업을 시작한다.</strong></p>
<ul>
<li>앞으로 할일 목록에 있는 한 항목에 대한 작업을 시작하면 그 항목을 <strong>이런 식으로</strong> 굵은 글씨체로 나타낼 것이다.</li>
</ul>
<p><strong>3.작업을 마친다.</strong></p>
<ul>
<li>작업을 끝낸 항목에는 <del>이런 식으로</del> 줄을 긋도록 한다.</li>
</ul>
<p><strong>4.새로운 테스트를 할일 목록에 추가한다.</strong></p>
<ul>
<li><p><strong>할일 목록</strong></p>
<pre><code class="language-swift">  $5 + 10CHF = $10(환율이 2:1일 경우)
  **$5 X 2 = $10****</code></pre>
<p>  → 두번째 할일 목록부터 진행하겠다는 의미</p>
</li>
<li><p><strong>테스트후 객체를 만든다.</strong></p>
<ul>
<li>테스트는 객체를 만들면서 시작하는게 아니라 테스트를 먼저 만들어야 한다!</li>
</ul>
</li>
<li><p><strong>테스트는 작은 것부터 시작한다.</strong></p>
<ul>
<li>두번째 할일 목록부터 시작해보자.</li>
</ul>
</li>
</ul>
<br />

<h3 id="💡tdd-주기-1---재빨리-테스트를-하나-추가한다">💡TDD 주기 1 - <strong>재빨리 테스트를 하나 추가한다</strong></h3>
<hr>
<ul>
<li><p><strong>테스트를 작성할 때는 오퍼레이션의 완벽한 인터페이스에 대해 상상해보는 것이 좋다.</strong></p>
</li>
<li><p>우리는 지금 오퍼레이션이 외부에서 어떤 식으로 보일지에 대한 이야기를 테스트 코드에 적고 있는 것이다.</p>
<ul>
<li>ViewController가 ViewModel 메서드를 실제 사용하는 상황을 그대로 적용해야 한다는 의미인듯.</li>
</ul>
</li>
<li><p>가능한 최선의 API에서 시작해서 거꾸로 작업하는 것이 애초부터 일을 복잡하고 보기 흉하며 &#39;현실적&#39;이게 하는 것 보다 낫다.</p>
</li>
<li><p>간단한 곱셈 예</p>
<pre><code class="language-swift">  public func testMultiplication() { 
      let five = Dollar(5)
      five.times(2)
      XCTAssertEqual(10, five.amount)
  }</code></pre>
<ul>
<li>컴파일 조차 되지 않는다.</li>
<li>공용 필드(public field)에데가, 예기치 못한 부작용이 있을 수 있으며, 금액을 계산하는 데 정수형을 사용한다.</li>
<li>하지만 중요한 것은 이렇게 작은 단계로 시작하는 것이다.</li>
<li>위의 문제점들을 할일 목록에 적어놓다.</li>
<li>지금 우리에겐 실패하는 테스트가 주어진 상태고 최대한 빨리 초록 막대를 보고 싶을 뿐이다.</li>
</ul>
</li>
<li><p><strong>할일목록 갱신</strong></p>
</li>
</ul>
<pre><code class="language-swift">$5 + 10CHF = $10(환율이 2:1일 경우)
**$5 X 2 = $10**
amount를 private으로 만들기
Dollar 부작용
Money 반올림?</code></pre>
<br />

<h3 id="💡tdd-주기-2---모든-테스트를-실행하고-새로-추가한-것이-실패하는지-확인한다">💡TDD 주기 2 - <strong>모든 테스트를 실행하고 새로 추가한 것이 실패하는지 확인한다.</strong></h3>
<hr>
<ul>
<li><strong>실패 정복하기</strong><ul>
<li>위의 테스트 코드에서 컴파일 되지 않는 원인들을 작성하고 하나씩 정복해 나가자.<ul>
<li>원인들을 리스트업 해서 하나씩 정복해나가면 진행하고 있는 업무의 진척율을 계산할 수 있다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-swift">- Dollar 클래스가 없음.
- 생성자가 없음.
- times(int) 메서드가 없음.
- amount 필드가 없음.</code></pre>
<ul>
<li>Dollar 클래스를 정의하여 에러 하나를 없애자</li>
<li>생성자를 만들자</li>
<li>times()의 스텁 구현</li>
<li>amount 필드 추가</li>
</ul>
<pre><code class="language-swift">Dollar
class Dollar { 
    var amount: Int!
    init(_ amount: Int) { }
    func times(_ multiplier: Int) { }
}</code></pre>
<ul>
<li><p><strong>실패에 대한 구체적인 척도</strong></p>
<ul>
<li><p>위 테스트 코드 결과는 실패!</p>
<p>  <img src="https://images.velog.io/images/gooreum_90/post/b5867da0-0ef6-4ca6-80d5-6b86f58b2ea5/Untitled.png" alt=""></p>
<ul>
<li>10을 기대했지만 결과는 nil이다.</li>
<li>그러나 중요한 것은 이제 실패에 대한 구체적인 척도를 갖게 되었다는 점이다.<ul>
<li>막연히 실패했다는 사실만 아는 것보다 나아진 것이다.</li>
</ul>
</li>
<li>우리 문제는 &#39;<strong>다중 통화 구현</strong>&#39;에서 &#39;<strong>이 테스트를 통과시킨 후 나머지 테스트들도 통과시키기</strong>&#39;로 변형된 것이다.<ul>
<li>훨씬 간단해졌으며, 범위도 훨씬 적어서 걱정이 줄었다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<br />

<h3 id="💡tdd-주기-3---코드를-조금-바꾼다">💡TDD 주기 3 - <strong>코드를 조금 바꾼다.</strong></h3>
<hr>
<ul>
<li><p><strong>조금 수정한다.</strong></p>
<pre><code class="language-swift">  class Dollar {
      var amount: Int! = 10 //수정부분
      init(_ amount: Int) {}
      func times(_ multiplier: Int) {}
  }</code></pre>
</li>
</ul>
<br />

<h3 id="💡tdd-주기-4---모든-테스트를-실행하고-전부-성공하는지-확인한다">💡TDD 주기 4 - <strong>모든 테스트를 실행하고 전부 성공하는지 확인한다.</strong></h3>
<hr>
<ul>
<li>위와 같이 <code>amount</code>를 수정하면 <code>XCTAssertEqual(10, five.amount)</code>테스트는 성공한다.</li>
</ul>
<br />

<h3 id="💡tdd-주기-5---리팩토링을-통해-중복을-제거한다">💡TDD 주기 5 - <strong>리팩토링을 통해 중복을 제거한다.</strong></h3>
<hr>
<blockquote>
<p>💡 의존성과 중복
스티브 프리만(Steve Freeman)은 테스트와 코드 간의 문제는 중복이 아님을 지적하였다. 문제는 테스트와 코드 사이에 존재하는 의존성이다. 즉, 코드나 테스트 중 한쪽을 수정하면 반드시 다른 한쪽도 수정해야만 한다는 것이다.
우리의 목표는 코드를 바꾸지 않으면서도 뭔가 의미 있는 테스트를 하나 더 작성하는 것인데, 현재의 구현으로는 불가능하다. </p>
</blockquote>
<blockquote>
<p>의존성은 소프트웨어 개발의 모든 부분에서 핵심적인 문제다. 만약 특정 데이터베이스 벤더가 제공하는 세세한 기능들을 코드 여기저기에서 사용하는 상황에서 다른 벤더의 제품으로 변경하고자 한다면 코드가 해당 벤더에 대해 의존성을 갖는다는 사실을 알게 될 것이다.</p>
</blockquote>
<blockquote>
<p> 의존성이 문제 그 자체라면 중복(duplication)은 문제의 징후다. 중복의 가장 흔한 예는 로직의 중복이다. 중복된 로직이란 동일한 문장이 코드의 여러 장소에 나타나는 것을 의미한다. 중복된 로직을 하나로 끄집어내는 일엔 객체를 이용하는 것이 최고다.</p>
</blockquote>
<blockquote>
<p> 문제 자체는 남겨둔 채로 징후만을 제거하면 다른 어딘가에서 최악의 형태로 문제가 드러나곤 하는 현실 세계의 일반적인 양상과는 달리, 프로그램에서는 중복만 제거해 주면 의존성도 제거된다. 이게 바로 TDD의 두번째 규칙이 존재하는 이유다. 다음 테스트로 진행하기 전에 중복을 제거함으로써, 오직 한 가지 (one and only one)의 코드 수정을 통해 다음 테스트도 통과하게 만들 가능성을 최대화하는 것이다.</p>
</blockquote>
</aside>

<ul>
<li><p>지금까지 TDD 주기의 1번부터 4번 항목까지를 수행했으므로, 이제 중복을 제거할 차례다.</p>
</li>
<li><p><strong>데이터도 중복으로 간주한다.</strong></p>
<ul>
<li><p>보통 중복을 찾기 위해 코드를 비교할 테지만, 이번 경우엔 중복이 &#39;<strong>테스트에 있는 데이터&#39;</strong>와 &#39;<strong>코드에 있는 데이터&#39;</strong> 사이에 존재한다.</p>
<pre><code class="language-swift">class Dollar {
  var amount = 5 * 2
}</code></pre>
</li>
<li><p><code>amount = 10</code>을 <code>amount = 5 * 2</code>로 수정하였다. (원래 10은 5와 2의 곱이니..)</p>
</li>
<li><p>테스트 코드에 보면 5와 2가 있고, Dollar 클래스에도 5와 2가 있으므로 중복되는 데이터가 된다.</p>
</li>
</ul>
</li>
<li><p><strong>중복을 제거해보자</strong></p>
<pre><code class="language-swift">  class Dollar {
      var amount: Int!
      init(_ amount: Int) {}
      func times(_ multiplier: Int) {
          amount = 5 * 2 //수정부분
      }
  }</code></pre>
<ul>
<li><p>5와 2를 한 번에 제거할 수 있는 방법은 없지만, 객체의 초기화 단계에 있는 설정 코드를 <code>times()</code> 메서드 안으로 옮겨보자.</p>
</li>
<li><p>테스트는 통과될 것이고, 테스트 막대 역시 초록색이다.</p>
</li>
<li><p>이러한 단계가 작아보이지만 TDD의 핵심은 이런 작은 단계를 밟아야 한다는 것이 아니라, <strong>이런 작은 단계를 밟을 능력을 갖추어야 한다는 것</strong>이다.</p>
<pre><code class="language-swift">class Dollar {
  var amount: Int!
  init(_ amount: Int) {
          self.amount = amount    //수정부분
      }
  func times(_ multiplier: Int) {
      amount = amount * 2 //수정부분
  }
}</code></pre>
</li>
<li><p>5는 테스트 코드의 생성자에서 넘어오는 값이니 amount 변수에 저장해보자.</p>
<pre><code class="language-swift">class Dollar {
  var amount: Int!
  init(_ amount: Int) {
          self.amount = amount    
      }
  func times(_ multiplier: Int) {
      amount = amount * multiplier //수정부분
  }
}</code></pre>
</li>
<li><p>테스트 코드에서 인자 multiplier의 값이 2이므로, 상수를 이 인자로 대체할 수 있다.</p>
<pre><code class="language-swift">class Dollar {
  var amount: Int!
  init(_ amount: Int) {
          self.amount = amount    
      }
  func times(_ multiplier: Int) {
      amount *= multiplier //수정부분
  }
}</code></pre>
</li>
<li><p>Dollar 클래스의 중복 제거를 위해 <code>amount = amount * multiplier</code>의 중복되는 부분도 제거하자</p>
<br />
### 💡할일목록 완료 표시하기
</li>
</ul>
</li>
</ul>
<hr>
<pre><code class="language-swift">$5 + 10CHF = $10(환율이 2:1일 경우)
$5 X 2 = $10(완료)
amount를 private으로 만들기
Dollar 부작용
Money 반올림?</code></pre>
<br />

<h3 id="💡지금까지-한일-정리하기">💡지금까지 한일 정리하기</h3>
<hr>
<ul>
<li>우리는 알고 있는 작업해야 할 테스트 목록을 만들었다.</li>
<li>오퍼레이션이 외부에서 어떻게 보이길 원하는지 말해주는 이야기를 코드로 표현했다.</li>
<li>JUnit에 대한 상세한 사항들은 잠시 무시하기로 했다.</li>
<li>스텁 구현을 통해 테스트를 컴파일했다.</li>
<li>끔찍한 죄악을 범하여 테스트를 통과시켰다.</li>
<li>돌아가는 코드에서 상수를 변수로 변경하여 점진적으로 일반화했다.</li>
<li>새로운 할일들을 한번에 처리하는 대신 할일 목록에 추가하고 넘어갔다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[도전적이며 실현 가능한 제안]]></title>
            <link>https://velog.io/@gooreum_90/1.%EB%8F%84%EC%A0%84%EC%A0%81%EC%9D%B4%EB%A9%B0-%EC%8B%A4%ED%98%84-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%A0%9C%EC%95%88</link>
            <guid>https://velog.io/@gooreum_90/1.%EB%8F%84%EC%A0%84%EC%A0%81%EC%9D%B4%EB%A9%B0-%EC%8B%A4%ED%98%84-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%A0%9C%EC%95%88</guid>
            <pubDate>Sun, 21 Nov 2021 13:04:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/gooreum_90/post/a9355865-f982-4381-ac64-d523b58b176a/image.png" alt=""></p>
<h3 id="기본소득이란-정확히-무엇인가">기본소득이란 정확히 무엇인가</h3>
<blockquote>
<p>💡 기본소득은 <strong>모든 사회 구성원 혹은 거주자 개인에게</strong>, <strong>유급고용에 참여하고자 하는 의지 여부와 관계없이</strong>, <strong>가난하든 부유하든 따지지 않고</strong>(개인의 다른 수입원과 독립적으로), <strong>가정이라는 영역 내의 동거 형태와 무관</strong>하게 <strong>국가에 의해 주어진다.</strong></p>
</blockquote>
<p><strong>기본소득지구네트워크 정의</strong></p>
<blockquote>
<p>💡 기본소득은 어떠한 자산 조사도 하지 않고 근로 여부와도 관계없이 무조건 개인 모두에게 지급되는 소득을 말한다. 이는 최저소득보장제도의 한 형태이기는 하나, 현재 유럽 국가들에 존재하는 것들과는 크게 세 가지 점에서 다르다.</p>
</blockquote>
</aside>

<ol>
<li>가계 단위가 아닌 개인 단위로 지급되고</li>
<li>다른 소득의 유무와 무고나하게 지급되며</li>
<li>노동을 할 의지 및 현재 노동 여부와 관계없이 지급된다. </li>
</ol>
<p><strong>핵심 키워드</strong></p>
<p><strong>1. &quot;국가에 의해 주어지는 소득&quot;</strong></p>
<ul>
<li>&#39;국가&#39;는 유럽연합처럼 현재 존재하는 국민국가보다 더 넓은 법적,정치적 실재를 뜻하거나, 국민국가보다 작은 자치구 같은 법적, 정치적 영역을 지칭하기도 한다. 그러므로 기본소득은 공공의 영역 내에 있는 하나 이상의 기관이 지급하는 것이다.</li>
</ul>
<p><strong>2. &quot;모든 사회 구성원 혹은 거주자 개인에게&quot;</strong></p>
<ul>
<li>기본소득은 예산 구조가 어떠한 형태든 간에 시민 개인 누구에게나 지급되는 금전적 총액이다.</li>
</ul>
<p><strong>3. &quot;유급고용에 참여하고자 하는 의지 여부와 관계없이&quot;</strong></p>
<ul>
<li>현재 노동은 흔히 &#39;일&#39; 혹은 &#39;유급활동&#39;으로 여겨진다는 점을 이해할 필요가 있다. 하지만 여러 이유들 때문에 다음과 같은 분류 체계가 더 적합하다.<ul>
<li>노동시장에서의 유급활동</li>
<li>가사노동</li>
<li>자원봉사</li>
</ul>
</li>
</ul>
<p><strong>4. &quot;가난하든 부유하든 따지지 않고&quot;(개인의 다른 수입원과 독립적으로)</strong></p>
<ul>
<li>기본소득은 빈곤층과 부유층 모두 수령한다. 시민권과 같은 개념이라고 생각하면 된다.</li>
<li>투표권처럼, 기본소득 제도는 시민권(또는 공인된 거주권) 이상의 조건을 요구하지 않는다.</li>
</ul>
<p><strong>5. &quot;가정이라는 영역 내의 동거 형태와 무관하게 주어진다.&quot;</strong></p>
<ul>
<li>기본소득은 특정한 동거 형태만 선호하지 않는다.</li>
<li>거주 형태는 공동 생활의 여러 형태 중 하나일 뿐이지, 기본소득을 수령할 수 있는 자격과는 전혀 관련이 없다.</li>
</ul>
<p>→ 기본소득은 세속적이고, 무조건적이며, 보편적으로 적용된다고 볼 수 있다. </p>
<h3 id="비슷하지만-기본소득이-아닌-것들">비슷하지만 기본소득이 아닌 것들</h3>
<p><strong>참여소득(participation income)</strong></p>
<ul>
<li>현재 사회적으로 유용하다고  판단되는 활동을 하고 있는 시민들에게만 돈을 제공한다.</li>
<li>그러한 활동에는 유급노동, 자원봉사, 가사노동, 공부 등이 포함된다.</li>
</ul>
<p><strong>음의 소득세(Negative Income Tax, NIT)</strong></p>
<ul>
<li>조세정책에 의해 최저소득이 보장되는, 일률적이고 환급받을 수 있는 세금이다.</li>
<li>소득세 신고서의 금액이 만약 이 최저소득의 기준을 넘어선다면 그에 상응하는 세금을 납부해야 하며, 최저소득기준 이하거나 소득이 아예 없다면 국가로부터 약정한 최저 기준과의 차액을 받는다.</li>
</ul>
<p><strong>그외</strong></p>
<ul>
<li>스페인의 최저소득지원</li>
<li>프랑스의 최저통합수당</li>
<li>실업급여나 실업수당</li>
</ul>
<p>중요한 것은 기본소득을 수령하기 위한 유일한 조건은 시민권이거나 공인된 거주권뿐이기 때문에, 기본소득은 보조금이나 장려금 또는 어떤 종류의 조건부 실업수당과도 다르다는 점이다.</p>
<h3 id="지금-기본소득은-존재하는가">지금 기본소득은 존재하는가</h3>
<ul>
<li>2000년 미국 알래스카 주는 모든 거주자에게 총 2,000달러를 기본소득으로 제공했다.</li>
<li>최근 수십년 동안 미국에서는 가장 부유한 사람들을 중심으로 부의 재분배가 이루어져온 반면, 알래스카 주는 이와 정반대로 미국에서 &#39;가장 평등한&#39;주로 가는 방향을 택했다.</li>
<li>그러나 알래스카 주의 재원 마련 방식은 석유 수입의 일부를 기금으로 마련하였기에 이론적으로나 정치적으로나 가장 만족스러운 형태는 아니지만 현재로서는 진정한 의미의 기본소득 사례라고 볼 수 있다.</li>
</ul>
<h3 id="기본소득의-오래된-뿌리">기본소득의 오래된 뿌리</h3>
<ul>
<li>토머스 모어의 &#39;유토피아(1516)&#39; - 대략적인 연관성을 찾을 수 있음.</li>
<li>토머스 페인의 &#39;토지 분배의 정의(1796)&#39; - 기본소득의 원형에 대해 언급</li>
<li>토머스 스펜스, 샤를 푸리에, 허버트 스펜서, 헨리 조지, 버트런드 러셀</li>
<li>제임스 미드, 제임스 토빈</li>
<li>밀턴 프리드먼 &#39;자본주의와 자유&#39; - &#39;음의 소득세&#39; 제안<ul>
<li>프리드먼은 복지국가의 해체를 의도</li>
</ul>
</li>
<li>제임스 토빈 - &#39;최저소득보장&#39; 제안<ul>
<li>경제적으로 어려운 국민들에 대한 처우 개선과 빈곤의 종식에 대한 열망에 기반하여 제안</li>
</ul>
</li>
<li>리처드 닉슨<ul>
<li>음의 소득세의 형식을 취한 소득보장과 노동자들을 위한 가족부양책을 융합한 개혁안 만듦.</li>
</ul>
</li>
<li>캐나다에서 1980년대까지 음의 소득세가 관심 속에서 고려됨.</li>
</ul>
<h3 id="지난-20년의-급격한-변화">지난 20년의 급격한 변화</h3>
<ul>
<li>기본소득에 대한 연구는 1970년대와 1980년대에 초에 많은 진척이 있었음.</li>
<li>1986년 기본소득유럽네트워크(BIEN) 설립<ul>
<li>1986년은 특히 중요한 해인데, 그보다 2년 앞서 루뱅 대학 소속 노동자합원과 연구자들이 모인 &#39;샤를푸리에 그룹&#39;은 &lt;기본소득&gt;이라는 제목의 논문을 제출했다.</li>
<li>1986년 이 논문에 상을 수여한 조직인 벨기에 프라이즈로부터 재정 지원을 받은 회의가 루뱅 대학에서 조직되었고, 전세계의 수많은 기본소득 연구자들을 불러 모았다.</li>
<li>이 회의에서 기본소득유럽네트워크(Basic Income European Network, BIEN)의 설립이 결정되었다.</li>
<li>이후 BIEN은 10번의 대회를 개최</li>
</ul>
</li>
<li>기본소득지구네트워크(BIEN - Basic Income Earth Network)로 확장<ul>
<li>기존 유럽국가들만 참가하던 회의에서 미국, 남아메리카, 남아프리카, 호주, 뉴질랜드에서 기본소득 모임이 생겨남.</li>
<li>국제기구로서 기본소득지구네트워크의 공식적인 첫 회의는 2006년 11월남아프리카공화국 케이프타운에서 열렸음.</li>
<li>2007년 초 기준으로 네 개의 대륙에 열두 개 국가의 공식적인 BIEN 네트워크가 있다.</li>
</ul>
</li>
<li>신자유주의의 만연<ul>
<li>BIEN이 설립된지 20년이 넘는 시간 동안 세상은 여러 방면으로 변화하였다.</li>
<li>1980년대 세계는 신자유주의의가 만연하였다.<ul>
<li>미국의 로널드 레이건, 영국의 마거릿 대처는 신자유주의를 옹호하였고, 당시 라틴아메키라도 신자유주의의 물결에 있었다.</li>
<li>신자유주의 광신도들이 약속한 경이에 찬 비전은 당시 노동자들과 불우한 사람들이 견뎌야 했던 가혹한 환경들과는 너무도 먼 이야기였고, 서유럽의 실업률 수치는 수년간 본 적도 없는 수준까지 올랐다.</li>
</ul>
</li>
<li>이 같은 흐름 속에서 BIEN이 설립되었다.</li>
</ul>
</li>
</ul>
<h3 id="자유를-박탈하는-불평등에-맞서기">자유를 박탈하는 불평등에 맞서기</h3>
<ul>
<li>소련이 해체되고, 전세계적으로 신자유주의가 만연하게 되었지만 전세계적 빈익빈 부익부 현상은 심해졌다. 라틴아메카등과 같이 그리 부유하지 않은 나라들이 기본소득에 관심을 갖는 건 우연이 아닐 것이다.<ul>
<li>1980년과 2000년 사이에 부유한 국가들의 1인당 GDP는 1년에 2만 달러에서 3만 달러로 오른 반면, 가난한 국가들의 경우에는 265달러에서 257달러로 하락했다.</li>
</ul>
</li>
<li>저자는 이를 통해 21세기를 여는 지금 시점에 있어서 기본소득은 흥미로운 정치적 역할을 할 것이라 생각한다.</li>
<li>기본소득의 세속적이고 무조건적이며 보편적인 특징에 대해 앞서 언급했는데, 이는 민주주의에서 성별, 인종, 수입, 성적 기호, 종교에 상관없이 부여되는 투표권과 매우 비슷하다.<ul>
<li>19세기부터 20세기 초까지 노동자들은 투표권에서 배제되었는데, 민주주의를 위한 그들의 항쟁은 도구주의적인 생각으로부터 영향을 많이 받았을 것이다. 민주주의를 사회주의나 재분배 정의등 다른 목표를 위한 초석이라고 보았기 때문이다.</li>
<li>하지만 투표권의 보편성과 무조건적인 본질은 결국 국민들에게 그 자체로서의 가치를 부여했다.</li>
</ul>
</li>
<li>저자는 기본소득을 위한 시민들의 투쟁이 이와 비슷한 역할을 할 수 있다고 생각한다.<ul>
<li>기본소득의 목적이 빈곤을 물리치고 신자유주의 정책을 뿌리 뽑는 것이기에, 다시 말해 세계 인구의 다수가 더 이상 빈곤에 시달리지 않고 부유한 자들의 변덕과 자비에 의존하지 않도록 하는 것이기 때문이다.</li>
<li>하지만 이와 동시에 기본소득은 박탈할 수 없는 정의에 대한 권리와 자긍심 그리고 그 자체의 가치로 말미암아 도구적이지 않은 형태를 띨 수 있을 것이다.</li>
<li>또한 반민주적인 21세기의 신보수주의자들이 처참한 결과를 불러올 것이라며 아무리 협박을 해도, 기본소득을 주장함으로써 사회운동이 구체화되고 민주적인 여론이 형성될 것이다.</li>
</ul>
</li>
<li>기본소득을 지지하는 행위의 가장 큰 도덕적 강점 중 하나는 단순히 끔찍한 불평등의 증거를 드러내는 것뿐 아니라 소득과 부의 거대한 격차로 인한 <strong>자유의 침해에도 관심을 조명</strong>해준다는 것이다.<ul>
<li>평등과 자유는 별개의 목표가 아니다. 심각한 사회적 불평등은 수백만에 이르는 사람들의 자유를 해치고 있다. 거꾸로 생각하면 너무나 많은 사람들의 부자유, 즉 자신들의 주인인 부유한 자들에게 허락을 받아야만 삶을 영위할 수 있는 노동계급의 부자유는 불평등을 더욱 악화시킨다.</li>
</ul>
</li>
<li>빈곤은 단순히 궁핌이나 물질적 욕구의 불충족 또는 소득 격차만 의미하는 것이 아니다.<ul>
<li>타인의 자의적인 변덕과 탐욕에 대한 의존, 자부심의 소멸, 고립, 가난한 자에 대한 사회적 격리도 빈곤이다.</li>
<li>자신의 물질적 생존이 불확실할수록 &#39;첫 일자리 계약&#39;, 임시 계약, 계약 부재, 불안정성, 직무 &#39;유연성&#39; 그리고 어떠한 사회적 보호도 없는 완전한 실직 등의 형태로 개인의 자유가 침해되어 더욱 고통받게 된다.</li>
<li>이러한 자유의 침해는 나아가 금융소득이나 기업소득은 하늘을 찌를 듯하면서도 실제 월급은 감소하는 현상, 불안정한 퇴직연금, 공공 서비스와 기반 시설의 취약화 또는 사유화 등의 형태로 물질적 불평등을 심화시킨다.</li>
<li>이 물질적 불평등은 세계에서 가장 강력한 국가에서도 예외가 아니어서 현상 유지를 지지하는 사람들조차도 정당화시키기 힘들 정도로 악화되었다.<ul>
<li>2006년 미국의 가장 부유한 인구 250만 명이 나머지 1억 명 소득 총합의 두 배가 넘는 소득을 올리고 있다. (상위 1퍼센트 인구의 소득이 하위 34퍼센트 인구 소득 총합의 두 배나 된다)</li>
<li>이러한 심각한 불평등이 어떻게 절대다수의 자유에 영향을 끼치지 않을 수 있을까!?</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="돈을-위해-일하지-않아도-되는-사회">돈을 위해 일하지 않아도 되는 사회</h3>
<p><strong>기본소득의 장점</strong></p>
<ul>
<li>기본소득은 사회적 낙인이라는 질병과 같은 현상을 완전히 없앨 수 있다.<ul>
<li>시민 혹은 거주자라는 조건을 제외하고는 모두에게 적용되는 보편적 권리이며, 모두가 수령하기 때문에 사회적 낙인 같은 현상은 있을 수가 없다.</li>
</ul>
</li>
<li>기본소득은 노동시장을 보다 유연하게 만드는데, 이 유연성은 꽤 강력하게 노동자를 보호한다.<ul>
<li>직업 선택에 있어서 더 많은 자유를 제공하며, 특히나 노동자에게 매우 중요한 한 가지 선택을 가능하게 된다.</li>
<li>바로 임금을 위해 일하지 않아도 된다는 것이다.</li>
</ul>
</li>
<li>기본소득은 빈곤과 실업의 함정을 피할 수 있게 한다.<ul>
<li>조건부 보조금과 달리 기본소득은 어떤 상항선이 아니라 필요한 최소한의 소득수준을 명시하며, 사람들이 그 이상의 소드을 얻는 것에 아무런 제약을 가하지 않는다. 기본소득은 무조건적이며 유급활동을 포함한 어떤 종류의 수입과도 완벽히 양립할 수 있으므로 빈곤의 함정을 피해 갈 수 있다.</li>
</ul>
</li>
<li>기본소득은 다른 형태의 노동 분배를 가능하게 해준다.<ul>
<li>사람들은 자신의 노동력을 언제, 어떻게 사용할지 결정하기가 더욱 쉬워질 것이다.</li>
<li>급여를 받으려고 일할수도 있고 자원봉사나 육아, 가사에 전념할 수도 있다.</li>
<li>더욱이 기본소득은 위험 부담에 대한 기피를 완화시켜 더 큰 혁신을 가능케 한다.<ul>
<li>사업을 하는 경우 투자자본뿐 아니라 생계수단도 잃는 것이 위험요인이 될텐데, 그만큼 일을 시작히가가 불안해진다. 기본소득은 이들이 사업을 추진하는 데 도움을 줄 뿐 아니라 살아남기 위해 사업의 성공에 너무 의존하지 않아도 되도록 해주낟.</li>
</ul>
</li>
</ul>
</li>
<li>기본소득은 노동관게에서 자본가의 힘을 약화시킬 수 있다.<ul>
<li>약자들이 더 강해지기 때문에 노동자와 자본가 관계에서 협상력과 전략이 바뀌게 된다.</li>
</ul>
</li>
</ul>
<p>이러한 장점들에 대한 의구심은 이 책의 다른 장에서 살펴보도록 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[상속 2 (클래스의 이니셜라이저 상속과 재정의)]]></title>
            <link>https://velog.io/@gooreum_90/%EC%83%81%EC%86%8D-2-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EC%9D%B4%EB%8B%88%EC%85%9C%EB%9D%BC%EC%9D%B4%EC%A0%80-%EC%83%81%EC%86%8D%EA%B3%BC-%EC%9E%AC%EC%A0%95%EC%9D%98</link>
            <guid>https://velog.io/@gooreum_90/%EC%83%81%EC%86%8D-2-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EC%9D%B4%EB%8B%88%EC%85%9C%EB%9D%BC%EC%9D%B4%EC%A0%80-%EC%83%81%EC%86%8D%EA%B3%BC-%EC%9E%AC%EC%A0%95%EC%9D%98</guid>
            <pubDate>Sun, 14 Nov 2021 19:22:14 GMT</pubDate>
            <description><![CDATA[<h3 id="클래스의-이니셜라이저-상속과-재정의">클래스의 이니셜라이저 상속과 재정의</h3>
<ul>
<li>참조 타입인 클래스의 이니셜라이저 위임은 <code>지정 이니셜라이저</code>와 <code>편의 이니셜라이저</code>로 역할을 구분한다.</li>
<li>값 타입의 이니셜라이저는 상속을 고려할 필요가 없지만 클래스는 상속이 가능하므로 상속받았을 때 이니셜라이저를 어떻게 재정의하는지에 대한 규칙을 알아야 한다.</li>
</ul>
<h3 id="지정-이니셜라이저와-편의-이니셜라이저">지정 이니셜라이저와 편의 이니셜라이저</h3>
<p><strong>지정 이니셜라이저(Designated Initializer)</strong></p>
<ul>
<li>지정 이니셜라이저는 클래스의 주요 이니셜라이저로써 필요에 따라 <code>부모클래스의 이니셜라이저를 호출할 수</code> 있으며, 이니셜라이저가 정의된 클래스의 <code>모든 프로퍼티를 초기화</code>해야 하는 임무를 가지고 있다.</li>
<li>지정 이니셜라이저는 클래스의 이니셜라이저 중 기둥과 같은 역할을 하므로 <code>클래스에 하나 이상 정의</code>한다.<ul>
<li>편의 이니셜라이저에 비하면 적은 수이다.</li>
</ul>
</li>
<li>만약 조상클래스에서 지정 이니셜라이저가 자손클래스의 지정 이니셜라이저 역할을 충분히 할 수 있다면, 자손클래스는 지정 이니셜라이저를 갖지 않을 수도 있다.<ul>
<li>아마도 이런 경우는 조상클래스로부터 물려받은 프로퍼티를 제외하고 옵셔널 저장 프로퍼티 외에 다른 저장 프로퍼티가 없을 가능성이 크다.</li>
</ul>
</li>
</ul>
<p><strong>편의 이니셜라이저(Convenience Initializer)</strong></p>
<ul>
<li>편의 이니셜라이저는 <code>초기화를 좀 더 손쉽게 도와주는 역할</code>을 한다.</li>
<li>편의 이니셜라이저는 <code>지정 이니셜라이저</code>를 <code>자신 내부에서 호출</code>한다.</li>
<li>지정 이니셜라이저의 매개변수가 많아 외부에서 일일이 전달인자를 전달하기 어렵거나 특정 목적에 사용하기 위해서 편의 이니셜라이저를 설계할 수도 있다.</li>
<li>지정 이니셜라이저를 사용하면 인스턴스를 생성할 때마다 전달인자로 초깃값을 전달해야 하지만 편의 이니셜라이저를 사용하면 <code>항상 같은 값으로 초기화가 가능</code>하다.</li>
<li>편의 이니셜라이저는 필수 요소는 아니지만, 클래스 설계자의 의도대로 외부에서 사용하길 원하거나 인스턴스 생성 코드를 작성하는 수고를 덜 때 유용하게 사용가능 하다.</li>
</ul>
<pre><code class="language-swift">[지정 이니셜라이저]
init(매개변수들) { 
    초기화 구문
}

[편의 이니셜라이저]
convenience init(매개변수들) { 
    초기화 구문
}</code></pre>
<h3 id="클래스의-초기화-위임">클래스의 초기화 위임</h3>
<ul>
<li>지정 이니셜라이저와 편의 이니셜라이저 관계<ol>
<li>자식클래스의 지정 이니셜라이저는 <code>부모클래스의 지정 이니셜라이저를 반드시 호출</code>해야 한다.</li>
<li>편의 이니셜라이저는 <code>자신을 정의한 클래스의 다른 이니셜라이저를 반드시 호출</code>해야 한다.</li>
<li>편의 이니셜라이저는 궁극적으로는 <code>지정 이니셜라이저를 반드시 호출</code>해야 한다. </li>
</ol>
</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/bd925115-64b8-4020-8dd1-991fbc39f50a/%E1%84%8F%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A2%E1%84%89%E1%85%B3%E1%84%8B%E1%85%B4%20%E1%84%8E%E1%85%A9%E1%84%80%E1%85%B5%E1%84%92%E1%85%AA%20%E1%84%8B%E1%85%B1%E1%84%8B%E1%85%B5%E1%86%B7.png" alt=""></p>
<h3 id="2단계-초기화">2단계 초기화</h3>
<p>스위프트의 클래스 초기화는 <strong>2단계(two-phase)</strong>를 거친다.</p>
<p><strong>1단계</strong></p>
<ul>
<li>1단계 초기화는 클래스에 정의한 각각의 저장 프로퍼티에 <code>초깃값이 할당</code>된다.</li>
<li>모든 저장 프로퍼티의 초기 상태가 결정되면 2단계로 돌입해 저장 프로퍼티들을 사용자 정의할 기회를 얻는다.</li>
<li>그 후 비로소 새로운 인스턴스를 사용 할 준비가 끝난다.</li>
</ul>
<p><strong>2단계</strong></p>
<ul>
<li><p>2단계 초기화는 <code>프로퍼티를 초기화하기 전에 프로퍼티 값에 접근하는 것을 막아 초기화를 안전하게</code> 할 수 있도록 해준다.</p>
</li>
<li><p><code>다른 이니셜라이저가 프로퍼티의 값을 실수로 변경하는 것을 방지</code>할 수도 있다.</p>
</li>
<li><p>스위프트 컴파일러는 2단계 초기화를 오류 없이 처리하기 위해 다음과 같은 <code>네 가지 안전확인</code>(Safty-Checks)을 실행한다.</p>
<ol>
<li><code>자식클래스</code>의 <code>지정 이니셜라이저</code>가 부모클래스의 이니셜라이저를 호출하기 전에 <code>자신의 프로퍼티를 모두 초기화했는지 확인</code>한다. 
 a. 그래서 자식클래스의 지정 이니셜라이저에서 부모클래스의 이니셜라이저를 호출하기 전에 자신의 모든 (기본값이 없는) 저장 프로퍼티에 값을 할당해주어야 한다.</li>
<li><code>자식클래스</code>의 <code>지정 이니셜라이저</code>는 <code>상속받은 프로퍼티에 값을 할당하기 전</code>에 반드시 <code>부모클래스의 이니셜라이저를 호출</code>해야 한다.</li>
<li><code>편의 이니셜라이저</code>는 자신의 클래스에 정의한 프로퍼티를 포함하여 그 어떤 프로퍼티라도 값을 할당하기 전에 <code>다른 이니셜라이저를 호출</code>해야 한다.</li>
<li>초기화 1단계를 마치기 전까지는 이니셜라이저는 인스턴스 메서드를 호출할 수 없다. 또 인스턴스 프로퍼티의 값을 읽어들일 수도 없다. self 프로퍼티를 자신의 인스턴스를 나타내는 값으로 활용할 수도 없다. </li>
</ol>
</li>
<li><p>위의 네 가지 안전확인에 근거하여 <code>어떻게 2단계 초기화가 이루어지는지</code> 살펴보도록 한다.</p>
<p>  <strong>1단계</strong></p>
<ol>
<li><p>클래스가 지정 또는 편의 이니셜라이저를 호출한다.</p>
</li>
<li><p>그 클래스의 새로운 인스턴스를 위한 메모리가 할당된다. 메모리는 아직 초기화되지 않은 상태이다.</p>
</li>
<li><p>지정 이니셜라이저는 클래스에 정의된 모든 저장 프로퍼티에 값이 있는지 확인한다. 현재 클래스 부분까지의 저장 프로퍼티를 위한 메모리는 이제 초기화되었다.</p>
</li>
<li><p>지정 이니셜라이저는 부모클래스의 이니셜라이저가 같은 동작을 행할 수 있도록 초기화를 양도한다.</p>
</li>
<li><p>부모클래스는 상속 체인을 따라 최상위 클래스에 도달할 때까지 이 작업을 반복한다. </p>
<p><img src="https://images.velog.io/images/gooreum_90/post/d4d0821e-8142-451b-b131-76ca52889299/1%E1%84%83%E1%85%A1%E1%86%AB%E1%84%80%E1%85%A8.png" alt=""></p>
</li>
</ol>
</li>
</ul>
<pre><code>**2단계**

1.최상위 클래스로부터 최하위 클래스까지 상속 체인을 따라 내려오면서 지정 이니셜라이저들이 인스턴스를 제 각각 사용자 정의하게 된다. 이 단계에서는 self를 통해 프로퍼티 값을 수정할 수 있고, 인스턴스 메서드를 호출하는 등의 작업을 진행할 수 있다.
2.마지막으로 각각의 편의 이니셜라이저를 통해 self를 통한 사용자 정의 작업을 진행할 수 있다.

![](https://images.velog.io/images/gooreum_90/post/ba17bd39-1696-47fb-9f87-316077fd7775/2%E1%84%83%E1%85%A1%E1%86%AB%E1%84%80%E1%85%A8.png)</code></pre><pre><code class="language-swift">class Person {
    var name: String = &quot;&quot;
    var age: Int = 0

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

class Student: Person {
    var major: String = &quot;F&quot;

    init(name: String, age: Int, major: String) {
        self.major = major
        super.init(name: name, age: age)
    }

    convenience init(name: String) {
        self.init(name: name, age: 4, major: &quot;&quot;)
    }
        func test() { print(&quot;test&quot;) }
}</code></pre>
<ul>
<li><p>Student 클래스의 지정 이니셜라이저는 부모클래스의 지정 이니셜라이저를 호출하기 전에 자신의 self 프로퍼티를 이용해 major 프로퍼티의 값을 할당한다.</p>
<p>  → 안전확인 중 1번의 조건을 만족한다.</p>
</li>
<li><p><code>super.init(name: name, age: age)</code> 를 통해 부모클래스의 이니셜라이저를 호출했으며 그 외에 상속받은 프로퍼티가 없으므로 부모의 이니셜라이저 호출 이후에 값을 할당해줄 프로퍼티가 없다.</p>
<p>  → 안전확인 중 2번의 조건을 만족한다. </p>
</li>
<li><p>편의 이니셜라이저인 convenience init(name:)은 따로 차후에 값을 할당할 프로퍼티가 없고, 다른 이니셜라이저를 호출했다.</p>
<p>  → 안전확인 중 3번의 조건을 만족한다.</p>
</li>
<li><p>이니셜라이저 어디에서도 인스턴스 메서드를 호출하거나 인스턴스 프로퍼티의 값을 읽어오지 않는다.</p>
<ul>
<li><p><code>super.init(name: name, age: age)</code> 아래에 <code>self.test()</code> 메서드를 호출하면 호출 가능해진다.</p>
<p>→ 안전확인 중 4번의 조건을 만족한다. </p>
</li>
</ul>
</li>
</ul>
<h3 id="이니셜라이저-상속-및-재정의">이니셜라이저 상속 및 재정의</h3>
<ul>
<li>보통 부모클래스의 이니셜라이저와 똑같은 이니셜라이저를 자식클래스에서 사용하고 싶다면 자식클래스에서 부모의 이니셜라이저와 똑같은 이니셜라이저를 구현해주면 된다.<ul>
<li>기본적으로 스위프트의 이니셜라이저는 부모클래스의 이니셜라이저를 상속받지 않는다. 부모클래스로부터 물려받은 이니셜라이저는 자식클래스에 최적화되지 않기 때문이다.</li>
</ul>
</li>
<li>부모클래스와 동일한 지정 이니셜라이저를 자식클래스에서 구현해주려면 <code>재정의</code>하면 된다. 그러려면 <code>override</code> 수식어를 붙여야 한다. 자식클래스의 편의 이니셜라이저가 부모클래스의 지정 이니셜라이저를 재정의하는 경우에도 <code>override</code> 수식어를 붙여주면 된다.</li>
<li>반대로 부모클래스의 편의 이니셜라이저와 동일한 이니셜라이저를 자식클래스에 구현할 때는 <code>override</code> 수식어를 붙이지 않는다. <code>자식클래스에서 부모클래스의 편의 이니셜라이저는 절대로 호출할 수 없기 때문</code>이다. 즉, 재정의할 필요가 없다.</li>
</ul>
<pre><code class="language-swift">class Person {
    var name: String = &quot;&quot;
    var age: Int = 0

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    convenience init(name: String) {
        self.init(name: name, age: 0)
    }
}

class Student: Person {
    var major: String = &quot;F&quot;

    override init(name: String, age: Int) {
        self.major = &quot;Swift&quot;
        super.init(name: name, age: age)
    }

    convenience init(name: String) {
        self.init(name: name, age: 4)
    }
}</code></pre>
<ul>
<li>부모클래스의 실패 가능한 이니셜라이저를 자식클래스에서 재정의하고 싶을 때는 실패 가능한 이니셜라이저로 재정의해도 되고 필요에 따라서 실패하지 않는 이니셜라이저로 재정의해줄 수도 있다.</li>
</ul>
<h3 id="이니셜라이저-자동-상속">이니셜라이저 자동 상속</h3>
<ul>
<li><p>기본적으로 스위프트의 이니셜라이저는 부모클래스의 이니셜라이저를 상속받지 않지만 <code>특정 조건</code>에 부합한다면 부모클래스의 이니셜라이저가 자동으로 상속된다.</p>
</li>
<li><p><code>자식클래스에서 프로퍼티 기본값을 모두 제공한다고 가정</code>할 때, 다음 두 가지 규칙에 따라 이니셜라이저가 자동으로 상속된다.</p>
<p>  <code>**규칙1**</code> : 자식클래스에서 별도의 지정 이니셜라이저를 구현하지 않는다면, 부모클래스의 지정 이니셜라이저가 자동으로 상속된다.</p>
<p>  <code>**규칙2**</code> : 만약 <code>규칙1</code>에 따라 자식클래스에서 부모클래스의 지정 이니셜라이저를 자동으로 상속받은 경우 또는 부모클래스의 지정 이니셜라이저를 모두 재정의하여 부모클래스와 동일한 지정 이니셜라이저를 모두 사용 할 수 있는 상황이라면 부모클래스의 <code>편의 이니셜라이저</code>가 모두 자동으로 상속된다. </p>
</li>
</ul>
<pre><code class="language-swift">class Person {
    var name: String = &quot;&quot;
    var age: Int = 0

    init(age: Int) {
        self.age = age
    }

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    convenience init(name: String) {
        self.init(name: name, age: 0)
    }
}

class Student: Person {
    var major: String = &quot;F&quot;
}

class Developer: Person {
    var company: String
    //부모클래스의 지정이니셜라이저를 자식클래스의 편의이니셜라이저로 재정의
    override convenience init(age: Int) {
        self.init(name: &quot;developer&quot;, age: age)
        self.company = &quot;Unknown&quot;
    }
    //부모클래스의 지정이니셜라이저 재정의
    override init(name: String, age: Int) {
        self.company = &quot;Unknown&quot;
        super.init(name: name, age: age)
    }

    //자식클래스의 지정 이니셜라이저
    init(name: String, age: Int, company: String) {
        self.company = company
        super.init(name: name, age: 0)
    }
    //자식클래스의 편의 이니셜라이저
    convenience init(company: String) {
        self.init(name: &quot;Unknown&quot;, age: 0)
        self.company = company
    }
}

//부모클래스의 지정 이니셜라이저 자동 상속
let yagom: Person = Person(name: &quot;야곰&quot;, age: 5)
let haha: Student = Student(name: &quot;하하&quot;, age: 20)
print(yagom.name)
print(haha.name)
print()

//부모클래스의 편의 이니셜라이저 자동 상속
let wizplan: Person = Person(name: &quot;wizplan&quot;)
let jinsung: Student = Student(name: &quot;jinsung&quot;)
print(wizplan.name)
print(jinsung.name)
print()

//부모클래스의 편의 이니셜라이저 자동 상속
let developer: Developer = Developer(name: &quot;개발자&quot;)
print(developer.name)
print(developer.company)
print(developer.age)
print()

//부모클래스의 지정 이니셜라이저를 자식클래스에서 편의 이니셜라이저로 재정의
let developer2: Developer = Developer(age: 55)
print(developer2.name)
print(developer2.company)
print(developer2.age)
print()

//자식클래스에서 편의 이니셜라이저를 정의해도 자동 상속에 문제가 없음.
let developer3: Developer = Developer(company: &quot;Naver&quot;)
print(developer3.name)
print(developer3.company)
print(developer3.age)

--result--
야곰
하하

wizplan
jinsung

개발자
Unknown
0

developer
Unknown
55

Unknown
Naver
0</code></pre>
<ul>
<li>Person - Student 클래스 관계에서의 자동상속<ul>
<li>Student의 major <code>프로퍼티에 기본값이 있으며</code>, 따로 지정 이니셜라이저를 구현해주지 않았으므로 부모클래스인 Person클래스의 지정 이니셜라이저가 자동으로 상속됨.</li>
<li>이는 <code>규칙1</code>에 부합한다.</li>
<li>부모클래스의 지정 이니셜라이저를 모두 자동으로 상속받았으므로 <code>편의 이니셜라이저도 자동으로 상속</code>되었다.</li>
</ul>
</li>
<li>Person - Developer 클래스 관계에서의 자동상속<ul>
<li>Developer 클래스의 company 프로퍼티에 기본값이 없더라도 이니셜라이저에서 적절히 초기화했고, 부모클래스의 지정 이니셜라이저를 모두 재정의하여 부모클래스의 지정 이니셜라이저와 동일한 이니셜라이저를 모두 사용할 수 있는 상황이므로 <code>규칙1</code> 에 부합한다.<ul>
<li>따라서 부모클래스의 편의 이니셜라이저가 자동으로 상속되었다.</li>
</ul>
</li>
<li>자동 상속 규칙은 자식클래스에 편의 이니셜라이저를 추가한다고 하더라도 유효하다.</li>
<li>또 부모클래스의 지정 이니셜라이저를 자식클래스의 편의 이니셜라이저로 구현하더라도 <code>규칙2</code> 를 충족한다.</li>
</ul>
</li>
</ul>
<h3 id="요구-이니셜라이저">요구 이니셜라이저</h3>
<ul>
<li><code>required</code> 수식어를 클래스의 이니셜라이저 앞에 명시해주면 이 클래스를 상속받은 자식클래스에서 반드시 해당 이니셜라이저를 구현해주어야 한다.</li>
<li>다만 자식클래스에서 요구 이니셜라이저를 재정의할 때는 <code>override</code> 수식어 대신에 <code>required</code> 수식어를 사용한다.</li>
<li>만약 부모클래스에 요구 이니셜라이저가 있지만, 자식클래스의 프로퍼티에 기본값이 있으며 별다른 지정 이니셜라이저가 없다면 자식클래스는 부모클래스의 이니셜라이저를 자동 상속받게 된다.</li>
</ul>
<pre><code class="language-swift">class Person { 
    var name: String
    required init() { 
        self.name = &quot;Unknown&quot;
    }
}

class Student: Person { 
    var major: String = &quot;Unknown&quot;
}

let miJeong: Student = Student()</code></pre>
<ul>
<li>그러나 Student 클래스에 새로운 지정 이니셜라이저를 구현한다면 부모클래스로부터 이니셜라이저가 자동으로 상속되지 않으므로 요구 이니셜라이저를 구현해주어야 한다.</li>
</ul>
<pre><code class="language-swift">class Person { 
    var name: String
    required init() { 
        self.name = &quot;Unknown&quot;
    }
}

class Student: Person { 
    var major: String = &quot;Unknown&quot;
    init(major: String) { 
        self.major = major
        super.init()
    }

    required init() { 
        self.major = &quot;Unknown&quot;
        super.init()
    }
}

let miJeong: Student = Student()</code></pre>
<ul>
<li>부모클래스의 지정 이니셜라이저를 자식클래스에서 요구 이니셜라이저로 변경 가능하다.<ul>
<li><code>required override</code>를 명시해주어 재정의됨과 동시에 요구 이니셜라이저가 될 것임을 명시해준다.</li>
</ul>
</li>
<li>또한 부모클래스의 편의 이니셜라이저를 자식 클래스에서 요구 이니셜라이저로 변경 가능하다.<ul>
<li><code>required convenience</code>를 명시해주어 재정의됨과 동시에 요구 이니셜라이저가 될 것임을 명시해준다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[상속 1 (클래스 상속 / 메서드, 프로퍼티, 프로퍼티 감시자 / 재정의 방지) ]]></title>
            <link>https://velog.io/@gooreum_90/%EC%83%81%EC%86%8D-1-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%83%81%EC%86%8D-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EA%B0%90%EC%8B%9C%EC%9E%90-%EC%9E%AC%EC%A0%95%EC%9D%98-%EB%B0%A9%EC%A7%80</link>
            <guid>https://velog.io/@gooreum_90/%EC%83%81%EC%86%8D-1-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%83%81%EC%86%8D-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EA%B0%90%EC%8B%9C%EC%9E%90-%EC%9E%AC%EC%A0%95%EC%9D%98-%EB%B0%A9%EC%A7%80</guid>
            <pubDate>Sun, 14 Nov 2021 19:18:26 GMT</pubDate>
            <description><![CDATA[<h3 id="상속">상속</h3>
<ul>
<li>다른 클래스로부터 상속을 받지 않은 클래스를 <code>기반클래스</code>(Base Class)라고 한다.</li>
<li>상속은 기반클래스를 다른 클래스에서 물려받는 것을 말한다.</li>
<li>클래스는 <code>메서드</code>나  <code>프로퍼티</code> 등을 다른 클래스로부터 상속 가능하다.</li>
<li>상속은 스위프트의 다른 타입과 클래스를 구별 짓는 <strong>클래스만의 특징</strong>이다.</li>
<li>스위프트의 클래스는 부모클래스로부터 물려받은 메서드를 호출할 수 있고 프로퍼티에 접근할 수 있으며 서브스크립트도 사용할 수 있다.</li>
<li>자식클래스는 부모클래스의 프로퍼티, 메서드, 서브스크립트 등을 자신만의 내용으로 <code>재정의</code>할 수도 있다.<ul>
<li>이때 재정의한다는 것을 명확히 확인해주어야 한다.</li>
</ul>
</li>
<li>상속받은 프로퍼티에 프로퍼티의 값이 변경되었을 때 알려주는 <code>프로퍼티 감시자도 구현</code>할 수 있다.<ul>
<li>연산 프로퍼티를 정의해준 클래스에서는 연산 프로퍼티에 프로퍼티 감시자를 구현 할 수 없지만, 부모클래스에서 연산 프로퍼티로 정의한  프로퍼티든 저장 프로퍼티로 정의한 프로퍼티든 <strong>자식클래스에서는 프로퍼티 감시자를 구현할 수 있다.</strong></li>
</ul>
</li>
</ul>
<h3 id="재정의">재정의</h3>
<ul>
<li>자식클래스는 부모클래스로부터 물려받은 특성을 그대로 사용하지 않고 자신만의 기능으로 변경하여 사용할 수 있는데, 이를 <code>재정의(override)</code>라고 한다.</li>
<li>상속받은 특성들을 재정의하려면 새로운 정의 앞에 <code>override</code> 키워드를 사용한다.<ul>
<li>사용하지 않으면 컴파일 오류가 발생한다.</li>
</ul>
</li>
<li>만약 자식클래스에서 부모클래스의 특성을 재정의했을 때, 부모클래스의 특성을 자식클래스에서 사용하고 싶다면 <code>super</code> 프로퍼티를 사용하면 된다.<ul>
<li>super 키워드를 메서드 내에서 사용한다면, 부모클래스의 타입 메서드와 타입 프로퍼티에 접근할 수 있으며 인스턴스 메서드 내에서 사용한다면, 부모클래스의 인스턴스 메서드와 인스턴스 프로퍼티, 서브스크립트에 접근 가능하다.</li>
</ul>
</li>
</ul>
<h3 id="메서드-재정의">메서드 재정의</h3>
<ul>
<li>부모클래스의 메서드를 재정의하기 위해선 메서드 앞에 <code>override</code> 키워드를 사용한다.</li>
<li>부모클래스의 메서드를 재정의하더라도 부모클래스의 메서드 기능을 그대로 사용하고자 한다면 메서드 내부에서 <code>super</code> 프로퍼티를 사용하면 된다.</li>
</ul>
<pre><code class="language-swift">class Person {
    var name: String = &quot;&quot;
    var age: Int = 0

    var introduction: String {
        return &quot;이름 : \(name), 나이 : \(age)&quot;
    }

    func speak() {
        print(&quot;가나다라마바사&quot;)
    }

    class func introduceClass() -&gt; String {
        return &quot;인류의 소원은 평화입니다.&quot;
    }
}

class Student: Person {
    var grade: String = &quot;F&quot;

    func study() {
        print(&quot;Study hard&quot;)
    }

    override func speak() {
        print(&quot;저는 학생입니다.&quot;)
    }
}

class UniversityStudent: Student {
    var major: String = &quot;&quot;

    class func introduceClass() {
        print(super.introduceClass())
    }

    override class func introduceClass() -&gt; String {
        return &quot;대학생의 소원은 A+ 입니다.&quot;
    }

    override func speak() {
        super.speak()
        print(&quot;대학생이죠.&quot;)
    }
}

print(&quot;----------yagom----------&quot;)
let yagom : Person = Person()
yagom.speak()

print(&quot;----------jay----------&quot;)
let jay: Student = Student()
jay.speak()

print(&quot;----------jenny----------&quot;)
let jenny: UniversityStudent = UniversityStudent()
jenny.speak()

print(&quot;----------Person.introduceClass----------&quot;)
print(Person.introduceClass())
print(&quot;----------Student.introduceClass----------&quot;)
print(Student.introduceClass())
print(&quot;----------UniversityStudent.introduceClass as String----------&quot;)
print(UniversityStudent.introduceClass() as String)
print(&quot;----------UniversityStudent.introduceClass as Void----------&quot;)
UniversityStudent.introduceClass() as Void

--result--
----------yagom----------
가나다라마바사
----------jay----------
저는 학생입니다.
----------jenny----------
저는 학생입니다.
대학생이죠.
----------Person.introduceClass----------
인류의 소원은 평화입니다.
----------Student.introduceClass----------
인류의 소원은 평화입니다.
----------UniversityStudent.introduceClass as String----------
대학생의 소원은 A+ 입니다.
----------UniversityStudent.introduceClass as Void----------
인류의 소원은 평화입니다.</code></pre>
<ul>
<li>UniversityStudent 클래스에서 override가 있는 introduceClass() 메서드와 없는 introduceClass() 메서드를 살펴보자.<ul>
<li>UniversityStudent 클래스는 Person 클래스를 상속받았기 때문에 introduceClass()를 상속받아 사용가능하다. Override가 붙은 introduceClass()는 해당 introduceClass()메서드를 재정의한 것이고, override가 없는 것은 단순히 메서드 명칭만 같을 뿐 반환 타입이 다르기 때문에 상속받은 메서드가 아니다.</li>
</ul>
</li>
</ul>
<h3 id="프로퍼티-재정의">프로퍼티 재정의</h3>
<ul>
<li>부모클래스로부터 상속받은 인스턴스 프로퍼티나 타입 프로퍼티를 자식클래스에서 용도에 맞게 재정의 가능하다.</li>
<li>프로퍼티를 재정의할 때는 <code>저장 프로퍼티로 재정의할 수는 없다.</code></li>
<li>프로퍼티를 재정의한다는 것은 프로퍼티 자체가 아니라 프로퍼티의 <code>접근자(Getter)</code>, <code>설정자(Setter)</code>, <code>프로퍼티 감시자(Property Observer)</code> 등을 재정의하는 것을 의미한다.</li>
<li>조상클래스에서 <code>저장 프로퍼티</code>로 정의한 프로퍼티는 물론이고 <code>연산 프로퍼티</code>로 정의한 프로퍼티도 <code>접근자와 설정자를 재정의</code>할 수 있다.<ul>
<li>프로퍼티를 상속받은 자식클래스에서는 조상클래스의 프로퍼티 종류(저장, 연산 등)는 알지 못하고 <code>단지 이름과 타입만을 알기 때문</code>이다.</li>
</ul>
</li>
<li>조상클래스에서 <code>읽기 전용</code> 프로퍼티였더라도 자식클래스에서 <code>읽고 쓰기가 가능한 프로퍼티로 재정의</code>해줄 수도 있다.<ul>
<li>그러나 읽기 쓰기 모두 가능했던 프로퍼티를 읽기 전용으로 재정의해줄 수는 없다.</li>
</ul>
</li>
<li>읽기 쓰기가 모두 가능한 프로퍼티를 재정의할 때 접근자 설정자 모두 재정의해야 하며, 설정자만 재정의할 수는 없다.<ul>
<li>만약 접근자에 따로 기능 변경이 필요 없다면 super.someProperty와 같은 식으로 부모클래스의 접근자를 사용하여 값을 받아와 반환해주면 된다.</li>
</ul>
</li>
</ul>
<pre><code class="language-swift">class Person {
    var name: String = &quot;&quot;
    var age: Int = 0
    var koreanAge: Int { //get 전용 연산프로퍼티
        return self.age + 1
    }
    var introduction: String {
        return &quot;이름 : \(name), 나이 : \(age)&quot;
    }
}

class Student: Person {
    var grade: String = &quot;F&quot;

    override var introduction: String {
        return super.introduction + &quot;, &quot; + &quot;학점: \(self.grade)&quot;
    }

    override var koreanAge: Int { //get set 연산프로퍼티로 재정의
        get {
            return super.koreanAge
        }
        set {
            self.age = newValue - 1
        }
    }
}

let yagom = Person()
yagom.name = &quot;야곰&quot;
yagom.age = 55
//yagom.koreanAge = 56 -&gt; 컴파일 오류발생
print(yagom.introduction)
print(yagom.koreanAge)

let jay: Student = Student()
jay.name = &quot;제이&quot;
jay.age = 14
jay.koreanAge = 15
print(jay.introduction)
print(jay.koreanAge)</code></pre>
<ul>
<li>Person 클래스의 koreanAge 연산 프로퍼티는  읽기 전용이지만 자식클래스인 Student에서는 읽기-쓰기 연산프로퍼티로 재정의 하였습니다.</li>
<li>Student 클래스에서 koreanAge 연산 프로퍼티 재정의시 접근자(getter)는 따로 기능을 재정의할 필요가 없어 부모클래스의 property를 그대로 사용하기 위해 <code>super.koreanAge</code> 작성</li>
</ul>
<h3 id="프로퍼티-감시자-재정의">프로퍼티 감시자 재정의</h3>
<ul>
<li>프로퍼티 감시자도 프로퍼티의 접근자와 설정자처럼 재정의 가능하다.</li>
<li>마찬가지로 조상클래스에 정의한 프로퍼티가  <code>연산 프로퍼티인지 저장 프로퍼티인지는 상관없다</code>. 다만 <code>상수 저장 프로퍼티</code>나 <code>읽기 전용 연산 프로퍼티</code>는 <code>프로퍼티 감시자를 재정의 할 수 없다</code>.<ul>
<li>왜냐하면 상수 저장 프로퍼티나 읽기 전용 연산 프로퍼티는 값을 설정할 수 없으므로 willSet이나 didSet 메서드를 사용한 프로퍼티 감시자를 원천적으로 사용할 수 없기 때문이다.</li>
</ul>
</li>
<li>프로퍼티 감시자를 재정의하더라도 <code>조상클래스에 정의한 프로퍼티 감시자도 동작</code>한다는 점을 잊지 말아야 한다.</li>
<li><code>프로퍼티 접근자</code>와 <code>프로퍼티 감시자</code>는 <code>동시에 재정의할 수 없다</code>. 만약 둘 다 동작하길 원한다면 재정의하는 접근자에 프로퍼티 감시자의 역할을 구현해야 한다.</li>
</ul>
<pre><code class="language-swift">class Person {
    var name: String = &quot;&quot;
    var age: Int = 0 {
        didSet {
            print(&quot;Person age: \(self.age)&quot;)
        }
    }
    var koreanAge: Int {
        return self.age + 1
    }
    var fullName: String {
        get {
            return self.name
        }
        set {
            self.name = newValue
        }
    }
}

class Student: Person {
    var grade: String = &quot;F&quot;

    override var age: Int {
        didSet {
            print(&quot;Student age: \(self.age)&quot;)
        }
    }

    override var koreanAge: Int {
        get {
            return super.koreanAge
        }
        set {
            self.age = newValue - 1
        }
        //didSet { } -&gt; 오류발생
    }
    override var fullName: String {
        didSet {
            print(&quot;FullName: \(self.fullName)&quot;)
        }
    }
}

let yagom = Person()
yagom.name = &quot;야곰&quot;
yagom.age = 55
yagom.fullName = &quot;조야곰&quot;
print(yagom.koreanAge)

let jay: Student = Student()
jay.name = &quot;제이&quot;
jay.age = 14
jay.koreanAge = 15
jay.fullName = &quot;김제이&quot;
print(jay.koreanAge)

--result--
Person age: 55
56
Person age: 14
Student age: 14
Person age: 14
Student age: 14
FullName: 김제이
15</code></pre>
<ul>
<li>Student 클래스에는 Person 클래스의 age라는 저장 프로퍼티의 프로퍼티 감시자를 재정의 하였으며, koreanAge와 fullName이라는 연산 프로퍼티의 프로퍼티 감시자를 재정의.</li>
<li>Person 클래스의 age라는 저장 프로퍼티에 이미 프로퍼티 감시자를 정의했으므로 jay의 age 프로퍼티의 값을 할당할 때는 Person에 정의한 프로퍼티 감시자와 Student에 정의한 프로퍼티 감시자가 모두 동작한다.</li>
<li>기존에 연산 프로퍼티로 정의했던 fullName 프로퍼티에도 프로퍼티 감시자를 정의하였다.</li>
<li>그러나 koreanAge 프로퍼티에는 프로퍼티 감시자와 프로퍼티 설정자를 동시에 정의할 수 없다.</li>
</ul>
<h3 id="재정의-방지">재정의 방지</h3>
<ul>
<li>부모클래스를 상속받는 자식클래스에서 몇몇 특성을 재정의할 수 없도록 제한하고 싶다면 재정의를 방지하고 싶은 특성 앞에 <code>final</code> 키워드를 명시하면 된다.<ul>
<li>final 키워드가 명시된 특성을 재정의 할 경우 <code>컴파일 오류</code>가 발생한다.</li>
</ul>
</li>
<li>클래스를 상속하거나 재정의할 수 없도록 하고 싶다면 class 키워드 앞에 final 키워드를 명시하면 된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로토콜]]></title>
            <link>https://velog.io/@gooreum_90/%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C</link>
            <guid>https://velog.io/@gooreum_90/%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C</guid>
            <pubDate>Thu, 11 Nov 2021 21:55:00 GMT</pubDate>
            <description><![CDATA[<h3 id="프로토콜이란">프로토콜이란</h3>
<ul>
<li>프로토콜은 특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의한다.</li>
<li>어떤 프로토콜의 요구사항을 모두 따르는 타입은 &#39;해당 프로토콜을 준수한다&#39;고 표현한다.</li>
<li>타입에서 프로토콜의 요구사항을 충족시키려면 프로토콜이 제시하는 청사진의 기능을 모두 구현해야 한다.</li>
<li>즉, 프로토콜은 정의를 하고 제시를 할 뿐이지 스스로 기능을 구현하지는 않는다.</li>
<li>protocol 키워드를 사용하여 정의하며, 프로토콜을 채택하는 클래스, 열거형, 구조체는 자신의 이름 뒤에 콜론(:)을 붙여준 후 채택할 프로토콜 이름을 쉼표(,)로 구분하여 명시해준다.</li>
</ul>
<h3 id="프로토콜-요구사항">프로토콜 요구사항</h3>
<ul>
<li>프로토콜이 자신을 채택한 타입에 요구하는 사항은 프로퍼티나 메서드와 같은 기능들이다.</li>
</ul>
<p><strong>(1) 프로퍼티 요구</strong></p>
<ul>
<li>프로토콜은 자신을 채택한 타입이 어떤 프로퍼티를 구현해야 하는지 요구할 수 있다.</li>
<li>그렇지만 프로퍼티의 종류(연산 / 저장)는 따로 신경쓰지 않는다.</li>
<li>프로토콜을 채택한 타입은 프로토콜이 요구하는 프로퍼티의 이름과 타입만 맞도록 구현해주면 된다.</li>
<li>다만 프로퍼티를 읽기 전용으로 할지 혹은 읽고 쓰기가 모두 가능하게 할지는 프로토콜이 정해야 한다.</li>
<li>만약 프로토콜이 읽고 쓰기가 가능한 프로퍼티를 요구한다면 읽기만 가능한 상수 저장 프로퍼티 또는 읽기 전용 연산 프로퍼티를 구현할 수 없다.</li>
<li>만약 프로토콜이 읽기 가능한 프로퍼티를 요구한다면 타입에 프로퍼티를 구현할 때 상수 저장 프로퍼티나 읽기 전용 연산 프로퍼티를 포함해서 어떤 식으로든 프로퍼티를 구현할 수 있다.</li>
<li>쓰기만 가능한 프로퍼티는 없으니 타입에 구현해주는 프로퍼티는 무엇이 되어도 상관없다.</li>
<li>프로토콜의 프로퍼티 요구사항은 항상 <code>var</code> 키워드를 사용한 변수 프로퍼티로 정의해야 한다.</li>
<li>타입 프로퍼티를 요구하려면 <code>static</code> 키워드를 사용한다.<ul>
<li>클래스의 타입 프로퍼티에는 상속 가능한 프로퍼티인 class 타입 프로퍼티와 상속 불가능한 static 타입 프로퍼티가 있지만 이 두 타입 프로퍼티를 따로 구분하지 않고 모두 static 키워드를 사용하여 타입 프로퍼티를 요구하면 된다.</li>
</ul>
</li>
</ul>
<p><strong>(2) 메서드 요구</strong></p>
<ul>
<li>프로토콜이 요구할 메서드는 프로토콜 정의에서 작성하며, 특정 인스턴스 메서드나 타입 메서드를 요구할 수도 있다.</li>
<li>다만 메서드의 실제 구현부인 <strong>중괄호 {} 부분은 제외</strong>하고 <code>메서드의 이름</code> <code>매개변수</code> <code>반환 타입</code> 등만 작성하며 가변 매개변수도 허용한다.</li>
<li>프로토콜의 메서드 요구에서는 <strong>매개변수 기본값을 지정할 수 없다.</strong></li>
<li>타입 메서드를 요구할 때는 타입 프로퍼티 요구와 마찬가지로 앞에 <code>static</code> 키워드를 명시한다.<ul>
<li>static 키워드를 사용하여 요구한 타입 메서드를 클래스에서 실제 구현할 때는 <code>static</code> 키워드나 <code>class</code> 키워드 어느 쪽을 사용해도 무방하다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>💡 <strong>타입으로서의 프로토콜</strong>
프로토콜은 요구만 하고 스스로 기능을 구현하지는 않는다. 그렇지만 프로토콜은 코드에서 완전한 하나의 타입으로 사용되기에 여러 위치에서 프로토콜을 타입으로 사용할 수 있다. </p>
</blockquote>
<ul>
<li>함수, 메서드, 이니셜라이저에서 매개변수 타입이나 반환 타입으로 사용될 수 있다.</li>
<li>프로퍼티, 변수, 상수 등의 타입으로 사용될 수 있다.</li>
<li>배열, 딕셔너리 등 컨테이너 요소의 타입으로 사용될 수 있다.</li>
</ul>
<p><strong>(3) 가변 메서드 요구</strong></p>
<ul>
<li>값 타입(구조체와 열거형)의 인스턴스 메서드에서 자신 내부의 값을 변경하고자 할 때는 메서드의 func 키워드 앞에 <code>mutating</code> 키워드를 적어 메서드에서 인스턴스 내부의 값을 변경한다.</li>
<li>프로토콜이 어떤 타입이든 간에 인스턴스 내부의 값을 변경해야 하는 메서드를 요구하려면 프로토콜의 메서드 정의 앞에 <code>mutating</code> 키워드를 명시해야 한다.</li>
<li>참조 타입인 클래스의 메서드라면 명시하지 않아도 상관 없지만 값 타입인 구조체와 열거형의 메서드 앞에는 <code>mutating</code> 키워드를 붙인 <code>가변 메서드 요구</code>가  필요하다.</li>
</ul>
<p><strong>(4) 이니셜라이저 요구</strong></p>
<ul>
<li>프로토콜은 이니셜라이저를 요구할 수 있다.</li>
<li>물론 프로토콜 내에서 정의만 하지 중괄호를 포함한 이니셜라이저 구현은 하지 않는다.</li>
<li>구조체는 상속을 할 수 없기 때문에 이니셜라이저 요구에 대해 크게 신경쓸 필요가 없지만 클래스의 경우라면 조금 다르다.<ul>
<li>클래스 타입에서 프로토콜의 이니셜라이저 요구에 부합하는 이니셜라이저를 구현할 때는 이니셜라이저가 지정 이니셜라이저인지 편의 이니셜라이저인지는 중요하지 않다.</li>
<li>그러나 이니셜라이저 요구에 부합하는 이니셜라이저를 구현할 때는 <code>required</code> 식별자를 붙인 요구 이니셜라이저로 구현해야 한다.</li>
<li>만약 클래스 자체가 상속받을 수 없는 final 클래스라면 required 식별자를 붙여줄 필요 없다.</li>
</ul>
</li>
</ul>
<pre><code class="language-swift">protocol Named { 
    var name: String { get }
    init(name: String)
}

class Person: Named { 
    var name: String

    required init(name: String) { 
        self.name = name 
    }
}</code></pre>
<ul>
<li>만약 특정 클래스에 프로토콜이 요구하는 이니셜라이저가 이미 구현되어 있는 상황에서 그 클래스를 상속받은 클래스가 있다면, <code>required</code>와 <code>override</code> 식별자를 모두 명시하여 프로토콜에서 요구하는 이니셜라이저를 구현해주어야 한다. (순서는 상관없다.)</li>
</ul>
<pre><code class="language-swift">class School { 
    var name: String
    init(name: String) { 
        self.name = name
    }
}

class MiddleSchool: School, Named { 
        required override init(name: String) {
            super.init(name: name)
        }
}</code></pre>
<ul>
<li>프로토콜은 실패가능한 이니셜라이저를 요구할 수도 있다.<ul>
<li>해당 프로토콜을 준수하는 타입은 해당 이니셜라이저를 구현할 때 실패 가능한 이니셜라이저로 구현해도, 일반적인 이니셜라이저로 구현해도 무방하다.</li>
</ul>
</li>
</ul>
<h3 id="프로토콜의-상속과-클래스-전용-프로토콜">프로토콜의 상속과 클래스 전용 프로토콜</h3>
<ul>
<li>프로토콜의 상속 리스트에 class 키워드를 추가해 프로토콜이 클래스 타입에만 채택될 수 있도록 제한할 수도 있다.</li>
<li>클래스 전용 프로토콜로 제한을 주기 위해서는 프로토콜의 상속 리스트의 맨 처음에 class 키워드가 위치해야 한다.</li>
</ul>
<pre><code class="language-swift">protocol ClassOnlyProtocol: class, Readable, Writable { 
    //추가 요구사항
}</code></pre>
<h3 id="프로토콜-조합과-프로토콜-준수-확인">프로토콜 조합과 프로토콜 준수 확인</h3>
<ul>
<li>하나의 매개변수가 여러 프로토콜을 모두 준수하는 타입이어야 한다면 하나의 매개변수에 여러 프로토콜을 한 번에 조합하여 요구할 수 있다.</li>
<li>프로토콜을 조합하여 요구할 때는 SomeProtocol &amp; AnotherProtocol과 같이 표현한다.</li>
<li>또 하나의 매개변수가 프로토콜을 둘 이상 요구할 수도 있다.<ul>
<li>이때도 마찬가지로 앰퍼샌드(&amp;)를 여러 프로토콜 이름 사이에 써주면 된다.</li>
</ul>
</li>
<li>더불어 특정 클래스의 인스턴스 역할을 할 수 있는지 함께 확인할 수 있다.</li>
<li>구조체나 열거형 타입은 조합할 수 없고, 조합 중 클래스 타입은 한 타입만 조합할 수 있다.</li>
</ul>
<pre><code class="language-swift">protocol Named { 
    var name: String { get }
}

protocol Aged { 
    var age: Int { get }
}

struct Person: Named, Age {
    var name: String
    var age: Int
}

class Car: Named {
    var name: String 
    init(name: String) { 
        self.name = name
    }
}

class Truck: Car, Aged { 
    var age: Int
    init(name: String, age: Int) { 
        self.age = age
        super.init(name: name)
    }
}

func celebrateBirthday(to celebrator: Named &amp; Aged) { 
    print(&quot;Happy birthday \(celebrator.name)!! Now you are \(celebrator.age)&quot;)
}

let yagom: Person = Person(name: &quot;yagom&quot;, age: 90)
celebrateBirthday(to: yagom) // Happy birthday yagom!! Now you are 90

let myCar: Car = Car(name: &quot;Boong Boong&quot;)
//celebrateBirthday(to: myCar) //Aged를 충족하지 못하므로 오류발생

//Car 클래스의 인스턴스 역할도 수행할 수 있고,
//Aged 프로토콜을 준수하는 인스턴스만 할당할 수 있다.
var someVariable: Car &amp; Aged

//Truck 인스턴스는 Car 클래스 역할도 할 수 있고 Aged 프로토콜도 준수하므로 할당할 수 있다.
someVariable = Truck(name: &quot;Truck&quot;, age: 5)

//Car의 인스턴스인 myCar는 Aged 프로토콜을 준수하지 않으므로 할당할 수 없다.
//오류발생!
//someVariable = myCar
</code></pre>
<ul>
<li>타입캐스팅에 사용하던 is와 as 연산자를 통해 대상이 프로토콜을 준수하는지 확인할 수도 있고, 특정 프로토콜로 캐스팅할 수 있다.<ul>
<li>is 연산자를 통해 해당 인스턴스가 특정 프로토콜을 준수하는지 확인 가능.</li>
<li>as? 다운캐스팅 연산자를 통해 다른 프로토콜로 다운캐스팅을 시도할 수 있다.</li>
<li>as! 다운캐스팅 연산자를 통해 다른 프로토콜로 강제 다운캐스팅 할 수 있다.</li>
</ul>
</li>
</ul>
<pre><code class="language-swift">print(yagom is Named) // True
print(yagom is Aged) // True

print(myCar is Named) // True
print(myCar is Aged) // False

if let castedInstance: Named = yagom as? Named { 
    print(&quot;\(castedInstance) is Named&quot;)
} //Person is Named</code></pre>
<h3 id="프로토콜의-선택적-요구">프로토콜의 선택적 요구</h3>
<ul>
<li><code>objc</code> 속성이 부여된 프로토콜은 프로토콜의 요구사항 중 일부를 선택적 요구사항으로 지정할 수 있다.</li>
<li><code>objc</code> 속성이 부여되는 프로토콜은 <code>Objective-C</code> 클래스(<code>NSObject</code>)를 상속받은 클래스에서만 채택할 수 있다. 즉, 열거형이나 구조체 등에서는 <code>objc</code> 속성이 부여된 프로토콜은 아예 채택이 불가하다.</li>
<li><code>objc</code> 속성을 사용하려면 <code>Foundation</code> 프레임워크 모듈을 임포트해야 한다.</li>
<li>선택적 요구사항은 <code>optional</code> 식별자를 요구사항의 정의 앞에 붙여주면 된다.<ul>
<li>만약 메서드나 프로퍼티를 선택적 요구사항으로 요구하게 되면 그 요구사항의 타입은 <strong>자동적으로</strong> <code>옵셔널이</code> 된다.</li>
<li>메서드의 매개변수나 반환 타입이 옵셔널이 된 것이 아니라 메서드(함수) 자체의 타입이 옵셔널이 된 것이라는 점을 놓치지 말자!</li>
</ul>
</li>
<li>선택적 요구사항은 그 프로토콜을 준수하는 타입에 구현되어 있지 않을 수 있기 때문에 옵셔널 체이닝을 통해 호출할 수 있다.</li>
</ul>
<pre><code class="language-swift">@objc protocol Moveable {
    func walk()
    @objc optional func fly()
}

class Tiger: NSObject, Moveable {
    func walk() {
        print(&quot;Tiger walks&quot;)
    }
}

class Bird: NSObject, Moveable {
    func walk() {
        print(&quot;Bird walks&quot;)
    }

    func fly() {
        print(&quot;Bird flys&quot;)
    }
}

let tiger: Tiger = Tiger()
let bird: Bird = Bird()

tiger.walk()
bird.walk()

bird.fly()

var moveableInstance: Moveable = tiger
moveableInstance.fly?() //응답 없음

moveableInstance = bird
moveableInstance.fly?()

--result--
Tiger walks
Bird walks
Bird flys
Bird flys</code></pre>
<ul>
<li>Moveable 프로토콜 변수에 할당된 moveableInstance는 인스턴스의 타입에 실제로 fly() 메서드가 구현되어 있는지 알 수 없으므로 옵셔널 체인을 이용하여 fly() 메서드 호출을 시도해본다. 옵셔널 체인을 사용할 때는 메서드 이름 뒤에 물음표를 붙여 표현한다.</li>
</ul>
<h3 id="프로토콜-변수와-상수">프로토콜 변수와 상수</h3>
<ul>
<li>프로토콜은 자기 스스로 인스턴스를 생성하고 초기화 할 수는 없지만, 프로토콜 변수나 상수를 생성하여 특정 프로토콜을 준수하는 타입의 인스턴스를 할당할 수는 있다.</li>
</ul>
<h3 id="위임을-위한-프로토콜">위임을 위한 프로토콜</h3>
<ul>
<li><p><strong>위임(Delegation)</strong>은 클래스나 구조체가 자신의 책임이나 임무를 다른 타입의 인스턴스에게 위임하는 디자인 패턴이다.</p>
</li>
<li><p>위임은 사용자의 특정 행동에 반응하기 위해 사용되기도 하며, 비동기 처리에도 많이 사용한다.</p>
</li>
<li><p><strong>위임 패턴(Delegation Pattern)</strong>은 애플의 프레임워크에서 사용하는 주요한 패턴 중 하나이다.</p>
<p>  ex) UITableViewDelegate는 UITableView의 인스턴스가 해야 하는 일을 대신 처리해줄 수 있다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[접근제어]]></title>
            <link>https://velog.io/@gooreum_90/%EC%A0%91%EA%B7%BC%EC%A0%9C%EC%96%B4</link>
            <guid>https://velog.io/@gooreum_90/%EC%A0%91%EA%B7%BC%EC%A0%9C%EC%96%B4</guid>
            <pubDate>Thu, 11 Nov 2021 14:38:22 GMT</pubDate>
            <description><![CDATA[<h3 id="접근제어란">접근제어란?</h3>
<ul>
<li>객체지향 프로그래밍 패러다임에서 은닉화를 구현하기 위한 핵심 기능이다.</li>
<li>접근제어란 코드끼리 상호작용시 파일 간 또는 모듈 간에 접근을 제한할 수 있는 기능이다.</li>
<li>접근제어를 통해 코드의 상세 구현은 숨기고 허용된 기능만 사용하는 인터페이스를 제공할 수 있다.</li>
</ul>
<h3 id="모듈과-소스파일">모듈과 소스파일</h3>
<p><strong>모듈(module)</strong></p>
<ul>
<li>배포할 코드의 묶음 단위이다.</li>
<li>하나의 프레임워크나 라이브러리 또는 애플리케이션이 모듈 단위가 될 수 있음.</li>
<li>스위프트에서는 import 키워드를 사용해 불러온다.</li>
</ul>
<p><strong>소스파일</strong></p>
<ul>
<li>하나의 스위프트 소스코드 파일을 의미.</li>
<li>보통 다른 프로그래밍 언어에서는 통상 파일 하나에 타입을 하나만 정의하지만 스위프트에서는 파일 하나에 여러 타입을 정의가능하다.</li>
</ul>
<h3 id="접근수준">접근수준</h3>
<ul>
<li>접근제어는 접근수준(Access Level) 키워드를 통해 구현가능하다.</li>
<li>각 타입(클래스, 구조체, 열거형 등)에 특정 접근수준을 지정할 수 있고, 타입 내부의 프로퍼티, 메서드, 이니셜라이저, 서브스크립트 각각에도 접근수준을 지정할 수 있다.</li>
<li>open, public, internal, fileprivate, private 5가지가 있다.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/37cdaa1a-79e2-4e47-b4ea-cfe89c880251/image.png" alt=""></p>
<h3 id="개방-접근수준---open">개방 접근수준 - open</h3>
<ul>
<li>공개 접근수준 이상으로 높은 수준이며, 클래스와 클래스의 멤버에서만 사용할 수 있다.</li>
<li>클래스를 개방 접근수준으로 명시하는 것은 그 클래스를 다른 모듈에서도 부모클래스로 사용하겠다는 목적으로 클래스를 설계하고 코드를 작성했음을 의미한다.</li>
</ul>
<h3 id="공개-접근수준---public">공개 접근수준 - public</h3>
<ul>
<li>어디서든 쓰일 수 있다.</li>
<li>자신이 구현된 소스 파일은 물론, 그 소스파일이 속해 있는 모듈, 그 모듈을 가져다 쓰는 모듈 등 모든 곳에서 사용 할 수 있다.</li>
<li>공개 접근수준은 주로 프레임워크에서 외부와 연결될 인터페이스를 구현하는데 많이 사용된다.</li>
<li>스위프트의 기본 요소는 모두 공개 수준으로 구현되어 있다고 볼 수 있다.</li>
<li><strong>개방 접근수준 VS 공개 접근수준 차이점</strong><ul>
<li>개방 접근수준을 제외한 다른 모든 접근수준의 클래스는 그 클래스가 정의된 모듈 안에서만 상속할 수 있다.</li>
<li>개방 접근수준을 제외한 다른 모든 접근수준의 클래스 멤버는 해당 멤버가 정의된 모듈 안에서만 재정의할 수 있다.</li>
<li>개방 접근수준의 클래스는 그 클래스가 정의된 모듈 밖의 다른 모듈에서도 상속할 수 있다.</li>
<li>개방 접근수준의 클래스 멤버는 해당 멤버가 정의된 모듈 밖의 다른 모듈에서도 재정의할 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="내부-접근수준---internal">내부 접근수준 - Internal</h3>
<ul>
<li>내부 접근수준은 <strong>기본적으로 모든 요소에 암무적으로 지정하는 기본 접근수준</strong>이다.</li>
<li>소스파일이 속해 있는 모듈 어디에서든 쓰일 수 있다.</li>
<li>다만 그 모듈을 가져다 쓰는 외부 모듈에서는 접근할 수 없다.</li>
<li>보통 외부에서 사용할 클래스나 구조체가 아니며, 모듈 내부에서 광역적으로 사용할 경우 내부 접근 수준을 지정한다.</li>
</ul>
<h3 id="파일외부비공개-접근수준---fileprivate">파일외부비공개 접근수준 - fileprivate</h3>
<ul>
<li>fileprivate으로 지정된 요소는 그 요소가 구현된 소스파일 내부에서만 사용할 수 있다.</li>
<li>해당 소스파일 외부에서 값이 변경되거나 함수를 호출하면 부작용이 생길 수 있는 경우에 사용하면 좋다.</li>
</ul>
<h3 id="비공개-접근수준---private">비공개 접근수준 - private</h3>
<ul>
<li>가장 한정적인 범위이다.</li>
<li>비공개 접근수준으로 지정된 요소는 그 기능을 정의하고 구현한 범위 내에서만 사용할 수 있다.</li>
<li>비공개 접근수준으로 지정한 기능은 심지어 같은 소스파일 안에 구현한 다른 타입이나 기능에서도 사용할 수 없다.</li>
</ul>
<h3 id="fileprivate-vs-private">fileprivate VS private</h3>
<ul>
<li>fileprivate 접근수준으로 지정한 요소는 같은 파일 어떤 코드에서도 접근 가능하다.<ul>
<li>A.swift 소스파일 내부에 A Class, B Class, C Struct가 있고, C Class에 fileprivate 접근수준의 a 프로퍼티가 있다면 B Class, C Struct에서 모두 a 프로퍼티에 접근 가능하다.</li>
</ul>
</li>
<li>private 접근수준으로 지정한 요소는 같은 파일 내부에 다른 타입의 코드가 있더라도 접근이 불가하다.<ul>
<li>하지만 extension 코드가 <strong>같은 파일에 존재</strong>하는 경우에는 접근가능하다.</li>
</ul>
</li>
</ul>
<h3 id="접근제어-구현-참고사항">접근제어 구현 참고사항</h3>
<blockquote>
<p>💡 모든 타입에 적용되는 접근수준의 규칙은 <strong>&#39;상위 요소보다 하위 요소가 더 높은 접근수준을 가질 수 없다.&#39;</strong></p>
</blockquote>
<ul>
<li>비공개 접근수준으로 정의한 구조체 내부의 프로퍼티로 내부수준이나 공개수준을 갖는 프로퍼티를 정의할 수 없다.</li>
<li>또한 함수의 매개변수로 특정 접근수준이 부여된 타입이 전달되거나 반환된다면, 그 타입의 접근수준보다 함수의 접근수준이 높게 설정될 수 없다.</li>
<li>함수뿐만 아니라 튜플의 내부 요소 타입 또한 튜플의 접근수준보다 같거나 높아야 한다.</li>
</ul>
<h3 id="읽기-전용-구현">읽기 전용 구현</h3>
<ul>
<li><p>구조체 또는 클래스를 사용하여 저장 프로퍼티 구현시 허용된 접근수준에서 프로퍼티 값을 가져갈 수 있다.</p>
</li>
<li><p>그런데 값을 변경할 수 없도록 구현하고 싶다면 어떻게 해야 할까?</p>
<ul>
<li><p>설정자(setter)만 낮은 접근수준을 갖도록 제한할 수 있다.</p>
</li>
<li><p>요소의 접근수준 키워드 뒤에 <strong>접근수준</strong><code>(set)</code> 처럼 표현하면 설정자의 접근수준만 더 낮도록 지정해줄 수 있다.</p>
<pre><code class="language-swift">public private(set) var publicGetOnlyStoredProperty: Int = 0</code></pre>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[커맨드 패턴]]></title>
            <link>https://velog.io/@gooreum_90/%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@gooreum_90/%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Tue, 09 Nov 2021 22:46:49 GMT</pubDate>
            <description><![CDATA[<h3 id="핵심">핵심</h3>
<ul>
<li><strong>메소드 호출을 캡슐화</strong>하여 인보커와 리시버를 분리시켜, 인보커(리모컨 같이 계산하는 코드를 호출한 객체)는 자신이 구체적으로 어떤 작업을 하는지에 대해 전혀 신경쓰지 않도록 하는 것이 핵심입니다.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/19a1d0f1-6743-46c5-a3fa-4f1ffc3ec768/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.47.15.png" alt=""></p>
<h3 id="커맨드-패턴-정의">커맨드 패턴 정의</h3>
<blockquote>
</blockquote>
<p>💡 NOTE
커맨드 패턴을 이용하면 요구사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구사항을 집어넣을 수도 있습니다. 또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업 취소 기능도 지원 가능합니다.</p>
<h3 id="리모컨-api-제작">리모컨 API 제작</h3>
<ul>
<li>TV, 전등 등과 같은 가전제품을 on/off 할 수 있는 리모컨 API를 커맨드 패턴을 활용하여 만들어 봅시다.</li>
<li>커맨드 패턴을 활용하기 위해 크게 4가지 주체가 필요합니다.<ul>
<li>클라이언트:  리모컨을 호출하는 주체</li>
<li>인보커: 클라이언트와 리시버 사이의 매개체</li>
<li>커맨드: 리시버를 가지고 있으며, 리시버가 어떤 작업을 진행해야 하는지를 명령하는 주체<ul>
<li>커맨드 인터페이스 - 커맨드 구체 클래스 구조로 이루어져 있습니다.</li>
</ul>
</li>
<li>리시버: 리모컨의 명령을 받아 실제 작업을 진행하는 TV, 전등과 같은 가전제품에 해당하는 주체</li>
</ul>
</li>
</ul>
<h3 id="코드로-살펴봅시다">코드로 살펴봅시다</h3>
<p><strong>1.Command 인터페이스 만들기</strong></p>
<pre><code class="language-swift">protocol Command {
    func execute()
}</code></pre>
<ul>
<li>커맨드가 해야할 일은 execute() 기능밖에 없습니다.</li>
<li>단지 어떤 일을 실행하기만 하면 되는 역할이죠.</li>
</ul>
<p><strong>2.전등을 켜기 위한 커맨드 클래스 구현</strong></p>
<pre><code class="language-swift">
public class Light {
    public func on() { print(&quot;Light On&quot;) }
    public func off() { print(&quot;Light Off&quot;) }
}

//1
public class LightOnCommand: Command {
        //2
    let light: Light
    init(light: Light) {
        self.light = light
    }
        //3
    public func execute() {
        light.on()
    }
}</code></pre>
<ul>
<li><code>주석 1</code> : 우리가 만들어야 할 리모컨은 전등을 On 할수 있어야 하므로, 클라이언트가 리모컨을 눌렀을때 전등을 on하라는 명령을 내릴 Command 인터페이스를 구현하는 LightOnCommand를 생성해주어야 합니다.</li>
<li><code>주석 2</code> : 앞서 커맨드는 리시버(전등)을 알아야 된다고 했으므로, LightOnCommand에서는 전등을 가지고 있어야 합니다.</li>
<li><code>주석 3</code>: LightOnCommand는 구체 클래스이므로 execute()에서 전등을 on 하라는 명령을 수행합니다.</li>
</ul>
<p><strong>3.커맨드 객체 사용하기</strong></p>
<pre><code class="language-swift">public class SimpleRemoteControl {
        //1
    var slot: Command?
    //2
    func setCommand(command: Command) {
        slot = command
    }
    //3
    func buttonWasPressed() {
        slot?.execute()
    }
}</code></pre>
<ul>
<li>인보커 역할을 하는 리모컨 클래스입니다. 아래 세가지 내용을 가지고 있어야 합니다.</li>
<li><code>주석 1</code> : 인보커인 리모컨은 커맨드를 통해 리시버와 통신하기 때문에 커맨드 인스턴스 변수를 가지고 있어야 합니다. 다만, 구체 커맨드 클래스를 가지는 것이 아니라 Command 인터페이스를 의존하고 사용하여 의존성 역전이 이루어진 구조이어야 합니다.</li>
<li><code>주석 2</code> : 외부에서 구체 커맨드 클래스를 주입해주기 위한 메소드가 필요합니다. 이를 통해 인보커는 단순히 무언가를 execute() 하는 역할만 하고, 자신이 구체적으로 어떤 작업을 하는지에 대해 전혀 신경쓰지 않을 수 있습니다.</li>
<li><code>주석 3</code> : 실행 명령을 내립니다.</li>
</ul>
<p><strong>4.리코먼을 사용해봅시다</strong></p>
<pre><code class="language-swift">let remote = SimpleRemoteControl()
let light = Light()
let lightOn = LightOnCommand(light: light)

remote.setCommand(command: lightOn)
remote.buttonWasPressed()

---result---
Light On</code></pre>
<ul>
<li>클라이언트는 메인 함수가 되겠습니다.</li>
<li>인보커는 <code>remote</code> 상수가 되겠습니다.</li>
<li>커맨드는 <code>lightOn</code> 상수가 되겠습니다.</li>
<li>리시버는 <code>light</code> 상수가 되겠습니다.</li>
<li>인보커 클래스 내부에는 command 인터페이스를 가지고 있기 때문에 외부에서 <code>remote.setCommand()</code>를 통해 command 구체 클래스를 주입해줍니다.</li>
<li><code>remote</code>는 이제 <code>lightOn</code>이라는 구체 커맨드 클래스에서 구현하고 있는 <code>execute()</code>를 통해 전등을 on 시킬 수 있습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[싱글턴 패턴]]></title>
            <link>https://velog.io/@gooreum_90/%EC%8B%B1%EA%B8%80%ED%84%B4-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@gooreum_90/%EC%8B%B1%EA%B8%80%ED%84%B4-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Sun, 07 Nov 2021 11:01:58 GMT</pubDate>
            <description><![CDATA[<h2 id="5싱글턴-패턴">5.싱글턴 패턴</h2>
<h3 id="핵심">핵심</h3>
<ul>
<li>애플리케이션에서 단 하나의 인스턴스만 만들수 있도록 하고, 멀티 스레드 환경에서도 안전한 싱글턴 패턴 구성 방법을 살펴봅니다.</li>
</ul>
<h3 id="싱글톤-패턴의-정의">싱글톤 패턴의 정의</h3>
<blockquote>
<p>💡 </p>
<p>싱글톤 패턴은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴입니다. </p>
</blockquote>
<p><strong>구현 원리</strong></p>
<ul>
<li>클래스에서 자신의 단 하나뿐인 인스턴스를 관리하도록 만들어야 합니다.</li>
<li>다른 어떤 클래스에서도 자신의 인스턴스를 추가로 만들지 못하도록 해야 합니다.</li>
<li>인스턴스가 필요하면 반드시 클래스 자신을 거치도록 해야 합니다.</li>
<li>어디서든 그 인스턴스에 접근할 수 있도록 만들어야 합니다.</li>
<li>싱글톤 클래스의 객체가 자원을 많이 잡아먹는 경우 게으른 생성 기법을 활용할 수 있습니다.</li>
</ul>
<h3 id="멀티스레드를-고려하지-않은-싱글톤-패턴-구현법java">멀티스레드를 고려하지 않은 싱글톤 패턴 구현법(Java)</h3>
<pre><code class="language-java">public class Singleton { 
    //1
    private static Singleton uniqueInstance;

    //2
    private Singleton() {}

    //3
    public static Singleton getInstance() { 
        if ( uniqueInstance == null ) { 
                uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}</code></pre>
<ol>
<li><p>Singleton 클래스의 유일한 인스턴스를 저장하기 위한 정적 변수입니다.</p>
</li>
<li><p>생성자를 private으로 선언했기 때문에 Singleton에서만 클래스의 인스턴스를 만들 수 있습니다.</p>
</li>
<li><p>getInstance() 메소드에서는 클래스의 인스턴스를 만들어서 리턴해줍니다. </p>
<p> uniqueInstance가 null이면 아직 인스턴스가 생성되지 않았다는 것을 의미합니다. </p>
<p> 아직 인스턴스가 만들어지지 않았다면 private으로 선언된 생성자를 이용해서 Single 객체를 만든 다음 uniqueInstance에 그 객체를 대입합니다. 이렇게 하면 인스턴스가 필요한 상황이 닥치기 전에는 아예 인스턴스를 생성하지 않게 됩니다. 이런 방법을 &quot;게으른 인스턴스 생성(lazy instantiatioin)&quot;이라고 부릅니다.</p>
</li>
</ol>
<p><strong>문제점</strong></p>
<ul>
<li>위 방식은 싱글톤 인스턴스 객체가 되도록 의도했지만 멀티스레드 환경에서  두 개의 객체가 생성될 수 있습니다.</li>
<li>스레드1과 스레드2에서 new Singleton을 동시에 접근하게 된다면 두 개의 싱글톤 인스턴스 객체가 생성될 수 있는 것입니다.</li>
</ul>
<h3 id="멀티스레드를-고려한-싱글톤-패턴-3가지-구현법java">멀티스레드를 고려한 싱글톤 패턴 3가지 구현법(Java)</h3>
<p><strong>(1) 인스턴스를 필요할 때 생성하지 말고, 처음부터 만들어 버립니다.</strong></p>
<ul>
<li>애플리케이션에서 반드시 Singleton의 인스턴스를 생성하고, 그 인스턴스를 항상 사용한다면, 또는 인스턴스를 실행중에 수시로 만들고 관리하기가 성가시다면 다음과 같은 식으로 처음부터 Singleton 인스턴스를 만들어버리는 것도 괜찮은 방법입니다.</li>
<li>이 접근법을 사용하면 클래스가 로딩될 때 JVM에서 Singleton의 유일한 인스턴스를 생성해줍니다. JVM에서 유일한 인스턴스를 생성하기 전에는 그 어떤 스레드도 uniqueInstance 정적 변수에 접근할 수 없습니다.</li>
</ul>
<pre><code class="language-java">public class Singleton { 
    //1
    private static Singleton uniqueInstance = new Singleton()

    //2
    private Singleton() {}

    //3
    public static Singleton getInstance() { 
        return uniqueInstance;
    }
}</code></pre>
<p><strong>(2) getInstance()를 동기화시켜줍니다.</strong></p>
<pre><code class="language-java">public class Singleton { 
    private static Singleton uniqueInstance;

    private Singleton() {}

    //1
    public static **synchronized** Singleton getInstance() { 
        if ( uniqueInstance == null ) { 
                uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}</code></pre>
<ol>
<li>getInstance()에 synchronized 키워드만 추가하면 한 스레드가 메소드 사용을 끝내기 전까지 다른 스레드는 기다려야 합니다. 즉, 두 세르다가 이 메소드를 동시에 실행시키는 일은 일어나지 않게 되죠.</li>
</ol>
<ul>
<li>동기화가 필요한 시점은 getInstance()가 시작되는 때 뿐입니다. 즉, uniqueInstance 변수가 Singleton()으로 초기화되고 나면 굳이 getInstance()를 동기화된 상태로 유지시킬 필요가 없어지게 됩니다. 따라서 첫번째 과정을 제외하면 동기화는 불필요한 오버헤드만 증가시킬 뿐입니다.<ul>
<li>이 문제는 아래 방법으로 해결할 수 있습니다.</li>
</ul>
</li>
</ul>
<p><strong>(3)DCL(Double-Checing Locking)을 써서 getInstance()에서 동기화되는 부분을 줄입니다.</strong> </p>
<ul>
<li>인스턴스가 생성되어 있는지 확인한 다음, 생성되어 있지 않을 때만 동기화를 할 수 있습니다.</li>
<li>처음에만 동기화를 하고 나주에는 동기화를 하지 않게 됩니다.</li>
</ul>
<pre><code class="language-java">public class Singleton { 
    //1
    private static volatile Singleton uniqueInstance;

    private Singleton() {}


    public static Singleton getInstance() { 
        //2
        if ( uniqueInstance == null ) { 
                //3
                synchronized (Singleton.class) { 
                    if (uniqueInstance == null) { 
                            //4
                            uniqueInstance = new Singleton();
                    }
                }
        }
        return uniqueInstance;
    }
}</code></pre>
<ol>
<li>volatile 키워드를 사용하면 멀티스레딩을 사용하더라도 uniqueInstance 변수가 Singleton 인스턴스로 초기화되는 과정이 올바르게 진행되도록 할 수 있습니다. </li>
<li>인스턴스가 있는지 확인하고, 없으면 동기화된 블록으로 들어갑니다.</li>
<li>이렇게 하면 처음에만 동기화가 됩니다.</li>
<li>블록으로 들어온 후에도 다시 한번 변수가 null인지 확인한 다음 인스턴스를 생성합니다. </li>
</ol>
<h3 id="swift로-multi-thread-safety-singleton-구성해보기">Swift로 Multi-Thread-Safety Singleton 구성해보기</h3>
<pre><code class="language-swift">public class Singleton {
        //1
    private static let shared = Singleton()
    //2
    private init() {}
    //3
    public static func getInstance() -&gt; Singleton {
        return shared
    }

    public func toString() {
        print(&quot;Singleton&quot;)
    }
}

Singleton.getInstance().toString()

---result---
Singleton</code></pre>
<ol>
<li>Singleton 클래스의 객체를 보관할 전역 변수를 private으로 선언 및 생성합니다.<ol>
<li>이 전역 변수 네이밍은 보통 shared로 지정합니다.</li>
</ol>
</li>
<li>외부 객체에서 Singleton 클래스를 생성할 수 없도록 private으로 생성자를 만듭니다.</li>
<li>어떤 객체에서든 Singleton클래스에 접근하여 Singleton 객체를 가져갈 수 있도록 public 정적 메서드 getInstance()를 정의해줍니다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[빠져 있는 장]]></title>
            <link>https://velog.io/@gooreum_90/%EB%B9%A0%EC%A0%B8-%EC%9E%88%EB%8A%94-%EC%9E%A5</link>
            <guid>https://velog.io/@gooreum_90/%EB%B9%A0%EC%A0%B8-%EC%9E%88%EB%8A%94-%EC%9E%A5</guid>
            <pubDate>Fri, 05 Nov 2021 10:41:10 GMT</pubDate>
            <description><![CDATA[<ul>
<li>이 장은 시몬 브라운이 기고한 것으로, 클린 아키텍처를 실전에 도입시(코드화) 맞딱뜨릴 문제점과 해결책에 대해 설명한다.</li>
<li>따라서 클린 아키텍처는 잠시 한쪽으로 제쳐 놓고, 설계나 <strong>코드 조직화</strong>와 관련된 몇 가지 접근법을 살펴보자.</li>
</ul>
<h3 id="계층-기반-패키지"><strong>계층 기반 패키지</strong></h3>
<ul>
<li>전형적인 계층형 아키텍처는 웹, 업무규칙, 영속성 코드를 위해 수평으로 계층을 나눈다.</li>
<li>엄격한 계층형 아키텍처라면 계층은 반드시 아래 게층에만 의존해야 한다.</li>
<li>자바의 경우 계층은 주로 패키지로 구현된다.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/f38ce3c5-fa23-4f2c-af7a-c0c5c7f6d9a8/%E1%84%88%E1%85%A1%E1%84%8C%E1%85%A7%E1%84%8B%E1%85%B5%E1%86%BB%E1%84%82%E1%85%B3%E1%86%AB%E1%84%8C%E1%85%A1%E1%86%BC1.png" alt=""></p>
<ul>
<li>계층형 아키텍처는 처음 시작 프로젝트를 시작히기엔 큰 문제가 없지만 시간이 지나면서 업무 도메인에 대해 알수 없다는 문제점이 드러나게 된다. (단순히 웹 - 업무규칙 - 영속성 코드로만 패키지화 되어 있기 때문, 만약 새로운 유스케이스를 추가하거나 기존의 것을 수정해야 될 때 어디서 시작해야할지 찾기 어려워짐.)</li>
</ul>
<h3 id="기능-기반-패키지"><strong>기능 기반 패키지</strong></h3>
<ul>
<li>서로 연관된 기능, 도메인 개념, 또는 Aggregate Root에 기반하여 수직의 얇은 조각으로 코드를 나누는 방식.</li>
<li>자바로 구현시 모든 타입이 하나의 자바 패키지에 속하며, 패키지 이름은 그 안에 담긴 개념을 반영해 지음.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/cbb19e17-40a1-4189-85d7-383bada9f87c/%E1%84%88%E1%85%A1%E1%84%8C%E1%85%A7%E1%84%8B%E1%85%B5%E1%86%BB%E1%84%82%E1%85%B3%E1%86%AB%E1%84%8C%E1%85%A1%E1%86%BC2.png" alt=""></p>
<ul>
<li><p>계층 기반 패키지와 달리 단 하나의 패키지로 묶여 있다.</p>
<p>  → 주문과 관련된 도메인이라는 점을 쉽게 파악할 수 있다. 또한 &#39;주문 조회하기&#39; 유스케이스가 변경될 경우 변경해야 할 코드를 모두 찾는 작업이 더 쉬워짐. </p>
</li>
<li><p>서비스가 복잡해질수록 계층기반 패키지에서 기능기반 패키지로 리팩토링 하는 경향이 많지만 이는 좋은 코드 조직화 관점에서 보자면 차선책에 불과하다.</p>
</li>
</ul>
<h3 id="포트와-어댑터"><strong>포트와 어댑터</strong></h3>
<ul>
<li>엉클밥이 얘기하는 업무/도메인에 초점을 둔 코드가 프레임워크나 데이터베이스 같은 기술적인 세부 구현과 독립적이며 분리된 아키텍처를 코드 베이스는 &#39;내부&#39;(도메인)와 &#39;외부&#39;(인프라)로 구성된다는 점을 말함.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/5f1ac50c-0f42-4011-af1c-5e1111ff9e4a/%E1%84%88%E1%85%A1%E1%84%8C%E1%85%A7%E1%84%8B%E1%85%B5%E1%86%BB%E1%84%82%E1%85%B3%E1%86%AB%E1%84%8C%E1%85%A1%E1%86%BC3.png" alt=""></p>
<ul>
<li>여기서 중요한 것은 외부가 내부에 의존하며, 절대 그 반대로는 안된다.</li>
<li>이 방식에 따라 주문 조회하기 유스케이스를 구현한 모습을 보자.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/f9d8386c-2561-4b2c-80e8-470c51610d83/%E1%84%88%E1%85%A1%E1%84%8C%E1%85%A7%E1%84%8B%E1%85%B5%E1%86%BB%E1%84%82%E1%85%B3%E1%86%AB%E1%84%8C%E1%85%A1%E1%86%BC4.png" alt=""></p>
<ul>
<li>com.mycompany.myapp.domain 패키지가 &#39;내부&#39; 도메인 이며 나머지 패키지는 &#39;외부&#39;임.</li>
<li>추가로 Orders는 OrdersRepository라는 이름에서 바뀐 이름인데, DDD에서는 유비쿼터스 도메인 언어를 관점으로 기술하라고 했기때문에 반영된것임.</li>
</ul>
<p>→ 예를들면 도메인에 대해 논의할 때 &#39;주문&#39;에 대해 말하는것이지 &#39;주문 레파지토리&#39;에 대해 말하는것이 아니기 때문임.</p>
<h3 id="컴포넌트-기반-패키지"><strong>컴포넌트 기반 패키지</strong></h3>
<ul>
<li><p>SOLID, REP, CCP, CRP에 대해 동감하지만, 코드를 조직화하는 방법에 대해 다른 결론에 이름. 그래서 또 다른 선택지를 제시하려고 하는데 이를 &#39;컴포넌트 기반 패키지&#39;라고 부름.</p>
</li>
<li><p>컴포넌트 기반 패키지를 살펴보기 앞서 계층형 아키텍처 문제점을 살펴보자.</p>
</li>
<li><p><strong>계층형 아키텍처</strong></p>
<ul>
<li>계층형아키텍처는 의존성 화살표가 항상 아래로 향해야하고 각 계층은 반드시 바로 아래 계층에만 의존해야 함.</li>
<li>이런 방식으로 깔끔한 비순환 의존성 그래프를 만들 수 있지만 코드베이스의 요소들이 서로 의존할 때는 몇가지 규칙을 반드시 지켜야함.</li>
<li>계층형 아키텍처를 비순환 의존성 그래프 형식으로 만들 수 있지만 문제는 속임수를 써서 몇몇 의존성을 의도치 않은 방식으로 추가하더라도 보기에는 여전히 좋은 비순환 의존성 그래프가 생긴다.<ul>
<li>신입사원의 완화된 계층형 아키텍처 예시.<ul>
<li>OrdersController가 OrdersRepository로 아래로 의존하기 때문에 비순환 원칙을 어기진 않는다. 그렇지만 이는 원하는 흐름이 아니다. 웹은 반드시 업무규칙와 분리되어야 하는데, 그러지 못하고 있기 때문에 잘못된 것이며 반드시 강제하는 규칙을 팀 내에서 정해야 한다. 하지만 이는 사람에게 강제하는 규칙이기 때문에 상황에 따라 위반되는 경우들이 많이 발생한다..</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>포트 어댑터에서 웹을 그저 또 다른 전달 메커니즘으로 취급하는 것과 마찬가지로, 컴포넌트 기반 패키지에서도 사용자 인터페이스를 큰 단위의 컴포넌트로부터 분리해서 유지한다.</p>
</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/4ebf9b57-853a-47a8-bd6f-2ad62b21ac43/%E1%84%88%E1%85%A1%E1%84%8C%E1%85%A7%E1%84%8B%E1%85%B5%E1%86%BB%E1%84%82%E1%85%B3%E1%86%AB%E1%84%8C%E1%85%A1%E1%86%BC5.png" alt=""></p>
<ul>
<li>이 접근법은 &#39;업무 로직&#39;과 영속성 관련 코드를 하나로 묶는다.</li>
<li>컴포넌트 기반 패키지 접근법의 주된 이점은 주문과 관련된 무언가를 코딩해야 할 때 오직 OrdersComponent만 둘러보면 된다.<ul>
<li>이 컴포넌트 내부에서 관심사의 분리는 여전히 유효하며 따라서 업무 로직은 데이터 영속성과 분리되어 있다.</li>
</ul>
</li>
</ul>
<h3 id="구현-세부사항엔-항상-문제가-있다"><strong>구현 세부사항엔 항상 문제가 있다</strong></h3>
<ul>
<li>살펴본 네 가지 아키텍처를 구현시 public 지시자를 과용한다면 설계 의도에 부합하는 아키텍처 스타일을 만들 수 없다.</li>
</ul>
<h3 id="조직화-vs-캡슐화"><strong>조직화 vs. 캡슐화</strong></h3>
<ul>
<li>모든 타입을 public으로 지정한다면 패키지는 단순히 조직화를 위한 메커니즘(폴더와 같이 무언가를 묶는 방식)으로 전락하여 캡슐화를 위한 메커니즘이 될 수 없다.</li>
<li>public 지시자를 과용하면 네 가지 아키텍처 접근법은 본질적으로 같아진다.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/40823f6b-ca15-4982-af72-4e14c61caad7/%E1%84%88%E1%85%A1%E1%84%8C%E1%85%A7%E1%84%8B%E1%85%B5%E1%86%BB%E1%84%82%E1%85%B3%E1%86%AB%E1%84%8C%E1%85%A1%E1%86%BC6.png" alt=""></p>
<ul>
<li><p>채택하려는 아키텍처 접근법과 관계 없이 화살표들이 모두 동일한 방향을 가리키고 있다.</p>
</li>
<li><p>모든 타입을 public으로 선언한다면 수평적 계층형 아키텍처를 표방하는 네 가지 방식에 지나지 않는다.</p>
</li>
<li><p>자바에서 접근 지시자를 적절하게 사용하면, 타입을 패키지로 배치하는 방식에 따라서 각 타입에 접근할 수 있는 정도가 실제로 크게 달라질 수 있다.</p>
</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/e74062a9-826e-4bb6-99af-559c02d8024a/%E1%84%88%E1%85%A1%E1%84%8C%E1%85%A7%E1%84%8B%E1%85%B5%E1%86%BB%E1%84%82%E1%85%B3%E1%86%AB%E1%84%8C%E1%85%A1%E1%86%BC7.png" alt=""></p>
<ul>
<li>계층기반 패키지 접근법<ul>
<li>OrdersServiced와 OrdersRepository 인터페이스는 외부 패키지의 클래스로부터 자신이 속한 패키지 내부로 들어오는 의존성이 존재하므로 public으로 선언되어야 한다.</li>
<li>반면 구현체 클래스(ordersServiceImpl과 JdbcOrdersRepository)는 더 제한적으로 선언할 수 있다(패키지 protected). 이들 클래스는 누구도 알 필요가 없는 구현 세부사항이다.</li>
<li>나머지 아키텍처 패키지 접근법도 마찬가지이다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[사례연구: 비디오 판매]]></title>
            <link>https://velog.io/@gooreum_90/%EC%82%AC%EB%A1%80%EC%97%B0%EA%B5%AC-%EB%B9%84%EB%94%94%EC%98%A4-%ED%8C%90%EB%A7%A4</link>
            <guid>https://velog.io/@gooreum_90/%EC%82%AC%EB%A1%80%EC%97%B0%EA%B5%AC-%EB%B9%84%EB%94%94%EC%98%A4-%ED%8C%90%EB%A7%A4</guid>
            <pubDate>Fri, 05 Nov 2021 10:38:23 GMT</pubDate>
            <description><![CDATA[<h3 id="사례연구-의의"><strong>사례연구 의의</strong></h3>
<ul>
<li>사례를 통해 지금까지 살펴 보았던 아키텍처에 대한 규칙과 견해를 종합적으로 살펴본다.</li>
<li>뛰어난 아키텍트가 일을 처리하는 과정과 결정을 내리는 모습을 볼 수 있다.</li>
</ul>
<h3 id="제품"><strong>제품</strong></h3>
<ul>
<li><p>제품은 웹 사이트에서 비디오를 판매하는 소프트웨어이다.</p>
<ul>
<li>판매하길 원하는 비디오가 있으며, 개인과 기업에게 웹을 통해 판매한다.</li>
<li><strong>구매자</strong><ul>
<li>개인<ul>
<li>단품 가격을 지불해 스트리밍으로 보거나, 더 높은 가격을 내고 비디오를 다운로드해서 영구 소장 가능.</li>
</ul>
</li>
<li>기업<ul>
<li>기업용 라이선스는 스트리밍 전용이며, 대량 구매를 하면 할인을 받을 수 있다.</li>
<li>기업은 다른 사람들이 시청할 비디오를 구매하는 사람이 따로 있다.</li>
</ul>
</li>
</ul>
</li>
<li><strong>시청자</strong></li>
<li><strong>비디오 제작자</strong><ul>
<li>비디오 파일과 비디오에 대한 설명서, 부속 파일(시험, 문제, 해법, 소스 코드 등)을 제공해야 한다.</li>
</ul>
</li>
<li><strong>관리자</strong><ul>
<li>신규 비디오 시리즈물을 추가하거나 기존 시리즈물에 비디오를 추가 또는 삭제하며, 다양한 라이선스에 맞춰 가격을 책정한다.</li>
</ul>
</li>
</ul>
</li>
<li><p>시스템의 초기 아키텍처를 결정하는 첫 단계인 액터와 유스케이스를 식별 해보자!</p>
</li>
</ul>
<h3 id="유스케이스-분석"><strong>유스케이스 분석</strong></h3>
<p><img src="https://images.velog.io/images/gooreum_90/post/ca643060-64d5-4643-b0e0-75a9deba3aa9/%E1%84%89%E1%85%A1%E1%84%85%E1%85%A8%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%80%E1%85%AE1.png" alt=""></p>
<p>이미지 출처 : <a href="https://hwannny.tistory.com/51">https://hwannny.tistory.com/51</a></p>
<ul>
<li><strong>단일 책임 원칙</strong><ul>
<li>단일 책임 원칙에 따라 네 &#39;제작자, 관리자, 구매자, 시청자&#39; 네 액터가 시스템이 변경되어야 할 네 가지 주요 근원이 된다.</li>
<li>신규 기능을 추가하거나 기존 기능을 변경해야 한다면, 그 이유는 반드시 이들 액터 중 하나에게 해당 기능을 제공하기 위해서다.</li>
</ul>
</li>
<li><strong>중앙의 점선 유스케이스</strong><ul>
<li>추상 유스케이스다.</li>
<li>범용적인 정책을 담고 있으며, 다른 유스케이스에서 이를 더 구체화한다.</li>
<li>카탈로그 조회하기 유스케이스는 모두 카탈로그 조회하기라는 추상 유스케이스를 상속받는다.</li>
<li>이러한 추상화가 반드시 필요한 작업은 아니었음. 다이어그램에 없더라도 전체 제품의 기능을 조금도 손상시키지 않음. 단지 이들 두 유스케이스가 너무 비슷해서 유사성을 식별해서 분석 초기에 통합하는 방법을 찾는 편이 더 현명하다고 판단하였음.</li>
</ul>
</li>
</ul>
<h3 id="컴포넌트-아키텍처"><strong>컴포넌트 아키텍처</strong></h3>
<p><img src="https://images.velog.io/images/gooreum_90/post/497bde32-9f9c-45c9-b1f6-69360613ec65/%E1%84%89%E1%85%A1%E1%84%85%E1%85%A8%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%80%E1%85%AE2.png" alt=""></p>
<p>이미지 출처 : <a href="https://hwannny.tistory.com/51">https://hwannny.tistory.com/51</a></p>
<ul>
<li>액터와 유스케이스를 식별했으므로, 예비 단계의 컴포넌트 아키텍처를 만들어 볼 수 있다.</li>
<li>이중선은 아키텍처 경계를 나타내며, 이 아키텍처는 뷰 / 프레젠터 / 인터렉터 / 컨트롤러라는 전형적인 분할법을 적용함.</li>
<li>또한 대응하는 액터에 따라 카테고리를 분리했다.</li>
<li>각 컴포넌트는 단일 .jar 파일 또는 단일 .dll 파일에 해당한다.<ul>
<li>이들 컴포넌트 각각은 자신에게 할당된 뷰, 프레젠터, 인터렉터, 컨트롤러를 포함한다.</li>
</ul>
</li>
<li>특수한 컴포넌트인 Catalog View와 Catalog Presenter는 해당 컴포넌트 내부에 추상 클래스로 코드화될 것이며, 상속받는 컴포넌트에서는 이들 추상 클래스로부터 상속받은 뷰와 프레젠터 클래스들을 포함한다.</li>
<li>각 컴포넌트는 단일 .jar에 해당할 수 있지만 경계를 구분해서 뷰, 프레젠터, 인터렉터, 컨트롤러, 유틸리티 각각을 하나의 jar로 구분할 수 있음. 또한 뷰와 프레젠터를 같은 .jar에 두고 나머지는 개별로 둘 수 있으며 이처럼 각 컴포넌트들이 독립적으로 컴파일하고 빌드할 수 있는 환경으로 구성되게끔 선택지를 열어두면 시스템이 변경되는 양상에 맞춰 배포방식을 조절할 수 있음.</li>
</ul>
<h3 id="의존성-관리"><strong>의존성 관리</strong></h3>
<ul>
<li>입력이 컨트롤러에서 발생하면 인터랙터에 의해 처리되고 프레전터가 결과의 포맷을 변경하고 뷰가 화면에 표시된다.</li>
<li>아키텍처가 의존성 규칙을 준수하기 때문에 모든 의존성은 경계선을 한 방향으로만 가로지르는데, 항상 더 높은 수준의 정책을 포함하는 컴포넌트를 향한다.</li>
<li>개방 폐쇄 원칙을 적용했기 때문에 사용관계(열린 화살표)는 제어흐름과 같은 방향을 가리키며, 상속관계(닫힌 화살표)는 제어흐름과는 반대 방향을 가리킨다.</li>
</ul>
<h3 id="결론"><strong>결론</strong></h3>
<ul>
<li>이 아키텍처는 두 가지 서로 다른 차원의 분리 개념을 포함한다.<ul>
<li>단일 책임 원칙에 기반한 액터의 분리</li>
<li>의존성 규칙</li>
</ul>
</li>
<li>이 두 차원은 모두 서로 다른 이유로 서로 다른 속도로 변경되는 커모넌트를 분리하는 데 그 목적이 있다.</li>
<li>서로 다른 이유라는 것은 액터와 관련, 서로 다른 속도라는 것은 정책 수준과 관련</li>
<li>이렇게 구조화하면 시스템을 실제로 배포하는 방식은 다양하게 선택가능해진다.</li>
<li>컴포넌트들을 배포 가능한 단위로 묶을 수도 있고, 묶는 단위를 바꾸기도 쉬워진다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프레임워크는 세부사항이다]]></title>
            <link>https://velog.io/@gooreum_90/%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EB%8A%94-%EC%84%B8%EB%B6%80%EC%82%AC%ED%95%AD%EC%9D%B4%EB%8B%A4</link>
            <guid>https://velog.io/@gooreum_90/%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EB%8A%94-%EC%84%B8%EB%B6%80%EC%82%AC%ED%95%AD%EC%9D%B4%EB%8B%A4</guid>
            <pubDate>Fri, 05 Nov 2021 10:37:28 GMT</pubDate>
            <description><![CDATA[<h3 id="프레임워크-제작자"><strong>프레임워크 제작자</strong></h3>
<ul>
<li>프레임워크 제작자는 나를 위해서가 아니라 자신이나 자신의 동료와 친구들의 문제를 해결하기 위해 프레임워크를 제작한다.</li>
</ul>
<h3 id="혼인-관계의-비대칭성"><strong>혼인 관계의 비대칭성</strong></h3>
<ul>
<li>나는 프레임워크를 위해 헌신해야 하지만, 프레임워크 제작자는 나를 위해 헌신하지 않는다.</li>
<li>대개의 경우 프레임워크 제작자들은 나의 애플리케이션이 가능하면 프레임워크에 공고하게 결합될 것을 강하게 역설한다.</li>
</ul>
<h3 id="위험요인"><strong>위험요인</strong></h3>
<ul>
<li>이러한 프레임워크 특성의 위험요인은 무엇인가?<ul>
<li>프레임워크 아키텍처가 그다지 깔끔하지 않을 수 있다.<ul>
<li>프레임워크는 의존성 규칙을 위반하는 경향이 있다.</li>
<li>프레임워크 객체 상속시 영원히 묶여 버리게 되어 헤어나오지 못할 수있다..</li>
</ul>
</li>
<li>애플리케이션 초반에는 도움이 되겠지만 시간이 지나면서 제품은 프레임워크가 제공하는 기능과 틀을 벗어나게 될 것이다.</li>
<li>프레임워크는 나에게 도움되지 않는 방향으로 진화할 수도 있다.<ul>
<li>신규 버전이 다른 일을 못하게 할 수도 있다.</li>
</ul>
</li>
<li>새롭고 더 나은 프레임워크가 등장해서 갈아타고 싶을 수도 있다.</li>
</ul>
</li>
</ul>
<h3 id="해결책"><strong>해결책</strong></h3>
<ul>
<li>프레임워크와 결혼하지 말라!</li>
<li>프레임워크를 아키텍처 바깥 원에 속하는 세부사항으로 취급하여 원 안으로 들어오지 못하게 하라!</li>
<li>업무 객체를 만들 때 프레임워크가 자신의 기반 클래스로부터 파생하기를 요구한다면 거절하고, 대신 프락시를 만들어 업무 규칙에 플러그인할 수 있는 컴포넌트에 이들 프락시를 위치시켜라.<ul>
<li>예를 들어 Spring 사용시, 업무 객체보다는 메인 컴포넌트에서 스프링을 사용해서 의존성을 주입하는 편이 낫다.(Spring은 의존성 주입 프레임워크)</li>
</ul>
</li>
<li>프레임워크가 핵심 코드 안으로 들어오지 못하게 하라.<ul>
<li>대신 핵심 코드에 플러그인할 수 있는 컴포넌트에 프레임워크를 통합하고, 의존성 규칙을 준수하라.</li>
</ul>
</li>
</ul>
<h3 id="이제-선언합니다"><strong>이제 선언합니다</strong></h3>
<ul>
<li>정말 결혼해야 하는 프레임워크도 있다.<ul>
<li>C++ 과 STL / 자바와 표준 라이브러리</li>
</ul>
</li>
<li>그러나 선택적이어야 한다.</li>
<li>프레임워크를 사용한다는 뜻은 애플리케이션이 프레임워크와 결혼한다는 의미이기 때문에 신중하게 선택해야 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹은 세부사항이다]]></title>
            <link>https://velog.io/@gooreum_90/%EC%9B%B9%EC%9D%80-%EC%84%B8%EB%B6%80%EC%82%AC%ED%95%AD%EC%9D%B4%EB%8B%A4</link>
            <guid>https://velog.io/@gooreum_90/%EC%9B%B9%EC%9D%80-%EC%84%B8%EB%B6%80%EC%82%AC%ED%95%AD%EC%9D%B4%EB%8B%A4</guid>
            <pubDate>Fri, 05 Nov 2021 10:37:10 GMT</pubDate>
            <description><![CDATA[<h3 id="끝없이-반복하는-추"><strong>끝없이 반복하는 추</strong></h3>
<ul>
<li><p>모든 연산 능력을 중앙 서버에 두는 방식과 모든 연산 능력을 단말에 두는 방식 사이에서 끊임없이 움직여 왔다.</p>
</li>
<li><p>웹 또한 이러한 진동을 겪고 있다.</p>
<p>  연산능력의 위치는 아래와 같이 반복된다. </p>
<p>  서버 팜(server farm) → 브라우저에 애플릿 추가 → 동적처리 서버로 이동 → 웹 2.0 고안, 브라우저에 Ajax와 자바스크립트 추가. → 현재는 거대한 애플리케이션 전부를 브라우저에서 실행되도록 작성가능. → Node.js를 이용해 자바스크립트를 다시 서버로 이동</p>
</li>
</ul>
<h3 id="요약"><strong>요약</strong></h3>
<ul>
<li>GUI는 세부사항이며, 웹은 GUI이다. 따라서 웹은 세부사항이다.</li>
<li>따라서 아키텍트라면 이러한 세부사항을 핵심 업무 로직에서 분리된 경계 바깥에 두어야 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스는 세부사항이다.]]></title>
            <link>https://velog.io/@gooreum_90/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%8A%94-%EC%84%B8%EB%B6%80%EC%82%AC%ED%95%AD%EC%9D%B4%EB%8B%A4</link>
            <guid>https://velog.io/@gooreum_90/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%8A%94-%EC%84%B8%EB%B6%80%EC%82%AC%ED%95%AD%EC%9D%B4%EB%8B%A4</guid>
            <pubDate>Fri, 05 Nov 2021 10:36:51 GMT</pubDate>
            <description><![CDATA[<h3 id="관계형-데이터베이스"><strong>관계형 데이터베이스</strong></h3>
<ul>
<li>관계형 데이터베이스는 데이터를 저장하고 접근하는 데 탁월한 기술이다.</li>
<li>그러나 관계형 데이터베이스도 결국 아키텍처 관점에서 보자면 세부사항이다.</li>
<li>따라서 데이터를 테이블에 행 단위로 배치한다는 자체는 아키텍처적으로 볼 때 전혀 중요하지 않다.</li>
<li>애플리케이션의 유스케이스는 이러한 방식을 알아서는 안되며 관여해서도 안 된다.</li>
</ul>
<h3 id="데이터베이스-시스템은-왜-이렇게-널리-사용되는가"><strong>데이터베이스 시스템은 왜 이렇게 널리 사용되는가?</strong></h3>
<ul>
<li>그 이유는 &#39;디스크&#39;에 있음.</li>
<li>디스크 구조상 필요한 바이트를 읽기위해선 밀리초 단위가 걸림, 대다수의 프로세서는 한명령어를 처리하는 주기에 비해 백만배나 오래걸리는 시간임. 이런 시간 지연문제를 완화하기위해 색인, 캐시, 쿼리 계획 최적화, 데이터 접근 및 관리 시스템이 필요해졌으며 두가지 유형의 시스템으로 발전됨.</li>
<li>파일 시스템과 관계형 데이터베이스 시스템임.</li>
</ul>
<h3 id="디스크가-없다면-어떻게-될까"><strong>디스크가 없다면 어떻게 될까?</strong></h3>
<ul>
<li>디스크는 RAM으로 대체되고 있다.</li>
<li>데이터가 데이터베이스나 파일 시스템에 있더라도, RAM으로 읽은 후에는 다루기 편리한 형태(리스트, 집합, 스택, 큐, 트리 등로 그 구조를 변경한다.</li>
<li>데이터를 파일이나 테이블 형태로 그대로 두는 경우는 거의 없다.</li>
</ul>
<h3 id="세부사항"><strong>세부사항</strong></h3>
<ul>
<li>데이터베이스는 그저 매커니즘에 불과하며, 디스크 표면과 RAM 사이에서 데이터를 이리저리 옮길 때 사용할 뿐이다.</li>
<li>데이터베이스는 사실상 비트를 담는 거대한 그릇이며, 데이터를 장기적으로 저장하는 공간에 지나지 않는다.</li>
<li>데이터베이스는 세부사항일 뿐이다!</li>
</ul>
<h3 id="하지만-성능은"><strong>하지만 성능은?</strong></h3>
<ul>
<li>성능은 아키텍처와 관련된 관심사이지만 데이터 저장소의 측면에서 보는 성능은 완전히 캡슐화되어 업무 규칙과는 분리될 수 있는 저수준 관심사이다.</li>
<li>그러므로 데이터 저장소 측면의 성능은 전반적인 아키텍처와는 아무런 관련이 없음.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[테스트 경계]]></title>
            <link>https://velog.io/@gooreum_90/%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%BD%EA%B3%84</link>
            <guid>https://velog.io/@gooreum_90/%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B2%BD%EA%B3%84</guid>
            <pubDate>Fri, 05 Nov 2021 10:34:52 GMT</pubDate>
            <description><![CDATA[<h3 id="시스템-컴포넌트인-테스트"><strong>시스템 컴포넌트인 테스트</strong></h3>
<ul>
<li>여러 테스트 종류가 있지만 중요한 것은 아키텍처 관점에서 모든 테스트가 동일하다는 점이다.</li>
<li>테스트는 아키텍처에서 가작 바깥쪽 원으로 생각할 수 있다.<ul>
<li>시스템 내부의 어떤 것도 테스트에는 의존하지 않으며, 테스트는 시스템의 컴포넌트를 향해, 항상 원의 안쪽으로 의존한다.</li>
</ul>
</li>
<li>테스트는 독립적으로 배포 가능하다.</li>
<li>테스트는 시스템 컴포넌트 중에서 가장 고립되어 있다.<ul>
<li>그렇다고 테스트가 시스템 컴포넌트가 아니라는 뜻은 아니다.</li>
<li>많은 면에서 테스트는 다른 모든 시스템 컴포넌트가 반드시 지켜야 하는 모델을 표현해준다.</li>
</ul>
</li>
</ul>
<h3 id="테스트를-고려한-설계"><strong>테스트를 고려한 설계</strong></h3>
<ul>
<li>소프트웨어 설계의 첫 번째 규칙은 변동성이 있는 것에 의존하지 말아야 한다는 것이다.</li>
<li>따라서 시스템과 테스트를 설계할 때, GUI를 사용하지 않고 업무 규칙을 테스트할 수 있게 해야 한다.<ul>
<li>GUI는 변동성이 크므로 GUI로 시스템을 조작하는 테스트 스위트는 분명 깨지기 쉽다.</li>
</ul>
</li>
</ul>
<h3 id="테스트-api"><strong>테스트 API</strong></h3>
<ul>
<li>이 목표를 달성하려면 테스트가 모든 업무 규칙을 검증하는 데 사용할 수 있도록 특화된 API를 만들면 된다.<ul>
<li>이러한 API는 보안 제약사항을 무시할 수 있으며</li>
<li>DB와 같은 값비싼 자원은 건너뛰고,</li>
<li>시스템을 테스트 가능한 특정 상태로 강제하는 강력한 힘을 지녀야 한다.</li>
<li>이 API는 사용자 인터페이스가 사용하는 인터랙터와 인터페이스 어댑터들의 상위 집합이 될 것.</li>
</ul>
</li>
<li>테스트 API는 테스트를 애플리케이션으로부터 분리할 목적으로 사용한다.</li>
</ul>
<h3 id="구조적-결합"><strong>구조적 결합</strong></h3>
<ul>
<li>구조적 결합이란<ul>
<li>모든 상용클래스별로 테스트 클래스가 존재하고, 모든 상용 메서드에 테스트 메서드 집합이 각각 존재하는 테스트 스위트와 같은 결합을 의미.</li>
</ul>
</li>
<li>테스트 API의 역할은 이러한 애플리케이션의 구조를 테스트로부터 숨기는 데 있다.</li>
<li>이렇게 만들면 상용 코드를 리팩터링하거나 진화시키더라도 테스트에는 전혀 영향을 주지 않는다. 그 반대도 마찬가지.</li>
</ul>
<h3 id="보안"><strong>보안</strong></h3>
<ul>
<li>테스트 API 자체와 테스트 API 중 위험한 부분의 구현부는 독립적으로 배포할 수 있는 컴포넌트로 분리해야 한다.</li>
</ul>
<h3 id="결론"><strong>결론</strong></h3>
<ul>
<li>테스트는 시스템 외부에 있지 않으며 시스템의 일부이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA['크고 작은 모든' 서비스들]]></title>
            <link>https://velog.io/@gooreum_90/%ED%81%AC%EA%B3%A0-%EC%9E%91%EC%9D%80-%EB%AA%A8%EB%93%A0-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%93%A4</link>
            <guid>https://velog.io/@gooreum_90/%ED%81%AC%EA%B3%A0-%EC%9E%91%EC%9D%80-%EB%AA%A8%EB%93%A0-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%93%A4</guid>
            <pubDate>Fri, 05 Nov 2021 10:33:32 GMT</pubDate>
            <description><![CDATA[<ul>
<li>서비스 지향 아키텍처 / 마이크로서비스 아키텍처가 유행이다.</li>
<li>다음과 같은 이유로.<ul>
<li><strong>상호 결합이 철저하게 분리</strong>된다.</li>
<li><strong>개발과 배포 독립성을 지원</strong>한다.</li>
</ul>
</li>
<li>그러나 이 두가지 이유는 일부만 맞는 말이다.</li>
</ul>
<h3 id="서비스-아키텍처란"><strong>서비스 아키텍처란?</strong></h3>
<aside>
💡 시스템의 아키텍처는 의존성 규칙을 준수하며 고수준의 정책을 저수준의 세부사항으로 분리하는 경게에 의해 정의된다.

</aside>

<ul>
<li>서비스의 역할이 단순히 애플리케이션의 행위를 분리하는 것이라면 값비싼 함수를 호출하는 것에 불과하다.</li>
<li>따라서 서비스를 사용한다는 것 자체가 아키텍처를 나타내는 것은 아니다.</li>
<li>그렇다면 아키텍처적으로 중요한 서비스는 무엇인가?<ul>
<li>어떻게 하면 완벽한 Dev-Ops 가 될 수 있느냐 하는 질문인가..</li>
</ul>
</li>
</ul>
<h3 id="서비스의-이점"><strong>서비스의 이점?</strong></h3>
<p><strong>결합 분리의 오류</strong></p>
<ul>
<li><p>시스템이 서비스로 분리되면 각각 여러 프로세스 및 프로세서에서 실행되기 때문에, 다른 서비스의 변수에 직접 접근할 수 없다. 따라서 모든 서비스의 인터페이스가 잘 정의되어야 한다.</p>
<p>  → 이러한 점에서 서비스 사용을 통해 상호 결합이 철저하게 분리될 수 있다고 본다.</p>
</li>
<li><p>그러나 프로세서 내의 또는 네트워크 상의 공유 자원 때문에 결합될 가능성이 여전히 존재한다. 오히려 서로 공유하는 데이터에 의해 이들 서비스가 강력히 결합되기도 한다.</p>
</li>
</ul>
<p><strong>개발 및 배포 독립성의 오류</strong></p>
<ul>
<li>대규모 엔터프라이즈 시스템을 독립적으로 개발하고 배포 가능한 수십, 수백, 수천 개의 서비스들을 이용하여 만들 수 있다. 이러한 개발 및 배포 독립성은 확장 가능한 것으로 간주된다.</li>
<li>그러나,<ul>
<li>첫째로 대규모 엔터프라이즈 시스템은 서비스 기반 시스템 이외에도, 모노리틱 시스템이나 컴포넌트 기반 시스템으로도 구축할 수 있다는 사실은 역사적으로 증명되어 왔다.</li>
<li>둘째 결합 분리의 오류에 따르면 서비스라고 해서 항상 독립적으로 개발하고, 배포하며, 운영할 수 있는 것은 아니다.</li>
</ul>
</li>
</ul>
<h3 id="야옹이-문제"><strong>야옹이 문제</strong></h3>
<ul>
<li>택시 통합 시스템이 아래와 같이 각각의 서비스별로 나뉘어 개발 운영되고 있다고 생각해보자.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/1898c335-5322-4a47-a1c6-0ef5c2a636a3/%E1%84%8F%E1%85%B3%E1%84%80%E1%85%A9%E1%84%8C%E1%85%A1%E1%86%A8%E1%84%8B%E1%85%B3%E1%86%AB1.png" alt=""></p>
<p>이미지 출처 : <a href="https://hwannny.tistory.com/46">https://hwannny.tistory.com/46</a></p>
<ul>
<li>그러나 여기서 야옹이 배달 서비스를 제공하겠다고 한다면 어떻게 될까?</li>
<li>이 서비스들은 모두 결합되어 있어서 새로운 기능을 추가하기가 매우 어려움.</li>
</ul>
<h3 id="객체가-구출하다"><strong>객체가 구출하다</strong></h3>
<ul>
<li>이 문제는 컴포넌트 기반 아키텍처에서 해결 가능하다.</li>
<li>다형적으로 확장할 수 있는 클래스 집합을 생성해 새로운 기능을 처리하도록 하는 것이다.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/918aba57-ba10-4033-95ae-22e2c0b5fb00/%E1%84%8F%E1%85%B3%E1%84%80%E1%85%A9%E1%84%8C%E1%85%A1%E1%86%A8%E1%84%8B%E1%85%B3%E1%86%AB2.png" alt=""></p>
<h3 id="컴포넌트-기반-서비스"><strong>컴포넌트 기반 서비스</strong></h3>
<ul>
<li>서비스도 SOLID 원칙대로 설계할 수 있으며 컴포넌트 구조를 갖출 수도 있다.</li>
<li>이를 통해 서비스 내의 기존 컴포넌트들을 변경하지 않고도 새로운 컴포넌트를 추가할 수 있다.</li>
</ul>
<h3 id="횡단-관심사"><strong>횡단 관심사</strong></h3>
<p><img src="https://images.velog.io/images/gooreum_90/post/7197ba91-fdf2-4194-83d7-3c17583bbc51/%E1%84%8F%E1%85%B3%E1%84%80%E1%85%A9%E1%84%8C%E1%85%A1%E1%86%A8%E1%84%8B%E1%85%B3%E1%86%AB3.png" alt=""></p>
<ul>
<li>횡단 관심사를 처리하려면 다이어그램처럼 서비스 내부의 의존성 규칙을 준수하는 컴포넌트 아키텍처로 설계되어야함.</li>
<li>아키텍처경계는 서비스 사이에 존재하지 않으며, 아키텍처 경계를 정의하는것은 서비스 내에 위치한 컴포넌트임.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[메인(Main) 컴포넌트]]></title>
            <link>https://velog.io/@gooreum_90/%EB%A9%94%EC%9D%B8Main-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@gooreum_90/%EB%A9%94%EC%9D%B8Main-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Fri, 05 Nov 2021 10:32:20 GMT</pubDate>
            <description><![CDATA[<h3 id="궁극적인-세부사항"><strong>궁극적인 세부사항</strong></h3>
<ul>
<li>메인 컴포넌트는 궁극적인 세부사항으로, 가장 낮은 수준의 정책이다.</li>
<li>메인은 클린 아키텍처에서 가장 바깥 원에 위치하는 ,지저분한 저수준 모듈이다.</li>
<li>메인은 고수준의 시스템을 위한 모든 것을 로드한 후, 제어권을 고수준의 시스템에게 넘긴다.</li>
<li>움퍼스 사냥 게임의 메인은 입력 스트림 생성 부분, 게임의 메인 루프 처리, 간단한 입력 명령어 해석 등은 직접 처리하지만 명령어를 실제로 처리하는 일은 다른 고수준 컴포넌트로 위임한다.</li>
</ul>
<h3 id="결론"><strong>결론</strong></h3>
<ul>
<li>메인을 애플리케이션의 플러그인이라 생각한다면, 메인은 초기 조건과 설정을 구성하고, 외부 자원을 모두 수집한 후, 제어권을 애플리케이션의 고수준 정책으로 넘기는 플러그인이다.</li>
<li>메인은 플러그인이므로 애플리케이션의 설정별로 하나씩 두도록 하여 둘 이상의 메인 컴포넌트를 만들 수도 있다.<ul>
<li>개발용 메인 플러그인, 테스트용 메인 플러그인, 상용 메인 플러그인, 국가별 플러그인, 관할 영역별, 고객별 메인 플러그인을 만들 수도 있음.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[계층과 경계]]></title>
            <link>https://velog.io/@gooreum_90/%EA%B3%84%EC%B8%B5%EA%B3%BC-%EA%B2%BD%EA%B3%84</link>
            <guid>https://velog.io/@gooreum_90/%EA%B3%84%EC%B8%B5%EA%B3%BC-%EA%B2%BD%EA%B3%84</guid>
            <pubDate>Fri, 05 Nov 2021 10:30:39 GMT</pubDate>
            <description><![CDATA[<p>시스템 컴포넌트 크게 3가지(UI / 업무규칙 / 데이터베이스)로 구성된다고 생각할 수 있으나, 실제로 대규모 시스템에서는 그 이상이 될수도 있다. </p>
<h3 id="움퍼스-사냥-게임"><strong>움퍼스 사냥 게임</strong></h3>
<p><img src="https://images.velog.io/images/gooreum_90/post/55abd078-4db7-43fb-a05a-679d01248d85/%E1%84%80%E1%85%A6%E1%84%8E%E1%85%B3%E1%86%BC%E1%84%80%E1%85%AA%20%E1%84%80%E1%85%A7%E1%86%BC%E1%84%80%E1%85%A81.png" alt=""></p>
<ul>
<li>다양한 언어로 지원되는 게임이며 게임 규칙은 어떤 언어 UI로 사용 되더라도 의존성을 잘 관리하면 그림과 같이 게임임 규칙을 재사용할 수 있음.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/a2b7e371-ca5a-4484-9831-b1009e79a373/%E1%84%80%E1%85%A8%E1%84%8E%E1%85%B3%E1%86%BC%E1%84%80%E1%85%AA%20%E1%84%80%E1%85%A7%E1%86%BC%E1%84%80%E1%85%A82.png" alt=""></p>
<ul>
<li>게임상태를 저장하는 컴포넌트가 있더라도 마찬가지로 의존성 관리가 되어 게임 규칙을 의존하는 형태이다.</li>
</ul>
<h3 id="클린-아키텍처"><strong>클린 아키텍처?</strong></h3>
<ul>
<li>UI에서 언어가 유일한 변경의 축은 아니기 때문에 텍스트를 주고받는 메커니즘을 다양하게 만들고 싶을 수도 있다. 따라서 아래 그림과 같이 해당 변경의 축에 의해 정의되는 아키텍처 경계를 살펴볼 수 있다.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/e9e3c58d-9710-4005-9d9b-1935b6b77e8d/%E1%84%80%E1%85%A8%E1%84%8E%E1%85%B3%E1%86%BC%E1%84%80%E1%85%AA%20%E1%84%80%E1%85%A7%E1%86%BC%E1%84%80%E1%85%A83.png" alt=""></p>
<p>이미지 출처 : <a href="https://hwannny.tistory.com/44">https://hwannny.tistory.com/44</a></p>
<ul>
<li>점선으로 된 테두리는 API를 정의하는 추상 컴포넌트를 가리키며, 해당 API는 추상 컴포넌트 위나 아래의 컴포넌트가 구현한다.</li>
<li>이 모든 경우에 해당 Boundary 인터페이스가 정의하는 API는 의존성 흐름의 상위에 위치한 컴포넌트에 속한다.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/fc97908d-fd59-439b-ae93-fde6fbd8a133/%E1%84%80%E1%85%A8%E1%84%8E%E1%85%B3%E1%86%BC%E1%84%80%E1%85%AA%20%E1%84%80%E1%85%A7%E1%86%BC%E1%84%80%E1%85%A84.png" alt=""></p>
<ul>
<li>정보가 흐르는 방향을 보면 데이터 흐름을 두 개의 흐름으로 효과적으로 분리한다.<ul>
<li>왼쪽의 흐름은 사용자와의 통신에 관여</li>
<li>오른족의 흐름은 데이터 영속성에 관여.</li>
</ul>
</li>
<li>즉, 두 흐림이 GameRules에서 만나, GameRules는 두 흐름이 모두 거치게 되는 데이터에 대한 최종적인 처리기가 된다.</li>
</ul>
<h3 id="흐름-횡단하기"><strong>흐름 횡단하기</strong></h3>
<ul>
<li><p>데이터 흐름이 추가 될 수 있다.</p>
</li>
<li><p>네트워크를 통한 여러 사람과의 플레이를 위해 네트워크 컴포넌트를 추가함.</p>
<p>  → 시스템이 복잡해질수록 컴포넌트 구조는 더 많은 흐름으로 분리될 것이다.</p>
</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/051b0e26-14a3-4228-b798-7dd6f0c8b47c/%E1%84%80%E1%85%A8%E1%84%8E%E1%85%B3%E1%86%BC%E1%84%80%E1%85%AA%E1%84%80%E1%85%A7%E1%86%BC%E1%84%80%E1%85%A85.png" alt=""></p>
<h3 id="흐름-분리하기"><strong>흐름 분리하기</strong></h3>
<ul>
<li>모든 흐름이 상단의 단일 컴포넌트에서 만나는 것은 아니다.</li>
<li>GameRules 컴포넌트가 플레이어의 이동 관련된 정책(Move Management)과 그보다 고수준 정책인 플레이어 정책(Player Management)이 있다면  GameRules 컴포넌트를 아래와 같이 분리할 수도 있다.</li>
</ul>
<p><img src="https://images.velog.io/images/gooreum_90/post/3ed3f6fc-8c76-460e-8b89-9b4737d94111/%E1%84%80%E1%85%A8%E1%84%8E%E1%85%B3%E1%86%BC%E1%84%80%E1%85%AA%20%E1%84%80%E1%85%A7%E1%86%BC%E1%84%80%E1%85%A86.png" alt=""></p>
<h3 id="결론"><strong>결론</strong></h3>
<ul>
<li>이런 작은 예제 속에서도 아키텍처 경계는 어디에나 존재할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[부분적 경계]]></title>
            <link>https://velog.io/@gooreum_90/%EB%B6%80%EB%B6%84%EC%A0%81-%EA%B2%BD%EA%B3%84</link>
            <guid>https://velog.io/@gooreum_90/%EB%B6%80%EB%B6%84%EC%A0%81-%EA%B2%BD%EA%B3%84</guid>
            <pubDate>Fri, 05 Nov 2021 10:25:04 GMT</pubDate>
            <description><![CDATA[<ul>
<li>아키텍처 경계를 완벽하게 만들기 위해선 많은 비용이 든다.<ul>
<li>쌍방향의 다형적 Boundary 인터페이스, Input과 Output을 위한 데이터 구조 생성, 두 영역을 독립적으로 컴파일하고 배포할 수 있는 컴포넌트로 격리하는 데 필요한 모든 의존성 관리르 해야 하기 때문.</li>
</ul>
</li>
<li>처음부터 완벽한 경계를 나누지 않고 부분적 경계를 구현해보는 방법은 없을까.</li>
</ul>
<h3 id="마지막-단계를-건너뛰기"><strong>마지막 단계를 건너뛰기</strong></h3>
<ul>
<li>부분적 경계를 생성하는 방법 하나는 독립적으로 컴파일하고 배포할 수 있는 컴포넌트를 만들기 위한 작업은 모두 수행한 후, 단일 컴포넌트에 그대로 모아만 두는 것이다.</li>
<li>이러한 전략을 토대로 FitNesse의 웹 서버 컴포넌트는 FitNesse의 위키나 테스트 영역과는 분리되도록 설계되었다. 그러나 시간이 흐르면서 별도로 분리한 웹 컴포넌트가 재사용될 가능성은 없어지고, 웹 컴포넌트와 위키 컴포넌트 사이의 구분도 약화되기 시작했다.</li>
</ul>
<h3 id="일차원-경계"><strong>일차원 경계</strong></h3>
<p><img src="https://images.velog.io/images/gooreum_90/post/96b88a99-4367-4110-9e73-ef73091715ee/%E1%84%87%E1%85%AE%E1%84%87%E1%85%AE%E1%86%AB%E1%84%8C%E1%85%A5%E1%86%A8%20%E1%84%80%E1%85%A7%E1%86%BC%E1%84%80%E1%85%A8.png" alt=""></p>
<p>이미지 출처 : <a href="https://hwannny.tistory.com/43">https://hwannny.tistory.com/43</a></p>
<ul>
<li>완벽한 형태의 아키테처 경계는 양방향으로 격리된 상태를 유지해야 하므로 쌍방향 Boundary 인터페이스를 사용한다. 그러나 비용이 많이 든다.</li>
<li>위 그림은 전통적인 전략 패턴을 사용한 사례로써, 추후 완벽한 형태의 경계로 확장할 수 있는 공간을 확보하고자 할 대 사용할 수 있는 구조이다.</li>
<li>ServiceBoundary 인터페이스는 Client가 사용하며, ServiceImpl 클래스가 구현한다.</li>
<li>그러나 쌍방향 인터페이스가 없기 대문에 ServiceImpl와 Client 사이의 비밀통로가 생기는 일을 막을 수는 없다.</li>
</ul>
<h3 id="퍼사드"><strong>퍼사드</strong></h3>
<ul>
<li>일차원 경계보다 훨씬 단순한 경계를 만들지만 의존성 역전을 희생하였다.</li>
<li>경계는 Facade 클래스로만 간단히 정의된다.<ul>
<li>Facade 클래스에는 모든 서비스 클래스를 메서드 형태롤 정의하고, 서비스 호출이 발생하면 해당 서비스 클래스로 호출을 전달한다.</li>
<li>클라이언트는 이들 서비스 클래스에 직접 접근할 수 없다.</li>
</ul>
</li>
<li>하지만 Client는 이 모든 Service 클래스에 대해 추이 종속성을 가지게 된다.<ul>
<li>서비스 클래스 중 하나에서 소스 코드가 변경되면 Client도 무조건 재컴파일해야 한다.</li>
<li>비밀통로 또한 쉽게 생길 수 있다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>