<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Jade.Devlog</title>
        <link>https://velog.io/</link>
        <description>응애 iOS 개발자</description>
        <lastBuildDate>Fri, 02 Feb 2024 14:58:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Jade.Devlog</title>
            <url>https://velog.velcdn.com/images/a-jb97/profile/1ec07426-0e7d-417e-b5dd-e7accc6bd9f8/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Jade.Devlog. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/a-jb97" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Swift] Async, Await]]></title>
            <link>https://velog.io/@a-jb97/Swift-Async-Await</link>
            <guid>https://velog.io/@a-jb97/Swift-Async-Await</guid>
            <pubDate>Fri, 02 Feb 2024 14:58:16 GMT</pubDate>
            <description><![CDATA[<p>Async, Await에 대해서 Araboza</p>
<hr>
<blockquote>
<h3 id="async-await">Async, Await</h3>
</blockquote>
<ul>
<li><strong>비동기</strong> 처리를 할 때 사용한다.</li>
<li><strong>동시성</strong>을 구현하는데 유용하다.</li>
</ul>
<pre><code class="language-swift">// 예시
func bringWebImageUrl(url: String) async -&gt; URL

func processWebImage() async -&gt; Image {
    let result = await bringWebImageUrl(&quot;imageUrl&quot;)
    return result
}

let image = processWebImage()</code></pre>
<hr>
<ul>
<li><p>실행 흐름</p>
<ol>
<li><code>processWebImage()</code> 실행</li>
<li><code>bringWebImageUrl(”imageUrl”)</code> 로 데이터를 받아오는 요청<ul>
<li>작업을 백그라운드에서 실행</li>
</ul>
</li>
</ol>
<p>  <strong><em>Suspension Point</em> : 함수가 실행되는 도중 스레드를 포기한 시점</strong></p>
<ol>
<li>받아온 데이터 <code>result</code>에 저장</li>
<li><code>result</code> 를 <code>return</code></li>
</ol>
</li>
</ul>
<hr>
<ul>
<li>함수명 뒤에 붙어있는  <code>async</code> 는 비동기적으로 일을 처리하는 함수라는 뜻이다.<ul>
<li>async 함수 타입이라고 한다. (async → URL 타입)</br></li>
</ul>
</li>
<li>하지만 async 함수 내부가 <strong>모두 비동기적으로 처리되는 것은 아니다.</strong><ul>
<li>그래서 명령 앞에 <code>await</code> 키워드를 붙여서 <strong>‘이곳이 비동기적으로 작업이 수행되는 곳이다’</strong>라고 명시한다.<ul>
<li><code>await</code>가 붙은 명령이 처리되는 동안 <strong>다른 작업들이 실행될 수 있도록</strong> 스레드를 포기한다.<ul>
<li>비동기(백그라운드) 작업이 완료되면 <strong>_Suspension Point_부터 함수를 재시동한다.</strong></li>
<li>이 작업을 <strong>동기적</strong>으로 처리하면 데이터를 받아오는 동안 <strong>다른 작업이 불가능하다.</strong></br></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><code>async</code> 함수 내부에는 <code>sync</code> 함수가 포함될 수 있지만 <strong>반대는 불가능하다.</strong><ul>
<li>sync 함수 내부에 async 함수가 포함되면 <strong>async함수를 실행하긴 했으니</strong> 작업이 끝나기 전에 sync함수가 작업을 끝내버릴 수도 있기 때문이다.</li>
<li>하지만 반대는 비동기 작업이 필요한 명령 앞에만 <code>await</code> 키워드를 붙이면 된다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] ViewModifier (Protocol)]]></title>
            <link>https://velog.io/@a-jb97/SwiftUI-ViewModifier-Protocol</link>
            <guid>https://velog.io/@a-jb97/SwiftUI-ViewModifier-Protocol</guid>
            <pubDate>Tue, 16 Jan 2024 16:19:57 GMT</pubDate>
            <description><![CDATA[<p><strong>ViewModifier</strong>에 대해서 Araboza</p>
<hr>
<blockquote>
<h3 id="viewmodifier">ViewModifier</h3>
</blockquote>
<ul>
<li><code>View</code> 또는 View의 <code>modifier</code>에 적용하여 다른 형태의 View를 반환하는 <code>modifier</code>이다.</li>
<li>모든 View에서 재사용 할 수 있는 <code>modifier</code>를 구현할 수 있다.</li>
<li>자주 사용될 것 같은 <strong>디자인 컴포넌트</strong>들을 설계할 때 유용하다.</li>
</ul>
<pre><code class="language-swift">// 예시
struct customTextModifier: ViewModifier {
    func body(content: Content) -&gt; some View {
        content
            .font(.title2)
            .bold()
            .foregroundColor(.blue)
    }
}

extension View { // extension으로 modifier 메소드 정의
    func titleBoldBlue() -&gt; some View {
        modifier(customTextModifier())
    }
}

// 사용 예시
struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: &quot;globe&quot;)
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(&quot;Hello, world!&quot;)
                .titleBoldBlue() // extension으로 정의했기 때문에 다른 modifier들처럼 간편하게 사용 가능
        }
        .padding()
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] @ViewBuilder (Attribute)]]></title>
            <link>https://velog.io/@a-jb97/SwiftUI-ViewBuilder-Attribute</link>
            <guid>https://velog.io/@a-jb97/SwiftUI-ViewBuilder-Attribute</guid>
            <pubDate>Tue, 16 Jan 2024 16:12:48 GMT</pubDate>
            <description><![CDATA[<p><strong>@ViewBuilder</strong> 속성에 대해서 Araboza</p>
<hr>
<blockquote>
<h3 id="viewbuilder">@ViewBuilder</h3>
</blockquote>
<ul>
<li><p>클로저 매개변수에서 복수의 View들을 생성할 때 사용하는 특성(Attribute)이다.</p>
</li>
<li><p>Button View에서의 예시</p>
<pre><code class="language-swift">  Button.init(action: @escaping () -&gt; Void, @ViewBuilder label: () -&gt; Label) // label에는 View가 들어가야 하기 때문에

  // 버튼 생성 구조
  Button {
          // action
  }, label: {
          // View들이 들어감
          Text(&quot;&quot;)
          Image(&quot;&quot;)
  }</code></pre>
</li>
<li><p><code>VStack</code>, <code>List</code>, <code>ScrollView</code> 등의 컨테이너 View들은 모두 <code>@ViewBuilder</code> 특성을 통해 복수의 View들을 선언한다.</p>
</li>
<li><p>View 프로토콜의 <code>body</code> 프로퍼티 선언에도 <code>@ViewBuilder</code> 특성이 붙어있다.</p>
<ul>
<li>때문에 View를 상속 받는 사용자 정의 View의 <code>body</code>에는 <code>@ViewBuilder</code>를 붙이지 않아도 된다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] Identifiable (Protocol)]]></title>
            <link>https://velog.io/@a-jb97/SwiftUI-Identifiable-Protocol</link>
            <guid>https://velog.io/@a-jb97/SwiftUI-Identifiable-Protocol</guid>
            <pubDate>Tue, 16 Jan 2024 16:02:28 GMT</pubDate>
            <description><![CDATA[<p><strong>Identifiable</strong> 프로토콜에 대해서 Araboza</p>
<hr>
<blockquote>
<h3 id="identifiable">Identifiable</h3>
</blockquote>
<ul>
<li><p>고유 개체를 구분하기 위해 사용하는 프로토콜이다.</p>
</li>
<li><p><code>var id</code> 프로퍼티 하나만 가지는 프로토콜이다.</p>
</li>
<li><p>값이 다르더라도 <code>id</code>가 같으면 동일한 개체로 취급한다.</p>
</li>
<li><p>타입이 <code>Identifiable</code>을 채택했다면 id 프로퍼티 구현은 필수</p>
</li>
</ul>
<pre><code class="language-swift">// 예시
struct Plant: Identifiable {
    var id = UUID()
    var name: String
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 오류 처리]]></title>
            <link>https://velog.io/@a-jb97/Swift-%EC%98%A4%EB%A5%98-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@a-jb97/Swift-%EC%98%A4%EB%A5%98-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 31 Jul 2023 11:04:43 GMT</pubDate>
            <description><![CDATA[<p><strong>오류 처리</strong>에 대해서 Araboza</p>
<hr>
<ul>
<li>스위프트의 오류(Error)는 <code>Error</code>라는 프로토콜을 준수하는 타입의 값을 통해 표현한다</li>
<li>오류의 표현은 주로 열거형을 사용한다</li>
</ul>
<pre><code class="language-swift">enum CoffeeMachineError: Error {
    case emptyCoffeeCapsule
    case notEnoughWater
}</code></pre>
<ul>
<li><p>오류를 던져줄 때는 <code>throw</code> 구문을 사용한다</p>
<pre><code class="language-swift">  throw CoffeeMachineError.emptyCoffeeBeans</code></pre>
</li>
<li><p>오류가 던져지는 것에 대비하여 던져진 오류를 처리하기 위한 코드 작성도 필요하다</p>
<ol>
<li><p>함수에서 발생한 오류 알리기</p>
<ul>
<li><p><code>try</code> 키워드로 던져진 오류를 받는 것이 가능하다</p>
<pre><code class="language-swift">// 호출했을 때, 동작 도중 오류가 발생하면 자신을 호출한 코드에 오류를 던져서 오류를 알림
func canThrowError() throws -&gt; String</code></pre>
</li>
<li><p><code>throws</code>는 함수나 메서드의 자체 타입에도 영향을 미친다</p>
<ul>
<li>즉, <code>throws</code> 함수나 메서드는 같은 이름의 <code>throws</code>가 명시되지 않는 함수나 메서드와 구분된다<pre><code class="language-swift">enum CoffeeMachineError: Error {
case emptyCoffeeCapsule
case emptyWater
}
</code></pre>
</li>
</ul>
</li>
</ul>
</li>
</ol>
</li>
</ul>
<p>class CoffeeMachine {
    private var coffeeCapsule: Int = 10
    private var waterLiters: Double = 2.0</p>
<pre><code>var notEnoughCoffeeCapsule: Bool {
    return coffeeCapsule &lt; 1
}

var notEnoughWater: Bool {
    return waterLiters &lt; 0.2
}

func makeCoffee() throws -&gt; String {
    guard notEnoughCoffeeCapsule == false else {
        throw CoffeeMachineError.emptyCoffeeCapsule
    }

    guard notEnoughWater == false else {
        throw CoffeeMachineError.emptyWater
    }

    coffeeCapsule -= 1
    waterLiters -= 0.2

    return &quot;커피&quot;
}</code></pre><p>}</p>
<p>```</p>
<ul>
<li>코드 동작 중 발생한 오류는 자신을 호출한 코드로 던져서 알릴 수 있지만, 오류를 받은 코드가 적절히 오류를 처리해주지 않는다면 이후의 코드는 작동하지 않는다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 프로퍼티 래퍼]]></title>
            <link>https://velog.io/@a-jb97/Swift-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EB%9E%98%ED%8D%BC</link>
            <guid>https://velog.io/@a-jb97/Swift-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EB%9E%98%ED%8D%BC</guid>
            <pubDate>Mon, 24 Jul 2023 10:20:03 GMT</pubDate>
            <description><![CDATA[<p><strong>프로퍼티 래퍼</strong>에 대해서 Araboza</p>
<hr>
<ul>
<li><p>프로퍼티 래퍼는 클래스(class)와 구조체(struct)의 구현부에 getter, setter, 연산 프로퍼티 코드의 중복을 줄이는 방법을 제공한다. (Swift 5.1부터 생김)</p>
</li>
<li><p>여러 클래스나 구조체에 생성한 연산 프로퍼티들이 유사한 패턴을 갖는 경우가 발생해서 불편할 때가 있다.</p>
<ul>
<li>이 때, 프로퍼티 래퍼를 사용하면 연산 프로퍼티 기능을 <strong>개별 클래스, 구조체와 분리할 수 있고</strong>, 앱 코드에서 <strong>재사용</strong>할 수 있다.</li>
</ul>
</li>
</ul>
<blockquote>
<h3 id="예제">예제</h3>
</blockquote>
<ul>
<li><p>다음과 같이 도시 이름을 저장하는 String 프로퍼티를 가진 구조체가 있다고 하자.</p>
<pre><code class="language-swift">struct Address {
  private var cityName: String

  var city: String {
      get { cityName }
      set { cityName = newValue.uppercased()}
  }
}</code></pre>
</li>
<li><p>사용자가 도시 이름을 어떻게 입력했는지와는 상관 없이 대문자로 저장되어야 한다면 다음과 같이 연산 프로퍼티를 구조체에 추가할 수 있다.</p>
</li>
<li><p>도시 이름 프로퍼티에 할당되면 연산 프로퍼티의 <strong>setter</strong>가 <strong>private cityName</strong> 변수에 값을 저장하기 전에 대문자로 변환하게 된다.</p>
<pre><code class="language-swift">var address = Address()
address.city = &quot;Seoul&quot;
print(address.city) // LONDON</code></pre>
</li>
<li><p>연산 프로퍼티를 사용하는 대신에 이  로직을 프로퍼티 래퍼로 구현할 수 있다.</p>
<pre><code class="language-swift">@propertyWrapper // 이 지시자를 이용하여 선언
struct FixCase {
  private(set) var value: String = &quot;&quot;

  var wrappedValue: String {
      get { value }
      set { value = newValue.uppercased() }
  }
  init(wrappedValue initialValue: String) {
      self.wrappedValue = initialValue
  }
}</code></pre>
</li>
<li><p>모든 프로퍼티 래퍼는 값을 변경하거나 유효성을 검사하는 <strong>getter</strong>, <strong>setter</strong> 코드가 포함된 <strong>wrappedValue</strong> 프로퍼티를 가져야 한다.</p>
<ul>
<li>초깃값이 전달되는 초기화 메서는 선택 사항으로 포함될 수도 있다.    </li>
<li>위 코드의 <strong>초깃값(init)</strong> 에서는 문자열을 <strong>대문자</strong>로 변환하고 <strong>private 변수</strong>에 저장하는 프로퍼티에 할당한다.</li>
</ul>
</li>
</ul>
<hr>
<ul>
<li>이제 래퍼에 대한 정의가 끝났으니 이와 동일한 동작이 필요한 다른 프로퍼티 변수에 적용하여 재사용할 수 있다.</li>
</ul>
<pre><code class="language-swift">struct Contact {
    @FixCase var name: String // FixCase동작이 필요한 프로퍼티 앞에 @FixCase를 붙인다.
    @FixCase var city: String
    @FixCase var country: String
}

var contact = Contact(name: &quot;GilDong&quot;, city: &quot;Seoul&quot;, country: &quot;Korea&quot;)

print(&quot;\(contact.name), \(contact.city), \(contact.country)&quot;)
// GILDONG, SEOUL, KOREA</code></pre>
<ul>
<li>이처럼 미리 정의된 프로퍼티 래퍼는 나중에 설명할 SwiftUI 작업을 할 때 광범위하게 사용된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 타입 캐스팅]]></title>
            <link>https://velog.io/@a-jb97/Swift-%ED%83%80%EC%9E%85-%EC%BA%90%EC%8A%A4%ED%8C%85</link>
            <guid>https://velog.io/@a-jb97/Swift-%ED%83%80%EC%9E%85-%EC%BA%90%EC%8A%A4%ED%8C%85</guid>
            <pubDate>Sun, 16 Jul 2023 15:59:58 GMT</pubDate>
            <description><![CDATA[<p><strong>타입 캐스팅</strong>에 대해서 Araboza</p>
<hr>
<blockquote>
<h3 id="타입-캐스팅">타입 캐스팅</h3>
</blockquote>
<ul>
<li><p>인스턴스의 타입을 확인 하는 용도 또는 인스턴스를 부모 혹은 자식 클래스의 타입으로 사용할 수 있는지, 프로토콜로 취급할 수 있는지 등을 확인하는 용도로 사용한다. (<code>is</code>, <code>as</code> 사용)</p>
</li>
<li><p>타입 확인</p>
<ul>
<li><code>is</code> 사용</li>
</ul>
</li>
<li><p>업 캐스팅</p>
<ul>
<li><code>as</code> 사용</li>
<li>부모 클래스의 인스턴스로 사용할 수 있도록 컴파일러에게 타입정보를 전환한다.</li>
<li><code>Any</code>, <code>AnyObject</code>로도 변환 가능하다. (암시적으로 처리되므로 꼭 필요한 경우가 아니라면 생략해도 무방)</li>
</ul>
</li>
<li><p>다운 캐스팅</p>
<ul>
<li><p><code>as?</code> 또는 <code>as!</code> 사용</p>
</li>
<li><p>자식 클래스의 인스턴스로 사용할 수 있도록 컴파일러에게 타입정보를 전환한다.</p>
</li>
<li><p>조건부 다운 캐스팅</p>
<ul>
<li><code>as?</code> 사용</li>
<li>캐스팅에 실패하면, 즉 캐스팅하려는 타입에 부합하지 않는 인스턴스라면 nil을 반환하기 때문에 결과의 타입은 옵셔널 타입이다.</li>
</ul>
</li>
<li><p>강제 다운 캐스팅</p>
<ul>
<li><code>as!</code> 사용</li>
<li>캐스팅에 실패하면 런타임 오류 발생, 캐스팅에 성공하면 옵셔널이 아닌 일반 타입 반환한다.</li>
</ul>
</li>
</ul>
</li>
<li><p>프로토콜과 타입 캐스팅</p>
<ul>
<li>프로토콜을 채택하고 있는지 확인한다.</li>
<li>포로토콜로서 타입 캐스팅을 통해 프로토콜에서 정의된 프로퍼티와 메서드 사용 가능하다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 프로퍼티]]></title>
            <link>https://velog.io/@a-jb97/Swift-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0</link>
            <guid>https://velog.io/@a-jb97/Swift-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0</guid>
            <pubDate>Wed, 12 Jul 2023 16:03:58 GMT</pubDate>
            <description><![CDATA[<p><strong>프로퍼티</strong>의 종류에 대해서 Araboza</p>
<hr>
<blockquote>
<h3 id="저장-프로퍼티-stored-property">저장 프로퍼티 (Stored Property)</h3>
</blockquote>
<ul>
<li>일반적인 프로퍼티</li>
</ul>
<hr>
<blockquote>
<h3 id="지연-저장-프로퍼티-lazy-stored-property">지연 저장 프로퍼티 (Lazy Stored Property)</h3>
</blockquote>
<ul>
<li><p>프로퍼티가 <code>처음 사용되는 시점에 초기화</code>가 이루어진다. (프로퍼티를 처음 사용해 주어야 초기화)</p>
<pre><code class="language-swift">  struct GamingMouse {
      var companyName: String = &quot;로지텍&quot;
      var productName: String = &quot; G502X&quot;
      lazy var productInfo: String = self.companyName + self.productName
  }</code></pre>
</li>
<li><p>특징</p>
<ul>
<li><p>의존적인 값을 할당할 수 있다. (자신의 프로퍼티의 값을 또 다른 프로퍼티에서 사용)</p>
<pre><code class="language-swift">struct GamingMouse {
  var companyName: String = &quot;로지텍&quot;
  var productName: String = &quot; G502X&quot;
  lazy var productInfo: String = self.companyName + self.productName
}

  var gamingMouse = GamingMouse()
  gamingMouse.productname = &quot; G PRO Superlight&quot;

  print(gamingMouse.productInfo) // 로지텍 G PRO Superlight </code></pre>
</li>
<li><p>변수로만 선언할 수 있다. (<code>lazy let</code>은 사용 불가)</p>
<ul>
<li>인스턴스 초기화가 완료 될 때까지 값이 없을 수도 있기 때문</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<blockquote>
<h3 id="연산-프로퍼티-computed-property">연산 프로퍼티 (Computed Property)</h3>
</blockquote>
<ul>
<li><p>관계에서 얻어지는 값    </p>
<pre><code class="language-swift">  struct PersonA {
      var koreanAge: Int
      var americanAge: Int {
          get {
              return koreanAge - 1
          }
          set(newValue) {
              koreanAge = newValue + 1
          }
      }
  }

  var minseokAge = PersonA(koreanAge: 30)
  minseokAge.americanAge = 26

  print(minseokAge) // PersonA(koreanAge: 27)</code></pre>
<ul>
<li><strong><code>get</code></strong> : 값을 얻어올 때 호출</li>
<li><strong><code>set</code></strong> : 값이 설정되었을 때 호출</li>
<li>읽기 전용으로도 구현 가능 (<code>get</code>만 정의) (한 쪽으로만 의존적인 관계)<br/></li>
</ul>
</li>
</ul>
<ul>
<li><p><strong>프로퍼티 감시자 (Property Observer)</strong></p>
<ul>
<li><p>프로퍼티를 감시하고 있다가 프로퍼티의 값이 변경됨에 따라 적절한 작업이 가능하다.</p>
</li>
<li><p>변경되는 값이 현재 값과 같더라도 호출한다. (초기화 시점에는 호출 X)</p>
<pre><code class="language-swift">struct PersonB {
  var name: String {
      willSet(newValue // 생략 가능) {
          // some code
      }
      didSet(oldValue // 생략 가능) {
          // some code
      }
  }
}</code></pre>
</li>
<li><p><strong><code>willSet</code></strong> : 값이 변경되기 직전 호출</p>
</li>
<li><p><strong><code>didSet</code></strong>: 값이 변경된 직후 호출<br/></p>
</li>
</ul>
</li>
</ul>
<ul>
<li><p><strong>타입 프로퍼티 (Type Property)</strong></p>
<ul>
<li><p>타입 자체의 프로퍼티 ( 인스턴스를 만들지 않아도 사용 가능)</p>
</li>
<li><p>static을 붙여 사용</p>
<pre><code class="language-swift">struct SomeStucture {
  static var someProperty: Int = 10
}

let number = SomeStucture.someProperty</code></pre>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 접근제어, 접근수준]]></title>
            <link>https://velog.io/@a-jb97/TIL-%EC%A0%91%EA%B7%BC%EC%A0%9C%EC%96%B4-%EC%A0%91%EA%B7%BC%EC%88%98%EC%A4%80</link>
            <guid>https://velog.io/@a-jb97/TIL-%EC%A0%91%EA%B7%BC%EC%A0%9C%EC%96%B4-%EC%A0%91%EA%B7%BC%EC%88%98%EC%A4%80</guid>
            <pubDate>Mon, 10 Jul 2023 03:12:09 GMT</pubDate>
            <description><![CDATA[<p>Swift의 <strong>접근제어</strong>와 <strong>접근수준</strong>에 대해서 Araboza</p>
<hr>
<blockquote>
<h3 id="접근-제어access-control">접근 제어(Access Control)</h3>
</blockquote>
<ul>
<li>외부로 굳이 보여주지 않아도 될 것들은 감추는 것이 좋다.</li>
<li><h3 id="은닉화-hiding">은닉화 (Hiding)</h3>
<ul>
<li>주요 사항이 겉으로 드러나지 않도록 감추는 것</li>
</ul>
</li>
<li><h3 id="캡슐화-encapsulation">캡슐화 (Encapsulation)</h3>
<ul>
<li>중요사항을 감춘 상태에서 외부에서 그것을 사용할 수 있는 방법을 제공하고 외부와 소통하는 것</li>
</ul>
</li>
<li>운전자가 직접 자동차 하부의 전진 기어를 엔진으로 연결하도록 하는 것이 아니라, 차에게 전진 명령을 주면 자동차의 인스턴스가 전진 기어를 엔진으로 연결하게 만드는 것이 <code>캡슐화</code>이다.</li>
<li>그 과정에서 전진 기어는 운전자에게 직접 보이지 않도록 내부로 감추는 것이 <code>은닉화</code>이다.</li>
<li>타입을 설계할 때 순서<ul>
<li><code>일반화 → 추상화 → 은닉화 → 캡슐화</code></li>
</ul>
</li>
</ul>
<hr>
<blockquote>
<h3 id="접근-수준access-level">접근 수준(Access Level)</h3>
</blockquote>
<ul>
<li><p>접근제어는 <code>접근 수준</code> 키워드를 통해 구현할 수 있다.</p>
</li>
<li><p>각 타입(클래스, 구조체, 열거형 등)에 특정 접근수준을 지정할 수 있다.</p>
</li>
<li><p>타입 내부의 <code>프로퍼티</code>, <code>메서드</code>, <code>이니셜라이저</code>, <code>서브스크립트</code> 각각에도 접근수준을 지정할 수 있다.</p>
</li>
<li><h3 id="public---공개-접근-수준-모듈-외부까지">public - 공개 접근 수준 (모듈 외부까지)</h3>
<ul>
<li><p>어디서든 쓰일 수 있다.</p>
</li>
<li><p>자신이 구현된 소스파일, 그 소스파일이 속해 있는 모듈, 그 모듈을 가져다 쓰는 모듈 등 모든 곳</p>
</li>
<li><p>주로 <code>프레임워크</code>에서 외부와 연결될 인터페이스를 구현하는데 많이 쓰인다.</p>
</li>
<li><p>스위프트의 기본 요소는 모두 공개 접근 수준으로 구현한다.</p>
<pre><code class="language-swift">// 스위프트 표준 라이브러리에 정의되어 있는 Bool 타입의 일부
/// A value type whose instances are either &#39;true&#39; or &#39;false&#39;.
public struct Bool {
     /// Default-initialize Boolean value to &#39;false&#39;.
     public init()
}</code></pre>
</li>
</ul>
</li>
<li><h3 id="open---개방-접근-수준-모듈-외부까지">open - 개방 접근 수준 (모듈 외부까지)</h3>
<ul>
<li><p><code>public</code> 접근 수준 이상으로 높은 접근 수준</p>
</li>
<li><p>클래스와 클래스의 멤버에서만 사용 가능하다.</p>
</li>
<li><p><code>public</code> 접근 수준과 비슷하지만 다음과 같은 차이점이 있다.</p>
<ul>
<li><p>개방 접근 수준을 제외한 다른 모든 접근수준의 클래스는 그 클래스가 정의된 모듈 안에서만 <code>상속</code>할 수 있다.</p>
</li>
<li><p>개방 접근 수준의 클래스는 그 클래스가 정의된 모듈 밖의 다른 모듈에서도 <code>상속</code>할 수 있다.</p>
</li>
<li><p>개방 접근 수준을 제외한 다른 모든 접근수준의 클래스 멤버는 해당 멤버가 정의된 모듈 안에서만 <code>재정의</code>할 수 있다.</p>
</li>
<li><p>개방 접근 수준의 클래스 멤버는 해당 멤버가 정의된 모듈 밖의 다른 모듈에서도 <code>재정의(override)</code>할 수 있다.</p>
</li>
<li><p>클래스를 개방 접근 수준으로 명시하는 것은 그 클래스를 다른 모듈에서도 <code>부모클래스</code>로 사용 하겠다는 목적으로 클래스를 설계하고 코드를 작성했음을 의미한다.</p>
<pre><code class="language-swift">// Foundation 프레임워크에 정의되어 있는 개방 접근 수준의 NSString 클래스
open class NSString : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
     open var length: Int { get }
     open func character(at index: Int) -&gt; unichar
     public init()
     public init?(coder aDecoder: NSCoder)
}</code></pre>
</li>
</ul>
</li>
<li><p>라이브러리 혹은 프레임워크를 설계할 때 라이브러리나 프레임워크를 사용하는 프로그래머가 이 타입을 사용할 수 있도록 하려면 <code>open</code> 혹은 <code>public</code> 공개수준으로 타입을 정의하면 된다.</p>
<ul>
<li>라이브러리 사용자(프로그래머)가 상속받아 사용하면 좋겠다거나 상속 후 재정의하여 사용하면 좋겠다는 생각을 한다면 <code>open</code> 개방 접근 수준으로, 그렇지 않고 클래스를 상속받거나 재정의 할 수 없도록 막고싶다면 <code>public</code> 공개 접근 수준으로 정의한다.</li>
<li><code>open</code> 및 <code>public</code> 접근수준이 아닌 다른 클래스 및 타입은 모듈 외부에서 접근할 수 없으므로 라이브러리 혹은 프레임워크를 사용하는 프로그래머가 사용할 수 없다.</li>
</ul>
</li>
</ul>
</li>
<li><h3 id="internal---내부-접근-수준-모듈-내부">internal - 내부 접근 수준 (모듈 내부)</h3>
<ul>
<li>기본적으로 모든 요소에 암묵적으로 지정하는 기본 접근수준</li>
<li>소스파일이 속해 있는 모듈 어디에서든 쓰일 수 있다.</li>
<li>다만 그 모듈을 가져다 쓰는 외부 모듈에서는 접근할 수 없다.</li>
<li>보통 외부에서 사용할 클래스나 구조체가 아니며, 모듈 내부에서 광역적으로 사용할 경우 지정한다.</li>
</ul>
</li>
<li><h3 id="fileprivate---파일-외부-비공개-접근-수준-파일-내부">fileprivate - 파일 외부 비공개 접근 수준 (파일 내부)</h3>
<ul>
<li>요소가 구현된 소스파일 내부에서만 사용 가능하다.</li>
<li>해당 소스파일 외부에서 값이 변경되거나 함수를 호출하면 부작용이 생길 수 있는 경우 사용하면 좋다.</li>
</ul>
</li>
<li><h3 id="private---비공개-접근-수준-기능-정의-내부">private - 비공개 접근 수준 (기능 정의 내부)</h3>
<ul>
<li>가장 한정적인 범위</li>
<li>기능을 정의하고 구현한 범위 내에서만 사용 가능하다.</li>
<li>심지어 같은 소스파일 안에 구현한 다른 타입이나 기능에서도 사용이 불가능하다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 클로저와 고차함수]]></title>
            <link>https://velog.io/@a-jb97/TIL-%ED%81%B4%EB%A1%9C%EC%A0%80%EC%99%80-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@a-jb97/TIL-%ED%81%B4%EB%A1%9C%EC%A0%80%EC%99%80-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98</guid>
            <pubDate>Sun, 09 Jul 2023 16:36:22 GMT</pubDate>
            <description><![CDATA[<p>Swift의 <strong>클로저</strong>와 <strong>고차함수</strong>에 대해서 Araboza</p>
<hr>
<blockquote>
<h3 id="1-클로저closure-기초">1. 클로저(Closure) 기초</h3>
</blockquote>
<ul>
<li><p>클로저는 <code>코드의 블록</code> 이며 <code>일급시민(first-citizen)</code>이다.<br/></p>
</li>
<li><p>함수의 전달인자, 변수, 상수 등으로 저장, 전달이 가능하다.</p>
</li>
<li><p>함수 내부에서 원하는 <code>코드블럭</code> 실행이 가능하다.</p>
<pre><code class="language-swift">// 클로저의 사용
// sum이라는 상수에 클로저를 할당
let sum: (Int, Int) -&gt; Int = { (a: Int, b: Int) in
  return a + b
}
let sumResult: Int = sum(1, 2)
print(sumResult) // 3</code></pre>
</li>
</ul>
<hr>
<blockquote>
<h3 id="2-다양한-클로저-표현">2. 다양한 클로저 표현</h3>
</blockquote>
<ul>
<li><p>함수의 마지막 매개변수에 전달되는 클로저는 후행클로저(trailing closure)로 함수 소괄호 밖에 구현이 가능하다.<br/></p>
</li>
<li><p>컴파일러가 클로저의 타입을 유추할 수 있는 경우 <code>매개변수, 반환 타입 생략이 가능</code>하다.</p>
</li>
<li><p>반환 값이 있는 경우, 암시적으로 클로저의 맨 마지막 줄은 <code>return</code> 키워드를 생략해도 반환 값으로 취급한다.</p>
</li>
<li><p>전달인자의 이름이 굳이 필요없고, 컴파일러가 타입을 유추할 수 있는 경우 축약된 전달인자 이름<code>($0, $1, $2…)</code> 사용 가능하다.</p>
</li>
</ul>
<hr>
<blockquote>
<h3 id="3-고차함수higher-order-function">3. 고차함수(Higher-order function)</h3>
</blockquote>
<ul>
<li><p>고차함수는 다른 함수를 매개변수의 전달인자로 받거나 함수실행의 결과를 함수로 반환하는 함수이다.<br/></p>
</li>
<li><p>유용한 고차함수는 스위프트 표준 라이브러리에서 제공한다. (<code>map</code>, <code>filter</code>, <code>reduce</code> 등)</p>
</li>
<li><h3 id="map">map</h3>
<ul>
<li><p>컨테이너 내부의 기존 데이터를 변형(transform)하여 새로운 컨테이너를 생성한다. <br/></p>
</li>
<li><p><strong>flatMap</strong></p>
<ul>
<li>컨테이너의 각 요소를 사용하여 지정된 조건을 호출할 때, 순차적인 결과의 배열 반환한다.</li>
<li>= 중첩된 배열을 제거하고 평평한 배열(flattened array) 반환한다.</li>
</ul>
</li>
<li><p><strong>compactMap</strong></p>
<ul>
<li>컨테이너의 각 요소를 조건을 지정하여 호출할 때, <code>nil</code>이 아닌 배열 반환한다.</li>
</ul>
</li>
</ul>
</li>
<li><h3 id="filter">filter</h3>
<ul>
<li>컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출한다.</li>
</ul>
</li>
<li><h3 id="reduce">reduce</h3>
<ul>
<li>컨테이너 내부의 콘텐츠를 하나로 통합한다.</li>
<li><code>result</code> : 초깃값으로부터 출발하여 마지막 요소까지 순회하는 내내의 결괏값</li>
<li><code>currentItem</code> : 현재 순회하고 있는 요소의 값</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] SwiftUI로 Log in 화면 구현하기]]></title>
            <link>https://velog.io/@a-jb97/SwiftUI%EB%A1%9C-Log-in-%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@a-jb97/SwiftUI%EB%A1%9C-Log-in-%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 05 Jul 2023 15:10:28 GMT</pubDate>
            <description><![CDATA[<p>현재 멋쟁이사자처럼에서 진행하는 <strong>&#39;테킷 앱스쿨: iOS 2기&#39;</strong> 교육을 받고 있다.
이곳에서 <strong>&#39;모공모공&#39;</strong> 이라는 스터디에 참여하고 있는데, 모공모공 태그가 붙은 글들은 이 스터디에서 진행한 프로젝트나 공부한 주제들을 다룰 것이다 💭</p>
<hr>
<p>오늘은 제목처럼 <strong>SwiftUI</strong>를 사용한 <strong>Log in 화면</strong> 구현 과정을 풀어내 보겠다 🧐</p>
<p>먼저, 요구사항은 다음과 같다.</p>
<ul>
<li>아이디에는 Email 형식만 입력<ul>
<li>형식이 맞지 않으면 Alert 창 띄우기</li>
</ul>
</li>
<li>아이디랑 비밀번호 칸이 두 개가 다 입력되어 있고, 토글 버튼이 true여야 회원가입 버튼이 활성화</li>
<li>비밀번호 텍스트 필드 값은 암호 표시</li>
<li>아이디와 비밀번호가 설정한 것과 일치하면 다른 뷰로 넘어가기<ul>
<li>일치하지 않으면 Alert 창 띄우기</li>
</ul>
</li>
<li>화면의 빈 부분을 터치하면 키보드 숨기기</li>
<li>아이디 텍스트 필드에서 키보드의 Enter(Return) 버튼을 누르면 비밀번호 텍스트 필드로 넘어가기</li>
</ul>
<center><img src=https://velog.velcdn.com/images/a-jb97/post/f3ed5dbc-d88b-49d5-800f-9aa410c8bc6f/image.png width="200" height="400"></center>


<p>이제 기본 뼈대 구현 방법은 제외하고, <strong>각 요구사항 별로 내가 구현한 방법</strong>들을 설명해 보겠다 💡</p>
<hr>
<blockquote>
<h3 id="아이디에는--email-형식만-입력">아이디에는  Email 형식만 입력</h3>
<p>형식이 맞지 않으면 Alert 창 띄우기</p>
</blockquote>
<p>여기에서는 정규 표현식을 사용하는 메서드를 만들어서 다음의 코드와 같은 형식을 만족하지 않으면 Sign in 버튼을 눌렀을 때 Alert 창을 띄우도록 했다.</p>
<pre><code class="language-swift">// 정규 표현식을 사용해 Email 형식 유무 체크

private func checkEmailForm(input: String) -&gt; Bool {
    let emailRegex = &quot;[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}&quot;
    return NSPredicate(format: &quot;SELF MATCHES %@&quot;, emailRegex).evaluate(with: input)
}</code></pre>
<pre><code class="language-swift">// Email 형식을 준수하지 않았을 때 띄우는 Alert

.alert(isPresented: $isValid) {
    Alert(title: Text(&quot;경고&quot;), message: Text(&quot;이메일 형식이 올바르지 않습니다.&quot;), dismissButton: .default(Text(&quot;확인&quot;)))
}</code></pre>
<hr>
<blockquote>
<h3 id="아이디랑-비밀번호-칸이-두-개가-다-입력되어-있고-토글-버튼이-true여야-회원가입-버튼이-활성화">아이디랑 비밀번호 칸이 두 개가 다 입력되어 있고, 토글 버튼이 true여야 회원가입 버튼이 활성화</h3>
</blockquote>
<p>이 요구사항은 간단한 조건식을 사용해서 구현해 보았다.</p>
<pre><code class="language-swift">// 모든 TextField가 채워지고 토글버튼을 On 해야 버튼이 활성화 되는 조건식

.disabled(email.isEmpty || password.isEmpty || !toggling)</code></pre>
<hr>
<blockquote>
<h3 id="비밀번호-텍스트-필드-값은-암호-표시">비밀번호 텍스트 필드 값은 암호 표시</h3>
</blockquote>
<p>이 요구사항은 간단하게 <strong>TextField</strong> 대신 <strong>SecureField</strong>를 사용해서 해결했다.</p>
<pre><code class="language-swift">// 비밀번호 텍스트 필드 암호 표시

SecureField(&quot;Password&quot;, text: $password)</code></pre>
<hr>
<blockquote>
<h3 id="아이디와-비밀번호가-설정한-것과-일치하면-다른-뷰로-넘어가기">아이디와 비밀번호가 설정한 것과 일치하면 다른 뷰로 넘어가기</h3>
<p>일치하지 않으면 Alert 창 띄우기</p>
</blockquote>
<p>여기에서는 <strong>checkLogin</strong>이라는 메서드를 만들어서 이메일과 패스워드 둘 중 하나라도 설정한 값들과 맞지 않으면 미리 만들어 놓은 Bool 타입의 <strong>@State notCorrectLogin</strong> 변수가 <strong>true</strong>가 되도록 했다.</p>
<pre><code class="language-swift">// 이메일, 패스워드의 일치 여부 확인

private func checkLogin(isEmail: String, isPassword: String) {
    if isEmail != correctEmail || isPassword != correctPassword {
        notCorrectLogin = true
    }
}</code></pre>
<p>계정 정보가 일치할 때 다른 뷰로 넘어가는 기능은 <strong>NavigationLink</strong>와 Bool 타입의 <strong>@State isActive 변수</strong>
를 사용해서 구현했다.</p>
<pre><code class="language-swift">@State private var isActive: Bool = false

NavigationLink(destination: DetailView(), isActive: $isActive) {
    EmptyView()
}</code></pre>
<p>그리고 계정 정보가 일치하지 않았을 때 <strong>Sign in</strong> 버튼을 누르면 Alert 창을 띄우도록 했다.</p>
<pre><code class="language-swift">// 계정 정보가 일치하지 않을 때 띄우는 Alert

.alert(isPresented: $notCorrectLogin) {
    Alert(title: Text(&quot;주의\n&quot;), message: Text(&quot;이메일, 또는 비밀번호가 일치하지 않습니다.&quot;), dismissButton: .default(Text(&quot;확인&quot;)))
}</code></pre>
<hr>
<blockquote>
<h3 id="화면의-빈-부분을-터치하면-키보드-숨기기">화면의 빈 부분을 터치하면 키보드 숨기기</h3>
</blockquote>
<p>이 요구사항은 그 동안 해본 적 없는 생소한 부분이라서 구현하는 데 좀 더 시간이 걸렸다.
먼저, <strong>extension</strong>을 사용해서 View에 다음과 같은 메서드를 추가했다.</p>
<pre><code class="language-swift">// UIKit에서도 활용하는 resignFirstResponder 메서드 추가

extension View {
    func endTextEditing() {          
    UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)    
    }
}</code></pre>
<p>그리고 빈 곳을 탭 했을 때 <strong>키보드를 내리고 싶은 뷰</strong> 하단에 다음과 같이 onTapGesture 이벤트 메서드를 추가하면 요구사항이 구현된다.</p>
<pre><code class="language-swift">.onTapGesture{
    self.endTextEditing()
}</code></pre>
<hr>
<blockquote>
<h3 id="아이디-텍스트-필드에서-키보드의-enterreturn-버튼을-누르면-비밀번호-텍스트-필드로-넘어가기">아이디 텍스트 필드에서 키보드의 Enter(Return) 버튼을 누르면 비밀번호 텍스트 필드로 넘어가기</h3>
</blockquote>
<p>이 요구사항에서는 @FocusState 변수를 옵셔널로 선언해서 각 TextField마다 submitLabel을 설정해두고 onSubmit을 통해 submit이 되면 자동으로 다음 TextField로 넘어가도록 하였다. </p>
<pre><code class="language-swift">@FocusState private var focusedField: Field?

var body: some View {
    NavigationStack {
        VStack {
        TextField(&quot;Email&quot;, text: $email)
            .focused($focusedField, equals: .email)
            .textContentType(.givenName)

        SecureField(&quot;Password&quot;, text: $password)
            .focused($focusedField, equals: .password)
            .textContentType(.familyName)
        }
        .onSubmit {
            switch focusedField {
            case .email:
                focusedField = .password
            default:
                print(&quot;Done&quot;)
            }
        }
    }
}</code></pre>
<hr>
<blockquote>
<p>전체 코드</p>
</blockquote>
<pre><code class="language-swift">import SwiftUI

extension View {
    func endTextEditing() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

struct ContentView: View {
    enum Field {
        case email
        case password
    }

    @State private var email: String = &quot;&quot;
    @State private var password: String = &quot;&quot;
    @State private var toggling = false
    @State private var isValid: Bool = false
    @State private var notCorrectLogin: Bool = false
    @State private var isShowingDetailView = false
    @State private var isActive: Bool = false
    @FocusState private var focusedField: Field?

    private var correctEmail: String = &quot;a_jb97@naver.com&quot;
    private var correctPassword: String = &quot;mindol97&quot;

    var body: some View {
        NavigationStack {
            VStack(alignment: .leading) {
                Text(&quot;Introduce your credentials&quot;)
                    .foregroundColor(.gray)

                TextField(&quot;Email&quot;, text: $email)
                    .textFieldStyle(.roundedBorder)
                    .keyboardType(.emailAddress)
                    .textInputAutocapitalization(.never)
                    .focused($focusedField, equals: .email)
                    .textContentType(.givenName)

                SecureField(&quot;Password&quot;, text: $password)
                    .textFieldStyle(.roundedBorder)
                    .textInputAutocapitalization(.never)
                    .focused($focusedField, equals: .password)
                    .textContentType(.familyName)

                Toggle(isOn: $toggling) {
                    Text(&quot;Agree to terms and conditions&quot;)
                        .font(.subheadline)
                }
            }
            .onSubmit {
                switch focusedField {
                case .email:
                    focusedField = .password
                default:
                    print(&quot;Done&quot;)
                }
            }
            .padding()

            Button {
                isValid = !checkEmailForm(input: email)
                checkLogin(isEmail: email, isPassword: password)

                if notCorrectLogin == false {
                    print(&quot;로그인 성공&quot;)
                    isActive = true
                }
            } label: {
                Text(&quot;Sign in&quot;)
                    .frame(width: 300, height: 30)
            }
            .alert(isPresented: $isValid) {
                Alert(title: Text(&quot;경고&quot;), message: Text(&quot;이메일 형식이 올바르지 않습니다.&quot;), dismissButton: .default(Text(&quot;확인&quot;)))
            }
            .alert(isPresented: $notCorrectLogin) {
                Alert(title: Text(&quot;주의\n&quot;), message: Text(&quot;이메일, 또는 비밀번호가 일치하지 않습니다.&quot;), dismissButton: .default(Text(&quot;확인&quot;)))
            }
            .disabled(email.isEmpty || password.isEmpty || !toggling)
            .buttonStyle(.borderedProminent)
            .padding()

            NavigationLink(destination: DetailView(), isActive: $isActive) {
                EmptyView()
            }

            Spacer()

            .navigationTitle(&quot;Log in&quot;)
        }
        .onTapGesture{
            self.endTextEditing()
        }
    }

    private func checkEmailForm(input: String) -&gt; Bool {
        let emailRegex = &quot;[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}&quot;
        return NSPredicate(format: &quot;SELF MATCHES %@&quot;, emailRegex).evaluate(with: input)
    }

    private func checkLogin(isEmail: String, isPassword: String) {
        if isEmail != correctEmail || isPassword != correctPassword {
            notCorrectLogin = true
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>