<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bo_bo_.log</title>
        <link>https://velog.io/</link>
        <description>꿈꾸자 그리고 그것을 이뤄내자</description>
        <lastBuildDate>Thu, 11 Sep 2025 12:03:01 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bo_bo_.log</title>
            <url>https://velog.velcdn.com/images/bo_bo_/profile/13c718ab-a996-4eb8-b93a-fbd93ee7a2bf/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bo_bo_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bo_bo_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Product Card UI (UI챌린지 1일차)]]></title>
            <link>https://velog.io/@bo_bo_/Product-Card-UI-UI%EC%B1%8C%EB%A6%B0%EC%A7%80-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bo_bo_/Product-Card-UI-UI%EC%B1%8C%EB%A6%B0%EC%A7%80-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 11 Sep 2025 12:03:01 GMT</pubDate>
            <description><![CDATA[<p>최근, 업무를 하면서 머리 속에서 생성되던 &#39;디자인 리소스나 레이아웃 아이디어들이 고갈되고 있다&#39;는 생각을 했다.
프론트 개발을 하는데 있어서도 이 부분이 시간을 많이 잡아먹기 시작했고,
이를 해결해보고자 각 잡고 공부 해 보자는 심정으로 챌린지를 시작했다!</p>
<h2 id="1일차-과제-product-card-만들기">1일차 과제: Product Card 만들기</h2>
<p>조건은 위시 리스트 버튼, 장바구니 버튼, 상품 이미지, 상품 제목, 상품 description과 shade 정도였다.
Figma를 사용해서 만들었고, 최대한 오토레이아웃(Auto Layout)을 사용해서 만들었으며, 필요한 경우 절대 위치를 사용했다.
[디자인 가이드 반영 전 결과물]
<img src="https://velog.velcdn.com/images/bo_bo_/post/76f74112-eabe-4a10-9b9b-3a396439bedb/image.png" alt=""></p>
<p>자세히 레이아웃을 뜯어보고 싶은 경우 아래의 피그마 링크를 이용하면 된다!
<a href="https://www.figma.com/design/kLFglJmwqBgrA4lQIubflQ/Daily-UI-Challenge--1?node-id=0-1&amp;t=1akzvyyp42Jw6Mex-1">👉 피그마 바로가기</a></p>
<p>챌린지는 SquarePlanet을 통해서 진행하고 있으며, 나 같은 경우 pro를 결제했는데, 그 이유는 Michał이 작성한 해답을 볼 수 있기 때문!
내가 해답을 보고 따라하는 것은 절대 아니다...
다만, 내가 한 방법과 이 챌린지 출제자의 의도가 일치하는 부분이 있는지, 다르게 구현한 부분이 있는지, 나보다 더 뛰어난 방법이 있는지를 알 수 있어서 좋다(부가세 포함 $11 이지만...😭)</p>
<h3 id="michał의-방식과-내-방식에서-차이점">Michał의 방식과 내 방식에서 차이점</h3>
<p>둘 사이에 다른 점은 다음과 같았다</p>
<ol>
<li>설계 순서</li>
</ol>
<ul>
<li>나: 외부 &gt; 내부</li>
<li>Michał: 가로의 넓이에 가장 크게 영향을 주는 컴포넌트 먼저(버튼)</li>
</ul>
<ol start="2">
<li>디자인 규칙</li>
</ol>
<ul>
<li>나: 없음...^^</li>
<li>Michał: 폰트, 간격, 색상 선택, radius 등에서 strict한 rule이 있었음.
자세한 내용들이 무지하게 많았지만 이것을 블로그에 공유해도 되는지는 한 번 알아보고 올려보겠다!</li>
</ul>
<h4 id="참고">참고!</h4>
<p>사진 리소스 같은 것이 가장 고민이 될텐데, 사진 리소스를 무료로 제공하는 곳들이 있다.
보통은 Canva 같은 업체를 사용했지만 &gt;&gt; 사진만 &lt;&lt; 필요할 때는 굉장히 불편하고, 저작권도 신경쓰인다.
이번에는 무료로 이용가능한 <strong>pixabay</strong>를 사용했고, 아주 만족스러웠다!
🔗 링크: <a href="https://pixabay.com/ko/">https://pixabay.com/ko/</a></p>
<p>나는 이번 챌린지 설명을 보는 것 만으로도, 그 동안 내가 굉장히 머리 아파하던 부분이 한 번에 해결됨을 느꼈다.
그 동안은 &quot;보기에 좋은&quot; 디자인이면 된다고 생각했던 것 같다.
그렇지만 생각보다 엄격한 디자인의 규칙이 있다는 점이 충격이면서도 이것 때문에 매 번 어떤 지점이 아름답다고 판단하고, 어떤 값을 기본으로 설정해야하는지 고민하던 것이 한 번에 풀려버렸다.</p>
<p>혹시 SquarePlanet에서 챌린지를 하시는 분이 또 있다면 소통하면서 같이 챌린지를 진행하고 싶다.
(댓글에 남겨 주신다면 매우 반가울 것입니다!! 🥹💖)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WIL] iOS숙련 개념 배운 내용 정리]]></title>
            <link>https://velog.io/@bo_bo_/TIL-Cocoa-Touch-Framework</link>
            <guid>https://velog.io/@bo_bo_/TIL-Cocoa-Touch-Framework</guid>
            <pubDate>Sun, 03 Sep 2023 12:25:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>[8/18 ~ 8/24] 기간 동안의 학습 내용</p>
</blockquote>
<h1 id="📌-cocoa-touch-framework">📌 Cocoa Touch Framework</h1>
<h2 id="1-url-session">1) URL Session</h2>
<ul>
<li>Apple <code>Foundation</code> framework에서 제공하는 API.</li>
<li>HTTP 요청을 보내고 받는 기능을 제공하는 class</li>
<li>비동기적인 방식으로 동작하며 데이터를 주고 받기 때문에, 백그라운드에서 작업이 이루어질 수 있음 -&gt; 추후 push 알람 등에도 사용가능</li>
<li>HTTP, HTTPS, FTP, 등 네트워크 대부분의 통신 관련 요청을 보내고 받을 수 있음</li>
<li>파일 업로드, 다운도르 기능을 제공</li>
<li>백그라운드에서 작업이 가능하며, 앱이 백그라운드에 있을 때도 작업을 수행할 수 있음 </li>
<li>Authentication, Cookie, Cache 등의 기능을 제공</li>
</ul>
<blockquote>
<p>여기서 비동기 방식이란? </p>
</blockquote>
<p>우리가 서버에서 데이터를 받아올 때는 Json 데이터 형식으로 받아옴
Json 데이터 형식을 받아올 때 해당 데이터에 대한 경로를 서버에서 찾아야하고(path), completion handler를 이용해서 함수가 어떤시점에 특정 동작을 할지 지정</p>
<pre><code class="language-swift">class URLManager {
    static let shared = URLManager()
    let url = URL(string: &quot;https://jsonplaceholder.typicode.com/todos&quot;)!

    private init() { }

    func getJsonData(path: String, completion: @escaping (Result&lt;Data, Error&gt;) -&gt; Void) {
        // Result type을 가진 completion handler를 정의
        let task = URLSession.shared.dataTask(with: url.appending(path: path)) 
{ data, response, error in
            if let error {
                print(&quot;Error: \(error.localizedDescription)&quot;)
                completion(.failure(NetworkError.unknown(error.localizedDescription)))
                return
            }
            guard let httpResponse = response as? HTTPURLResponse,
                  (200...299).contains(httpResponse.statusCode) else {
                print(&quot;Error: invalid response&quot;)
                completion(.failure(NetworkError.invalidResponse))
                return
            }

            guard let data = data else {
                print(&quot;Error: no data&quot;)
                completion(.failure(NetworkError.emptyResponse))
                return
            }
            completion(.success(data))
        }
        task.resume()
    }
}
enum NetworkError: Error {
    case emptyResponse
    case invalidResponse
    case unknown(String)

}</code></pre>
<h2 id="2-segue를-통한-viewcontroller간-데이터-통신">2) Segue를 통한 viewController간 데이터 통신</h2>
<h3 id="👀-segue란">👀 Segue란?</h3>
<p>: Storyboard에서 ViewController 사이의 화살표로 표현하며 화면 전환을 위해 사용하는 인터페이스 요소임</p>
<ul>
<li>다음 ViewController로 데이터를 전달 할 수 있음</li>
<li>화면 전화 수행 전 원하는 작업을 설정, 수행할 수 있음</li>
<li>화면 전환시 애니메이션을 적용할 수 있으며 destivation, source로 구성됨</li>
<li>storyboard에서 <code>identifier</code>를 정해주지 않았을 경우 의도치 않은 동작을 할 가능성이 있음</li>
<li>화면 전환 후 데이터 전달을 하거나 <code>completion handler</code>에서 데이터 전달 처리</li>
</ul>
<blockquote>
<p>구현 방식은 2가지가 있다.</p>
</blockquote>
<ol>
<li><code>Storyboard</code>에서 구성한 <code>segue</code></li>
</ol>
<ul>
<li><code>prepare(for:sender:)</code>에서 처리</li>
<li><code>showDetail</code>이라는 <code>identifier</code>를 사용하여 <code>segue</code> 설정</li>
</ul>
<pre><code class="language-swift">// 첫 번째 화면
class FirstViewController: UIViewController {
    @IBAction func buttonTapped(_ sender: UIButton) {
        performSegue(withIdentifier: &quot;showDetail&quot;, sender: nil)
    }
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == &quot;showDetail&quot; {
            if let secondViewController = segue.destination as? SecondViewController {
                secondViewController.data = &quot;Hello, Second View Controller!&quot;
            }
        }
    }
}
// 두 번째 화면
class SecondViewController: UIViewController {
    var data: String?
    @IBOutlet weak var label: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = data
    }
}</code></pre>
<blockquote>
<ol start="2">
<li><code>code</code>로 구성한 <code>segue</code></li>
</ol>
</blockquote>
<ul>
<li><code>present</code>를 사용해서 <code>completion handler</code>를 사용해 데이터를 전달</li>
</ul>
<pre><code class="language-swift">// 첫 번째 화면
class FirstViewController: UIViewController {
    @IBAction func buttonTapped(_ sender: UIButton) {
        let storyboard = UIStoryboard(name: &quot;Main&quot;, bundle: nil) // 여기서 &quot;Main&quot;은 스토리보드의 이름입니다.
        if let secondViewController = storyboard.instantiateViewController(withIdentifier: &quot;SecondViewController&quot;) as? ViewController2 {
            secondViewController.data = &quot;Hello, Second View Controller!&quot;
            present(secondViewController, animated: true, completion: {
                secondViewController.label.text = secondViewController.data
            })
        }
    }
}

// 두 번째 화면
class SecondViewController: UIViewController {
    var data: String?
    @IBOutlet weak var label: UILabel!
}</code></pre>
<h2 id="3-animation">3) Animation</h2>
<h3 id="💁🏻♀️-push---slide-animation">💁🏻‍♀️ <code>push</code> - slide animation</h3>
<pre><code class="language-swift">// 화면 전환하는 버튼을 누를 때
@IBAction func nextButtonTapped(_ sender: UIButton) {
    performSegue(withIdentifier: &quot;showNext&quot;, sender: nil)
}

// 다음 화면으로 전환할 때
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // 특정 identifier일 경우 data를 가져옴
    if segue.identifier == &quot;showNext&quot; {

        // 수직 방향으로 슬라이드하는 애니메이션 적용
        segue.destination.modalTransitionStyle = .coverVertical

    }
}</code></pre>
<h3 id="💁🏻♀️-modal---fade-animation">💁🏻‍♀️ <code>modal</code> - fade animation</h3>
<blockquote>
<p><strong><code>modalTransitionStyle</code></strong>의 종류</p>
</blockquote>
<ol>
<li><code>coveVertical</code>: 아래에서 세로로 다음 <code>ViewController</code>가 올라오는 방식으로 default 방식</li>
<li><code>filpHorizontal</code>: 수직으로 가운데를 축으로 삼아 가로방향으로 카드가 뒤집히는 듯한 효과를 냄</li>
<li><code>crossDissolve</code>: 화면이 교차되는 효과</li>
<li><code>partialCurl</code>: 종이를 넘기는 듯한 효과</li>
</ol>
<pre><code class="language-swift">// 다음 화면으로 전환하는 버튼을 누를 때
@IBAction func nextButtonTapped(_ sender: UIButton) {
    performSegue(withIdentifier: &quot;showNext&quot;, sender: nil)
}

// 다음 화면으로 전환할 때
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == &quot;showNext&quot; {
        // 페이드 애니메이션 
        segue.destination.modalTransitionStyle = .crossDissolve 적용
    }
}</code></pre>
<blockquote>
<p>💁🏻‍♀️ <strong><code>presentationStyle</code></strong>의 종류
: 지금의 화면이 얼마 정도의 크기인지에 영향을 받음</p>
</blockquote>
<ul>
<li>세로 상태 - 가로(<code>Compact</code>), 세로(<code>Regular</code>)</li>
<li>가로 상태 - 가로(<code>Regular</code>), 세로(<code>Compact</code>)</li>
</ul>
<ol>
<li><code>fullScreen</code>: <code>presentationStyle</code>의 기본값, 다른 <code>presentationStyle</code>을 적용할 수 없는 경우 <code>fullScreen</code>으로 동작</li>
<li><code>pageSheet</code>: 가로가 <code>compact</code>일 경우 <code>fullScreen</code>과 동작이 동일, sheet를 띄우듯이 위와 양옆에 여백을 두고 아래에 반투명 View를 배치</li>
<li><code>formSheet</code>: 가로가 <code>Regular</code>일 때 content 영역의 크기를 유지해주고 아래에 반투명 View를 배치. 가로모드이고 키보드가 나온다면 View가 위로 올라가는 처리를 자동으로 해줌 (가로 <code>compact</code>는 <code>fullScreen</code>)</li>
<li><code>currentContext</code>: <code>definesPresentationContext</code>가 <code>true</code>로 설정되어 있는 Viewcontroller의 영역에 맞춰 새로운 view가 띄워짐. true인 ViewController가 없는 경우 FullScreen으로 동작</li>
</ol>
<ul>
<li>일반 View : <code>false</code>가 기본값</li>
<li><code>navigationController</code>등 : <code>true</code>가 기본값
화면에 2개 이상의 ViewController가 영역을 차지할 때(SplitViewController 등) 하나만 바꿀경우 유용하게 사용됨</li>
</ul>
<ol start="5">
<li><code>overFullScreen</code>: <code>fullScreen</code>과 비슷하지만 띄워지는 View가 투명할 경우 아래 쌓인 View가 비쳐보임(<code>fullScreen</code>에서는 아래의 view가 계층에서 지워지기 때문에 이를 방지하고 싶은 경우 사용)</li>
<li><code>overCurrentContext</code>: <code>currentContext</code>와 동일하고 <code>overFullScreen</code>처럼 아래 쌓인 View가 지워지지 않기 때문에 비쳐 보임</li>
<li><code>popover</code>: 특정 위치에서 팝업창처럼 뜨는 형태. 두 가지의 필수 옵션(가로가 <code>regular</code>인 경우만 적용)</li>
</ol>
<ul>
<li>어느 정도의 크기? - <code>preferredContentSize</code>를 ViewController에 설정</li>
<li>어떤 view를 띄울 것인가? - <code>popoverPresentationController</code> 프로퍼티로 설정. 두 가지 방법 중에 <strong>반드시 하나만 적용</strong> 되어야 함
👆 barButtonItem 프로퍼티를 설정
✌️ sourceView와 sourveRect 프로퍼티를 설정</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WIL] UIKit 및 Swift 내용 정리 - SNS 어플 팀프로젝트를 마치며 ]]></title>
            <link>https://velog.io/@bo_bo_/WIL-UIKit-%EB%B0%8F-Swift-%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC-SNS-%EC%96%B4%ED%94%8C-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EB%A7%88%EC%B9%98%EB%A9%B0</link>
            <guid>https://velog.io/@bo_bo_/WIL-UIKit-%EB%B0%8F-Swift-%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC-SNS-%EC%96%B4%ED%94%8C-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EB%A7%88%EC%B9%98%EB%A9%B0</guid>
            <pubDate>Sun, 03 Sep 2023 06:26:51 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>[8/14~8/20] 사이 간단한 SNS 어플리케이션을 만드는 프로젝트를 진행 배운 점을 정리해보고자 한다. </p>
</blockquote>
<h1 id="📌-scrollview와-tableview에-대한-생각의-전환">📌 ScrollView와 TableView에 대한 생각의 전환</h1>
<p>내가 맡은 부분은 프로필 화면이었고 개인의 프로필 정보가 나오는 동시에 아래쪽에 tableView를 이용해서 자신이 쓴 일기를 확인할 수 있어야 했다.</p>
<p>tableView는 ScrollView의 자식이기 때문에 Scroll이 가능하지만 화면 아래쪽만 스크롤이 되는 것이 사용성에 좋지 않을 것 같았다. </p>
<p>처음에는<code>ScrollView</code>안에 다른 View들을 넣고 거기에 TableView도 넣는 방식으로 했으나, 잘 되지 않았다. 이를 해결하기 위해서 하루 동안 찾아보고 이리저리 시도하며 끙끙 앓다가 튜터님을 결국 찾아갔는데, 튜터님은 새로운 관점을 제시해주셨다!!</p>
<blockquote>
<p>화면 전체를 tableView로 사용, cell이외의 내용들은 다른 cell을 이용하거나 tableView의 Header를 사용해보시는게 좋을 것 같습니다!</p>
</blockquote>
<p>그 순간 머리가 깨지는 느낌(?)이었다. 결국 tableView를 이용해서 완성하는데 성공했고 스토리보드로 header를 간단하게 만들어서 빠르게 완성할 수 있었다. 더 중요하다고 생각되는 부분은 iOS 개발에 나의 생각보다 tableView가 더 다양하게 자주 사용된다는 것을 알게된 것!</p>
<pre><code class="language-swift">
// viewDidLoad
override func viewDidLoad() {
    super.viewDidLoad()

    // 테이블뷰에 HeaderFooterView 등록
    myDiaryTable.register(UITableViewHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: &quot;customHeader&quot;)

    // subview 관계
    view.addSubview(myDiaryTable)
    myDiaryTable.addSubview(profileBox)
    profileBox.addSubview(profileCard)
    profileCard.addSubview(profileImg)

    // tableView의 delegate와 dataSource
    myDiaryTable.delegate = self
    myDiaryTable.dataSource = self

    // profile 정보 load
    loadProfile()
}</code></pre>
<h1 id="📌-layer-개념">📌 layer 개념</h1>
<h2 id="1-calayer와-uiview의-차이점">1. CAlayer와 UIView의 차이점</h2>
<blockquote>
<p>CAlayer의 CA -&gt; <code>Core Animation</code>의 약자</p>
</blockquote>
<h3 id="1-calayer-등장의-배경">(1) CAlayer 등장의 배경</h3>
<p>어플리케이션 작동 시 부드러운 작동을 위해 최소 초당 60프레임 필요
➡️ <code>OpenGL</code>의 탄생 - 그래픽 하드웨어에 가장 빠르게 엑세스</p>
<p>하지만 요구 기능이 많아지고 그에 따라 코드의 양이 방대해지니 이보다 좀 더 적은 코드로 구현할 수 있는 것이 필요했음</p>
<p>➡️ <code>Core Graphics</code>의 탄생 - CGColor, CGRect 등</p>
<p>이것을 더 사용성이 좋게 단순화 한 것이</p>
<p>➡️ <code>CAlayer</code></p>
<p>그리고 CAlayer에 있는 고급 그래픽 기능들을 개발하며 모두 이용하지는 않기 때문에 이를 더 간소화 한 것이 </p>
<p>➡️ <code>UIKit</code> - 사용은 간편하지만 기능이 제한되어 있다는 단점도 있음</p>
<h3 id="2-uiview와-calayer-구조의-상관관계">(2) UIView와 CAlayer 구조의 상관관계</h3>
<blockquote>
<p><code>UIView</code>를 이용해서 쉽게 그리고 간편하게 화면을 그릴 수 있다. <code>UIView</code>가 레이아웃 터치 이벤트 등 많은 작업을 처리하는데 사실은 View의 <code>sublayer</code>로 있는 <code>CAlayer</code> 타입의 <code>property</code>인 <code>Core Animation</code>에 시각적 행위들을 위임한다.</p>
</blockquote>
<ul>
<li><p>** UIView**는 하나의 <code>sublayer</code>인 <code>root CAlayer</code>만을 갖고 있다.
<code>UIView</code>에 <code>subview</code>를 넣게 되면 <code>UIView</code> 안으로 직접 들어가는 것이 아니라 <code>root CAlayer</code> 안으로 들어가게 된다.</p>
</li>
<li><p>하나의 <code>root CAlayer</code>는 여러개의 <code>sublayer</code>를 만들어서 가질 수 있고 이를 이용해서 다양한 그래픽 작업들을 처리할 수 있다. </p>
</li>
</ul>
<p>💁🏻‍♀️ <strong>사용법</strong>
별건 아니지만 이번 프로젝트에서 UI를 통일시켜야 해서 layer를 이용해서 같은 코드를 여러번 써야했다. 때문에 method를 만들고 UIView를 파라미터로 받아서 원하는 layout을 그릴 수 있게 코드를 작성해봤다!!</p>
<pre><code class="language-swift">func viewLayout(_ view: UIView) {
    // 테두리 둥글게
    view.layer.cornerRadius = 30

    // 그림자 방향 이동 width ➡️, height ⬇️
    view.layer.shadowOffset = CGSize(width: 0, height: 0)

    // 그림자의 투명한 정도, 1이 가장 불투명, 0은 투명
    view.layer.shadowOpacity = 0.3

    // 그림자가 퍼지는 정도
    view.layer.shadowRadius = 10
}

// 사용 시

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
    let cell = myDiaryTable.dequeueReusableCell(withIdentifier: &quot;cell&quot;, for: indexPath) as! MyDiaryTableViewCell

    // ... 중략

    // cell card 인스턴스 생성 및 view layout
    let card = cell.cellCard!
    viewLayout(card)
    viewLayout(cell.postImage)
    viewLayout(cell)

    return cell
}</code></pre>
<p>➡️ 참고 자료 및 출처 : <a href="https://babbab2.tistory.com/53">https://babbab2.tistory.com/53</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] LifeCycle, UserDefaults, Dependency, Network]]></title>
            <link>https://velog.io/@bo_bo_/TIL-LifeCycle-UserDefaults-Dependency-Network</link>
            <guid>https://velog.io/@bo_bo_/TIL-LifeCycle-UserDefaults-Dependency-Network</guid>
            <pubDate>Wed, 23 Aug 2023 09:51:43 GMT</pubDate>
            <description><![CDATA[<h1 id="1-uiviewcontroller의-life-cycle">1. UIViewController의 Life Cycle</h1>
<h2 id="1-init">1) init()</h2>
<blockquote>
<p>UIViewController 객체가 생성되는 부분이다.
해당 초기화 메서드에서 필요한 초기화 작업들을 수행해주면 된다.</p>
</blockquote>
<h2 id="2-loadview">2) loadView()</h2>
<blockquote>
<p>Controller 내의 View 계층 구조가 생성되는 부분으로 직접 View를 생성해서 할당한다.</p>
</blockquote>
<pre><code class="language-swift">override func loadView() {
    self.view = UIView
}</code></pre>
<h2 id="3-viewdidload">3) viewDidLoad()</h2>
<blockquote>
<p>View의 계층 구조가 메모리에 로드 된 상태로 초기화 작업을 수행한다. 뷰에 대해서 추가적인 구성 작업 수행이 필요할 때 사용</p>
</blockquote>
<pre><code class="language-swift">override func viewDidLoad() {
    super.viewDidLoad()
    // View에 대해서 추가로 구성할 것이 있다면 여기에 작성
}</code></pre>
<h2 id="4-viewwillappear">4) viewWillAppear()</h2>
<blockquote>
<p>View가 화면에 나타나기 직전에 호출되는 method로 View의 상태를 update 해야하거나 animation을 시작할 때 사용한다. </p>
</blockquote>
<pre><code class="language-swift">override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // View가 나타나기 직전에 수행되어야 할 작업을 작성
}</code></pre>
<h2 id="5-viewdidappear">5) viewDidAppear()</h2>
<blockquote>
<p>View가 화면에 나타나면 호출되는 method로 Animation을 종료하거나 View의 상태를 update할 때 사용한다.</p>
</blockquote>
<pre><code class="language-swift">override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    // View가 나타난 직후에 수행되어야 할 작업을 작성
    //ㅕ UI 재조정 시에도 사용
}</code></pre>
<h2 id="6-viewwilldisappear">6) viewWillDisappear()</h2>
<blockquote>
<p>View가 화면에서 사라지기 직전에 호출되는 method로 데이터를 저장하거나 Animation을 시작할 때 사용.</p>
</blockquote>
<pre><code class="language-swift">override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    // View가 사라지기 직전에 수행되어야 할 작업을 작성
}</code></pre>
<h2 id="7-viewdiddisappear">7) viewDidDisappear()</h2>
<blockquote>
<p>View가 화면에서 사라지면 호출되는 method로 animation을 종료하거나 View의 상태를 update할 때 사용한다.</p>
</blockquote>
<pre><code class="language-swift">override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    // View가 사라진 직후에 수행되어야 할 작업을 작성
}</code></pre>
<h2 id="8-deinit">8) deinit</h2>
<blockquote>
<p>UIViewController 객체가 메모리에서 해제
deinit {
        // 뷰 컨트롤러가 메모리에서 해제되기 전에 수행할 작업 수행
}</p>
</blockquote>
<h1 id="2-app이-돌아가는-전반적인-흐름">2. App이 돌아가는 전반적인 흐름</h1>
<h2 id="1-application의-작동-흐름">1) Application의 작동 흐름</h2>
<ol>
<li>앱이 <strong>실행</strong> - 사용자 : 앱을 터치</li>
<li>main.swift에서<code>@main 함수가 호</code>출되어 <code>UIApplication</code> 객체 생성.</li>
<li><code>AppDeldgate</code> 객체 생성<blockquote>
<ul>
<li><code>UIApplication</code> 객체는 <code>AppDelegate</code> 객체를 delegate로 설정.<ul>
<li><code>AppDelegate</code>는 <code>UIResponder</code>, <code>UIApplicationDelegate</code>를 상속 및 참조</li>
</ul>
</li>
</ul>
</blockquote>
</li>
<li><code>AppDelegate</code> 객체의 <code>application(_:didFinishLaunchingWithOptions:)</code> 메서드를 호출<blockquote>
<p>어플 실행 시 <code>UIApplication</code> 객체는 <code>AppDelegate</code> 객체의 <code>application(_:didFinishLaunchingWithOptions:)</code> 메서드를 호출해서 앱의 초기화를 수행</p>
</blockquote>
</li>
<li><code>Root view controller</code>의 생성<blockquote>
<p>AppDelegate 객체는 <code>UIWindow</code>를 생성하고, 이 윈도우에 <code>Root view controller</code>를 할당합니다.</p>
</blockquote>
</li>
<li><code>Root view controller</code>의 <code>view</code>가 생성됩니다.<blockquote>
<ul>
<li>Root view controller의 view는 로드됩니다. 이때 <code>viewDidLoad()</code> 메서드가 호출됩니다.<ul>
<li><code>UIViewController</code>의 Life cycle에 따라 동작</li>
</ul>
</li>
</ul>
</blockquote>
</li>
<li>사용자가 앱을 조작합니다.<blockquote>
<ul>
<li>앱의 사용자 인터페이스에서 사용자의 입력에 따라 함수가 호출.</li>
</ul>
</blockquote>
</li>
<li>앱이 Background 상태로 전환됩니다.<blockquote>
<ul>
<li>앱이 Background 상태로 전환되면, <code>applicationWillResignActive()</code>함수가 호출. 이 함수는 앱이 Background로 전환되기 전에 실행해야 하는 작업을 수행합니다.</li>
</ul>
</blockquote>
</li>
<li>앱이 Foreground 상태로 전환됩니다.<blockquote>
<ul>
<li>앱이 Foreground 상태로 전환되면, <code>applicationDidBecomeActive()</code> 함수가 호출됩니다. 이 함수는 앱이 Foreground로 전환된 후 실행해야 하는 작업을 수행합니다.</li>
</ul>
</blockquote>
</li>
<li>앱이 종료됩니다.<blockquote>
<ul>
<li><code>applicationWillTerminate()</code>함수가 호출됩니다. 이 함수는 앱이 종료될때 수행</li>
</ul>
</blockquote>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] Cocoa Touch Framework, MVC]]></title>
            <link>https://velog.io/@bo_bo_/TIL-Cocoa-Touch-Framework-MVC</link>
            <guid>https://velog.io/@bo_bo_/TIL-Cocoa-Touch-Framework-MVC</guid>
            <pubDate>Wed, 23 Aug 2023 08:34:16 GMT</pubDate>
            <description><![CDATA[<h1 id="1-cocoa-touch-framework">1. Cocoa Touch Framework</h1>
<blockquote>
<p>Cocoa Touch Framework
Apple 기기에서 구동되는 App을 개발하기 위해 사용되는 통합 프레임워크
(별도로 MacOS 개발 시에는 Cocoa Framework를 사용한다고 한다.)</p>
</blockquote>
<h2 id="1-url-session">1) URL Session</h2>
<blockquote>
<p>Apple이 제공하는 Foundation framework에서 제공하는 API로 HTTP 요청을 보내고 받는 기능을 제공한다.
비동기적인 방식으로 동작하기 때문에 백그라운드에서 작업이 이루어질 수 있다!</p>
</blockquote>
<ul>
<li><p>여기서 <code>비동기적인 방식</code>이란 다른 task의 결과가 나오기 이전이라도 즉, 실행되는 와중이라도 실행되는 방식임을 의미한다. 우리가 음악을 틀고 웹 서핑을 하거나 다른 어플을 켜도 노래가 끊기지 않는 이유는 비동기 방식이기 때문!</p>
</li>
<li><p><strong>특징</strong>: 다양한 네트워크 통신 프로토콜을 제공(HTTP, HTTPS, FTP)하며 파일을 업로드하고 다운로드 하는 기능을 제공한다. Authentication, Vookie, Cache 등의 웹의 기본적인 데이터에 대한 기능들도 같이 제공한다.</p>
</li>
</ul>
<pre><code class="language-swift">class URLManager {
    static let shared = URLManager()
    //dummy url
    let url = URL(string: &quot;https://jsonplaceholder.typicode.com/todos&quot;)!
    private init() {}

    func getJsonData(path: String, completion: @escaping (Result&lt;Data, Error&gt;) -&gt; Void) {
        // Result type을 가진 completion handler를 정의
        // URLSession에 접근할 때는 shared를 사용해서 접근
        // dataTask라는 함수는 url에 대한 결과값을 가져오게 함
        let task = URLSession.shared.dataTask(with: url.appending(path: path))
// 여기서 path란 DNS 주소 뒤에 붙는 값을 칭함 
// url의 값 뒤에 path를 appending 함으로써 결과값을 task에 할당
{ data, response, error in
            if let error { // 에러 발생 시
                print(&quot;Error&quot;)
                completion(.failure(NetworkError.emptyResponse)
                return
            }
            guard let data = data else {
                print(&quot;Error: no data&quot;)
                completion(.failure(NetworkError.emptyResponse))
                return
            }
            completion(.success(data))
        }
        task.resume()
        //data task에 대한 내용을 실행하는 resume 함수
    }
}
enum NetworkError: Error {
    case emptyResponse
}

// 뷰컨에서 사용할 때
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        URLManager.shared.getJasonData(path: &quot;1&quot;) { result in switch result
        // completion handler로 지정한 값
            case .success(let data):
                print(&quot;success&quot;, data)
            case .failure(_):
                print(&quot;fail&quot;)
            }
        }
    }
}

//결과 success 83 bytes</code></pre>
<h1 id="2-mvc-architecture">2. MVC Architecture</h1>
<blockquote>
<p>Architecture 
앱의 구성요소들의 역할과 책임을 분리하여 유지보수를 용이하게 하여 협업을 손쉽게 만들고 확장성을 높이는 방법</p>
</blockquote>
<p>Architecture의 종류는 다양한 종류가 있지만 오늘은 iOS 개발에서 자주 쓰인다고 하는 MVC Architecture에 대해서 정리해보고자 한다.</p>
<p><img src="https://velog.velcdn.com/images/bo_bo_/post/460ba281-851b-4dd7-89f8-286fa5d01bfe/image.png" alt=""></p>
<p>그림의 내용을 이해하기 쉽도록 순서대로 정리해보자면 다음과 같다!</p>
<p>&lt;Model을 빼고 생각해보자&gt;</p>
<p>a. view를 통해서 (button or textfield 등) user의 action을 받아서 수행
b. controller는 이 action을 탐지하고 update가 가능한 정보를 감지한 경우 이를 view에 update</p>
<p>&lt;Model을 끼워서 생각해보자&gt;</p>
<p>a. view를 통해서 (button or textfield 등) user의 action을 받아서 수행
b. controller는 이 action을 탐지하고 update가 가능한 정보를 감지, 만약 action을 통해 입력된 정보가 Model에 있는 data라면 update가 가능한 경우 이를 Model로 전달
c. Model이 update 완료 후 Controller에게 notify
d. 변경된 정보를 토대로 Controller가 View에 update</p>
<blockquote>
<p>코드로 적용 시 막연하게 잘 이해가 되지 않을 수 있다. 좀 더 간단하게 생각해보자!
M: Model은 Data를 저장할 수 있는 구조를 만든다!(네트워크든 swift 문법을 이용한 것이든!)
V: View는 Model에서 구축한 data를 띄울 수 있어야 한다.
C: Controller는 View의 action을 감지해 Model의 변화를 View에 전달한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KPT] SNS App 팀 프로젝트를 마치며]]></title>
            <link>https://velog.io/@bo_bo_/KPT-SNS-App-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EB%A7%88%EC%B9%98%EB%A9%B0</link>
            <guid>https://velog.io/@bo_bo_/KPT-SNS-App-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EB%A7%88%EC%B9%98%EB%A9%B0</guid>
            <pubDate>Mon, 21 Aug 2023 11:02:52 GMT</pubDate>
            <description><![CDATA[<h1 id="kkeep">K(Keep)</h1>
<h2 id="이번-팀-프로젝트에서-유지하고-싶은-것은">이번 팀 프로젝트에서 유지하고 싶은 것은?</h2>
<ol>
<li><p>기술적인 것은 아니지만. 다른 무엇보다도 화목했던 팀의 분위기가 제일 좋았다. 힘든 부분이 있어도 같이 웃으면서 넘길 수 있었고, 고민하는 부분에 대해 나누면서 같이 해답을 찾아갈 수 있었다. 서로에게 정보를 공유하고 다른 사람이 공유하는 정보를 신경써서 보고, 서로의 말에 귀 기울이던 지난 3주간의 경험은 어디를 가든 계속 가져가고 싶은 경험이었다.</p>
</li>
<li><p>계속 서로를 챙겨서 누구도 뒤쳐지지 않도록 끌어가는 분위기가 좋았다. slack을 통해서 빈번하게 소통을 하고 매일 아침 스크럼을 통해 계획을 공유하고 저녁 회고를 통해 서로의 수행을 확인하면서 긍정적인 자극을 받을 수 있었고, 새로운 관점들을 배울 수 있었다.</p>
</li>
<li><p>이번 프로젝트에서 나는 UI에 관해서 좀 많이 신경 썼던 것 같다. 나의 할당량이 끝나고 시간이 남았던 탓도 있지만, 애플이 추구하는 디자인 스타일을 어플리케이션에 적용하고 싶기도 했다. UI적인 부분에 대해서 신경을 쓰는 것이 사실 어떻게 보면 문법을 익혀야하는 지금의 입장에서는 분수에 맞지 않는 것일 수도 있다. 하지만 애초에 내가 만들고 싶은 어플리케이션에 대한 생각을 다시 짚어 봤을 때 애플이 웹 프론트 엔드나 iOS 내에서 사용하는 UI를 코드로 구현해낼 수 있도록 계속 노력하고 싶다. 앱을 사용하는데 하드웨어와 운영체제간의 이질감이 없도록 사용자 경험을 최대한 살리고 싶기 때문이다!</p>
</li>
</ol>
<h1 id="pproblem--ttry">P(Problem) &amp; T(Try)</h1>
<h2 id="문제라고-느끼는-부분">문제라고 느끼는 부분</h2>
<p>1 - P. 나 자신은 의견을 제시하고 팀원들의 동의를 얻었다고 생각했지만 이런 부분들이 너무 잦아지다 보니 조금은 독단적으로 한다고 생각하는 분위기가 약간은 있었다. 그럴 때마다 나의 의도는 전혀 그런 것이 아니었기 때문에 한 발 물러서며 내가 실수를 했다고 생각했다. 처음에는 적극적으로 의견을 개진하고 동의를 구하는 것이 큰 잘못이라고는 생각하지 않았고 오히려 약간은 서운함도 있었다. 하지만 뒤돌아 생각해보니 당사자가 그렇게 느끼는데는 이유가 있을 것이라고 생각이 들었다.</p>
<p>1 - T. 내 의견을 내지 않는 것도 문제인 것은 맞다. 하지만 좀 더 다른 사람의 의견을 적극적으로 청취하고 수용하는 말투와 자세를 갖추도록 노력해야겠다.</p>
<p>2 - P. 이번 프로젝트를 진행하면서 내가 어플의 생명주기에 대해서 이해하고 있다고 생각한 것이 완전한 착각이었다는 것을 알게되었다. 개념은 어찌저찌 그 문장이 머리속에 들어있어서 그게 뭔지 물으면 대답할 수가 있다. 하지만 막상 화면에 view를 띄워야할 때 띄우지 못하고, 헤매는 나를 보면서 내가 이런식으로 얼렁뚱땅 알고 있는 것들이 생각보다 많을 거라는 판단이 섰다.</p>
<p>2 - T. Velog에 새로운 시리즈를 파서 내가 정확하게 모른다고 생각하는 개념들을 집요하게 파고들면서 iOS를 공부하는 사람이라면 누가봐도 쉽게 이해할 수 있을 정도로 명료하게 정리를 하고 싶다. 매일 한다기 보다는 내가 당장 애매하게 알고 있다는 개념이 생길 때 바로 실행에 옮기고자 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] UserDefaults 사용해서 팀 프로젝트 하기]]></title>
            <link>https://velog.io/@bo_bo_/TIL-UserDefaults-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bo_bo_/TIL-UserDefaults-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 15 Aug 2023 14:59:17 GMT</pubDate>
            <description><![CDATA[<p>사건의 발단...</p>
<p>이번 팀 프로젝트는 SNS 어플리케이션 만들기!
새로운 포스트를 작성하고 포스트들을 보여주는 feed 화면과 한 cell을 눌렀을 때 보이는 detail 화면 그리고 프로필 화면과 프로필 편집 화면 이렇게 5가지가 필요하다!</p>
<p>오늘은 그 중에서도 데이터를 UserDefaults를 활용해서 연결하는 방법을 적어보고자 한다.</p>
<h1 id="💁🏻♀️-userdefaults-초초초기본">💁🏻‍♀️ UserDefaults 초초초기본</h1>
<blockquote>
<p>우선 UserDefaults를 사용하기 위해서는 무조건 <code>Foundation</code>을 import 해야한다!</p>
</blockquote>
<p>우리 팀의 경우 글 작성 - 메인에 View 반환 - detail에 View 반환 - 프로필 tableView에서 cell 반환 시에 동일한 데이터를 사용해야 했다.</p>
<p>때문에 같이 공유해야 하는 데이터를 지정하고 변수명이나 key값 혹은 storyboardID 같은 것은 notion에 적어서 협업하자고 제안하여 notion으로 같이 공유하고 있다.</p>
<p>변수명과 데이터 타입 그리고 키값은 아래와 같이 정했다.</p>
<ul>
<li>이미지명 : [String] - 변수명: postImgNames - key: &quot;postImgNames&quot;</li>
<li>글 제목 : [String] - 변수명: postTitles - key: &quot;postTitles&quot;</li>
<li>작성일 : [String] - 변수명: postDates - key: &quot;postDates&quot;</li>
<li>내용 : [String] - 변수명: postContents - key: &quot;postContents&quot;</li>
</ul>
<p>UserDefaults는 Dictionary 타입이기 때문에 key값을 이용해서 불러와야하며 UserDefaults에서는 String 타입만 Key 값이 될 수 있다.</p>
<h2 id="userdefaults-사용법">&lt; UserDefaults 사용법 &gt;</h2>
<h3 id="📌-데이터-저장">📌 데이터 저장</h3>
<pre><code class="language-swift">let postTitles = [&quot;오늘의 일기&quot;]

UserDefaults.standard.set(postTitles, forKey = &quot;postTitles&quot;)

// 인스턴스를 사용하면 좀 더 짧게 표현할 수 있다

let defaults = UserDefaults.standard
defaults.set(postTitles, forKey = &quot;postTitles&quot;)</code></pre>
<h3 id="📌-데이터-사용">📌 데이터 사용</h3>
<pre><code class="language-swift">let arrayName = defaults.array(forKey = &quot;postTitles&quot;)

// 옵셔널 처리 필요시
let arrayName = defaults.array(forKey = &quot;postTitles&quot;) as? [String] ?? &quot;nil일 때 표현할 문자&quot;</code></pre>
<h1 id="💁🏻♀️-date-picker-값의-format-정하기dateformat">💁🏻‍♀️ Date Picker 값의 Format 정하기(DateFormat())</h1>
<p>작성일과 같이 date picker를 사용해서 데이터를 입력받을 경우 DateFomatter()를 사용하면 원하는 형태의 문자열로 반환받을 수 있다.</p>
<pre><code class="language-swift">let dateFormatter = DateFormatter()
dateFormatter.dateFormat = &quot;yyyy-MM-dd a h시 mm분&quot; // 날짜 format 정하기
dateFormatter.amSymbol = &quot;오전&quot; // 오전 어떻게 표시할지
dateFormatter.pmSymbol = &quot;오후&quot; // 오후 어떻게 표시할지 

let 변수명 = dateFormatter.string(from: self.datepickerOutlet이름.date)
저장할변수명 = 변수명</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WIL] 개인프로젝트에 사용된 iOS UIKit  내용 정리하기]]></title>
            <link>https://velog.io/@bo_bo_/WIL-%EA%B0%9C%EC%9D%B8%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EC%82%AC%EC%9A%A9%EB%90%9C-iOS-UIKit-%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bo_bo_/WIL-%EA%B0%9C%EC%9D%B8%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EC%82%AC%EC%9A%A9%EB%90%9C-iOS-UIKit-%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 12 Aug 2023 13:52:39 GMT</pubDate>
            <description><![CDATA[<p>오늘은 지난 주부터 이번 주까지 iOS 어플 만들기를 시작하면서 처음으로 받게 된 개인 프로젝트에 사용된 기술들을 모두 정리해보면서 다시 한 번 되짚어 보려고 한다!</p>
<p>과제 조건을 잘못 읽어서 의도치 않게 두 개의 버전을 만들었고 주어진 요건을 다 달성하지 못하는 고런 실수를 저질러 버렸는데... 다음부터는 다시 그러지 않겠다 다짐하면서 회고를 시작해보려 한다.
(혹여 시간이 된다면 어플을 초반 기획대로 마무리 해보고자 한다.)</p>
<p>먼저 제출용 버전부터!</p>
<h1 id="💁🏻♀️-story-board를-사용한-segue-화면전환">💁🏻‍♀️ Story Board를 사용한 Segue 화면전환</h1>
<p>Xcode에는 storyboard를 이용해서 어느정도 앱의 레이아웃을 구현할 수 있다. 이는 웹 프론트엔드를 찍먹해 본 나로서는 굉장히 좋아보였고 적극적으로 활용을 해봐야겠다는 생각을 했다.</p>
<p>Segue는 화면 전환에 사용 되는데 화면 전환에는 4가지 종류로 구분해볼 수 있다.</p>
<blockquote>
<ol>
<li>Show(Push)</li>
<li>Show Details(Replace)</li>
<li>Present Modally</li>
<li>Present as Popover</li>
</ol>
</blockquote>
<p>📌 <code>Show(Push)</code> </p>
<p>: UINavigationController의 사용이 요구되는 segue 방식이다. navigation stack 위에 새로운 viewController를 push 하고 특히 어플에서 계층적인 화면이동이 있는 경우에 많이 사용된다. (navigation controller 미사용시 present modally 방식이 default값으로 사용된다.)</p>
<p><code>animates: true</code>일 때, 오른쪽에서 왼쪽으로 새로운 view controller 화면이 슬라이드 되면서 기존 화면을 왼쪽으로 밀어내고 나타난다. 상단에 navigation bar에 title과 back button이 새로운 view controller에 같이 나타난다. (custom 가능) 이전 view controller로 돌아가기 위해서는 back button을 사용하거나 추가적으로 왼쪽 가장자리를 swipe하는 gesture를 사용해서도 이전 화면으로 돌아갈 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/bo_bo_/post/c612a8cf-3e01-4f71-8e47-9288ed4f57ed/image.gif" alt=""></p>
<p>📌 <code>Show Details(Replace)</code> &amp; <code>Present Modally</code> : Show details 방식의 경우 <code>UISplitViewController object</code> 에서만 작동한다. 다른 ViewController 전체를 보여주는 것이 아니라 해당 Controller의 자식인 View를 target으로 삼아서 modal 방식으로 보여준다. modal 방식은 animation 채택 시 아래에서 위로 View를 띄워주는데 navigationBar가 보이지 않기 때문에 view의 내용을 대체할 때만 사용하면 좋을 것 같다.</p>
<p><img src="https://velog.velcdn.com/images/bo_bo_/post/bd6aab92-6884-4fd1-86ba-475462a08139/image.gif" alt=""></p>
<p>📌  <code>Present as Popover</code> : 보통 iPad(아이패드) 어플에서 조그만 창을 현재의 content위에 띄울 때 사용한다고 한다. 추가적인 정보나 옵션을 전체 화면을 차지하지 않고 보여줄 수 있다. 창이 나타날 때 background는 dimmed 혹은  blurred 처리된다. outside를 tapping하는 것으로 popover 할 수 있다. 아이폰의 경우에는 full-screen modal presentation이 기본값이다.(custom 가능)</p>
<h1 id="💁🏻♀️-navigation-bar-설정하기">💁🏻‍♀️ Navigation Bar 설정하기</h1>
<p>NavigationController를 이용하면서 Show 방법을 이용하면 Back버튼이 있는 navigation bar가 다음에 이어지는 ViewController에 나타난다. 이를 Custom 하는 방법을 정리해보고자 한다.</p>
<h2 id="1-storyboard를-이용하는-방법">1. Storyboard를 이용하는 방법</h2>
<p>생성된 navigation bar를 선택하고 우측 메뉴바(Navigation Item)에서 Title에 입력하면 중앙에 title이 나타난다.</p>
<p>우측 상단에 원하는 기능의 버튼을 배치하기 위해서는 navigation item object를 드래그해서 우측 상단에 드랍하면 된다. 사용해보지는 않았지만 우측 상단에 여러 개의 버튼도 배치할 수 있다. Bar Button Item Group을 이용하면 되는 듯 하다. Segue와 같은 Storyboard 상에서 해결되는 작업이 아니면 해당 ViewController의 class에 outlet과 action을 연결해서 action에 코드를 작성해줘야 한다. </p>
<p>Assets에 이미지를 미리 등록해놓으면 끌어다 놓은 아이템을 클릭하고 우측 메뉴바의 Image에서 선택할 수 있으며 스타일이나 색상, 이미지가 아닌 글자를 사용할 경우 title의 설정 등을 해줄 수 있다.
<img src="https://velog.velcdn.com/images/bo_bo_/post/318b406b-ba6e-4088-a406-67c40318407d/image.png" alt=""></p>
<h2 id="2-코드-작성으로-해결하는-방법">2. 코드 작성으로 해결하는 방법</h2>
<p>코드 작성으로는 스토리보드의 기능도 당연히 가능하지만 좀 더 상세한 설정을 해줄 수 있다. 예를들어, back버튼의 글자를 변경하고 색상을 변경하는 등의 작업을 해줄 수 있다. 우측의 navigation bar item도 개별로 설정하고 배열로 선언함으로써 한번에 나타낼 수 있다. 코드로 작성할 경우 차지하는 크기를 잘 지정해줘야 할 것 같다.</p>
<p><code>예시</code></p>
<pre><code class="language-swift">    override func viewDidLoad() {
        super.viewDidLoad()

        // navigaiton back 버튼 검정색 뒤로가기로 변경
        let backBarButtonItem = UIBarButtonItem(title: &quot;뒤로가기&quot;, style: .plain, target: self, action: nil)
        backBarButtonItem.tintColor = .black  // 색상 변경
        self.navigationItem.backBarButtonItem = backBarButtonItem


    }
</code></pre>
<p>설정이 나타나길 원하는 VeiwController로 이동하기 전의 VeiwController에서 <code>UIBarButtonItem()</code>을 사용해서 <code>title</code>에는 back 대신에 나타나기를 원하는 글자를 설정해주면 된다. 인스턴스를 생성하고 <code>.tintColor</code>를 이용하면 색상을 변경할 수 있다. 생성한 인스턴스가 navigation bar의 item이라는 것도 self를 통해 선언해주면 끝이다.</p>
<p>당연하겠지만 우측의 navigation bar item도 코드로 설정할 수 있다.</p>
<p><code>예시</code></p>
<pre><code class="language-swift"> override func viewDidLoad() {
     super.viewDidLoad()
    self.toDoTable.delegate = self
    self.toDoTable.dataSource = self

    // margin(여백) 설정을 위해 configuration 선언
   var configuration = UIButton.Configuration.plain()
        configuration.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 5)

    // UIButton 인스턴스 선언 및 위치 지정, size를 이미지 본연의 사이즈로 지정
    let plusButton = UIButton(frame: CGRect(x: 0, y: 0, width: plusButtonImage.size.width, height: plusButtonImage.size.height))

     // UIButton 색상 설정이 필요할 경우
    plusButton.tintColor = .black

    // Assets에 저장해둔 이미지 인스턴스 생성
    let plusButtonImage = UIImage(named: &quot;plus&quot;)!

    // UIButton을 글자가 아닌 이미지로 변경
    plusButton.setImage(plusButtonImage, for: .normal)

    // UIButton에 configuration 적용
    plusButton.configuration = configuration

    // UIButton 클릭 시 실행할 행동 지정
    plusButton.addTarget(self, action: #selector(plusPressed), for: .touchUpInside)

    // UIBarButtonItem의 인스턴스를 생성하면서 설정한 UIButton의 뷰를 입힘
   let plusBarButton = UIBarButtonItem(customView: plusButton)

    // navigationItem 중 오른쪽 아이템으로 선언
    self.navigationItem.rightBarButtonItems = [plusBarButton]
}</code></pre>
<p>마지막 줄의 코드를 보면 알 수 있듯이 배열을 통해 다수의 <code>rightbarButtonItems</code>를 선언할 수 있다.</p>
<h1 id="💁🏻♀️-uiviewcontroller를-이용해서-uitableview와-uitableviewcell-설정하는-법">💁🏻‍♀️ UIViewController를 이용해서 UITableView와 UITableViewCell 설정하는 법</h1>
<h2 id="1-uitableviewdelegate와-uidatasource">1. UITableViewDelegate와 UIDataSource</h2>
<p>이 부분도 storyboard와 코드로 나눠서 적고 싶지만 개인 프로젝트 기한 내에 공부를 하면서 섞어서 하는 법만 익혔기 때문에 이 부분이라도 정리해보고자 한다. (UINavigationViewController를 사용한다는 가정하)</p>
<p>Storyboard에 UIViewController를 불러온 뒤 UITableView를 드래그해서 넣고 prototype cell에 원하는 요소들을 넣는다.</p>
<p>이후, UIViewController에 대한 cocoa class 파일을 작성하고 이곳에 storyboard의 UITableView의 outlet을 연결해 넣어야 한다! UIViewController가 UITableView를 상속받게 해야하고 그에 따라오는 것이 <code>UITableViewDataSource</code>와 <code>UITableViewDelegate</code>이다. </p>
<p>필수적으로 작성해야 하는 것은 <code>UITableViewDataSource</code>에 속한 두 가지 method다.</p>
<blockquote>
<ol>
<li><code>numberOfRowsInSection</code> 섹션 당 반환해야하는 row의 수를 작성하는 것 - 보통 데이터의 .count를 return</li>
<li><code>cellForRowAt</code> 반환하는 cell에 대한 내용을 적고 cell을 반환</li>
</ol>
</blockquote>
<p>그 외 프로젝트에 사용한 것은 <code>UITableViewDelegate</code>의 <code>heightForRowAt</code> 하나 뿐이지만 정말 다양한 종류가 있으므로 더 많은 정보가 필요하다면 아래의 링크를 참고하자! </p>
<blockquote>
<p>(선택사항)
<code>heightForRowAt</code> row, 즉 cell의 높이를 설정</p>
</blockquote>
<p><a href="https://developer.apple.com/documentation/uikit/uitableviewdelegate">[애플 공식 문서] - UITableViewDelegate</a>
<a href="https://developer.apple.com/documentation/uikit/uitableviewdatasource">[애플 공식 문서] - UITableViewDatasource</a></p>
<pre><code class="language-swift">    // cell 행 수 반환
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
        return Int
    }

    // cell 반환
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
        // cell 구현
        return UITableViewCell
    }

    // cell 높이 지정
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -&gt; CGFloat {
        return 50
   }
</code></pre>
<h2 id="2-cell에-배열을-사용해서-데이터-연결하기">2. Cell에 배열을 사용해서 데이터 연결하기</h2>
<p>사실 UserDefaults를 사용해서 시도해보다가 실패해서 제출용에서는 단순히 배열만 사용했고, 따로 만들었던 부분에서는 protocol을 선언하고 delegate를 이용해서 runtime 동안에는 정보가 저장되도록 했다.</p>
<p>차근차근 이 과정들을 다시 정리해보자!</p>
<p>먼저 cocoa class file을 만드는데 <code>UITableViewCell</code> 파일을 만들어야 한다.
그리고 여기에는 cell의 요소들을 모두 outlet으로 연결한다.
또한, cell의 <code>identifier</code>를 지정해준다. </p>
<p><strong>참고로 한 TableViewController에서는 복수의 TableView에 대해 동일한 Identifier가 적용될 수 없고 한 TableView에서는 cell의 종류가 복수일 경우에 동일한 Identifier가 적용될 수 없다.</strong> </p>
<p>보통 대부분 한 종류인 경우 단순하게 &quot;<code>cell</code>&quot;이라고 짓는 것 같다.</p>
<p>UITableViewDataSource 중 cellForRowAt가 cell의 내용에 관한 것인데 이곳에 UITableViewCell에 대해 identifier와 함께 instance를 생성하고 각 outlet의 요소들을 연결지어 반환한다.</p>
<p><code>예시</code></p>
<pre><code class="language-swift">
// dummy 데이터
var todo = [&quot;고양이 밥 주기&quot;, &quot;고양이 수발들기&quot;, &quot;내배캠 출췤&quot;, &quot;설거지하기&quot;]
var isCompleted = [true, true, false, true]

// outlet 요소
@IBOutlet weak var segControl: UISegmentedControl!
@IBOutlet weak var toDoTable: UITableView! // 필수

// 두 개의 배열 index를 비교해서 true인 것만 배열로 반환
func trueArray() -&gt; [String]? {
    var result = [String]()
    for (index, value) in isCompleted.enumerated()  {
        if value == true{
        result.append(todo[index])
        }
    }
return result
}

// 두 개의 배열 index를 비교해서 false인 것만 배열로 반환
func falseArray() -&gt; [String]? {
    var result = [String]()
    for (index, value) in isCompleted.enumerated()  {
        if value == false{
            result.append(todo[index])
        }
    }
return result
}

// TableView가 delegate와 dataSource를 해당 클래스 내에 정의한 것으로 선언
override func viewDidLoad() {
     super.viewDidLoad()
     toDoTable.delegate = self
     toDoTable.dataSource = self
}

// cell 반환
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
    // TableViewCell의 instance를 생성, identifier로 구분
    let cell = toDoTable.dequeueReusableCell(withIdentifier: &quot;cell&quot;, for: indexPath) as! SegmentedControllerTableViewCell
    let empty = toDoTable.dequeueReusableCell(withIdentifier: &quot;empty&quot;) as! SegmentedControllerTableViewCell

    // TableViewCell의 outlet 요소에 원하는 데이터 배열을 연결하는데 IndexPath.Type을 이용
    if segControl.selectedSegmentIndex == 0 { // segmented control 구분
         cell.title.text = trueArray()?[indexPath.row] // UILable에 [String]을 index 값에 따라 적용
         cell.isCompleted.isOn = true // 해당 segmented control이 select 됐을 때 switch의 상태 정의
         return cell
    }
    else if segControl.selectedSegmentIndex == 1 {
         cell.title.text = falseArray()?[indexPath.row]
         cell.isCompleted.isOn = false
         return cell
    }
    return empty
}</code></pre>
<p><img src="https://velog.velcdn.com/images/bo_bo_/post/8072a8f5-518c-4e84-bda1-c7b9b1377205/image.gif" alt=""></p>
<h2 id="3-protocol-delegate를-이용해서-입력되는-데이터-유지하기">3. Protocol Delegate를 이용해서 입력되는 데이터 유지하기</h2>
<p>먼저 <code>데이터를 입력하는 cocoa class</code> 파일에 <code>Protocol</code>을 선언해준다. 나의 경우에는 protocol의 이름을 UpdatedDataDelegate로 지었고 파라미터의 데이터 타입은 해당 파일에서 받아서 넘겨줄 데이터 타입으로 정했다.</p>
<pre><code class="language-swift">protocol UpdatedDataDelegate: AnyObject {
    func updateData(_ newData: [[String]])
}</code></pre>
<p>이후 <code>데이터를 받아서 출력할 UITableView가 채택된 cocoa class</code> 파일에 <code>Protocol에 대한 extension</code>을 정의한다. 나의 경우에는 protocol의 함수를 재정의 했는데 받아온 데이터의 index 값을 해당 class의 변수에 할당했다.</p>
<pre><code class="language-swift">extension TodoViewController: UpdatedDataDelegate {
    func updateData(_ newData: [[String]]) {
        self.todo = newData[0]
        self.emoji = newData[1]
        self.info = newData[2]
        self.dates = newData[3]
        self.toDoTable.reloadData()
    }
}</code></pre>
<p>당연히 해당 데이터를 사용해서 cell을 반환하고 또 이 프로토콜의 delegate를 따라서 view를 그리라고도 선언해야한다.</p>
<pre><code class="language-swift">  override func viewDidLoad() {
      super.viewDidLoad()
      self.toDoTable.delegate = self
      self.toDoTable.dataSource = self
  }</code></pre>
<p>⭐️ ⭐️ 그리고 중요한 부분이자 내가 몇 시간을 헤맸던 부분은... 데이터를 입력받는 ViewController로 넘어가게 하는 object의 action에 이 뒤에 오는 ViewController가 이 delegate를 따를 것이라고 남겨줘야지 실제로 연결이 된다는 것!⭐️ ⭐️ </p>
<p>예를 들어, 나의 경우에는 &#39;+&#39; 모양의 버튼을 누르게 되면 데이터를 입력받는 ViewController로 넘어가는데 이곳의 action에 연결된 함수에 선언해줬다.</p>
<pre><code class="language-swift">    override func viewDidLoad() {
        super.viewDidLoad()
        self.toDoTable.delegate = self
        self.toDoTable.dataSource = self

        // 중략
        plusButton.addTarget(self, action: #selector(plusPressed), for: .touchUpInside)

        // 후략

    }

    // 불러와지는 function
    @objc private func plusPressed(_ sender: Any) {
        print(&quot;plus&quot;)
        // 데이터를 입력받는 ViewController의 Storyboard ID
        let popupViewControllerID = UIStoryboard(name: &quot;Main&quot;, bundle: .none).instantiateViewController(identifier: &quot;popupViewControllerID&quot;) as! PopupViewController
        popupViewControllerID.delegate = self // delegate를 따르겠다는 의미
        popupViewControllerID.view.backgroundColor = .white
        // 해당 ViewController로 전환(Show)
        navigationController?.pushViewController(popupViewControllerID, animated: true)
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/bo_bo_/post/72fa1e00-5d07-48d0-b2c2-2a51844d553c/image.gif" alt=""></p>
<h1 id="💁🏻♀️-uialertcontroller-사용하기">💁🏻‍♀️ UIAlertController 사용하기</h1>
<p>UIAlertController는 아래에서 위로 선택지가 뜨는 유형(.actionSheet)과 팝업처럼 알려서 버튼이나 textfield등으로 입력하는 유형(.alert) 이 두 가지가 있다. 이 중에서 내가 사용했고 정리해볼 내용은 .alert style이다.</p>
<pre><code class="language-swift">@IBAction func plusButtonAct(_ sender: Any) {
        plusPressed(self)
}

@objc func plusPressed(_ sender: Any) {
    // title 과 아래에 뜨는 message 설정, style은 .alert로 지정
    let popup = UIAlertController(title: &quot;할 일 입력&quot;, message: &quot;해야 할 일을 입력하세요&quot;, preferredStyle: .alert)

    // 팝업에 쓸 버튼 생성
    let cancel = UIAlertAction(title: &quot;취소&quot;, style: .cancel)
    let save = UIAlertAction(title: &quot;저장&quot;, style: .default) { [self] (_) in

    // 저장 클릭 시 처리할 내용
      let txt = popup.textFields?[0]

      if txt!.text?.isEmpty != nil {
          todo.append(txt!.text!)
          isCompleted.append(true)
          self.toDoTable.reloadData()
      }
      else {print(&quot;nil&quot;)}
    } // 여기까지 save closure

    popup.addAction(cancel)
    popup.addAction(save)

    popup.addTextField() {(tf) in
        tf.placeholder = &quot;해야 할 일을 입력하세요&quot; // textfield의 placeholder 설정
    } 
self.present(popup, animated: true)
}</code></pre>
<p><img src="https://velog.velcdn.com/images/bo_bo_/post/f8710572-6043-4f1f-a825-6e986b113a4b/image.gif" alt=""></p>
<h1 id="💁🏻♀️-segmented-control을-사용해서-tableview-그려내기">💁🏻‍♀️ Segmented Control을 사용해서 TableView 그려내기</h1>
<p><code>UIViewController</code>로 cocoa file을 만들고 해당 class가 <code>TableView의 Delegate와 Datasource</code>를 따르게 하자. 그리고 storyboard에 <code>segmented control</code>을 넣고 outlet을 해당 class로 연결해 넣는다.</p>
<p><code>segmented control</code>의 title은 storyboard 상에서 선택시 우측 메뉴에서도 변경 가능하고 코드로도 구현 가능하다.</p>
<p>action을 연결해 넣으면 값이 변함에 따라 action이 트리거 되는 것을 볼 수 있는데 <code>Outlet이름.selectedSegmentIndex == 0</code>과 같이 첫 번째 값은 0인 index 두 번째는 1 이런 식으로 값이 있고 이것을 조건문으로 코드를 짜면 된다.</p>
<pre><code class="language-swift">@IBOutlet weak var segControl: UISegmentedControl!
@IBAction func segAction(_ sender: Any) {
        self.toDoTable.reloadData()
}
// segmented control의 조건에 따라 cell 행의 수 반환
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
        print(segControl.selectedSegmentIndex)
        return segControl.selectedSegmentIndex == 0 ? trueArray()?.count ?? 1 : falseArray()?.count ?? 1
    }

// segmented control의 조건에 따라 cell 반환
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
        let cell = toDoTable.dequeueReusableCell(withIdentifier: &quot;cell&quot;, for: indexPath) as! SegmentedControllerTableViewCell
        let empty = toDoTable.dequeueReusableCell(withIdentifier: &quot;empty&quot;) as! SegmentedControllerTableViewCell
        if segControl.selectedSegmentIndex == 0 {
            cell.title.text = trueArray()?[indexPath.row]
            cell.isCompleted.isOn = true
            return cell
        }
        else if segControl.selectedSegmentIndex == 1 {
            cell.title.text = falseArray()?[indexPath.row]
            cell.isCompleted.isOn = false
            return cell
        }
        return empty
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/bo_bo_/post/63b360ba-02e5-4b5d-aa5a-636c37280ada/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] iOS 입문 개인프로젝트 - 2]]></title>
            <link>https://velog.io/@bo_bo_/TIL-iOS-%EC%9E%85%EB%AC%B8-%EA%B0%9C%EC%9D%B8%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2</link>
            <guid>https://velog.io/@bo_bo_/TIL-iOS-%EC%9E%85%EB%AC%B8-%EA%B0%9C%EC%9D%B8%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2</guid>
            <pubDate>Fri, 04 Aug 2023 12:37:18 GMT</pubDate>
            <description><![CDATA[<p>개인 프로젝트로 To do list 어플만들기를 하는 중이다.
어제 TIL을 쓰고나서 해결한 에러에 대해 간단히 정리하고 오늘 한 것들을 정리해보고자 한다.</p>
<p>어제 발생한 문제는 navigation controller로 push한 화면으로 넘어갈 때 계속해서 fatal 에러가 났었다.
ViewController에서 UITableView로 넘어갈 때 일어난 일이었다.
당연히 원래 화면이 넘어가던 것 위에 cell을 올리는 작업을 해서 연결에는 문제가 없을 것이라고 생각을 했었다.<img src="https://velog.velcdn.com/images/bo_bo_/post/eba535e1-97cb-4efa-aa64-dda795d8fa1a/image.png" alt=""></p>
<p>TableView와 TableViewCell 파일을 수정하면서 문제되는 요소들을 지워도 화면이 전환될 때 어디선가 nil값을 받아오면서 fatal에러가 떠버렸다.</p>
<p>튜터님과 머리를 싸매고 같이 고민을 하다가 튜터님께서 TableView의 화면을 initial launch로 띄우셨는데 잘 로딩이 됐다....!?
결국 이 때문에 TableView자체는 문제가 없으나 연결에서 문제가 있다는 것을 발견했다.</p>
<p>알고보니 Story 보드를 사용해서 각각의 뷰를 그리고 코드로 연결할 때 navigation 코드만 쓰고 끝나버려서는 안됐다. 무조건 Storyboard를 통해서 initation을 하지 않으면 UITableView의 상위클래스, 즉 UITableView의 원래 init을 불러오게 되면서 내가 그린 view가 불러지지 않으니 nil이 반환되고 이를 옵셔널 처리하지 않았기 때문에 fatal error가 나게된 것이었다!!</p>
<pre><code>@IBAction func willButtonAct(_ sender: Any) {
        // 화면전환버튼
        let 스토리보드의 ID = UIStoryboard(name: &quot;Main&quot;, bundle: .none).instantiateViewController(identifier: &quot;스토리보드의 ID&quot;) as! &lt;전환할 View의 클래스명&gt;
        스토리보드의 ID.title = &quot;전환된 View의 Navigation Bar Title&quot;
        스토리보드의 ID.view.backgroundColor = .white
        navigationController?.pushViewController(스토리보드의 ID, animated: true)
    }</code></pre><p>해석해보자면 스토리보드로 만든 버튼을 누를 때, Main.storyboard에 있는 <code>스토리보드의 ID</code>를 가진 View를 전환할 클래스명의 것으로 설정하는 인스턴스를 만든 뒤, 타이틀이나 여타 필요한 속성을 정의해주고 해당 인스턴스를 이용해 원하는 방식으로(여기서는 push) view를 새로 띄워주는 것이다. </p>
<h1 id="오늘-완성한-분량">오늘 완성한 분량</h1>
<img src="https://velog.velcdn.com/images/bo_bo_/post/1699e4a0-7259-4bf9-b60d-0bf4f9dc4b48/image.png" style="width: 300px;"/>

<p>뭔가를 많이 한 것 같아 보이지만 사실 어제 에러를 해결하고 난 뒤에 cell을 크기에 맞춰주고 plus 버튼을 추가하면서 margin을 설정했다! 사실 + 버튼을 누르면 데이터를 추가하는 팝업을 만들고 싶었지만 에러와 구글링의 늪에 빠져 구현까지는 아직 하지 못했다... 이번 주말안에 수정기능과 같은 팀원분이 보내주신 자료를 토대로 cell을 slide하는 기능까지 구현해보고자 한다!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] iOS 입문 개인프로젝트 -1]]></title>
            <link>https://velog.io/@bo_bo_/TIL-iOS-%EC%9E%85%EB%AC%B8-%EA%B0%9C%EC%9D%B8%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1</link>
            <guid>https://velog.io/@bo_bo_/TIL-iOS-%EC%9E%85%EB%AC%B8-%EA%B0%9C%EC%9D%B8%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1</guid>
            <pubDate>Thu, 03 Aug 2023 11:45:26 GMT</pubDate>
            <description><![CDATA[<p>이제 iOS를 어찌저찌 공부한 것을 바탕으로 본격적으로 개인프로젝트에 착수했다.
LV1. 조건은 버튼을 이용해서(segue) 화면을 이동하고, 이미지와 버튼 두 개를 만드는 것!</p>
<p>아침부터 시작해서 점심즈음에는 완성할 수 있었다.<img src="https://velog.velcdn.com/images/bo_bo_/post/180a2558-59c4-403b-a111-90a8eaa7c63a/image.png" alt=""></p>
<p>지금은 이것저것 추가하고 navigation controller도 이용하는 조건이 생겨서 segue 하나를 끊었다.</p>
<p>이후에는 TableView와 TableViewCell을 구성하고 있으나 머리 속에 있는 것만큼 시원시원하게 결과물이 나오지 않고 자꾸 에러가 뜬다...💧</p>
<p>어떤 에러는 되게 자주 떠서 외워두면 좋을 것 같다.</p>
<pre><code>Terminating app due to uncaught exception &#39;NSInternalInconsistencyException&#39;, reason: &#39;unable to dequeue a cell with identifier todoCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard&#39;</code></pre><p>스토리보드와 클래스 간에 연결이 되어있지 않거나 잘못 연결된 경우, 혹은 identify가 잘못된 경우 등 스토리보드와 클래스 코드 간의 사이를 뜯어보면 해결되는 에러였다.<img src="https://velog.velcdn.com/images/bo_bo_/post/7c76faee-90c2-4d87-b42a-0288f5c1f30f/image.png" alt=""></p>
<p>지금은 저렇게 cell을 구상했으나 화면에 나오지 않거나 fatal 에러가 뜨곤 한다...
아무래도 아직 view와 controller간의 구조적인 관계를 정확하게 이해하지 못한 채 스토리보드도 같이 쓰고 하다보니 난관에 봉착한 것 같다.</p>
<p>하지만 구조를 이해하게 되면 풀 수 있을 것이라는 생각을 갖고! 문제를 해결해보고자 한다.
이 뒤에도 구현해보고 싶은 기능들이 많기 때문에 빨리 해결하고 싶은 맘이 굴뚝같지만 잘 배우는 것이 제 1의 목표라는 것을 잊지 말아야겠다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] iOS 입문 강의 실습]]></title>
            <link>https://velog.io/@bo_bo_/TIL-iOS-%EC%9E%85%EB%AC%B8-%EA%B0%95%EC%9D%98-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@bo_bo_/TIL-iOS-%EC%9E%85%EB%AC%B8-%EA%B0%95%EC%9D%98-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Wed, 02 Aug 2023 11:58:46 GMT</pubDate>
            <description><![CDATA[<p>아무리 강의를 들어도 감이 잡히지 않아서 결국 실습만이 답이다!!라는 생각으로 하나하나 따라가면서 배우기 시작했다. 그 중 오늘은 <code>@IBOutlet</code>과 <code>@IBAction</code>을 통해 스토리보드 상의 객체를 연결하는 방법을 짧게 정리해서 남겨두려한다.</p>
<h1 id="스토리-보드에-버튼-만들기">스토리 보드에 버튼 만들기</h1>
<p><img src="https://velog.velcdn.com/images/bknam97/post/baaa199c-b5b8-4e37-ac34-a9391e9ee6a9/image.png" alt="">
우측 상단의 &#39;+&#39; 버튼을 누르면 위와 같은 팝업이 나온다.
이 중 필요한 것을 고르면 되는데 지금은 버튼을 선택할거다.
단순하게 끌어다가 원하는 위치에 놓기만 하면 된다!
<img src="https://velog.velcdn.com/images/bknam97/post/6663a47e-8fad-4aed-8fe8-3d0402e8e0b2/image.png" alt=""></p>
<p><code>control</code>을 누른채로 마우스를 위로 올려서 잡아당기면 원하는 위치에 넣을 수 있다.
<img src="https://velog.velcdn.com/images/bknam97/post/e3f7e14c-896c-41e7-b5b3-058d098b07cb/image.png" alt=""></p>
<p>Outlet과 Action을 select하는데 사실 둘 다 만들어 주면 된다!</p>
<blockquote>
<p><strong>막간 상식 👀</strong>
<code>@IBOutlet</code>은 스토리보드 상에서 선언한 해당 시각적 객체를 가리키는 변수다.
<code>@IBAction</code>은 스토리보드 상에서 선언한 객체가 특정한 이벤트가 트리거가 되어 호출되는 함수다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/bknam97/post/9490d488-36dc-464a-b0d6-f814a74597fe/image.png" alt="">
<code>@IBOutlet</code>과<code>@IBAction</code>을 추가하고 나면 위의 사진처럼 나오는데, 왼쪽의 동그라미 안쪽이 꼭 채워져 있는지 확인해야 한다!
이게 채워져 있지 않으면 스토리보드 상에 해당 객체가 없다는 의미로 앱이 종료되거나 빌드자체가 되지 않는 큰 에러가 발생한다. 그러므로 꼭꼭 확인하자!</p>
<p>강의만 봤을 때는 막막했지만 막상 직접해보니 재밌었고 빨리 더 많은 기능을 만들어보고 싶고, 더 많은 것들을 배우고 싶다. 개인 프로젝트를 하면서 천천히 지금처럼 잘 배워나가면 조금이나마 더 익숙해지지 않을까...? 😛 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 앱개발 입문 시작하기]]></title>
            <link>https://velog.io/@bo_bo_/TIL-%EC%95%B1%EA%B0%9C%EB%B0%9C-%EC%9E%85%EB%AC%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bo_bo_/TIL-%EC%95%B1%EA%B0%9C%EB%B0%9C-%EC%9E%85%EB%AC%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 31 Jul 2023 12:22:28 GMT</pubDate>
            <description><![CDATA[<p>이전까지는 swift 문법을 배웠고 오늘부터는 본격적으로 Xcode를 이용한 앱개발 입문과정이 시작됐다.
새로운 개념들이 비수가 되어 내 머리에 꽂히고 있지만,,,, 일단은 내 머리가 밑 빠진 독이든 말든 쑤셔 넣고 있다.
익숙하지 않은 개념들을 좀 더 정리해보고자 한다.</p>
<h1 id="uiview">UIView</h1>
<p>UI는 User Interface로 사용자가 어플상에서 마주치게 되는 요소들, 화면을 말한다</p>
<ul>
<li>UIView는 화면에 보이는 모든 요소들의 클래스기 때문에 모든 종류의 시각적 요소들은 UIView의 하위클래스(subClass)가 된다.</li>
<li>UIView 자체는 아무 것도 나타내지 않기 때문에 override 등을 이용해서 시각적 요소를 개발자의 입맛에 맞게 구성할 수 있다.</li>
<li>위치나 크기를 조절할 수 있으며 배경색이나 애니메이션 효과를 구현할 수 있다.
(frame, bound, center, backgroundColor, alpha, layer, UIView.animate, UIViewPropertyAnimator 등)</li>
<li>계층적 구조를 통해서 superview, subview로 정의하면서 조화롭게(?) 화면의 인터페이스를 그려낸다(addSubview(_:))활용, Auto layout을 이용하면 위치나 크기를 고정할 수 있다.</li>
<li>Storyboard에서는 기본적인 구성요소다.</li>
</ul>
<pre><code>// UIView 객체 생성
let myUIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

// 배경색상 설정하기 
myUIView.backgroundColor = UIColor.white

// 생성된 뷰를 서브뷰로 추가
self.view.addSubview(myUIView)

// frame을 이용한 뷰의 위치 및 크기 변경
myUIView.frame = CGRect(x: 50, y: 50, width: 200, height: 200)
</code></pre><h1 id="uiviewcontroller">UIViewController</h1>
<p>(<code>UIViewController</code>,<code>UITableViewController</code>,<code>UICollectionViewController</code>, <code>Container ViewController</code> 등)</p>
<ul>
<li>UI를 관리하는데 사용하며, UIViewController의 객체를 생성할 수 있다.</li>
<li>한 UIViewController 객체로 여러 UIView들을 관리하고, 사용자가 어플을 사용하는 사용자 상호작용을 처리한다(User Interaction/ 예시: 화면 터치, 드래그 등)</li>
<li>다른 UIViewController와 데이터를 공유할 수 있다.</li>
<li>생성하는 방법은 스토리 보드로 생성하거나, 생성자 코드를 이용한 객체 생성 방법 이렇게 두 가지</li>
<li>하나 이상의 UIView 객체와 연결되어야 하기 때문에 <code>self.view</code>와 같이 property를 가지고 있는데 이 view를 통해 UIView를 상속하는 UI 요소(components)들을 하위 View로 배치할 수 있다</li>
<li>생명주기를 통해서 다양한 method와 함께 관리할 수 있다.(viewDidLoad())</li>
</ul>
<h1 id="container-view-controller">Container view controller</h1>
<p>종류 - (<code>UISplitViewController</code> - iPadOS에서 많이 쓰임, <code>UINavigationController</code>, <code>UITabBarController</code>, <code>UIPageViewController</code>)</p>
<ul>
<li>Content view controller를 포함하고 있는 View controller다. -&gt; 그래서 Content view controller가 무엇인고 하니 UIViewController와 유사한 역할을 하는데, 화면에 표시될 내용의 구성을 담당하는 것이고 데이터를 표시하면서 사용자 상호작용을 처리한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 키오스크 팀 프로젝트 초안 코드 살펴보기]]></title>
            <link>https://velog.io/@bo_bo_/TIL-%ED%82%A4%EC%98%A4%EC%8A%A4%ED%81%AC-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%B4%88%EC%95%88-%EC%BD%94%EB%93%9C-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@bo_bo_/TIL-%ED%82%A4%EC%98%A4%EC%8A%A4%ED%81%AC-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%B4%88%EC%95%88-%EC%BD%94%EB%93%9C-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 25 Jul 2023 16:06:01 GMT</pubDate>
            <description><![CDATA[<p>내일배움캠프 이번 주차 과제는 키오스크 기능 만들기였다.
팀원들과 같이 해야해서 다같이 전체적인 부분은 잘 짠 사람 것으로 채택하고 나머지 상세 부분을 따로 작성해오기로 했다.</p>
<p>작성하면서 생각보다 파일이 많아지고 더 복잡해지다보니 머릿속에서 논리가 엉키는 느낌이라 좀 정리하고 개선할 부분은 없는지 찾아보고자 한다. </p>
<p>일단 전체적인 코드 구조는 인스턴스를 한 클래스에 몰아놓아서 해당 클래스로 모든 클래스에 접근할 수 있게 추상화를 해봤다.</p>
<p>또한, 숫자를 console창에 입력하는 것으로 각기 다른 화면과 기능들을 불러와야 했기 때문에 그리고 튜터님이 이전 프로젝트 때 예시로 보여준 enum 활용법이 너무 흥미로워서 enum을 활용해봤다.</p>
<p><strong>instances.swift</strong></p>
<pre><code>import Foundation

class Instances {
    let main = Main()
    let printing = PrintMenu()
    let operating = Operator()
    let toHome = Home()
    let selecting = Select()
    let picking = Pick()
    let cart = Cart()
    let checkout = Checkout()
    let administration = Admin()
    let exit = Exit()
    let pns = PikklesAndSauces()
}</code></pre><p><strong>Operator.swift</strong></p>
<pre><code>import Foundation

enum Set{
    // 기본 화면 및 기능
    case home, pick, cart, checkout, admin, exit, clearCart

    // 카테고리
    case pikklesAndSauces, pizza, pasta, drink, side

    // 피클 &amp; 소스 메뉴
    case pikkleM, pikkleL, garlicDip, hotSauce
}

class Operator {
    func oper(_ input: Set) {

        switch input {
        // 기본 메뉴
        case .pick : instances.picking.pick()
        case .cart : instances.cart.cart()
        case .checkout : instances.checkout.checkout()
        case .admin : instances.administration.admin()
        case .home: instances.toHome.home()
        case .clearCart : instances.cart.cartContent = [:]
        case .exit : instances.exit.exit()

        // 카테고리
        case .pikklesAndSauces : instances.pns.pns()

        default : instances.toHome.home()
        }
    }
}</code></pre><p>우리는 도미노피자 키오스크를 만들어보자고 정했다. 홈 화면에서는 처음하려는 액션을 고를 수 있게 되어있고, 두 번째 레이어(?)에는 메뉴별 카테고리, 세 번째 레이어에서는 상세 상품을 고를 수 있게 설계했다.</p>
<p>상세 상품을 고르면 몇 개를 구매할지 물어보고 그 숫자를 받아서 장바구니에 추가하는 방식으로 그려봤다.</p>
<p>조금 공을 들였던 부분은 메뉴를 프린팅 하는 부분, 번호를 입력하고 받는 과정과 옵셔널에 대한 에러처리 부분, 입력값에 따른 조건문을 모은 부분들을 참조하는 방식으로 짜는 과정이었다.</p>
<p><strong>Home.swift</strong></p>
<pre><code>class Home{
    let menus: [String] = [
        &quot;1. 주문하기&quot;,
        &quot;2. 장바구니&quot;,
        &quot;3. 결제하기&quot;,
        &quot;4. 관리자 메뉴&quot;,
        &quot;0. 종료&quot;
    ]

    let numset: [Int: Set] = [
        1 : .pick,
        2 : .cart,
        3 : .checkout,
        4 : .admin,
        0 : .exit
    ]

    func home() {
        print(&quot;[ WELCOME TO DOMINO PIZZA ]&quot;)
        instances.printing.printMenu(menus) // 메뉴 프린팅
        var numChoice: Int = instances.selecting.numSelect() // Int 반환
        var numToSet: Set = numset[numChoice]!

        while true {
            if 0 &lt;= numChoice &amp;&amp; numChoice &lt;= 4 {
                // 범위 내 숫자일 경우 Set type 변수에 할당하고 break
                numToSet = numset[numChoice]! 
                // numSelect 에서 Int 아닌 경우 오류처리 했으므로 강제 언래핑
                break
            }
            else {
                print(&quot;메뉴에 없는 숫자 입니다.&quot;)
                print(&quot;올바른 숫자를 다시 입력해주세요.&quot;)
                numChoice = instances.selecting.numSelect()
            }
        }

        instances.operating.oper(numToSet)

    }
}</code></pre><p>홈 화면 하나만 프린트할 것이 많았다면 그냥 냅뒀겠지만 모든 화면마다 출력하고 입력받아야 하는 값들이 존재해서 그냥 배열을 줬을 때 프린트 해버리는 기능을 만들었다. 매번 쓰는 것 보다는 편하긴 하겠지만... 좀 더 팀원들이 관리자 기능으로 메뉴나 가격을 변경하고 싶어하던 것을  생각해 보면 그 기능을 구현하기 위해 따로 클래스를 만들고 배열을 모아서 관리가능하면서도 불러오기 편하게, 코드가 간결해지도록 바꿔보고 싶다.</p>
<p>지금은 옵셔널 처리가 select 과정에서 한 번, 화면에서 입력받을 때 또 한 번 진행되는데 이 부분도 두 번이나 매 화면마다 거치게 되는 것이 좀 아니꼽다...?
옵셔널도 분리를 해버리고 팀원들의 파일도 받다보면 좀 너무 파일이 많아지려나 싶기도 하고... 하지만 코드가 그만큼 간결해지면 또 가치 있는 것 같다...</p>
<p><strong>Select.swift</strong></p>
<pre><code>class Select{
    func numSelect() -&gt; Int{
        print(&quot;&quot;)
        print(&quot;원하시는 메뉴를 숫자로 입력해주세요&quot;)
        var n: Int = Int(readLine()!) ?? 404

        // 에러코드가 뜨지 않는 경우 함수 종료되며 n값 리턴
        if n != 404 {
            return n
        }

        // 에러코드가 뜨는 경우 맞는 값이 입력될 때 까지 반복
        while n == 404 {
            print(&quot;잘못된 값이 입력 됐습니다&quot;)
            print(&quot;숫자만 다시 입력 해주세요&quot;)
            n = Int(readLine()!) ?? 404
        }
        return n
    }
}</code></pre><p>select 과정에서 옵셔널 값에 대한 처리가 좀 더 분명히 됐으면 좋겠다는 생각이 들었다.
Home 화면에서 입력 시에는 오류들이 없지만 좀 더 깊은 레이어로 파고들기 시작하니 자꾸 numSelect() 부분에서 fatal 에러가 발생했다. 이 에러만 어떻게 넘어가게 처리하는 것이 아니라 옵셔널 값을 처리하는 부분에 대해서 철저한 분석과 보정이 필요한 것 같다. 내일은 이부분을 팀원들과 의견을 나누면서 고쳐보려고 한다.</p>
<p><strong>PrintMenu.swift</strong></p>
<pre><code>class PrintMenu {
    func printMenu(_ menus: [String]) {
        for menu in menus {
            print(menu)
        }   
    }
}</code></pre><p>처음에는 이 for ~ in ~ 이 구문이 너무 낯설고 왜 선언되지도 않은 단어가 갑자기 튀어나오는지 이해도 되지 않았다. 그런데 직접 사용해보고 나니 넘나 꿀인것을...
while과는 비교도 되지 않을 정도로 간결하게 코드가 끝나버린다.</p>
<p><strong>PickklesAndSauces.swift</strong></p>
<pre><code>class PikklesAndSauces {

    var menuName: [String]  = [
        &quot;우리피클 M&quot;,
        &quot;우리피클 L&quot;,
        &quot;갈릭디핑소스&quot;,
        &quot;핫소스&quot;,
    ]

    var menuCost: [Int]  = [
        500,
        800,
        200,
        100,
    ]

    func pns() {
        var menus: [String] = [
            &quot;1. \(menuName[0])      | 가격: \(menuCost[0])&quot;,
            &quot;2. \(menuName[1])      | 가격: \(menuCost[1])&quot;,
            &quot;3. \(menuName[2])      | 가격: \(menuCost[2])&quot;,
            &quot;4. \(menuName[3])      | 가격: \(menuCost[3])&quot;,
            &quot;5. 홈으로 돌아가기&quot;,
            &quot;0. 종료&quot;
        ]

        let numset: [Int: Set]  = [
            1 : .pikkleM,
            2 : .pikkleL,
            3 : .garlicDip,
            4 : .hotSauce,
            5 : .home,
            0 : .exit,
        ]

        instances.printing.printMenu(menus) // 메뉴 프린팅
        var numChoice: Int = instances.selecting.numSelect() // Input Int 반환
        var numToSet: Set = numset[numChoice]! // Int를 Set값으로 변환

        while true {
            if 0 &lt;= numChoice &amp;&amp; numChoice &lt;= 5 {
                // 범위 내 숫자일 경우 Set type 변수인 numToSet에 할당하고 break
                numToSet = numset[numChoice]!
                // numSelect 에서 Int 아닌 경우 오류처리 했으므로 강제 언래핑
                break
            }
            else {
                // 범위 내 숫자 외의 경우에는 반복문에 갇힘
                print(&quot;메뉴에 없는 숫자 입니다.&quot;)
                print(&quot;올바른 숫자를 다시 입력해주세요.&quot;)
                numChoice = instances.selecting.numSelect()
            }
        }

        let cartAddName = menuName[numChoice - 1]
        print(&quot;\(cartAddName)를 몇 개 구매하시겠습니까?&quot;)
        print(&quot;(숫자만 입력 | 예시. 3개 -&gt; 3)&quot;)

        let cartAddCount: Int = Int(readLine()!) ?? 404
        if cartAddCount != 404 {
            let cartAddContent: [String : Int] = [cartAddName : cartAddCount]
            instances.cart.cart(cartAddContent)
        }
    }
}</code></pre><p>가장 많은 시간이 빨린 부분이다.
한 화면에서 생각보다 너무 많은 일들이 일어난다. 코드도 많이 길어지게 되니 조금 생각이 많아진다.</p>
<p>관리 및 수정이 편하도록 배열을 관리하고, 옵셔널 처리도 단순화 시키면서 참조하는 것처럼 다시 짜보고 싶다. 반복문도 계속 반복되는 부분이라 추출해서 메소드를 참조하는 것처럼 진행해야 하나 싶다.</p>
<p>그리고 줄바꿈 기능이나 화면이 넘어갈 때마다 console을 clear하고 작성할 수 있게도 하고 싶은데 시간이 부족해서 찾아보지 못했다. 내일은 이부분도 찾아봐서 좀 더 깔끔하게 출력이 나올 수 있게 해보고 싶다.</p>
<p><strong>개선 필요한 부분 정리</strong></p>
<ol>
<li>옵셔널 처리에 대해 어딘가에서 nil이 발생하는데 이 부분이 왜 발생하는지 확인하고 고치면서 옵셔널 처리를 단순화해서 클래스로 분리하기</li>
<li>관리자 기능에서 메뉴가 수정가능하도록 기능을 구현하면서 배열 혹은 다른 컬렉션 형태를 이용해서라도 좀 더 코드가 간결해지고 수정하기 용이하도록 변경하기</li>
<li>console clear 기능 찾아보기</li>
<li>줄바꿈 찾아보기</li>
<li>가능하다면 기능의 갯수를 줄여서 최대한 파일이 너무 많아지지 않게 관리</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] Swift closure, protocol]]></title>
            <link>https://velog.io/@bo_bo_/TIL-Swift-closure-protocol</link>
            <guid>https://velog.io/@bo_bo_/TIL-Swift-closure-protocol</guid>
            <pubDate>Mon, 24 Jul 2023 14:41:02 GMT</pubDate>
            <description><![CDATA[<h1 id="closure-이해하기">Closure 이해하기</h1>
<p>클로저는 이름 없는 함수, 코드 블록이다.
상수나 변수의 참조를 캡쳐(capture)해서 저장할 수 있다는 특징이 있어서 기능을 저장하는데 사용한다.</p>
<p>이 말을 들었을 때 가장 먼저 든 생각은 &#39;<strong>기능은 method로도 구현하고 충분히 저장할 수 있는데 왜 굳이 closure라는게 있고 이것을 사용할까?</strong>&#39;였다.</p>
<ul>
<li>closure는 기능을 구현해 두고 그것을 선언하면서 사용하고 이름 없이 중괄호 안에 코드 블록 형식으로 작성하는 특징이 있다.</li>
<li>상수나 변수의 참조를 캡쳐하면 이 캡쳐와 관련된 모든 메모리는 swift가 알아서 처리한다.</li>
<li>경량화 된 문법으로 표현 할 수 있으며 관련된 문맥(context)으로부터 값을 캡쳐할 수 있다.</li>
<li>parameter type과 return type의 추론 단일 표현 클로저에서 암시적 반환을 통해 코드가 깔끔해진다.</li>
<li>) 그리고 놀라웠던 것은 애초에 method에서 사용하는 {} -&gt; 이 안에 들어가는 것이 사실은 클로저였던 것...</li>
<li>비동기 처리가 필요할 때 사용할 수 있는 코드 블럭이다.</li>
</ul>
<p><strong>비동기 처리라고 하니 동기의 반댓말인 것 같긴한데 잘 와닿지 않아서 찾아봤다!!</strong></p>
<ul>
<li>동기(sync): 다른 쓰레드로 작업을 보내고 그 작업이 끝날 때 까지 기다림. 그 작업이 끝나야만 다른 작업을 시작할 수 있다.</li>
<li>비동기(Async): 다른 쓰레드로 작업을 보내고 그 작업이 끝나기를 기다릴 필요가 없이 다른 작업을 시작할 수 있다. <strong>설계는 복잡할지라도 자원을 효율적으로 사용할 수 있음</strong></li>
</ul>
<h1 id="plotocol-이해하기">Plotocol 이해하기</h1>
<p>특정 역할을 하기 위해서 method, property 등 기타 요구사항 등을 정의한 청사진이다.
프로토콜을 따르는 것은 채택했다고 표현하며 모든 요구사항을 충족할 때 준수했다고 표현한다.
프로토콜은 조건만 정의하고 제시할 뿐 스스로 기능을 구현하지는 않는다.</p>
<ul>
<li>이름, type, gettable, settable을 명시한다.</li>
<li>property를 선언할 시에는 항상 var를 이용해서 선언해야 한다.</li>
<li>method를 정의 할 때 이름과 return값을 지정하고 구현 코드는 적지 않는다.</li>
</ul>
<p><strong>Delegation == 위임</strong>
대리자, 위임자의 의미를 가지고 있으며 특정한 기능을 위임하는데 사용된다
프로토콜은 대리자 -&gt; 수신자로 내용이 전달될 때 지켜져야 하는 규칙을 말한다면, 대리자는 수신자를 대신해서 특정한 기능을 수행하며 수신자는 그 기능이 수행된 후의 내용을 전달받을 대상이다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] SWIFT 기본 문법 배우기]]></title>
            <link>https://velog.io/@bo_bo_/TIL-SWIFT-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@bo_bo_/TIL-SWIFT-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EB%B0%B0%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Thu, 20 Jul 2023 14:50:17 GMT</pubDate>
            <description><![CDATA[<p>swift 기본 문법 중 기존에 알고 있던 것을 제외하고 중요하다고 생각되는 것들 위주로 정리하고 간략하게 핵심만 간추려 보고자 한다.</p>
<p>Property - Stored Property(상수 선언, 변수 선언), Computed Property(var를 이용한 선언)</p>
<p>다양한 데이터 묶음 형식 - Tuple(튜플)은 관련된 값을 단순히 묶은 묶음에 가깝다. Any는 배열로 있으며 어떤 데이터 타입도 넣을 수 있지만사용할 때 꼭 형 변환을 해줘야 한다</p>
<p>Enum(열거형) - 관련된 값으로 이뤄진 그룹을 같은 타입으로 선언해서 type-safety를 보장</p>
<p>고차함수 </p>
<ul>
<li>map 함수는 컬렉션 내부의 기존 데이터를 변형해서 새로운 컬렉션을 생성해준다.</li>
</ul>
<p>*<em>변형시키고 싶은 데이터.map {} *</em></p>
<ul>
<li>filter 함수 기존 컨테이너의 요소 중 조건에 만족하는 값에 대해 새로운 컨테이너를 만들어 반환한다. 컨테이너라는 단어는 배열이 될 수 밖에 없기 때문에 배열이라고 생각해도 된다</li>
</ul>
<p>*<em>let 변수(새로운 컨테이너) = 기존 컨테이너.filter { 만족해야하는 조건 }
*</em></p>
<ul>
<li>reduce 함수는 기존의 컨테이너의 요소에 대해 정의한 클로저로 매핑한 결과를 새로운 컨테이너로 반환함</li>
</ul>
<p>옵셔널</p>
<ul>
<li><p>값이 없을 수도 있는 상황(nil)에서 옵셔널을 사용하며 물음표로 나타낸다. 옵셔널은 박스와 같다. 옵셔널로 포장한(wrapping)한 값을 언래핑(unwrapping)하여 해당값에 접근할 수 있다. 하지만 또 박스를 열어보면 값이 없을 수도 있는 것이다!(nil의 경우)</p>
</li>
<li><p>변수에 nil을 할당하면 값이 없는 상태의 옵셔널 프로퍼티를 만들 수 있다</p>
</li>
<li><p>옵셔널 바인딩은 옵셔널 값이 nil인지 검사한 후 값이 존재하는 경우에 그 값을 다른 변수에 대입시켜서 바인딩하는 것이다/강제 언래핑보다는 빈 값을 체크하는 것이 훨씬 안전하다.(강제 언래핑의 경우 어플이 강제종류 될 가능성을 높인다)</p>
</li>
<li><blockquote>
<p>그렇지만 꼭 강제 언래핑이 나쁜 것만은 아니라는 튜터님도 계셨다. 필요에 의해 사용될 수 있기에 잘 생각해서 사용하자.</p>
</blockquote>
</li>
<li><p>옵셔널 체이닝은 옵셔널을 연쇄적으로 사용하는 것으로 &#39;.&#39;을 통해 내부 프로퍼티나 메서드에 옵셔널 값이 있는 경우 옵셔널 체이닝을 이용해 연속적으로 접근한다.</p>
</li>
</ul>
<p>객체 지향 프로그래밍의 특성</p>
<ul>
<li><p>객체간의 관계를 설계하는 프로그래밍 방법론으로 각 객체들이 메세지를 주고 받으며 데이터를 처리할 수 있다.</p>
</li>
<li><p>클래스는 속성(property)과 행위(method)를 정의한 것으로, class 자체가 사용자가 정의한 데이터형(data type)이 된다고 할 수 있다. 클래스는 Initializer를 통해 초기값을 설정할 수 있는데 property에 기본 값이 없는 경우 이니셜라이저를 필수로 구현해야 한다. </p>
</li>
<li><p>클래스는 상속이 가능하다.(method, property 등등) 부모 클래스를 superclass라고 부르고 부모클래스를 상속한 클래스는 subclass라고 부른다. 부모 클래스의 요소에 접근하기 위해 &#39;super&#39;라는 키워드를 사용해서 접근가능</p>
</li>
<li><p>다른 클래스의 코드를 재사용할 목적으로 상속을 사용하는 경우, 서브 클래스는 슈퍼 클래스의 특성 외에 자기 자신의 프로퍼티, 메서드 등을 추가할 수 있다.</p>
</li>
<li><p>서브 클래스에서 overriding 키워드를 사용해 슈퍼 클래스에서 상속받은 것을 재정의 할 수 있다. 이를 막고 싶다면 부모 클래스에서 final로 method를 정의하면 된다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 iOS 앱개발 과정 시작!]]></title>
            <link>https://velog.io/@bo_bo_/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-iOS-%EC%95%B1%EA%B0%9C%EB%B0%9C-%EA%B3%BC%EC%A0%95-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@bo_bo_/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-iOS-%EC%95%B1%EA%B0%9C%EB%B0%9C-%EA%B3%BC%EC%A0%95-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Mon, 17 Jul 2023 02:23:37 GMT</pubDate>
            <description><![CDATA[<p>내일배움캠프 7기 iOS 앱개발 과정을 시작한지 2주차에 접어들었다.
초반의 나의 다짐과 수료 후의 나를 비교하면 재밌을 것 같다는 생각에 이 글을 쓴다.
(사전캠프 기간까지하면 3-4주 정도)</p>
<tmi>
1주차 때는 flutter를 이용한 앱개발로 팀별 미니 프로젝트를 했었다.
우리 조가 만든 스터디 플래너가 평가가 좋아서 매우 기부니가 좋았다.
하지만, 당시 반려묘인 삼호가 아파서 150만원이 깨지고 수술 후 곡기를 끊어,
각종 수발을 드느라 맘고생과 몸고생을 둘 다 했었고 지금도 진행중이다...
그래도 어떻게든 팀에 피해를 안끼치고 끝내보려고 노력했었다.
다른 팀원들만큼 100% 시간을 내서 참여할 수는 없었지만,
삼호 수술 당일 눈물을 흘리면서도 코드를 짰던 나를 생각하면...
그래도 최대한의 노력을 했다고 생각한다...!
 </tmi>

<p>2주차에 접어들며 드디어 Swift를 시작한다!!
앱등이로서 iOS 앱개발을 본격적으로 시작한다니 매우 기분이 고양된다.</p>
<p>내가 앱개발을 하면서 많은 것을 배우고 달라지겠지만,
몇 가지 나름대로 어플에 대한 추상적인 철학(?)이 있다.</p>
<ol>
<li>사용자 경험이 뛰어나야 한다.</li>
<li>앱을 사용함에 있어서 막힘이 없어야 한다.</li>
<li>사용하기에 아름다워야 한다.</li>
<li>계속해서 사용하고 싶은 가치있는 어플이어야 한다.</li>
<li>기기의 장치들을 최대한 활용하면 좋다.</li>
<li>호환성이 좋아야 한다.(내가 iOS 개발자를 선택한 이유!)</li>
</ol>
<p>이를 위해서 내일배움캠프 학습에 적극적으로 임하면서 좋은 개발자가 되는 첫걸음을 떼고자 한다.</p>
<h4 id="내일배움캠프를-통해-얻고-싶은-것">내일배움캠프를 통해 얻고 싶은 것</h4>
<ol>
<li>Swift 문법에 익숙해질 것</li>
<li>객체 지향에 대한 어림직한 개념을 확실히 잡고 이해할 것</li>
<li>제작부터 배포의 과정까지 전체적인 과정에 대한 흐름을 이해할 것</li>
<li>협업을 잘 할 수 있는 개발자가 될 것, 궁극적으로 같이 일하고 싶은 사람이 될 것
(Git/Github 사용에 익숙해지기, 다른 개발자와 소통하는 경험 쌓기)</li>
<li>TIL(Today I Learned)와 WIL(WIL I Learned)을 빼먹지 않고 써서 기록을 만들기</li>
</ol>
<p>내가 얼마나 어떠한 성과를 성취할지는 모르지만...</p>
<h4 id="뒤돌아-봤을-때-후회할-일은-만들고-싶지-않다">뒤돌아 봤을 때 후회할 일은 만들고 싶지 않다!</h4>
<p>때문에 주어진 상황 속에서 최선을 다하고자 한다.</p>
]]></description>
        </item>
    </channel>
</rss>