<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jamong-i.log</title>
        <link>https://velog.io/</link>
        <description>새해 목표 : 1일 1 깃, 블로그, 프로그래머스 2문제</description>
        <lastBuildDate>Sat, 11 Mar 2023 03:37:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jamong-i.log</title>
            <url>https://velog.velcdn.com/images/jamong-i/profile/92d5e858-c49f-45dc-bedc-1b3368f3b351/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jamong-i.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jamong-i" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[iOS] ToDoList 앱 만들기 기능구현]]></title>
            <link>https://velog.io/@jamong-i/iOS-ToDoList-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EA%B8%B0%EB%8A%A5%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@jamong-i/iOS-ToDoList-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EA%B8%B0%EB%8A%A5%EA%B5%AC%ED%98%84</guid>
            <pubDate>Sat, 11 Mar 2023 03:37:22 GMT</pubDate>
            <description><![CDATA[<p><a href="https://fastcampus.co.kr/courese/205949/clips/">[패스트캠퍼스]</a> 30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</p>
<p>강의를 학습하며 배운 내용을 복습하며 정리하는 글입니다.</p>
<blockquote>
<p>Part. Basic 4. To Do List 앱 만들기</p>
</blockquote>
<h2 id="1-to-do-list--앱의-기능">1. To Do List  앱의 기능</h2>
<p>To Do List 앱은 할 일 목록을 관리하는 앱으로 UITableView를 이용하여 할 일 목록을 표시하고, 사용자는 UIBarButtonItems를 이용하여 추가, 수정, 삭제 등의 작업을 수행할 수 있습니다.</p>
<p>또한 UserDefaults를 이용하여 할 일 목록을 저장하고 로드하므로, 앱을 종료한 후 다시 실행하더라도 저장된 할 일 목록을 유지할 수 있습니다. </p>
<p>사용자는 Add 버튼을 눌러 할 일을 등록할 수 있으며, 등록한 할 일은 UITableView에 나타나고, 사용자는 할 일을 선택하여 완료 여부를 표시할 수 있습니다.</p>
<p>사용자는 편집 버튼을 눌러 편집 모드로 전환하여 할 일 목록을 수정하거나  삭제할 수 있습니다. 이때, done 버튼을 누르면 편집 모드가 해제되고, 수정된 할 일 목록이 저장됩니다.</p>
<h2 id="2-코드-분석">2. 코드 분석</h2>
<h3 id="2-1-view-controller-클래스">2-1. View Controller 클래스</h3>
<p>ViewController 클래스는 UIViewController를 상속받으며, UITableViewDelegate, UITableViewDataSource 프로토콜을 채택합니다. </p>
<pre><code>ViewController.swift

class ViewController: UIViewController {
    // ...
}</code></pre><h3 id="2-2-할-일task-구조체">2-2. 할 일(Task) 구조체</h3>
<p>할 일 목록의 각 항목을 관리하기 위해, Task 라는 이름의 구조체를 정의합니다. Task 구조체는 할 일의 제목과 완료 여부(done)를 저장합니다.</p>
<pre><code>Task.swift

import Foudantion

struct Task {
    var title: String
    var done: Bool
}</code></pre><h3 id="2-3-iboutlet-및-변수-정의">2-3. IBOutlet 및 변수 정의</h3>
<p>View Controller 클래스 안에는 IBOutlet으로 연결된 UI 요소와 함께, 할 일 목록을 저장하기 위한 tasks 배열과 doneButton 변수가 정의 됩니다.</p>
<p>[##<em>Image|kage@XcCyO/btr2ugMHXQa/NtVHyRGkTkCDiDNk08kAwK/img.png|CDM|1.3|{&quot;originWidth&quot;:1838,&quot;originHeight&quot;:1110,&quot;style&quot;:&quot;alignCenter&quot;,&quot;caption&quot;:&quot;인터페이스 객체 코드 연결&quot;}</em>##]</p>
<pre><code>class ViewController: UIViewController {
    @IBOutlet var editButton: UIBarButtonItem!
    @IBOutlet weak var tableView: UITableView!
    var doneButton: UIBarButtonItem?
    var tasks = [Task]() {
        didSet {
            self.saveTasks()
        }
    }
    // ...
}</code></pre><p>- editButton : 편집 버튼 입니다.</p>
<p>- tableView : 할 일 목록을 표시하는 테이블 뷰 입니다.</p>
<p>- doneButton : 편집 모드에서 done 버튼입니다.</p>
<p>- tasks : 할 일 목록을 저장하는 배열입니다.</p>
<h3 id=""></h3>
<p>2-4. viewDidLoad()</p>
<p>View Controller 클래스의 viewDidLoad() 함수에서는, doneButton 변수에 UIBarButtonItem 인스턴스를 생성하고, tableView의 dataSource와 <a href="https://zeddios.tistory.com/8">delegate</a>를 ViewController로 설정합니다. 그리고, 이전에 저장한 할 일 목록을 불러와서 tasks 배열에 저장합니다.</p>
<pre><code>override func viewDidLoad() {
    super.viewDidLoad()
    self.doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonTap))
    self.tableView.dataSource = self
    self.tableView.delegate = self
    self.loadTasks()
}</code></pre><h3 id="2-5-donebuttontap">2-5. doneButtonTap()</h3>
<p>완료(done) 버튼을 눌렀을 때 호출되며, tableView의 setEditing(_:animated:) 메서드를 이용하여 편집 모드를 해제합니다.</p>
<pre><code>@objc func doneButtonTap() {
    self.navigationItem.leftBarButtonItem = self.editButton
    self.tableView.setEditing(false, animated: true)
}</code></pre><h3 id="2-6-tapeditbutton">2-6. tapEditButton()</h3>
<p>편집 버튼을 눌렀을 때 호출되며, tableView의 setEditing(_:animated:) 메서드를 이용하여 편집 모드를 설정합니다. 만약, tasks 배열이 비어있으면 편집모드를 설정하지 않습니다. setEditing(_:animated:) 메서드는 편집모드에 집입하거나 해제할 때, 애니메이션을 보여줍니다. </p>
<pre><code>@IBAction func tapEditButton(_ sender: UIBarButtonItem) {
    guard !self.tasks.isEmpty else { return }
    self.navigationItem.leftBarButtonItem = self.doneButton
    self.tableView.setEditing(true, animated: true)
}</code></pre><h3 id="2-7-tapaddbutton">2-7. tapAddButton()</h3>
<p>할 일을 추가하기 위해, Add 버튼을 누르면 UIAlertController를 이용한 팝업창이 나타납니다. 팝업창에서 사용자가 할 일을 입력하고 등록 버튼을 누르면, tasks 배열에 새로운 할 일 객체를 추가합니다.</p>
<pre><code>@IBAction func tapAddButton(_ sender: UIBarButtonItem) {
    let alert = UIAlertController(title: &quot;할 일 등록&quot;, message: &quot;할 일을 입력해주세요.&quot;, preferredStyle: .alert)
    let registerButton =  UIAlertAction(title: &quot;등록&quot;, style: .default, handler: { [weak self] _ in
        guard let title = alert.textFields?[0].text else { return }
        let task = Task(title: title, done: false)
        self?.tasks.append(task)
        self?.tableView.reloadData()
    })
    let cancelButton = UIAlertAction(title: &quot;취소&quot;, style: .cancel, handler: nil)
    alert.addAction(registerButton)
    alert.addAction(cancelButton)
    alert.addTextField(configurationHandler: { textFiled in
        textFiled.placeholder = &quot;할 일을 입력해주세요.&quot;
    })
    self.present(alert, animated: true, completion: nil)
}</code></pre><p>- UIAlertController : UIAlertController를 이용하여 팝업창을 띄웁니다.</p>
<p>- UIAlertAction : 등록 버튼과 취소 버튼을 추가합니다.</p>
<p>- addTextField() : 팝업창 내부에 UITextField를 추가합니다.</p>
<h3 id="2-8-savetasks-loadtask">2-8. saveTasks(), loadTask()</h3>
<p>UserDefaults를 이용하여 tasks 배열을 저장하고 불러올 수 있습니다.</p>
<pre><code>func saveTasks() {
    let data = self.tasks.map {
        [
            &quot;title&quot;: $0.title,
            &quot;done&quot;: $0.done
        ]
    }
    let userDefaults = UserDefaults.standard
    userDefaults.set(data, forKey: &quot;tasks&quot;)
}

func loadTasks() {
    let userDefaults = UserDefaults.standard
    guard let data = userDefaults.object(forKey: &quot;tasks&quot;) as? [[String: Any]] else { return }
    self.tasks = data.compactMap {
        guard let title = $0[&quot;title&quot;] as? String else { return nil }
        guard let done = $0[&quot;done&quot;] as? Bool else { return nil }
        return Task(title: title, done: done)
    }
}</code></pre><p>- saveTasks(): tasks 배열을 UserDefaults에 저장합니다.</p>
<p>- loadTasks(): UserDefaults에서 tasks 배열을 불러옵니다.</p>
<p>설명 : <a href="https://jamong-ios.tistory.com/6">※ userDefault</a>, ※compactMap</p>
<h3 id="2-9-uitableviewdatasource">2-9. UITableViewDataSource</h3>
<p>UITableViewDataSource 프로토콜의 메서드를 구현하여, 할 일 목록을 표시하고 관리합니다.</p>
<pre><code>extension ViewController: UITableViewDataSource {
    // 섹션 내의 행 수 반환
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
        return self.tasks.count
    }

    // 각 셀을 구성하고 반환
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: &quot;Cell&quot;, for: indexPath)
        let task = self.tasks[indexPath.row]
        cell.textLabel?.text = task.title
        if task.done {
            cell.accessoryType = .checkmark
        } else{
            cell.accessoryType = .none
        }
        return cell
    }

    // 편집 모드에서 삭제 버튼 누를 때 호출, tasks 배열에서 해당하는 할 일 삭제
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        self.tasks.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .automatic)

        if self.tasks.isEmpty {
            self.doneButtonTap()
        }
    }

    // 각 셀의 이동 가능 여부를 반환
    func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -&gt; Bool {
        return true
    }

    // 셀을 이동할 때 호출, tasks 배열에서 해당하는 할 일의 위치를 변경
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        var tasks = self.tasks
        let task = tasks[sourceIndexPath.row]
        tasks.remove(at: sourceIndexPath.row)
        tasks.insert(task, at: destinationIndexPath.row)
        self.tasks = tasks
    }
}</code></pre><p>- tableView(_:numberOfRowsInSection:) : 섹션 내의 행 수를 반환합니다.</p>
<p>- tableView(_:cellForRowAt:) : 각 셀을 구성하고 반환합니다.</p>
<p>- tableView(_:commit:forRowAt:) : 사용자가 편집 모드에서 삭제 버튼을 누를 때 호출되며, tasks 배열에서 해당하는 할 일을 삭제합니다.</p>
<p>- tableView(_:canMoveRowAt:) : 각 셀의 이동 가능 여부를 반환합니다.</p>
<p>- tableView(_:moveRowAt:to:) : 사용자가 셀을 이동할 때 호출되며, tasks 배열에서 해당하는 할 일의 위치를 변경합니다.</p>
<h3 id="2-10-uitableviewdelegate">2-10. UITableViewDelegate</h3>
<p>UITableViewDelegate 프로토콜의 메서드를 구현하며, 할 일의 완료 여부를 표시합니다.</p>
<pre><code>extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        var task = self.tasks[indexPath.row]
        task.done = !task.done
        self.tasks[indexPath.row] = task
        self.tableView.reloadRows(at: [indexPath], with: .automatic)
    }
}</code></pre><p>- tableView(_:didSelectRowAt:) : 셀을 선택하면 호출되면, 해당하는 할 일의 완료 여부를 토글합니다.</p>
<p>설명 : ※Delegate</p>
<p>설명 부분에 하이퍼링크가 안 달려있는 곳은 글 생성 후 연결 해주려고합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스 Lv.0] 가까운 수]]></title>
            <link>https://velog.io/@jamong-i/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.0-%EA%B0%80%EA%B9%8C%EC%9A%B4-%EC%88%98</link>
            <guid>https://velog.io/@jamong-i/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.0-%EA%B0%80%EA%B9%8C%EC%9A%B4-%EC%88%98</guid>
            <pubDate>Mon, 06 Mar 2023 02:38:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>코딩테스트 입문 [Lv. 0]<br>2023.02.27</p>
</blockquote>
<hr>
<h4 id="문제-설명"><strong>문제 설명</strong></h4>
<p>정수 배열 <code>array</code>와 정수 <code>n</code>이 매개변수로 주어질 때, <code>array</code>에 들어있는 정수 중 <code>n</code>과 가장 가까운 수를 return 하도록 solution 함수를 완성해주세요.</p>
<h4 id="제한-사항"><strong>제한 사항</strong></h4>
<ol>
<li><code>array</code> 의 길이는 1보다 크거나 같고 100보다 작거나 같다.</li>
<li><code>array</code> 의 원소는 1보다 크거나 같고 100보다 작거나 같다.</li>
<li><code>n</code> 의 크기는 1보다 크거나 같고 100보다 작거나 같다.</li>
<li>가장 가까운 수가 여러 개일 경우 더 작은 수를 return 한다.</li>
</ol>
<hr>
<h4 id="로직-설계"><strong>로직 설계</strong></h4>
<p>1. 배열 array에 n을 추가하고 정렬한다.</p>
<p>2. 배열 array에서 n의 인덱스 번호를 찾는다.</p>
<p>3. 배열 array에서 n의 인덱스 번호가 end인지 아닌지 확인하고 end이면 n의 인덱스 보다 하나 앞에 있는 값을 꺼낸다.</p>
<p>4. end가 아니면 n의 인덱스 보다 하나 앞, 뒤의 값을 꺼내 n과 가까운 수를 찾아낸다.</p>
<h4 id="코드-구현"><strong>코드 구현</strong></h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스 Lv.0] 369 게임]]></title>
            <link>https://velog.io/@jamong-i/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.0-369-%EA%B2%8C%EC%9E%84</link>
            <guid>https://velog.io/@jamong-i/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.0-369-%EA%B2%8C%EC%9E%84</guid>
            <pubDate>Mon, 06 Mar 2023 02:38:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>코딩테스트 입문 [Lv. 0]<br>2023.02.27</p>
</blockquote>
<hr>
<h4 id="문제-설명"><strong>문제 설명</strong></h4>
<p>머쓱이는 친구들과 369게임을 하고 있습니다. 369게임은 1부터 숫자를 하나씩 대며 3, 6, 9가 들어가는 숫자는 숫자 대신 3, 6, 9의 개수만큼 박수를 치는 게임입니다. 머쓱이가 말해야하는 숫자 <code>order</code>가 매개변수로 주어질 때,머쓱이가 쳐야할 박수 횟수를 retrun 하도록 solution 함수를 완성해 보세요. </p>
<h4 id="제한-사항"><strong>제한 사항</strong></h4>
<ol>
<li><code>order</code> 의 크기는 1보다 크거나 같고 1,000,000보다 작거나 같다. </li>
</ol>
<hr>
<h4 id="로직-설계"><strong>로직 설계</strong></h4>
<p>1. Charater 타입 배열을 생성 하고, 입력받을 order의 숫자를 String으로 타입 변환하여 배열에 추가한다.</p>
<p>2. 배열의 값을 하나씩 빼서 Int형으로 변환하고 3으로 나누어 떨어지고 배열의 값이 0이 아닐 때 count를 1씩 추가한다.</p>
<p>3. count 값을 return 해준다. </p>
<h4 id="코드-구현"><strong>코드 구현</strong></h4>
<pre><code>import Foundation

func solution(_ order:Int) -&gt; Int {
    // 제한 사항
    guard 1...1000000 ~= order else { return 0 }

    // 문제 풀이
    var arrayList: [Character] = []
    var count: Int = 0

    for i in String(order) {
        arrayList.append(i)
    }

    for i in arrayList{
        if i.wholeNumberValue! % 3 == 0 &amp;&amp; i.wholeNumberValue! != 0 {
            count += 1
        }
    }

    return count
}</code></pre><p><strong>[제한사항]</strong></p>
<p>guard 문을 이용하여 order의 크기를 잡고, 범위에 충족하지 않으면 0을 return 하는 코드를 작성하였다.</p>
<p><strong>[문제 풀이]</strong></p>
<p>1. order 값을 문자로 담아줄 arrayList 배열 변수와 return해 줄 count 변수를 생성하였다.</p>
<p>2. for문을 이용하여 정수타입 order을 문자열로 변환하여 문자 하나씩 arrayList에 추가하였다.</p>
<p>3. arrayList의 문자들을 하나씩 가져와 wholeNumberValue과 암시적 언랩핑(Implicitly Unwrapping)으로 정수타입으로 변환하고, 3으로 나누었을 때 나머지가 0이고 arrayList의 문자가 0이 아닐 때 count의 숫자를 1씩 증가하게 하였다.</p>
<p>4. count 값을 return 했다.</p>
<p><a href="https://jamong-ios.tistory.com/2">wholeNumberValue 설명</a></p>
<p>wholeNumberValue 대신에 Int(String(i))! 로 쓸 수 있다.</p>
<p><strong>[풀면서 문제 있었던 부분]</strong></p>
<p>wholeNumberValue를 찾아보기 전에 Character 타입을 Int형 타입으로 바로 캐스팅하면 될 줄 알았다.</p>
<p>그러나 Character 문자타입에서 Int 정수형 타입으로 바로 안되고 String 문자열로 캐스팅 하고 난 뒤에 Int형으로 캐스팅이 가능했다.</p>
<h4 id="다른-코드"><strong>다른 코드</strong> </h4>
<p>다른 사람들이 구현한 코드를 가져와 분석해 보았다.</p>
<pre><code>import Foundation

func solution2(_ order:Int) -&gt; Int {
    return String(order).filter { &quot;369&quot;.contains($0) }.count
}</code></pre><p>이 코드 같은 경우에는</p>
<p>1. 정수 타입 order 값을 문자열 타입으로 변환하여 문자열을 하나씩 3, 6, 9가 들어있는지 확인하고 있다면 filter로 걸러내 저장한다. </p>
<p>2. filter로 걸러진 문자의 갯수를 count로 return 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] wholeNumberValue 란?]]></title>
            <link>https://velog.io/@jamong-i/Swift-wholeNumberValue-%EB%9E%80</link>
            <guid>https://velog.io/@jamong-i/Swift-wholeNumberValue-%EB%9E%80</guid>
            <pubDate>Mon, 06 Mar 2023 02:37:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>문자/문자열을 정수로 변환하는 법<br>2023.02.27</p>
</blockquote>
<hr>
<h4 id="문자-또는-문자열을-정수형으로-변환하는-방법"><strong>문자 또는 문자열을 정수형으로 변환하는 방법</strong></h4>
<p>프로그래머스 369 게임을 풀면서 Character에서 Int으로 변환하면서 방법을 찾아 보았다.</p>
<h4 id="시도한-방법"><strong>시도한 방법</strong> </h4>
<pre><code>var chr: Character = &quot;4&quot;
var num: Int

// 잘못된 방법 (error: No exact matches in call to initializer)
num = Int(chr)!

// 가능한 방법
num = Int(Character(chr))!</code></pre><p>Character 타입에서 Int 타입으로 바로 캐스팅이 안되고 Character -&gt; String -&gt; Int 타입으로 캐스팅이 가능한 것 을 알게 되었다.</p>
<p>이 방법을 찾기 전에 문자에서 정수형으로 변환하는 방법을 찾은 것이 wholeNumberValue 이다. (변환하면 Optional이기 때문에 Optional unwrapping(!)을 해줘야 한다.) </p>
<h4 id="wholenumbervalue"><strong>wholeNumberValue</strong></h4>
<p>[##<em>Image|kage@bbysGd/btr1ddp10sh/7qc9yVkEGYbyVYwgi0u4UK/img.png|CDM|1.3|{&quot;originWidth&quot;:1590,&quot;originHeight&quot;:1502,&quot;style&quot;:&quot;alignCenter&quot;}</em>##]</p>
<p>공식 문서의 wholeNumberValue의 정의는 문자가 의미하는 숫자 값(wholeNumberValue)을 반환해 준다. </p>
<p>Declaration(선언)에서 wholeNumberValue는 optional 형태의 Int로 되어 있고, 공식문서의 예시로 &quot;4&quot;, &quot;④&quot;, &quot;万&quot;, &quot;a&quot;와 같은 문자들은 wholeNumberValue를 이용하면 Optional(4)로 넘겨주는 것을 확인 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] 계산기 앱 만들기 기능구현]]></title>
            <link>https://velog.io/@jamong-i/iOS-%EA%B3%84%EC%82%B0%EA%B8%B0-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EA%B8%B0%EB%8A%A5%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@jamong-i/iOS-%EA%B3%84%EC%82%B0%EA%B8%B0-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EA%B8%B0%EB%8A%A5%EA%B5%AC%ED%98%84</guid>
            <pubDate>Mon, 06 Mar 2023 02:36:41 GMT</pubDate>
            <description><![CDATA[<p><a href="https://fastcampus.co.kr/courese/205949/clips/">[패스트캠퍼스]</a> 30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</p>
<p>강의를 학습하며 배운 내용을 복습하며 정리하는 글입니다.</p>
<blockquote>
<p>Part. Basic 3. 계산기 앱 만들기</p>
</blockquote>
<h2 id="mvc-model">MVC Model</h2>
<p>아이폰의 기본 계산기 앱의 디자인 따라하며 작업하였고, 하나의 화면만 전달하기 때문에 MVC모델로 하나의 ViewController를 사용하였습니다.</p>
<h4 id="연산자-열거형">연산자 열거형</h4>
<p>일반적인 계산기를 구현하기 위해서는 사칙연산이 필요합니다.</p>
<p>따라서 사칙연산을 하는 연산자 함수를 만들기 전에 가독성을 위해 연산자 데이터를 열거형으로 정의 했습니다.</p>
<pre><code>// 연산자 열거형
enum Operation {
    case Add
    case Subtract
    case Divide
    case Multiply
    case unknown
}</code></pre><h4 id="기본-변수-선언">기본 변수 선언</h4>
<p>계산화는 과정에서 여러개의 값들을 저장하고 있어야 하기에 기본 변수를 선언해주는 과정입니다.</p>
<pre><code>// 값 변수 선언
var displayNumber: String = &quot;&quot;
var firstOperand: String = &quot;&quot;
var secondOperand: String = &quot;&quot;
var result = &quot;&quot;

// 연산자 값 변수 선언
var currentOperation: Operation = .unknown</code></pre><p>displayNumber은 화면에 숫자를 알려주는 변수이며 firstOpeand와 secondOperand는 연산하기 전 첫번째 수와 두번째 수를 담는 변수입니다. result는 결과 값 변수, currentOperation은 Operation의 열거형의 연산자 데이터를 가져오는 변수 입니다.</p>
<p>변수들에 들어가는 데이터의 흐름은 사칙연순 구현에서 다루겠습니다.</p>
<h4 id="연산자-함수">연산자 함수</h4>
<p>연산자 함수에는 더하기(Add), 빼기(Subtract), 곱하기(Multiply), 나누기(Divide)가 있습니다.</p>
<p>연산자 함수의 논리적인 흐름은 다음과 같습니다.</p>
<ol>
<li>사용자가 숫자 버튼을 누르면 숫자 버튼에 해당하는 숫자를 입력창에 추가합니다.</li>
<li>사용자가 연산자 버튼을 누르면 현재까지 입력된 숫자를 가져와서 임시로 저장합니다.</li>
<li>다시 숫자 버튼을 눌러 새로운 숫자를 입력합니다.</li>
<li>다시 연산자 버튼을 눌러 이전에 저장한 숫자와 새로 입력된 숫자를 연산합니다.</li>
<li>이전에 저장한 숫자와 연산 결과를 입력창에 표시합니다.</li>
</ol>
<p>위의 논리적인 흐름을 바탕으로 Swift로 계산기의 연산자 함수를 구현할 수 있습니다.</p>
<p>구현한 코드는 다음과 같습니다.</p>
<pre><code>// MARK: - Operation Function

func operation(_ operation: Operation) {
    if self.currentOperation != .unknown {
        if !self.displayNumber.isEmpty {
            self.secondOperand = self.displayNumber
            self.displayNumber = &quot;&quot;

            guard let firstOperand = Double(self.firstOperand) else { return }
            guard let secondOperand = Double(self.secondOperand) else { return }

            switch self.currentOperation {
            case .Add:
                self.result = &quot;\(firstOperand + secondOperand)&quot;

            case .Subtract:
                self.result = &quot;\(firstOperand + secondOperand)&quot;

            case .Multiply:
                self.result = &quot;\(firstOperand * secondOperand)&quot;

            case .Divide:
                self.result = &quot;\(firstOperand / secondOperand)&quot;

            default:
                break
            }

            if let result = Double(self.result), result.truncatingRemainder(dividingBy: 1) == 0 {
                self.result = &quot;\(Int(result))&quot;
            }

            self.firstOperand = result
            self.numberOutputLabel.text = self.result
        }

        self.currentOperation = operation
    } else {
        self.firstOperand = self.displayNumber
        self.currentOperation = operation
        self.displayNumber = &quot;&quot;
    }
}</code></pre><p>구현한 소스코드를 예시를 들어 설명해 보겠습니다.</p>
<p>예) 20 + 30 = 을 계산한다고 가정 할 때</p>
<p>1. 20을 입력하고 + 버튼을 눌렀을 때</p>
<p>currentOperation은 초기값 unknown 이라서 첫 번째 if-else 구문에서 else로 넘어가게 됩니다.</p>
<p>else구문을 넘어가게 되어 firstOperand에 displayNumber값인 20, currentOperation은 Add가 저장되고 displayNumber는 초기화 됩니다.</p>
<p>2. 30과 연산자버튼(등호포함)이 입력된다고 가정 할 때</p>
<p>currentOperation은 Add가 저장되어있기 때문에 <code>if self.currentOperation != .unknown</code>구문으로 넘어가게 됩니다.</p>
<p>secondOperand에 displayNumber값인 30이 저장되고, displayNumber는 초기화 됩니다.</p>
<p>Double타입인 firstOperand와 secondOperand에 String타입인 firstOpeand와 secondOperand 값이 넘어가고 Add 연산을 하고 result에 저장합니다.</p>
<p>그 후 result값을 firstOperand에 저장하며 화면 Label에 뿌려준다. 마지막으로 2번째 연산자 버튼의 값을 currentOperation에 저장합니다.</p>
<h4 id="ibaction-button-정리">IBAction Button 정리</h4>
<pre><code>// MARK: - IBAtion [/]Button

@IBAction func tapDivideButton(_ sender: UIButton) {
    self.operation(.Divide)
}

// MARK: - IBAtion [*]Button

@IBAction func tapMultiplyButton(_ sender: UIButton) {
    self.operation(.Multiply)
}

// MARK: - IBAtion [-]Button

@IBAction func tapSubtractButton(_ sender: UIButton) {
    self.operation(.Subtract)
}

// MARK: - IBAtion [+]Button

@IBAction func tapAddButton(_ sender: UIButton) {
    self.operation(.Add)
}

// MARK: - IBAtion [=]Button

@IBAction func tapEqualButton(_ sender: UIButton) {
    self.operation(self.currentOperation)
}</code></pre><p>[ + ], [ - ], [ * ], [ / ], [ = ]연산자 버튼은 operation 함수를 사용하고 currentOperation 값을 넘김으로서 연산을 수행하게 하였다.</p>
<p>[ = ] 버튼은 전에 사용했던 currentOperation을 넘겨준다.</p>
<p>추가로 구현한 기능은 [ 숫자 ], [ AC ], [ +/- ], [ % ], [ . ] 가 있습니다.</p>
<pre><code>// MARK: - IBAction NummberButton

@IBAction func tapNumberButton(_ sender: UIButton) {
    guard let numberValue = sender.titleLabel?.text else { return }
    if self.displayNumber.count &lt; 9 {
        self.displayNumber += numberValue
        self.numberOutputLabel.text = self.displayNumber
    }
}</code></pre><p>[ 숫자 ] 버튼은 1부터 9까지의 숫자 버튼이랑 연결 하였습니다.</p>
<p>1. 숫자버튼이 눌러지면 numberValue에 눌러진 버튼의 titleLabel의 text값을 저장합니다.</p>
<p>2. 숫자는 9개까지만 저장할 수 있게 displayNumber.count가 9보다 작을 때만 숫자를 저장하게 하였고</p>
<p>3. 숫자가 들어올 때 마다 displayNumber에 값을 저장하고, 화면(numberOutputLabel.text)에 나오게 displayNumber값을 넘겨주었습니다.</p>
<pre><code>// MARK: - IBAtion [AC]Button

@IBAction func tapClearButton(_ sender: UIButton) {
    self.displayNumber = &quot;&quot;
    self.firstOperand = &quot;&quot;
    self.secondOperand = &quot;&quot;
    self.result = &quot;&quot;
    self.currentOperation = .unknown
    self.numberOutputLabel.text = &quot;0&quot;
}</code></pre><p>[ AC ] 버튼은 초기화 버튼 입니다.</p>
<p>AC 버튼을 누르게 되면 모든 값들이 초기화 됩니다.</p>
<pre><code>// MARK: - IBAtion [+/-]Button

@IBAction func tapPostiveToNegativeButton(_ sender: UIButton) {
    if !self.displayNumber.contains(&quot;-&quot;) {
        if self.displayNumber.isEmpty {
            self.displayNumber = &quot;-0&quot;
            self.numberOutputLabel.text = displayNumber
        } else if !self.displayNumber.isEmpty {
            self.displayNumber = &quot;-&quot; + self.displayNumber
            self.numberOutputLabel.text = displayNumber
        }
    } else if self.displayNumber.contains(&quot;-&quot;) {
        self.displayNumber = self.displayNumber.components(separatedBy: [&quot;-&quot;]).joined()
        self.numberOutputLabel.text = self.displayNumber
    }
}</code></pre><p>[ +/- ] 버튼은 양수는 음수로 음수는 양수로 바꿔주는 버튼 입니다.</p>
<p>1. +/- 버튼을 눌렀을 때 양수 또는 0일 때 displayNumber값이 없으면 -0으로 displayNumber값이 있으면 저장된 값 앞에 -를 붙이고 화면에 뿌려줍니다.</p>
<p>2. 음수 또는 -0일 때는 compoents의 특성을 이용하여 &quot;-&quot;를 없애고 화면에 뿌려줍니다.</p>
<pre><code>// MARK: - IBAtion [%]Button

@IBAction func tapPercentButton(_ sender: UIButton) {
    if !self.displayNumber.isEmpty {
        guard let percentOperand = Double(self.displayNumber) else { return }
        self.result = &quot;\(percentOperand * 0.01)&quot;
        self.displayNumber = self.result
        self.numberOutputLabel.text = self.displayNumber
    }
}</code></pre><p>[ % ] 버튼은 퍼센트 버튼 입니다.</p>
<p>% 버튼을 눌렀을 때 displayNumber값이 없으면 동작하지 않고, 값이 있을 경우 0.01을 곱하여 퍼센트로 만들어 줍니다. </p>
<pre><code>// MARK: - IBAtion [.]Button

@IBAction func tapDotButton(_ sender: UIButton) {
    if self.displayNumber.count &lt; 8, !self.displayNumber.contains(&quot;.&quot;) {
        self.displayNumber += self.displayNumber.isEmpty ? &quot;0.&quot; : &quot;.&quot;
        self.numberOutputLabel.text = self.displayNumber
    }
}</code></pre><p>[ . ] 버튼은 소수점 버튼 입니다.</p>
<p>. 버튼을 눌렀을 때 displayNumber의 수가 8자리를 넘지 않고, 소수점이 없을 때 displayNumber가 없으면 0.1을 주고 있으면 displayNumber값 뒤에 .을 붙여줍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] UIStackView]]></title>
            <link>https://velog.io/@jamong-i/iOS-UIStackView</link>
            <guid>https://velog.io/@jamong-i/iOS-UIStackView</guid>
            <pubDate>Mon, 30 Jan 2023 07:11:42 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 3. 계산기 앱 만들기</p>
</blockquote>
<h2 id="uistackview">UIStackView</h2>
<p>열 또는 행에 View 들의 묶음을 배치할 수 있는 간소화된 인터페이스이다.</p>
<p>UIStackView는 AutoLayout을 이용하여 디바이스의 스크린 사이즈나 혹은 어떠한 변화에 맞쳐서 동적인 UI를 구성할 수 있다. 복잡한 UI를 구성하는데 있어서 일일히 AutoLayout 제약조건을 설정하면 제약조건이 많아지고 복잡해져서 관리하기 힘들어지며 때때로 원하는데로 UI구성이 안될 수도 있다.</p>
<p>그렇기에 그에 대응하는 UIStackView를 사용하게되면 제약조건을 많이 설정하지 않아도 쉽게 UI구성이 가능해진다.</p>
<h2 id="uistackview-attribute">UIStackView Attribute</h2>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/99ad4c17-8e03-4fd8-aabe-11ec9b3061da/image.png" alt=""></p>
<p>UIStackView에는 다양한 속성이 있다.</p>
<p>스토리보드에서 스택 뷰를 선택하면 속성 인스펙터에서 다양한 속성을 설정 할 수 있다.</p>
<p>스택 뷰 안에 있는 서브뷰들의 포지션과 사이즈를 맞추기 위해 속성들을 세팅할 수 있다.</p>
<h3 id="axis">Axis</h3>
<p>스택 뷰의 가로 또는 세로의 방향을 결정 할 수 있다.</p>
<ul>
<li>Vertical Stack View: 세로 방향으로 서브 뷰들이 정렬된다.</li>
<li>Horizontal Stack View: 가로 방향으로 서브 뷰들이 정렬된다.</li>
</ul>
<h3 id="alignment">Alignment</h3>
<p>스택 뷰의 서브 뷰들을 어떤식으로 정렬할지 결정하는 속성이다.</p>
<ul>
<li><p>Fill: 서브 뷰들을 너비를 스택 뷰에 맞게 늘린다.</p>
</li>
<li><p>Leading: 서브 뷰들이 스택뷰의 왼쪽에 정렬된다.</p>
</li>
</ul>
<h4 id="horizontal-stack-view">Horizontal Stack View</h4>
<ul>
<li><p>Top: 서브 뷰들이 스택 뷰의 위쪽으로 정렬된다.</p>
</li>
<li><p>Bottom: 서브 뷰들이 스택 뷰의 아래쪽에 맞춰서 정렬된다.</p>
</li>
<li><p>First Baseline: 서브 뷰들의 First Baseline에 맞춰 스택 뷰가 서브 뷰들을 정렬한다.</p>
</li>
<li><p>Last Baseline: 서브 뷰들이 Last Baseline에 맞춰 스택 뷰가 서브 뷰들을 정렬한다.</p>
</li>
<li><p>Center: 서브 뷰들의 센터를 스택 뷰의 센터에 맞춰 정렬된다.</p>
</li>
</ul>
<h4 id="vertical-stack-view">Vertical Stack View</h4>
<ul>
<li>Trailing: 서브 뷰들을 스택 뷰의 오른쪽에 맞춰서 정렬한다.</li>
</ul>
<h3 id="distribution">Distribution</h3>
<p>스택 뷰안에 들어가는 뷰들의 사이즈를 어떻게 분배할지 설정하는 속성이다.</p>
<ul>
<li><p>Fill: 스택 뷰의 방향에 따라 가능한 공간을 모두 채우기 위해 서브 뷰들의 사이즈를 재조정한다. 
스택 뷰안에 들어있는 서브 뷰들이 스택 뷰 크기를 초과하게 되면 각 뷰의 컴프레션 레지스턴스 프라이얼리티(Compression Resistance priority) 각 뷰의 크기를 감소시키고 서브 뷰들이 스택 뷰의 크기에 미달하게 되면 각 뷰의 허깅 프라이얼리티(Hugging priority)에 따라 각 서브 뷰의 크기를 늘려 스택 뷰를 꽉 채운다.
따라서 초과할 때는 Compression Resistance priority를 비교해 우선 순위가 낮은 서브 뷰 부터 크기를 감소시키고 미달 할 때는 Hugging priority를 비교해 우선 순위가 높은 서브 뷰 부터 크기를 증가시킨다.</p>
</li>
<li><p>Fill Equally: 스택 뷰의 크기에 맞게 서브 뷰들이 모두 동일한 크기로 조정됩니다.</p>
</li>
<li><p>Fill Proportionally: 스택 뷰의 방향에 따라 서브 뷰가 가지고 있던 크기에 비례하여 공간을 차지하도록 만들어진다. Fill과 다른점으로는 서브 뷰에 width값에 따라 간다.</p>
</li>
<li><p>Equal Sapcing: 서브 뷰들의 사이 공간을 균등하게 배치하는 옵션이다.</p>
</li>
<li><p>Equal Centering: 서브 뷰들의 센터와 센터간의 길이를 동일하게 맞추는 옵션이다.</p>
</li>
</ul>
<h3 id="spacing">Spacing</h3>
<p>스택 뷰 안에 들어가는 뷰들의 간격을 조정한다.</p>
<p>Spacing 값에 따라 서브 뷰들의 간격이 넓어지거나 좁아지게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] LED 전광판 앱 만들기 (2 / 2)]]></title>
            <link>https://velog.io/@jamong-i/iOS-LED-%EC%A0%84%EA%B4%91%ED%8C%90-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-1-2</link>
            <guid>https://velog.io/@jamong-i/iOS-LED-%EC%A0%84%EA%B4%91%ED%8C%90-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-1-2</guid>
            <pubDate>Thu, 26 Jan 2023 08:06:52 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 2. LED 전광판 앱 만들기</p>
</blockquote>
<h3 id="코드-구현">코드 구현</h3>
<p>먼저 설정 뷰의 SettingViewController에 오브젝트들을 연결 해주었고, 설정 값을 ViewController로 보내주기 위해 델리게이트를 생성하였다.</p>
<pre><code class="language-swift">SettingViewController.swift

import UIKit

// 설정 및 변경값(텍스트, 텍스트 색상, 배경 색상) 넘겨주는 델리게이트 생성
protocol LEDBoardSettingDelegate: AnyObject {
    func changedSetting(text: String?, textColor: UIColor, backgroundColor: UIColor)
}

class SettingViewController: UIViewController {

    // Text Field
    @IBOutlet weak var textField: UITextField!

    // Text Colors
    @IBOutlet weak var greenButton: UIButton!
    @IBOutlet weak var purpleButton: UIButton!
    @IBOutlet weak var yellowButton: UIButton!

    // Background Colors
    @IBOutlet weak var blackButton: UIButton!
    @IBOutlet weak var blueButton: UIButton!
    @IBOutlet weak var orangeButton: UIButton!

    // 초기 세팅 값 지정
    weak var delegate: LEDBoardSettingDelegate?
    var ledText: String?
    var textColor: UIColor = .yellow
    var backgroundColor: UIColor = .black </code></pre>
<p>이후 초기 세팅 값 지정, 설정 뷰 저장값 유지 기능, 색상 버튼 선택 기능 및 처리, 저장 버튼 기능을 구현하였다.</p>
<pre><code class="language-swift">    // 메모리 로드 호출 
    override func viewDidLoad() {
        super.viewDidLoad()
        // 설정 값 로드 
        self.configureView()
    }

    // 설정 값 초기화 방지 및 저장 기능 (설정 저장 후 LED 뷰에서 설정 뷰로 갔을 때 기존 값 유지)
    private func configureView() {
        if let ledText = self.ledText {
            self.textField.text = ledText
        }
        self.changeTextColor(

    // 글자 색상 버튼 눌림 기능
    @IBAction func tapTextColorButton(_ sender: UIButton) {
        if sender == self.yellowButton {
            self.changeTextColor(color: .yellow)
            self.textColor = .yellow
        } else if sender == self.purpleButton {
            self.changeTextColor(color: .purple)
        }
    }

    // 배경 색상 버튼 눌림 기능
    @IBAction func tpaBackgroundColorButton(_ sender: UIButton) {
        if sender == self.blackButton {
            self.changeBackgroundColorButton(color: .black)
            self.backgroundColor = .black
        } else if sender == self.blueButton {
            self.changeBackgroundColorButton(color: .blue)
            self.backgroundColor = .blue
        } else if sender == self.orangeButton {
            self.changeBackgroundColorButton(color: .orange)
            self.backgroundColor = .orange
        }
    }

    // 글자 색상 버튼 활성화 처리 (삼항연산자 Alpha값 조정)
    private func changeTextColor(color: UIColor) {
        self.yellowButton.alpha = color == UIColor.yellow ? 1 : 0.2
        self.purpleButton.alpha = color == UIColor.purple ? 1 : 0.2
        self.greenButton.alpha = color == UIColor.green ? 1 : 0.2
    }

    // 배경 색상 버튼 활성화 처리 (삼항연산자 Alpha값 조정)
    private func changeBackgroundColorButton(color: UIColor){
        self.blackButton.alpha = color == UIColor.black ? 1 : 0.2
        self.blueButton.alpha = color == UIColor.blue ? 1 : 0.2
        self.orangeButton.alpha = color == UIColor.orange ? 1 : 0.2
    }

    // 저장 버튼 기능 (저장 및 색상 보내기)
    @IBAction func tapSaveButton(_ sender: UIButton) {
        self.delegate?.changedSetting(
            text: textField.text,
            textColor: textColor,
            backgroundColor: backgroundColor
        )
        // 이전 뷰로 이동
        self.navigationController?.popViewController(animated: true)
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] LED 전광판 앱 만들기 (1 / 2)]]></title>
            <link>https://velog.io/@jamong-i/iOS-LED-%EC%A0%84%EA%B4%91%ED%8C%90-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@jamong-i/iOS-LED-%EC%A0%84%EA%B4%91%ED%8C%90-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 25 Jan 2023 12:59:13 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 2. LED 전광판 앱 만들기</p>
</blockquote>
<h3 id="stroyboard-desgine-제약조건">Stroyboard Desgine (제약조건)</h3>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/3293392c-ed68-4a54-9156-8ce162867d6c/image.png" alt=""></p>
<p>Navigation Controller를 추가하여 진입점으로 변경하였고 View Controller를 Root View Controller로 설정하였다.</p>
<h4 id="view-controller">View Controller</h4>
<p>Label 아이템 추가 후 가운데 정렬</p>
<ul>
<li>Horizontally in Container</li>
<li>Vertically in Container</li>
</ul>
<p>Navigation Bar에 Bar Button Item을 추가하고 타이틀을 설정으로 변경했다.</p>
<p>Bar Button(설정)은 Action Seaguway로 새로운 View Controller에 show로 설정했다.</p>
<h4 id="setting-view-controller">Setting View Controller</h4>
<p>새로운 View Controller를 만들어서 Class를 SettingViewController로 연결하고</p>
<p>Navigation Bar 타이틀을 설정으로 변경하고 스택 뷰로 오브젝트 아이템들을 배치했다.</p>
<hr>
<p>*<em>Stack View (전광판 글자) *</em></p>
<ul>
<li>Spacing: 15</li>
<li>Aling Top: 24</li>
<li>Aling Traling: 24</li>
<li>Algin Leading: 24</li>
</ul>
<p>Obejct Item: Label (전광판에 표시할 글자)</p>
<p>Object Item: Text Field (Placeholder: 전광판에 표시할 글자)</p>
<ul>
<li>Aling Traling: 0</li>
<li>Algin Leading: 0</li>
</ul>
<hr>
<p>*<em>Stack View (텍스트 색상) *</em></p>
<ul>
<li>Spacing: 15</li>
<li>Aling Top: 35</li>
<li>Aling Traling: 24</li>
<li>Algin Leading: 24</li>
</ul>
<p>Obejct Item: Label (텍스트 색상 설정)</p>
<p><strong>Stack View(텍스트 색상 버튼 모음)</strong></p>
<p>Object Item: Button
-&gt; image(Assets) : yellow_circle</p>
<p>Object Item: Button
-&gt; image(Assets) : purple_circle</p>
<p>Object Item: Button
-&gt; image(Assets) : green_circle</p>
<hr>
<p>*<em>Stack View (배경 색상) *</em></p>
<ul>
<li>Spacing: 15</li>
<li>Aling Top: 35</li>
<li>Aling Traling: 24</li>
<li>Algin Leading: 24</li>
</ul>
<p>Obejct Item: Label (배경 색상 설정)</p>
<p><strong>Stack View(배경 색상 버튼 모음)</strong></p>
<p>Object Item: Button
-&gt; image(Assets) : black_circle</p>
<p>Object Item: Button
-&gt; image(Assets) : blue_circle</p>
<p>Object Item: Button
-&gt; image(Assets) : orange_circle</p>
<hr>
<h4 id="button-저장">Button (저장)</h4>
<p>가운데 정렬</p>
<ul>
<li>Center Horizontally -&gt; Stack View(배경 색상)</li>
<li>Aling Top: 24</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] ViewController Life Cycle]]></title>
            <link>https://velog.io/@jamong-i/iOS-ViewController-Life-Cycle</link>
            <guid>https://velog.io/@jamong-i/iOS-ViewController-Life-Cycle</guid>
            <pubDate>Wed, 25 Jan 2023 07:34:59 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 2. LED 전광판 앱 만들기</p>
</blockquote>
<p>보통 앱을 만들때 단일 스크린 위에서 여러개의 뷰 컨트롤러들로 화면전환이 가능전환 앱을 만든다.</p>
<p>앱이 복잡해질수록 컨트롤러들을 잘 관리해줘야하는데 알맞은 타이밍에 내가 원하는 코드를 작성하는 것이 중요하다.</p>
<p>이를 위해서 ViewController Life Cycle를 이해해야 한다.</p>
<h3 id="viewcontroller-life-cycle">ViewController Life Cycle</h3>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/674a8220-22e7-4c25-a5f1-bfdb3345ad1b/image.png" alt=""></p>
<p>UIViewController의 객체에는 View 객체를 관리하는 메소드들이 정의되어있다.
이 메소드들은 각자 자신들이 불러져야 하는 타이밍일때 iOS 시스템에 의해 자동적으로 호출이 된다.</p>
<p>UIViewController의 하이클래스를 생성할 때 UIViewController 정의된 이 메소드들을 오버라이드하여 라이프사이클 상황에 맞게 적절한 로직들을 메소드에 추가할 수 있다.</p>
<p>시스템이 어떤 메소드를 언제 호출해야하는지 먼저 이해해야지만 그 시점에 잘 맞춰 UI의 변화나 데이터 변화를 잘 처리할 수 있다.</p>
<p>뷰가 보여지는 사항은 크게 4가지로 분류할 수 있다.</p>
<ul>
<li>Appearing: 뷰가 화면에 나타나는 중</li>
<li>Appeard: 뷰가 화면에 나타나는게 완료 된 상태</li>
<li>Disappearing: 뷰가 화면에서 사라지는 중</li>
<li>Disappeared: 뷰가 화면에서 사라진 상태</li>
</ul>
<h3 id="viewcontroller-life-cycle-method">ViewController Life Cycle Method</h3>
<h4 id="viewdidload">ViewDidLoad()</h4>
<ul>
<li>뷰 컨트롤러의 모든 뷰들이 메모리에 로드됐을 때 호출</li>
<li>메모리에 처음 로드될 때 한번만 호출</li>
<li>보통 딱 한번 호출될 행위들을 이 메소드 안에 정의 함</li>
<li>뷰와 관련된 추가적인 초기화 작업, 네트워크 호출과 같은 1회성 작업을 정의 함</li>
</ul>
<h4 id="viewwillappear">viewWillAppear()</h4>
<ul>
<li>뷰가 뷰 계층에 추가되고, 화면에 보이기 직전에 매 번 호출 
(다른 뷰로 갔다 돌아오면 재호출 된다는 의미)</li>
<li>다른 뷰로 이동했다가 돌아오면 재호출</li>
<li>뷰와 관련된 추가적인 초기화 작업</li>
</ul>
<h4 id="viewdidappear">viewDidAppear()</h4>
<ul>
<li>뷰 컨트롤러의 뷰가 뷰 계층에 추가된 후 호출된다.</li>
<li>뷰를 나타날 때 필요한 추가 작업</li>
<li>애니메이션을 시작하는 작업</li>
</ul>
<h4 id="viewwilldisappear">viewWillDisappear()</h4>
<ul>
<li>뷰 컨트롤러의 뷰가 뷰 계층에서 사라지기 전에 호출된다.</li>
<li>뷰가 생성된 뒤 작업한 내용을 되돌리는 작업</li>
<li>최종적으로 데이터를 저장하는 작업</li>
</ul>
<h4 id="viewdiddisappear">viewDidDisappear()</h4>
<ul>
<li>뷰 컨트롤러의 뷰가 뷰 계층에서 사라진 뒤에 호출</li>
<li>뷰가 사라지는 것과 관련된 추가 작업</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] 화면 전환]]></title>
            <link>https://velog.io/@jamong-i/iOS-%ED%99%94%EB%A9%B4-%EC%A0%84%ED%99%98</link>
            <guid>https://velog.io/@jamong-i/iOS-%ED%99%94%EB%A9%B4-%EC%A0%84%ED%99%98</guid>
            <pubDate>Tue, 24 Jan 2023 06:52:35 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 2. LED 전광판 앱 만들기</p>
</blockquote>
<h3 id="화면-전환">화면 전환</h3>
<p>화면 전환을 위한 방법으로는 크게 두가지가 있다.</p>
<ul>
<li>소스코드를 통해 전환하는 방식</li>
<li>Stroyboard를 통해 전환하는 방식</li>
</ul>
<p>나머지 작게는 4가지가 있다.</p>
<ul>
<li>View Controller의 View 위에 다른 View를 가져와 바꿔치기</li>
<li>View Controller에서 다른 View Controller를 호출하여 전환하기</li>
<li>Navigation Controller를 사용하여 화면 전환하기</li>
<li>화면 전환용 객체 세그웨이를 사용하여 화면 전환하기</li>
</ul>
<p>여기서 첫번째 방법은 되도록 사용하지 말아야하는 방법이다. 왜냐하면 View 위에 View를 가져오게 되면 메모리 누수 위험이 존재하기 때문이다.</p>
<h3 id="view-controller에서-다른-view-controller-호출하여-전환하기">View Controller에서 다른 View Controller 호출하여 전환하기</h3>
<p>현재 뷰 컨트롤러에서 이동할 대상의 뷰 컨트롤러를 직접 호출해서 화면에 표시하는 방법이다.
(직접 표시한다는 의미로 Presentation 방식이라고도 부름)</p>
<p>기존 뷰 컨트롤러 위에 새로운 뷰 컨트롤러 화면을 덮는 방식이다. </p>
<pre><code>// present 메소드에 이동할 화면의 뷰 컨트롤러 넘겨줌
func present(_ viewControllerToPresent: UIViewControoler,
    animated flag: Bool,
    completion: (() -&gt; Void)? = nil)</code></pre><p>present 메소드에 첫번째 파라미터에는 새로운 화면으로 이동할 화면의 뷰 컨트롤러 인스턴스를 넣어주고, 두번째 파라미터에는 화면을 전환할 때 애니메이션 효과를 줄 것인지 안 줄 것인지 Bool 값으로 넣어준다.
세번째 파라미터에는 completion이라는 클로저값을 받고 있는데 completion 파라미터에 클로저를 정의해주면 화면전환이 완료되는 시점에 맞춰 completion 클로저가 호출되게 된다.</p>
<p>화면전환 방식은 비동기 방식으로 처리되기 때문에 화면전환이 완료된 이후에 코드로 처리해야할 로직이 있다면 completion 클로저안에 로직을 작성하면 된다.</p>
<h4 id="이전화면으로-돌아오기">이전화면으로 돌아오기</h4>
<pre><code>func dismiss(animated flag: Bool,
    completion: (() -&gt; Void)? = nil)
</code></pre><p>이전 화면으로 돌아가는 메소드로 뷰 컨트롤러 인스턴스를 인자로 받지 않는다.
dismiss 메소드의 첫번째 파라미터에는 애니메이션을 적용할지 말지 Bool값으로 받고, 두번째 파라미터에는 이전화면으로 돌아가는게 완료된 이후에 코드로 처리해야할 로직을 작성한다.</p>
<h3 id="navigation-controller를-사용하여-화면-전환하기">Navigation Controller를 사용하여 화면 전환하기</h3>
<p>네비게이션 컨트롤러는 계층적인 성격을 띄는 컨텐츠 구조를 관리하기 위한 컨트롤러이다.</p>
<p>네비게이션 컨트롤러는 뷰 컨트롤러 전환을 직접 컨트롤하고 앱의 네비게이션 정보를 표시하는 역활을 할 뿐만아니라 네비게이션 스택으로 자식 뷰 컨트롤러를 관리한다.</p>
<p>네비게이션 스택은 선입 후출 방식으로 나중에 들어온 화면이 먼저 나가는 방식이기 때문에 pushViewController 메소드를 사용해서 네비게이션 스택에 화면을 추가하고 popViewController 메소드를 사용해서 네비게이션 스택에 있는 화면을 제거한다.</p>
<h4 id="화면-추가">화면 추가</h4>
<pre><code>func pushViewController(_ viewController: UIViewController,
    anmiated: Bool)</code></pre><p>첫번째 파라미터: 새로운 화면으로 이동할 ViewController의 인스턴스를 넘겨준다.
두번째 파라미터: 화면전환 될때 애니메이션 사용할지 말지 Bool값을 넘겨준다.</p>
<h4 id="화면-제거">화면 제거</h4>
<pre><code>func popViewController(animated: Bool) -&gt; UIViewController?</code></pre><p>첫번째 파라미터: 이전화면으로 전환될때 애니메이션 사용할지 말지 Bool값을 넘겨준다.</p>
<h3 id="화면-전환용-객체-세그웨이를-사용하여-화면-전환하기">화면 전환용 객체 세그웨이를 사용하여 화면 전환하기</h3>
<p>세그웨이에는 두개의 뷰 컨트롤러 사이에 연결된 화면전환 객체를 의미한다.</p>
<p>Stroyboard를 통해 출발지와 목적지를 직접 지정하는 방식을 세그웨이를 이용한 화면 전환이라고 한다. 세그웨이를 이용하면 따로 코드를 사용하지 않고 Stroyboard만으로 화면을 전환할 수 있다.</p>
<p>세그웨이 종류에는 Action Segueway와 Manual Segueway가 있다.</p>
<p>출발점이 뷰 컨트롤러 자체인 경우를 Manual Segueway라하고 출발점이 버튼, 셀 등인 경우 Action Segueway(Trigger Segueway)라 한다.</p>
<h4 id="action-segueway">Action Segueway</h4>
<p>액션 세그웨이는 버튼 터치와 같은 트리거 이벤트가 세그웨이 실행으로 바로 연결된다.
그래서 소스코드를 추가하지 않아도 화면전환 기능을 구현하지 않을 수 있다.</p>
<h4 id="action-segueway-종류">Action Segueway 종류</h4>
<ul>
<li>show    : 가장 일반적인 세그웨이로 네비게이션 컨트롤러를 사용하면 화면 전환시 뷰 컨트롤러가 네비게이션에 쌓이게 되고 만약 네비게이션 컨트롤러를 사용하지 않을 경우에는 뷰 컨트롤러가 삭제된다.</li>
<li>show Detail : Split View에서 사용하는 세그웨이로 아이폰에서 사용하게 되면 show segueway랑 똑같이 동작하지만 아이패드에서 사용하게되면 스플릿 구조의 마스터 슬레이브구조가 되어서 보이게 된다.</li>
<li>present Modally : 이전 뷰 컨트롤러를 덮으면서 새로운 화면이 나타나게 된다.</li>
<li>Present As Popover : 아이패드에서 사용하는 것으로 팝업창을 띄울때 사용한다. (아이폰 사용 x)</li>
<li>custom : 세그웨이를 사용자가 원하는 방식으로 커스텀할때 사용한다.</li>
</ul>
<h4 id="manual-segueway">Manual Segueway</h4>
<p>적절한 시점에 perform Seguewy를 호출하면서 세그웨이가 실행되어 화면전환이 일어난다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] UINavigationController]]></title>
            <link>https://velog.io/@jamong-i/iOS-UINavigationController</link>
            <guid>https://velog.io/@jamong-i/iOS-UINavigationController</guid>
            <pubDate>Tue, 24 Jan 2023 02:41:27 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 2. LED 전광판 앱 만들기</p>
</blockquote>
<h3 id="content-view-controller">Content View Controller</h3>
<p>기본적으로 화면을 구성하는 뷰를 직접 구현하고 관련된 이벤트를 처리하는 뷰 컨트롤러
(스토리보드 생성시 기본으로 생성되는 뷰 컨트롤러)</p>
<h3 id="container-view-controller">Container View controller</h3>
<p>여러요소를 조합한 인터페이스를 구성하고 직접 무언가를 보여주는 역활은 없으며, 뷰 컨트롤러 간에 부모-자식 관계를 형성하여 관리하는 역활을 맡는 뷰 컨트롤러</p>
<ul>
<li>하나 이상의 Child View Controller를 가지고 있다.</li>
<li>하나 이상의 Child View Controller를 관리하고 레이아웃과 화면 전환을 담당한다.</li>
<li>화면 구성과 이벤트 관리는 Child View Controller 에서 한다.</li>
<li>Container View Controller는 대표적으로 Naviagtion Controller와 TabBar Controller가 있다.</li>
</ul>
<h3 id="navigation-controller">Navigation Controller</h3>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/5e250788-8ae6-44c4-9a5c-2d1cfd816d1d/image.png" alt=""></p>
<p>계층 구조로 구성된 Content를 순차적으로 보여주는 스택 기반 체계인 Container View Controller</p>
<p>Settings 앱에서 General로 들어가게 되면 General 자식 뷰 컨트롤러가 나타나게 되고 이전의 뷰 컨트롤러는 가려지게 된다. 그리고 최상단 네비게이션 바에서 뒤로가기 버튼을 선택하면 이전에 숨겨진 뷰 컨트롤러가 나타나게 된다.
네비게이션 컨트롤러는 네비게이션 스택 칭하는 정렬된 배열을 사용하여 자식 뷰 컨트롤러를 관리한다.</p>
<p>배열의 첫번째 뷰 컨트롤러는 Root View Controller를 의미하고 스택의 가장 밑에 있다는 것을 의미한다. 배열의 마지막 뷰 컨트롤러는 스택의 최상단을 의미한다. (현재 화면을 의미한다.)</p>
<p>개발자는 세그웨이를 사용하거나 또는 UINavigation Controller Methood를 사용해서 스택으로부터 뷰 컨트롤러를 추가하고 제거할 수 있다.</p>
<p>사용자는 뒤로가기 버튼을 이용해서 최상단 뷰컨트롤러를 제거할 수 있으며 Left Edge Swipe를 이용해서도 제거할 수 있다.</p>
<h4 id="navigation-stack">Navigation Stack</h4>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/af98f46c-8825-4ee5-9060-2e292047e92f/image.png" alt=""></p>
<p>네비게이션 스택은 Last in first out로 스택에 나중에 들어온게 먼저나가는 구조이다.</p>
<p>A화면은 설정화면 B화면은 일반화면이다. 
A화면 - 설정화면으로 첫번째 뷰 컨트롤러이기 때문에 Root View Controller를 의미하고 B화면 - 일반화면은 Chiled View Controller 이다.</p>
<p>설정화면에서 일반을 탭하게 되면 일반화면 표시되고 네비게이션 뷰 스택 설정화면 위에 알림화면이 쌓이게 된다. 이렇게 쌓이게 되는 것을 PUSH 했다라고 말한다. 여기서 뒤로가기를 하게 되면 POP 했다라고 말한다.</p>
<h4 id="navigation-bar">Navigation Bar</h4>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/ab98f87d-3e6d-4a49-b9ea-89cfb86efdff/image.png" alt=""></p>
<p>Navigation Controller를 구현할 시 화면 최상단에 항상 보여지는 네비게이션 바</p>
<p>Root View Controller 외에 모든 뷰 컨트롤러에 Back버튼이 있어서 유저가 계층구조에서 빠져나올 수 있게 해준다.</p>
<p>네비게이션 바는 슬라이드의 사진과 같이 네비게이션 바, 버튼 아이템, 타이틀, 프롬프트로 되어 있으며 자식 뷰 컨트롤러마다 다른 네비게이션 바를 구성할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] 명언 생성기 앱 만들기]]></title>
            <link>https://velog.io/@jamong-i/iOS-%EB%AA%85%EC%96%B8-%EC%83%9D%EC%84%B1%EA%B8%B0-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@jamong-i/iOS-%EB%AA%85%EC%96%B8-%EC%83%9D%EC%84%B1%EA%B8%B0-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sun, 22 Jan 2023 06:26:43 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 1. 명언 생성기 앱 만들기</p>
</blockquote>
<h3 id="stoaryboard-desgine-제약조건">Stoaryboard Desgine (제약조건)</h3>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/7efbe659-41d7-49a2-9cf6-c92fcd9757df/image.png" alt=""></p>
<h4 id="명언-생성기-라벨">명언 생성기 라벨</h4>
<ul>
<li>Align Top: 24</li>
<li>Align Traling: 24</li>
<li>Align Leading: 24</li>
</ul>
<h4 id="view-quote-label-name-label">View (Quote Label, Name Label)</h4>
<ul>
<li>Align Top: 24  (to: 명언 생성기)</li>
<li>Align Traling: 24</li>
<li>Aling Leading: 24</li>
</ul>
<h5 id="quote-label">Quote Label</h5>
<ul>
<li>Align Top: 20</li>
<li>Align Traling: 20</li>
<li>Aling Leading: 20</li>
</ul>
<h5 id="name-label">Name Label</h5>
<ul>
<li>Align Top: 20  (to: Quote Label)</li>
<li>Align Traling: 20</li>
<li>Align Leading: 20</li>
</ul>
<h4 id="명언-생성">명언 생성</h4>
<ul>
<li>Align Top: 20  (to: View)</li>
<li>Align Center  (to: View)</li>
</ul>
<hr>
<h3 id="코드-구현">코드 구현</h3>
<pre><code>Quote.swift

import Foundation

// 구조체 Quote 생성 (내용, 이름 설정)
struct Quote {
    let contents: String
    let name: String
</code></pre><p>배열에 넣어줄 구조체 - 명언내용과 이름을 생성</p>
<pre><code>ViewController.swift

import UIKit

class ViewController: UIViewController {
    // View Outlet connted (
    @IBOutlet weak var quoteLabel: UILabel!
    @IBOutlet weak var nameLabel: UILabel!


    // 배열(구조체 Quote 이용)
    let quotes = [
        Quote(contents: &quot;죽음을 두려워하는 나머지 삶을 시작조차 못하는 사람이 많다.&quot;, name: &quot;벤다이크&quot;),
        Quote(contents: &quot;나는 나 자신을 빼 놓고는 모두 안다.&quot;, name: &quot;바용&quot;),
        Quote(contents: &quot;편견이란 실효성이 없는 의견이다.&quot;, name: &quot;암브로스 빌&quot;),
        Quote(contents: &quot;분노는 바보들의 가슴속에서만 살아간다.&quot;, name: &quot;아인슈타인&quot;),
        Quote(contents: &quot;몇 번이라도 좋다! 이 끔찍한 생이여...다시!&quot;, name: &quot;니체&quot;)
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // 명언 생성 - 새로고침 액션 버튼 연결
    @IBAction func tapQuoteGeneratorButton(_ sender: Any) {
        // 1..5 까지 랜덤 수 생성
        let random = Int(arc4random_uniform(5))
        // 명언 배열 뽑기
        let quote = quotes[random]
        // 명언 및 이름 라벨 텍스트 뿌려주기
        self.quoteLabel.text = quote.contents
        self.nameLabel.text = quote.name
    }
}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift 야곰] 3단원. 고차함수]]></title>
            <link>https://velog.io/@jamong-i/Swift-%EC%95%BC%EA%B3%B0-3%EB%8B%A8%EC%9B%90.-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@jamong-i/Swift-%EC%95%BC%EA%B3%B0-3%EB%8B%A8%EC%9B%90.-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98</guid>
            <pubDate>Fri, 20 Jan 2023 07:11:20 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>Day 14</strong> - 2023.01.20</p>
</blockquote>
<h3 id="고차-함수">고차 함수</h3>
<ul>
<li>고차 함수(Higher-order function)는 &#39;다른 함수를 전달인자로 받거나 함수실행의 결과를 함수로 반환하는 함수&#39;를 뜻합니다.</li>
<li>스위프트의 함수(클로저)는 일급시민(일급객체)이기 때문에 함수의 전달인자로 전달할 수 있으며, 함수의 결과값으로 반환할 수 있다.</li>
<li>map, filter, reduce 함수는 스위프트 표준 라이브러리의 컨테이너 타입(Array, Set, Dictionary 등)에 구현되어 있다.</li>
</ul>
<h3 id="map">map</h3>
<ul>
<li>map함수는 컨테이너 내부의 기존 데이터를 변형(transform)하여 새로운 컨테이너를 생성하다.</li>
</ul>
<pre><code>// 변형하고자 하는 numbers와 변형 결과를 받을 doubledNumbers, strings

let numbers: [Int] = [0, 1, 2, 3, 4]
var doubledNumbers: [Int]
var strings: [String]</code></pre><h4 id="기존의-for-구문-사용">기존의 for 구문 사용</h4>
<pre><code>doubledNumbers = [Int]()
strings = [String]()

for number in numbers {
    doubledNumbers.append(number * 2)
    strings.append(&quot;\(number)&quot;)
}

print(doubledNumbers)        // 출력: [0, 2, 4, 6, 8]
print(strings)                // 출력: [&quot;0&quot;, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;]</code></pre><h4 id="map-메서드-사용">map 메서드 사용</h4>
<pre><code>// numbers의 각 요소를 2배하여 새로운 배열 반환
doubledNumbers = numbers.map({ (numbers: Int) -&gt; Int in
    return numbers * 2
})

// numbers의 각 요소를 문자열로 변환하여 새로운 배열 반환
strings = numbers.map({ (number: Int) -&gt; String in
    return &quot;\(number)&quot;
})

print(doubledNumbers)         // 출력: [0, 2, 4, 6, 8]
print(strings)                // 출력: [ &quot;0&quot;, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;]

// 매개변수, 반환타입, 반환 키워드(return) 생략, 후행 클로저
doubledNumbers = numbers.map{$0 * 2}
print(doubeldNumbers)        // 출력: [0, 2, 4, 6, 8]</code></pre><h3 id="filter">filter</h3>
<ul>
<li>filter함수는 컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출한다.</li>
</ul>
<h4 id="기존의-for-구문-사용-1">기존의 for 구문 사용</h4>
<pre><code>// 변수 사용에 주목
var filterd: [Int] = [Int]()

for number in numbers {
    if numbers % 2 == 0 {
        filterd.append(number)
    }
}

print(filterd)            // 출력: [0, 2, 4]</code></pre><h4 id="filter-메서드-사용">filter 메서드 사용</h4>
<pre><code>// numbers의 요소 중 짝수를 걸러내어 새로운 배열로 반환
let evenNumbers: [Int] = numbers.filter { (number: Int) -&gt; Bool in
    return number % 2 == 0
}

print(evenNumbers)        // 출력: [0, 2, 4]

// 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저
let oddNumbers: [Int] = numbers.filter {
    $0 % 2 != 0
}

print(oddNumbers)        // 출력: [1, 3]</code></pre><h3 id="reduce">reduce</h3>
<ul>
<li>reduce 함수는 컨테이너 내부의 콘텐츠를 하나로 통합한다.<pre><code>// 통합하고자 하는 someNumbers
let someNumbers: [Int] = [2, 8, 15]</code></pre></li>
</ul>
<h4 id="기존의-for-구문-사용-2">기존의 for 구문 사용</h4>
<pre><code>// 변수 사용에 주목
var result: Int = 0

// someNumbers의 모든 요소를 더한다.
for number in someNumbers {
    result += number
}

print(result)            // 출력: 25</code></pre><h4 id="메서드-사용">메서드 사용</h4>
<pre><code>// 초깃값이 0이고 someNumbers 내부의 모든 값을 더한다.
let sum: Int = someNumbers.reduce(0, {first: Int, second: Int) -&gt; Int in
    return first + second
})

print(sum)        // 출력: 25

// 초기값이 0이고 someNumbers 내부의 모든 값을 뺀다.
var substract: Int = someNumbers.reduce(0, { (first: Int, second: Int) -&gt; Int in
    return first - second
})

print(subtract)    // 출력: -25

// 초기값이 3이고 someNumbers 내부의 모든 값을 더한다.
let sumeFromThree = someNumbers.reduce(3) { $0 + $1 }

print(sumFromThree)    // 출력: 28</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift 야곰] 3단원. 오류처리 및 고차함수]]></title>
            <link>https://velog.io/@jamong-i/Swift-%EC%95%BC%EA%B3%B0-3%EB%8B%A8%EC%9B%90.-%EC%98%A4%EB%A5%98%EC%B2%98%EB%A6%AC-%EB%B0%8F-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@jamong-i/Swift-%EC%95%BC%EA%B3%B0-3%EB%8B%A8%EC%9B%90.-%EC%98%A4%EB%A5%98%EC%B2%98%EB%A6%AC-%EB%B0%8F-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98</guid>
            <pubDate>Fri, 20 Jan 2023 05:16:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>** Day 14 ** - 2023.01.20</p>
</blockquote>
<h3 id="오류-처리">오류 처리</h3>
<ul>
<li>스위프트에서 오류(Error)는 Error라는 프로토콜을 준수하는 타입의 값을 통해 표현됩니다.</li>
<li>Error 프로토콜은 사실상 요구사항이 없는 빈 프로토콜일 뿐이지만, 오류를 표현하기 위한 타입(주로 열거형)은 이 프로토콜을 채택한다.</li>
<li>스위프트의 열거형은 오류의 종류를 나타내기에 아주 적합한 기능이다.</li>
<li>연관 값을 통해 오류에 관한 부가 정보를 제공할 수도 있다.</li>
</ul>
<p>이번 예제에는 프로그램 내에서 자판기를 작동시키려고 할 때 발생하는 오류상황을 구현해 보았다.</p>
<h3 id="오류-표현">오류 표현</h3>
<ul>
<li>Error 프로토콜과 (주로) 열거형을 통해서 오류를 표현한다.</li>
<li>자판기 동작 오류의 종류를 표현한 VendingMachineError 열거형<pre><code>enum VendingMachineError: Error {
  case invalidInput
  case insufficientFunds(moneyNeeded: Int)
  case outOfStock
}</code></pre></li>
</ul>
<h3 id="함수에서-발생한-오류-던지기">함수에서 발생한 오류 던지기</h3>
<p>자판기 동작 도중 발생한 오류를 던지는 메서드를 구현해보자</p>
<ul>
<li><p>오류 발생의 여지가 있는 메서드는 throws를 사용하여 오류를 내포하는 함수임을 표시한다.</p>
<pre><code>class VendingMachine {
  let itemPrice: Int = 100
  var itemCount: Int = 5
  var deposited: Int = 0

  // 돈 받기 메서드
  func receiveMoney(_ money: Int) throws {

      // 입력한 돈이 0이하면 오류를 던짐
      guard money &gt; 0 else { 
          throw VendingMachineError.invalidInput
      }

      // 오류가 없으면 정상 처리
      self.deposited += money
      print(&quot;\(money)원 받음&quot;)
  }

  // 물건 팔기 메서드
  func vend(numberOfItems numberOfItemsToVend: Int) throws -&gt; String {

      // 원하는 아이템의 수량이 잘못 입력되었으면 오류 던짐
      guard numberOfItemsToVend &gt; 0 else {
          throw VendingMachineError.invalidInput
      }

      // 구매하려는 수량보다 미리 넣어둔 돈이 적으면 오류를 던짐
      guard numberOfItemsToVend * itemPrice &lt;= deposited else {
          let moneyNeeded: Int
          moneyNeeded = numberOfItemsToVend * itemPrice - deposited
          throw VendingMachineError.insufficientFunds(moneyNeeded: moneyNeeded)
      }

      // 구매하려는 수량보다 요구하는 수량이 많으면 오류를 던짐
      guard itemCount &gt;= numberOfItemsToVend else {
          trhow VendingMachineError.outOfStock
      }

      // 오류가 없으면 정상처리를 함
      let totalPrice = numberOfItemsToVend * itemPrice

      self.deposited -= totalPrice
      self.itemCount -= numberOfItemsToVend

      return &quot;\(numberOfItemsToVend)개 제공함&quot;
  }
}

</code></pre></li>
</ul>
<p>// 자판기 인스턴스
let machine: VendingMachine = VendingMachine()</p>
<p>// 판매 결과를 전달받을 변수
var result: String?</p>
<pre><code>
### 오류 처리
- 오류를 던질 수도 있지만 오류가 던져지는 것에 대비하여 던져진 오류를 처리하기 위한 코드도 작성해야 한다. 예를 들어 던져진 오류가 무엇인지 판단하여 다시 문제를 해결한다든지, 다른 방법으로 시도해 본다든지, 사용자에게 오류를 알리고 사용자에게 선택 권한을 넘겨주어 다음에 어떤 동작을 할게 할 것인지 결정하도록 유도하는 등의 코드를 작성해야 한다.
- 오류발생의 여지가 있는 throws 함수(메서드)는 try를 사용하여 호출해야 한다. try와 do-catch, try?와 try! 등에 대해 알아보자.

#### do-catch
- 오류발생의 여지가 있는 throws 함수(메서드)는 do-catch 구문을 활용하여 오류발생에 대비한다.
- 가장 정석적인 방법으로 모든 오류 케이스에 대응한다.
</code></pre><p>do {
    try machine.receiveMoney(0)
} catch VendingMachineError.invalidInput {
    print(&quot;입력이 잘못되었습니다.&quot;)
} catch VendingMachineError.insufficientFunds(let moneyNeeded) {
    print(&quot;(moneyNeeded)원이 부족합니다.&quot;)
} catch VendingMachineError.outOfStock {
    print(&quot;수량이 부족합니다.&quot;)
}</p>
<pre><code>
- 케이스별로 오류처리 할 필요가 없으면 catch 구문 내부를 간략화 가능
</code></pre><p>do {
    result = try machine.vend(numberOfItems: 4)
} catch {
    print(error)
}
// insufficientFunds(100)</p>
<pre><code>
- 케이스별로 오류처리 할 필요가 없으면 do 구문만 써도 무방된다.</code></pre><p>do {
    result = try machine.vend(numberOfItems: 4)
}</p>
<pre><code>
#### try?와 try!
`try?`
- 별도의 오류처리 결과를 통보받지 않고 오류가 발생했으면 결과값을 nil로 돌려받을 수 있다.
- 정상동작 후에는 옵셔널 타입으로 정상 반환값을 돌려 받는다.
</code></pre><p>result = try? machine.vend(numberOfItems: 2)
result        // Optional(&quot;2개 제공함&quot;)</p>
<p>result = try? machine.vend(numberOfItems: 2)
result        // nil</p>
<pre><code>
`try!`
- 오류가 발생하지 않을 것이라는 강력한 확신을 가질 때 try!를 사용하면 정상동작 후에 바로 결과값을 돌려받는다.
- 오류가 발생하면 런타임 오류가 발생하여 애플리케이션 동작이 중지된다.</code></pre><p>result = try! machine.vend(numberOfItems: 1)
result // 1개 제공함</p>
<p>// result = try! machine.vend(numberOfItems: 1)
// 런타임 오류 발생!
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] IBOult과 IBAction]]></title>
            <link>https://velog.io/@jamong-i/iOS-IBOult%EA%B3%BC-IBAction</link>
            <guid>https://velog.io/@jamong-i/iOS-IBOult%EA%B3%BC-IBAction</guid>
            <pubDate>Thu, 19 Jan 2023 07:53:52 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 1. 명언 생성기 앱 만들기</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/8d137358-6fa6-4a5e-b2c5-76e7e2921979/image.png" alt=""></p>
<p>미리 만들어 놓은 Storyboard를 이용하여 색상을 랜덤하게 바꿔주는 기능을 구현하면서 IBOult과 IBAction을 알아보려고 한다.</p>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/0b03a476-2426-499c-a1bd-db1d6c86ae64/image.gif" alt=""></p>
<p>스토리보드에서 뷰를 선택하여 마우스 오른쪽 버튼으로 드래그해 ViewController에 드랍하면 IBOult을 설정할 수 있다.</p>
<p>스토리보드에 등록한 UIObject에 접근하여 컨트롤하기 위해 변수에 바인딩한 오브젝트 변수를 Oult변수라고 한다. (Oult변수를 등록하여야 코드상에서 UI을 설정 할 수 있다.)</p>
<h4 id="메모리-회수-정책-키워드">메모리 회수 정책 키워드</h4>
<ul>
<li>String: 다른 곳에서 참조하고 있을 경우에 메모리에서 제거되지않는다. (메모리 누수 가능성 있음)</li>
<li>Weak: 다른 곳에서 참조하고 있더라도 시스템이 인위적으로 메모리를 제거할 수 있다.</li>
</ul>
<p>IBAction도 마찬가지로 ViewController에 드랍하여 IBACtion을 설정 할 수 있다.</p>
<p>Action 함수는 동작을 나타내는 함수로 어떠한 동작을 할 수 있도록 정의하고 연결시켜준다.</p>
<pre><code>ViewController.swift

import UIKit

class ViewController: UIViewController {
    // 색상 뷰 아울렛 연결
    @IBOutlet weak var colorView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // 색상 뽑기 라벨 액션 연결
    @IBAction func tapChangeColorButton(_ sender: UIButton) {
    }
}
</code></pre><p>색상 뽑기 라벨을 클릭하였을 때 색상 뷰가 파랑색으로 변경되게 코드를 설정</p>
<pre><code>ViewController.swift

    // 액션함수가 실행될 때 (클릭 될 때)
    @IBAction func tapChangeColorButton(_ sender: UIButton) {
        // 색상 뷰의 백그라운드 컬러가 파랑색으로 변경
        self.colorView.backgroundColor = UIColor.blue
    }</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] AutoLayout]]></title>
            <link>https://velog.io/@jamong-i/iOS-AutoLayout</link>
            <guid>https://velog.io/@jamong-i/iOS-AutoLayout</guid>
            <pubDate>Thu, 19 Jan 2023 06:46:53 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 1. 명언 생성기 앱 만들기</p>
</blockquote>
<h3 id="autolayout">AutoLayout</h3>
<blockquote>
<p>AutoLayout은 제약 조건(Constraints)을 이용해서 뷰의 위치를 지정하는 것이다.
(두 View의 사이의 관계를 제약조건을 이용해서 뷰의 크기와 위치를 지정하는 것)</p>
</blockquote>
<p>아이폰의 다양한 해상도 비율에 대응하기 위해 나온 개념으로 여러 해상도에서 화면을 똑같이 보여주기 위해 AutoLayout을 사용한다.</p>
<p>세로 보기뿐만 아니라 가로보기 화면도 지원한다.</p>
<h3 id="stroyboard">Stroyboard</h3>
<p>iOS에서 AutoLayout을 이용한 화면 구성은 Storyboard에서 확인할 수 있다.</p>
<blockquote>
<p>Xcode File Navigater에서 Storyboard(Main.Stroyboard)를 선택하면 iPone과 같은 화면이 보이게 된다.</p>
<p>File Navigater 오른쪽에는 View Controller Scene 이라는 섹션이 보이게 된다.
이것이 Storyboard 이다.
<img src="https://velog.velcdn.com/images/jamong-i/post/a63305e2-cc26-4f10-8b79-64640ca8dac8/image.png" alt=""></p>
</blockquote>
<p>스토리보드는 iOS 앱에 사용자 인터페이스를 시각적으로 표현하여 컨텐츠 화면과 화면간의 연결을 보여주는 도구이다. </p>
<p>스토리보드는 Scean으로 구성되며 각 Scean은 ViewController와 View를 나타낸다.</p>
<p>여러 화면들을 스토리보드를 이용해 만든다.</p>
<p>스토리보드 오른쪽 하단에는 제약조건을 이용해서 View의 위치를 지정해 줄 수 있는 다양한 메뉴들이 있다.</p>
<h4 id="add-new-constraint">Add New Constraint</h4>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/22b435c9-a382-48f8-a0b8-e87849d32127/image.png" alt=""></p>
<blockquote>
<p>뷰 간의 제약조건 설정할 수 있는 메뉴</p>
</blockquote>
<p>시계 방향으로 Top, Trailing, Bottom, Leading의 제약조건의 값을 설정 할 수 있고 View의 너비와 높이 등 다양한 값을 설정 할 수 있다.</p>
<h4 id="align">Align</h4>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/caa07d94-3454-41a4-aa14-ae9c0053ef8d/image.png" alt=""></p>
<blockquote>
<p>뷰 간의 정렬을 설정할 수 있는 메뉴</p>
</blockquote>
<p>다른 View  간의 가로, 세로 정렬과 같은 정렬 제약조건을 추가할 수 있다.</p>
<p>정렬하고 싶은 두개의 View를 선택하여 수직정렬, 수평정렬을 추가할 수도 있다.</p>
<p>하지만 컨테이너에 수평이나 수직으로 정렬할 때 하나의 뷰를 선택할 수 있다.</p>
<h3 id="reslove-auto-layout-issues">Reslove Auto Layout Issues</h3>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/9e65c53b-09fc-4d8d-aa71-5cfd767c4947/image.png" alt=""></p>
<blockquote>
<p>AutoLayout Issue 들을 해결하기 위한 메뉴 </p>
</blockquote>
<p>현재 제약조건을 기준으로 뷰를 업데이트하거나, 캔버스안의 View의 현재 위치를 통해 제약을 업데이트 할 수 있다.</p>
<p>누락된 제약을 추가하거나, 제약을 삭제하거나, 추천하는 제약을 사용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] UIViewController]]></title>
            <link>https://velog.io/@jamong-i/iOS-UIViewController</link>
            <guid>https://velog.io/@jamong-i/iOS-UIViewController</guid>
            <pubDate>Thu, 19 Jan 2023 05:33:05 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 1. 명언 생성기 앱 만들기</p>
</blockquote>
<h3 id="uiview">UIView</h3>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/23afa0ec-0e0d-461d-9a93-3216908768c6/image.png" alt=""></p>
<p>UIView는 화면의 직사각형 영역에 대한 내용을 관리하는 개체이다.</p>
<p>UIView는 위치와 크기를 갖는 사각형으로 배경색을 가지고 있고 또 문자나 이미지 등의 컨텐츠를 갖는 것이 가능하다.</p>
<p>슬라이드와 같이 여러 UIComponent들의 부모클래스가 UIView이다.</p>
<p>그래서 UIView는 여러 UIComponent들을 보여주는 용도로 사용한다.</p>
<h3 id="viewcontroller">ViewController</h3>
<p>ViewController는 앱의 근간을 이루는 객체로 모든 앱은 최소한 하나 이상의 뷰 컨트롤러를 가지고 있다.</p>
<p>즉, 사용자가 화면을 보는 것에 대한 관리 기능을 제공한다.</p>
<blockquote>
</blockquote>
<p>우리가 앱을 사용할 때 화면마다 다른 컨텐츠가 표시되고 화면을 터치해서 다른 화면으로 이동할 때 ViewController가 사용된다.</p>
<h4 id="viewcontroller-역활">ViewController 역활</h4>
<ul>
<li><p>데이터 변화에 따라서 View 컨텐츠를 업데이트</p>
</li>
<li><p>View들과 함께 사용자 상호작용에 응답</p>
</li>
<li><p>View를 리사이징하고 전체적인 인터페이스의 레이아웃 관리</p>
</li>
<li><p>다른 ViewController들과 함께 앱을 구성한다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift 야곰] 2단원. 익스텐션]]></title>
            <link>https://velog.io/@jamong-i/Swift-%EC%95%BC%EA%B3%B0-2%EB%8B%A8%EC%9B%90.-%EC%9D%B5%EC%8A%A4%ED%85%90%EC%85%98</link>
            <guid>https://velog.io/@jamong-i/Swift-%EC%95%BC%EA%B3%B0-2%EB%8B%A8%EC%9B%90.-%EC%9D%B5%EC%8A%A4%ED%85%90%EC%85%98</guid>
            <pubDate>Thu, 19 Jan 2023 02:36:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>Day 13</strong> - 2023.01.19</p>
</blockquote>
<h3 id="익스텐션extension">익스텐션(Extension)</h3>
<ul>
<li>익스텐션은 스위프트의 강력한 기능 중 하나이다.</li>
<li>익스텐션은 구조체, 클래스, 열거형, 프로토콜 타입에 새로운 기능을 추가할 수 있는 기능이다.</li>
<li>기능을 추가하려는 타입의 구현된 소스 코드를 알지 못하거나 볼 수 없다 해도, 타입만 알고 있다면 그 타입의 기능을 확장할 수도 있다.</li>
</ul>
<h4 id="스위프트의-익스텐션이-타입에-추가할-수-있는-기능">스위프트의 익스텐션이 타입에 추가할 수 있는 기능</h4>
<ul>
<li>연산 타입 프로퍼티 / 연산 인스턴스 프로퍼티</li>
<li>타입 메서드 / 인스턴스 메서드</li>
<li>이니셜라이저</li>
<li>서브스크립트</li>
<li>중첩 타입</li>
<li>특정 프로토콜을 준수할 수 있도록 기능 추가</li>
</ul>
<p>※※ 익스텐션은 타입에 새로운 기능을 추가할 수 있지만, 기존에 존재하는 기능을 재정의할 수는 없다.</p>
<h4 id="클래스의-상속과-익스텐션-비교">클래스의 상속과 익스텐션 비교</h4>
<p>이 둘은 비슷해보이지만 실제 성격은 많이 다르다.</p>
<ul>
<li>클래스의 상속은 클래스 타입에서만 가능하지만 익스텐션은 구조체, 클래스, 프로토콜 등에 적용이 가능하다.</li>
<li>클래스의 상속은 특정 타입을 물려받아 하나의 새로운 타입을 정의하고 추가 기능을 구현하는 수직 확장이지만, 익스텐션은 기존의 타입에 기능을 추가하는 수평 확장이다. </li>
<li>클래스는 상속을 받으면 기존 기능을 재정의할 수 있지만, 익스텐션은 재정의할 수 없다는 것도 큰 차이 중 하나이다. </li>
<li>상황과 용도에 맞게 상속과 익스텐션을 선택하여 사용하면 된다.</li>
</ul>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">상속</th>
<th align="center">익스텐션</th>
</tr>
</thead>
<tbody><tr>
<td align="center">&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp확장&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</td>
<td align="center">&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp수직 확장&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</td>
<td align="center">&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp수평 확장&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</td>
</tr>
<tr>
<td align="center">&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp사용&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</td>
<td align="center">&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp클래스 타입&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</td>
<td align="center">&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp클래스, 구조체, 프로토콜, 제네릭 등 모든 타입&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</td>
</tr>
<tr>
<td align="center">&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp재정의&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</td>
<td align="center">&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp가능&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</td>
<td align="center">&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp불가능&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</td>
</tr>
</tbody></table>
<h4 id="익스텐션-활용">익스텐션 활용</h4>
<p>익스텐션을 사용하는 대신 원래 타입을 정의한 소스에 기능을 추가하는 방법도 있겠지만, 외부 라이브러리나 프레임워크를 가져다 썻다면 원본 소스를 수정하지 못한다. 이처럼 외부에서 가져온 타입에 내가 원하는 기능을 추가하고자 할 때 익스텐션을 사용한다.</p>
<p>따로 상속 받지 않아도 되며, 구조체와 열거형에도 기능을 추가할 수 있으므로 익스텐션은 매우 편리한 기능이다.</p>
<p>익스텐션은 모든 타입에 적용할 수 있다. 모든 타입이라 함은 구조체, 열거형, 클래스, 프로토콜, 제네릭 타입 등을 뜻한다. 즉, 익스텐션을 통해 모든 타입에 연산 프로퍼티, 메서드, 이니셜라이저, 서브스크립트, 중첩 데이터 타입 등을 추가할 수 있다. 더불어 익스텐션은 프로토콜과 함께 사용하면 굉장히 강력한 기능을 선사한다.</p>
<hr>
<h3 id="정의">정의</h3>
<ul>
<li>extension 키워드를 사용하여 정의한다.<pre><code>extension 확장할 타입 이름 { 
  /* 타입에 추가될 새로운 기능 구현 */
}</code></pre></li>
<li>익스텐션은 기존에 존재하는 타입이 추가적으로 다른 프로토콜을 채택할 수 있도록 확장할 수 있다. 이런 경우에는 클래스나 구조체에서 사용하던 것과 똑같은 방법으로 프로토콜 이름을 나열해준다.</li>
</ul>
<pre><code>extension 확장할 타입 이름: 프로토콜1, 프로토콜2, 프로토콜3... {
    /* 프로토콜 요구사항 구현 */
}</code></pre><p>스위프트 라이브러리를 살펴보면 실제로 익스텐션이 굉장히 많이 사용되고 있음을 알 수 있다.</p>
<p>Double 타입에는 수많은 프로퍼티와 메서드, 이니셜라이저가 정의되어 있으며 수많은 프로토콜을 채택하고 있을 것이라 예상되지만, 실제로 Double 타입의 정의를 살펴보면 그 모든것이 다 정의되어 있지는 않다. 그러면 Double 타입이 채택하고 준수해야 하는 수많은 프로토콜은 어디로 갔을까? 어디에서 채택하고 어디에서 준수하도록 정의되어 있을까?</p>
<p>답은 익스텐션이다. 이처럼 스위프트 표준 라이브러리 타입의 기능은 대부분 익스텐션으로 구현되어 있다. Double 외에도 다른 타입들의 정의와 익스텐션을 찾아보면 더 많은 예를 볼 수 있다.</p>
<hr>
<h3 id="구현">구현</h3>
<h4 id="연산-프로퍼티-추가">연산 프로퍼티 추가</h4>
<ul>
<li><p>아래 익스텐션은 Int 타입에 두 개의 연산 프로퍼티를 추가한 것이다.</p>
</li>
<li><p>Int 타입의 인스턴스가 홀수인지 짝수인지 판별하여 Bool타입으로 알려주는 연산 프로퍼티이다.</p>
</li>
<li><p>익스텐션으로 Int 타입에 추가해준 연산 프로퍼티는 Int 타입의 어떤 인스턴스에도 사용이 가능하다.</p>
</li>
<li><p>인스턴스 연산 프로퍼티를 추가할 수도 있으며, static 키워드를 사용하여 타입 연산 프로퍼티도 추가할 수 있다.</p>
<pre><code>extension Int {
  var isEven: Bool {
      return self % 2 == 0
  }

  var isOdd: Bool {
      return self % 2 == 1
  }
}
</code></pre></li>
</ul>
<p>print(1.isEven) // 출력: false
print(2.isEven) // 출력: true
print(1.isOdd)  // 출력: true
print(2.isOdd)  // 출력: false</p>
<p>var number: Int = 3
print(number.isEven)     // 출력: false
print(number.isOdd)     // 출력: true</p>
<p>number = 2
print(number.isEven)     // 출력: true
print(number.isOdd)     // 출력: false</p>
<pre><code>
#### 메서드 추가
- 메서드 익스텐션을 통해 Int 타입에 인스턴스 메서드인 multiply(by:) 메서드를 추가했다.
- 여러 기능을 여러 익스텐션 블록으로 나눠서 구현해도 전혀 문제가 없다.
- 관련된 기능별로 하나의 익스텐션 블록에 묶어주는 것도 좋다.</code></pre><p>extension Int {
    func multiply(by n: Int) -&gt; Int {
        return self * n
    }
}</p>
<p>print(3.multiply(by: 2))        // 출력: 6
print(4.multiply(by: 5))        // 출력: 20</p>
<p>number = 3
print(number.multiply(by: 2))   // 출력: 6
print(number.multiply(by: 3))   // 출력: 9</p>
<pre><code>
#### 이니셜라이저 추가
- 인스턴스를 초기화(이니셜라이즈)할 때 인스턴스 초기화에 필요한 다양한 데이터를 전달받을 수 있도록 여러 종류의 이니셜라이저를 만들 수 있다. 타입의 정의부에 이니셜라이저를 추가하지 않더라도 익스텐션을 통해 이니셜라이저를 추가할 수 있다.
- 익스텐션으로 클래스 타입에 편의 이니셜라이저는 추가할 수 있지만, 지정 이니셜라이저는 추가할 수 없다. 지정 이니셜라이저와 디이니셜라이저는 반드시 클래스 타입의 구현부에 위치해야 한다. (값 타입은 상관 없음)
</code></pre><p>extension String {
    init(int: Int) {
        self = &quot;(int)&quot;
    }</p>
<pre><code>intit(double: Double) {
    self = &quot;\(double)&quot;
}</code></pre><p>}</p>
<p>let stringFromInt: String = String(int: 100)
// &quot;100&quot;</p>
<p>let stringFromDouble: String = String(double: 100.0)
// &quot;100.0&quot;
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] MVC 모델 ]]></title>
            <link>https://velog.io/@jamong-i/iOS-MVC-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@jamong-i/iOS-MVC-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Wed, 18 Jan 2023 06:59:39 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://fastcampus.co.kr/courses/205949/clips/">패스트캠퍼스</a>] <strong>30개 프로젝트로 배우는 iOS 앱 개발 with Swift 초격차 패키지 Online</strong>을 학습하면서 나오는 이론 내용과 공식 문서에서 필요한 내용을 발췌하여 정리한 글입니다.</p>
<blockquote>
<p>Part2. Basic 1. 명언 생성기 앱 만들기</p>
</blockquote>
<h3 id="mvc-디자인-패턴">MVC 디자인 패턴</h3>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/f61eee68-efa0-4ecb-8a01-1474390b2889/image.png" alt=""></p>
<p>UIKit에서는 대부분 MVC 디자인 패턴을 사용한다.
M = Model (무엇을)
V = View (보여주다)
C = Controller (어떻게)</p>
<ul>
<li><p><strong>모델</strong>은 앱의 데이터와 비지니스 로직을 가지고 있다.</p>
</li>
<li><p><strong>뷰</strong>는 사용자에게 데이터를 보여주는 Ui를 담당한다.</p>
</li>
<li><p><strong>컨트롤러</strong>는 모델과 뷰의 중간다리 역활로 뷰로 부터 사용자 액션을 전달 받아서 모델에게 어떤 작업을 해야하는지 알려주거나 모델의 데이터 변화를 뷰에게 전달하여 뷰를 어떻게 업데이트 할지 알려주는 역활을 한다.</p>
</li>
</ul>
<h3 id="문제점">문제점</h3>
<p><img src="https://velog.velcdn.com/images/jamong-i/post/01e1e9c3-7206-41f8-98bd-eef7f5a0704b/image.png" alt=""></p>
<p>그림처럼 MVC 패턴에서는 뷰와 뷰컨트롤러가 강하게 연결되어 있어 뷰컨트롤러가 거의 모든 일을 담당하고 뷰컨트롤러는 컨트롤러가 뷰의 라이프 사이클을 관여하기 때문에 뷰와 컨트롤러를 분리하기 힘들다.</p>
<p>그렇기 때문에 프로젝트가 커질수록 컨트롤러가 비대해지고 내부구조는 복잡하게 되어 유지보수가 힘들어지게 된다.</p>
<p>이 문제를 해결하기 위해서 MVVM, VIPER 패턴 등 다양한 디자인 패턴을 통해 MVC 단점을 해결 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift 야곰] 2단원. 프로토콜]]></title>
            <link>https://velog.io/@jamong-i/Swift-%EC%95%BC%EA%B3%B0-2%EB%8B%A8%EC%9B%90.-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C</link>
            <guid>https://velog.io/@jamong-i/Swift-%EC%95%BC%EA%B3%B0-2%EB%8B%A8%EC%9B%90.-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C</guid>
            <pubDate>Wed, 18 Jan 2023 06:26:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>Day 12</strong> - 2023.01.18</p>
</blockquote>
<p>스위프트의 프로토콜은 다른 언어에서의 추상 클래스 혹은 인터페이스와 동치하여 생각하면 안된다.</p>
<h3 id="프로토콜protocol">프로토콜(Protocol)</h3>
<ul>
<li><p>프로토콜은 특정 역활을 수행하기 위한 메서드, 프로퍼티, 기타 요구사항을 정의한다. 
(어떤 타입: 구조체나 클래스나 열거형에다가 기능을 담는것을 요구하는 것과 같음)</p>
</li>
<li><p>구조체, 클래스, 열거형은 프로토콜을 채택(Adopted)해서 프로토콜의 요구사항을 실제로 구현할 수 있다.</p>
</li>
<li><p>어떤 프로토콜의 요구사항을 모두 따르는 타입은 그 프로토콜을 준수한다(Conform)고 표현한다.</p>
</li>
<li><p>타입에서 프로토콜의 요구사항을 충족시키려면 프로토콜이 제시하는 기능을 모두 구현해야 한다. 즉, 프로토콜은 기능을 정의하고 제시 할 뿐이지 스스로 기능을 구현하지는 않는다.</p>
</li>
</ul>
<h3 id="정의">정의</h3>
<ul>
<li>Protocol 키워드를 사용하여 정의한다.</li>
</ul>
<pre><code>protocol 프로토콜 이름 {
    /* 정의부 */
}</code></pre><h3 id="구현">구현</h3>
<p>&lt;&lt;프로퍼티 요구&gt;&gt;</p>
<ul>
<li>프로퍼티 요구는 항상 var 키워드를 사용한다.</li>
<li>get은 읽기만 가능해도 상관없다는 뜻이며 get과 set을 모두 명시하면 읽기 쓰기 모두 가능한 프로퍼티여야 한다.</li>
</ul>
<pre><code>protocol Talkable {
    // 프로퍼티 요구
    var topic: String { get set }
    var language: String { get }

    // 메서드 요구
    func talk()

    // 이니셜라이저 요구
    init(topic: String, language: String)
}</code></pre><h3 id="프로토콜-채택-및-준수">프로토콜 채택 및 준수</h3>
<pre><code>// Person 구조체는 Talkble 프로토콜을 채택했다.
struct Person: Talkable {
    // 프로퍼티 요구 준수
    var topic: String
    let language: String

    // 읽기전용 프로퍼티 요구는 연산 프로퍼티로 대체가 가능하다.
    // var language: String { return &quot;한국어&quot; }

    // 물론 읽기, 쓰기 프로퍼티도 연산 프로퍼티로 대체할 수 있다.
    // var subject: String = &quot;&quot;
    // var topic: String {
    //    sef {
    //        self.subject = newValue
    //  }
    //  get {
    //        return self.subject
    //  }
 // }


    // 메서드 요구 준수
    func talk() {
        print(&quot;\(topic)에 대해 \(language)로 말한다.&quot;)
    }

    // 이니셜라이저 요구 준수
    init(topic: String, language: String) {
        self.topic = topic
        self.language = laguage
    }
}</code></pre><p>프로퍼티 요구는 다양한 방법으로 해석 및 구현 할 수 있다.</p>
<pre><code>struct Person: Talkable {
    var subject: String = &quot;&quot;


    // 프로퍼티 요구는 연산 프로퍼티로 대체가 가능하다.
    var topic: String {
        sef {
            self.subject = newValue
        }
        get {
            return self.subject
        }
    }

    var language: String { return &quot;한국어&quot; }

    func talk() {
        print(&quot;\(topic)에 대해 \(language)로 말합니다.&quot;)
    }

    init(topic: String, language: String) {
        self.topic = topic
    }
}
</code></pre><h3 id="프로토콜-상속">프로토콜 상속</h3>
<ul>
<li>프로토콜은 하나 이상의 프로토콜을 상속받아 기존 프로토콜의 요구사항보다 더 많은 요구사항을 추가할 수 있다.</li>
<li>프로토콜 상속 문법은 클래스의 상속 문법과 유사하지만, 프로토콜은 클래스와 다르게 다중상속이 가능하다.</li>
</ul>
<pre><code>protocol 프로토콜 이름: 부모 프로토콜 이름 목록 {
    /* 정의부 */
}</code></pre><pre><code>protocol Readable {
    func read()
}

protocol Writeable {
    func write()
}

protocol ReadSpeakable: Readalbe {
    func speak()
}

protocol ReadWriteSpeakable: Readable, Writeable {
    func speak()
}


struct SomeType: ReadWriteSpeakable {
    func read() {
        print(&quot;Read&quot;)
    }

    func write() {
        print(&quot;Write&quot;)
    }

    func speak() {
        print(&quot;Speak&quot;)
    }
}</code></pre><p>&lt;&lt; 클래스 상속과 프로토콜 &gt;&gt;
클래스에서 상속과 프로토콜 채택을 동시에 하려면 클래스를 먼저 명시하고 그 뒤에 채택할 프로토콜 목록을 작성합니다.</p>
<pre><code>calss SuperClass: Readable {
    func read() {}
}

class subClass: SuperClass, Writealbe, ReadSpeakable {
    func write() {}
    func speak() {}
}</code></pre><h3 id="프로토콜-준수-확인">프로토콜 준수 확인</h3>
<p><code>is</code>, <code>as</code> 연산자를 사용해서 인스턴스가 특정 프로토콜을 준수하는지 확인할 수 있다.</p>
<pre><code>let sup: SuperClass = SuperClass()
let sub: SubClass = SubClass()

var someAny: Any = sup
someAny is Readable            // true
someAny is ReadSpeakable    // false

someAny = sub
someAny is Readable            // true
someAny is ReadSpeakable    // true

someAny = sup

if let someReadable: Readable = someAny as? Readable {
    someReadable.read()
}        // 출력 : read

if let someReadSpeakable: ReadSpeakable = someAny as? ReadSpeakable {
    someReadSpeakable.speak()
}        // 출력 : 동작하지 않음

somAny = sun

if let someReadable: Readable = someAny as? Readanle {
    someReadanle.read()
}        // 출력 : read</code></pre>]]></description>
        </item>
    </channel>
</rss>