<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jeffapd_.log</title>
        <link>https://velog.io/</link>
        <description>기본에 충실한 개발자가 목표!</description>
        <lastBuildDate>Mon, 20 Jan 2025 15:23:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jeffapd_.log</title>
            <url>https://velog.velcdn.com/images/jeffapd_/profile/eea11824-ca97-49fa-95c0-c8cf8c492090/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jeffapd_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jeffapd_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[swift]project_TripLog(1)]]></title>
            <link>https://velog.io/@jeffapd_/swiftprojectTripLog1</link>
            <guid>https://velog.io/@jeffapd_/swiftprojectTripLog1</guid>
            <pubDate>Mon, 20 Jan 2025 15:23:49 GMT</pubDate>
            <description><![CDATA[<p>이번에 새롭게 준비하는 프로젝트는 <strong>여행가계부</strong>입니다.
제가 직접 여행을 하면서 당시에 환율을 계산을 해서 어렴풋이 얼마를 썼는지만 알고 바로 기록하기가 어려웠던 점을 느껴서 나중에 직접 만들어보려고 기획을 하고 있었습니다. 이번에 좋은 기회로 좋은 팀원분들과 제가 구상한 앱을 함께 만들게 되었습니다. </p>
<p>저희의 <strong>TripLog</strong>앱은 여행 별로 가계부를 생성해 해당 여행기간에 작성한 기록을 볼 수 있고, 기록하며, 예산을 기반으로 여행기간 중 얼마나 썼는지 쉽게 확인할 수 있는 기능, 캘린더를 제공해 해당 날짜에 얼마나 썼는지에 대해 한눈에 확인할 수 있습니다. 그리고 앱의 지출내역을 기록시 여행중인 나라의 통화에 맞게 가격을 입력한다면 그날의 환율을 적용해 원화로 계산이 자동으로 이루어져 함께 기록이되는 주요기능을 가지고 있는 앱입니다. </p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/ac72e6d3-3501-4c8b-8914-602fbe213b77/image.png" alt=""></p>
<p>이번 앱은 배포까지 해보려고 합니다. 때문에 필수적인 기능을 먼저 구현해 추후 업데이트하는 과정으로 진행해보려고 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ KPT 회고]]></title>
            <link>https://velog.io/@jeffapd_/%EA%B0%9C%EC%9D%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jeffapd_/%EA%B0%9C%EC%9D%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 15 Jan 2025 07:44:06 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>Keep: 
칸반보드를 활용한 일정 공유와 기간을 선정해 일정 조절하기
트러블과 5분기록 보드를 활용한 기록과 공유하기
새로운 기능 구현 도전하기</p>
</li>
<li><p>Problem: 
자잘한 실수들로 인한 시간소비한 점
일정 계획시 타이트하게 잡음으로 일정이 밀리는 점
부족한 이해로 코드를 구현한 점</p>
</li>
<li><p>Try:
설계를 구체적으로 해보기
간단하게라도 기록으로 남기기
디버깅을 잘 다뤄보기</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]project_날씨App(2)]]></title>
            <link>https://velog.io/@jeffapd_/swiftproject%EB%82%A0%EC%94%A8App2</link>
            <guid>https://velog.io/@jeffapd_/swiftproject%EB%82%A0%EC%94%A8App2</guid>
            <pubDate>Mon, 13 Jan 2025 16:21:38 GMT</pubDate>
            <description><![CDATA[<h3 id="-네트워크-매니저"># 네트워크 매니저</h3>
<img src="https://velog.velcdn.com/images/jeffapd_/post/d5efea31-bebf-4590-9f12-8de79363cc9f/image.png" width="70%" height="30%">

<ul>
<li><p>RxSwift와 Alamofire을 동시에 사용해 비동기 작업을 효율적으로 관리 할 수 있게되었습니다.</p>
</li>
<li><p>하지만 두가지의 외부 라이브러리를 사용하기에 앱의 크기가 커질 수 있고 추가적인 관리가 필요하게 되는 단점이 될 수도 있습니다. 더해서 UrlSession보다 메모리 오버헤드가 발생할 수 있습니다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/0b16a424-d560-433a-825d-54bdaf029bcd/image.png" width="70%" height="30%">
</li>
<li><p>이제 api를 요청하기위해 url과 header에 원하는 쿼리 값을 넣어서 작성하면 요청 받을 수 있습니다.</p>
</li>
<li><p>그렇게 받아온 JSON데이터를 Decodable을 통해 사용할 수 있게 하기위해서 아래와 같이 Codable을 채택한 구조체를 만들어주면 됩니다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/a59c25ee-8a57-4f69-908b-15ebdb2853b0/image.png" width="40%" height="30%">
#### # 이슈</li>
<li><p>작업을 하던 와중에 API요청이 한 종류만 이루어지면 된다고 생각을 했는데 추후 작업을 하다보니 다른 API도 필요성을 느껴져 구현을 했습니다.</p>
</li>
<li><p>두 가지의 API에서 필요로 하는 데이터가 같고 JSON데이터 구조도 같기에 하나의 구조체로 사용할 수 있을 줄 알았는데 나중에 보니 위도와 경도를 반환하는 값들이 String과 Double타입으로 같이 쓸 수 없게 되었습니다.</p>
</li>
<li><p>그래서 2가지 각각의 구조체로 만들려고 했지만 너무 비효율적이라고 생각해 다른 방법을 찾다보니 제네릭을 활용해 유연한 타입으로 상황에 맞는 값을 받아오면 된다는걸 알았고 그에 맞게 다시 리팩토링을 거쳤습니다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/933ac4de-c257-4942-b43d-ece400a9d0ba/image.png" width="40%" height="30%">
</li>
<li><p>그래서 실제 어떤 데이터 타입이 들어갈지 미리 알려줌으로써 사용이 가능해졌습니다.</p>
</li>
</ul>
<table>
<thead>
<tr>
<th align="center">타입 명시</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/1cdf6a07-d637-4fd4-8e8a-9f41ad10f52b/image.png" width="70%" height="30%"></td>
</tr>
<tr>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/6c3001f7-6eeb-4452-96fd-54cf74188b02/image.png" width="70%" height="30%"></td>
</tr>
</tbody></table>
<h4 id="-이슈"># 이슈</h4>
<p>RxSwift를 사용해 이벤트를 받아 방출하게 구현을 하던 중 원하는 동작이 작동하지 않아서 log기록을 확인해가면 어떤 부분이 문제가 있는지 찾아보니 이벤트가 제대로 방출되지 않는걸 찾았습니다.</p>
<p>문제는 구독을 취소해주는 disposed에서 구독 취소가 제대로 동작하지 않아 발생했습니다.  </p>
<p>처음에는 disposed(by: )에 들어가는 타입이 같아 의심없이 코드를 작성했지만 위 아래 코드의 차이는 <strong>생성이 되고 사라지는 시점에 차이</strong>가 있었습니다.</p>
<ul>
<li><p>임시로 disposeBag이 생성이 되었다가 곧바로 메모리에서 해제(즉 생성 및 스코프 영역에 문제)가 되어 즉시 구독을 취소하게 된다. 그래서 정상적으로 동작이 되지 않았습니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/d56da95c-5fb6-4607-92fd-f5e7971fc966/image.png" alt=""></p>
</li>
<li><p>변수에 선언해서 사용한다면 메모리에 올라가 유지가 되어 정상적으로 구독을 관리하게 된다고합니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/0657aa4b-ab69-4f72-a6d5-b11f06a1fc5d/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]project_날씨App(1)]]></title>
            <link>https://velog.io/@jeffapd_/swiftproject%EB%82%A0%EC%94%A8App1</link>
            <guid>https://velog.io/@jeffapd_/swiftproject%EB%82%A0%EC%94%A8App1</guid>
            <pubDate>Wed, 08 Jan 2025 14:29:44 GMT</pubDate>
            <description><![CDATA[<p>이번 팀프로젝트를 통해 저희가 만들어 볼 앱은 날씨 관련 앱입니다.
기본적으로 날씨 정보를 제공해주는 앱이며, 강아지 그림을 통해 날씨에 대해 직관적으로 알 수 있습니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/71cb47b0-f299-44db-b624-235df3b628d1/image.png" width="80%" height="30%"></p>
<h3 id="-주소-검색-기능"># 주소 검색 기능</h3>
<ul>
<li><p>제가 이번에 맡은 기능은 중 하나인 주소 검색을 위한 API 네트워킹입니다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/9fe2012a-b6d1-4839-aa8a-8332b39c9f94/image.jpeg" width="30%" height="30%">
</li>
<li><p>보통 검색창에 값을 입력하면 위의 사진과 같이 해당하는 단어가 들어간 지역을 보여줍니다. 이번 앱은 국내를 기준으로 날씨 정보를 제공하기로 결정했기에 그에 맞게 국내의 주소가 필요했습니다. 그래서 Kakao Api를 활용해 주소와 위도, 경도의 값을 받아오려고 합니다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/73d4788e-4163-467f-9842-572dea17b5f5/image.png" width="80%" height="30%">


</li>
</ul>
<ul>
<li>아래와 같이 사용하고자 하는 정보를 받아볼 수 있게 모델을 짜 놓았습니다. swift는 camelCase를 따르기때문에 codingKey를 이용해서 바꿔서 저장해올 수 있다.<img src="https://velog.velcdn.com/images/jeffapd_/post/30cf48de-6232-405c-b57e-1d9b6bdd485c/image.png" width="60%" height="30%"> 





</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]project_서점App(3)]]></title>
            <link>https://velog.io/@jeffapd_/%EC%88%98%EC%A0%95</link>
            <guid>https://velog.io/@jeffapd_/%EC%88%98%EC%A0%95</guid>
            <pubDate>Sun, 05 Jan 2025 23:46:16 GMT</pubDate>
            <description><![CDATA[<p>저번에 이어서 개발을 이어가려고 합니다.</p>
<h3 id="-collectionview"># collectionView</h3>
<p>이번엔 화면을 구성하기 위해 collectionView를 사용해 보았습니다.</p>
<table>
<thead>
<tr>
<th align="center">Composition Layout</th>
<th align="center">참고 자료</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/a0da50f4-fc75-405b-a7fc-8899d25fe3f6/image.png" width="80%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/cdd76f2f-8658-48c8-b48b-abdfadb3f1e0/image.png" width="60%" height="30%"></td>
</tr>
</tbody></table>
<ul>
<li><p>왼쪽의 사진은 ios13부터 기존의 flowLayout방식을 보완해 나온 CompositionLayout입니다. 요즘 우리가 자주 볼 수 있는 컬렉션뷰의 형태이다. 기존에 없던 Group이라는 개념이 생겨 각 섹션마다 다르게 구성할 수 있게 되었다. </p>
</li>
<li><p>기존에 해왔던 방식으로 CollectionView 객체를 생성해서 만들었지만 아래와 같은 오류를 마주치게 되었습니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/510618cd-a134-4b52-bdb2-1347a4d870cf/image.png" alt="">
위의 오류는 UICollectionView는 nil 타입이 아닌 layout 파라미터로 초기화 해주어야하기에 아래와 같이 초기화를 해주면 간단하게 해결이 되었다.</p>
<pre><code class="language-swift">let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())</code></pre>
<ul>
<li>아래와 같이 두개의 섹션으로 나누어 첫번째 섹션은 가로로 스크롤이 가능하고 간단하게 책의 이미지와 책의 이름을 나타내려고 했습니다. 두번째 섹션은 테이블 뷰와 비슷한 느낌의 컬렉션 뷰를 구성하였습니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/78f8c768-e90e-443a-b705-7b5a652f909d/image.png" alt=""></li>
<li>초기에 최근 본 책부분에서 적용하려던 Layout이 구현이 되지 않아 찾아보았더니 각 섹션에 맞게 Layout은 구현을 해 놓았지만 아래의 두번째 사진과 같이 각 케이스별로 섹션에 맞는 셀을 적용해야 했던걸 잊고 있었던 것이였습니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/05ac2223-d398-49c5-8bf7-726f944d4b5d/image.png" alt=""><img src="https://velog.velcdn.com/images/jeffapd_/post/cbc4edaa-396d-4428-ab48-c95f840b6129/image.png" alt=""></li>
</ul>
</li>
<li><p>그리고 CompositionLayout을 적용하는 Layout 잡는 부분에서 조금 시간을 들였던거 같습니다.</p>
</li>
</ul>
<table>
<thead>
<tr>
<th align="center">최근 본 책 Layout</th>
<th align="center">검색 결과 Layout</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/947d5611-1d1e-492c-a1e7-0645b9cf9cf5/image.png" width="100%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/1e1b3982-b03c-47e3-b36e-52a35e2842ee/image.png" width="100%" height="30%"></td>
</tr>
</tbody></table>
<p>두 코드에서 보시는와 같이 먼저 작은 단위인 item -&gt; group -&gt; section 순으로 크기를 정해주면 된다.
CollectionView의 item 사이즈를 정하는 방법(NSCollectionLayoutDimension)
.absolute - 고정 크기
.estimated - 런타임에 크기가 정해지는경우(예상크기) 
.fractional - 비율(컨테이너에 대한 비율)
내가 원하는 옵션을 선택해서 작성해주면 됩니다.</p>
<h4 id="-개인-사정으로-인해-아직-개발이-마무리가-되지않아-계속-업데이트하고-있는-중입니다"># 개인 사정으로 인해 아직 개발이 마무리가 되지않아 계속 업데이트하고 있는 중입니다.</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]project_서점App(2)]]></title>
            <link>https://velog.io/@jeffapd_/swiftproject%EC%84%9C%EC%A0%90App2</link>
            <guid>https://velog.io/@jeffapd_/swiftproject%EC%84%9C%EC%A0%90App2</guid>
            <pubDate>Wed, 01 Jan 2025 23:01:11 GMT</pubDate>
            <description><![CDATA[<p>오늘은 본격적으로 앱을 만들기에 앞서 설계를 해보려고 합니다.</p>
<h3 id="-설계"># 설계</h3>
<ul>
<li>MVVM 아키텍쳐를 활용<ul>
<li>Model : BookInfo (api 통신을 통해 받아온 데이터를 관리하는 구조체)</li>
<li>View : 검색화면(View, VC), 상세화면(View, VC), 책을 담은 내역화면(View, VC)</li>
<li>ViewModel : MainViewModel (앱의 전반적인 비즈니스 로직을 담당)</li>
<li>Service : CoreData와 Networking을 담당하는 Manager</li>
</ul>
</li>
</ul>
<h3 id="-networkmanger"># NetworkManger</h3>
<ul>
<li>아래의 사진은 postman을 이용해 api요청 후 받아오는 값을 확인할 수 있었습니다. 이 데이터를 활용하기 앞서 범용적으로 쓰기 위해 NetworkManger를 전역으로 만들어 사용하려고 합니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/9a9a7ba9-5708-47fe-9ef6-b89c36ae4d61/image.png" alt=""></li>
</ul>
<ul>
<li><p>이번 네트워킹을 하기에 앞서 이번에는 RxSwift와 Alamofire의 조합으로 비동기처리와 네트워킹을 직관적이고 간단하게 할 수 있게 구현해 보았습니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/a1d30c67-d685-410a-b05e-f9bcf1348106/image.png" alt="">
위의 사진에서 보시면, Url은 파라미터로 받지만 header 부분을 추가하지 않아 계속해서 오류가 발생했었습니다. 아래 코드와 같이 수정을 통해 api요청을 할 수 있게 되었습니다.</p>
<pre><code class="language-swift">AF.request(url, headers: header).responseDecodable(of: T.self)
// headr 부분이 빠져있었던 오류</code></pre>
</li>
<li><p>그렇게 제대로 api통신이 이루어지는 줄 알았지만 아래와 같은 에러를 마주하게 되었습니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/d115c11f-5d2c-4157-8f0e-715a6736e99d/image.png" alt=""> <img src="https://velog.velcdn.com/images/jeffapd_/post/65085d16-d0d3-4ebc-86e0-1763e3a43112/image.png" alt="">
초기에 위의 두번째 사진과 같이 구조를 구성하지 않고 documents 부분이 누락되게 구성해 마주쳤던 문제였습니다.</p>
</li>
<li><p>그렇게 모든 수정을 마치고 api를 요청하는 메서드를 아래와 같이 작성하였고, 원하는 정보를 받아볼 수 있었습니다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/e6ee531e-55de-47a4-a9dc-d158daa06cfc/image.png" alt="">
<img src="https://velog.velcdn.com/images/jeffapd_/post/3318cae6-6a8d-40d5-a75e-3a60f428d3f9/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]project_서점App(1)]]></title>
            <link>https://velog.io/@jeffapd_/swiftproject%EC%84%9C%EC%A0%90App1</link>
            <guid>https://velog.io/@jeffapd_/swiftproject%EC%84%9C%EC%A0%90App1</guid>
            <pubDate>Sun, 29 Dec 2024 02:23:56 GMT</pubDate>
            <description><![CDATA[<p>이번 프로젝트는 서점앱을 만들 예정입니다.</p>
<h3 id="-참고-디자인"># 참고 디자인</h3>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/3a4736fd-a175-49f8-a179-261e1ba29d65/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/b565cb03-349e-4408-ab2a-1f5ccff1f87f/image.png" alt=""></p>
<h3 id="-프로젝트를-하면서-사용해보려는-목표는-아래와-같습니다"># 프로젝트를 하면서 사용해보려는 목표는 아래와 같습니다.</h3>
<ul>
<li>코드베이스 UI 구현</li>
<li>UIkit을 활용해 화면 구성 및 화면 전환</li>
<li>Rest API 활용한 네트워크 통신</li>
<li>CoreData 활용한 데이터 저장</li>
<li>MVVM 아키텍쳐를 활용한 구조 설계</li>
<li>RxSwift 활용한 비동기 작업</li>
</ul>
<h3 id="-restful-api"># Restful API</h3>
<ul>
<li>Get 매서드를 이용해 서버에 있는 데이터를 조회</li>
<li><img src="https://velog.velcdn.com/images/jeffapd_/post/1f17c5a8-88a3-4812-865b-79e53e0e905c/image.png" width="60%" height="30%">  <img src="https://velog.velcdn.com/images/jeffapd_/post/07c0c070-b008-40ae-b406-eb985a5bdfe7/image.png" width="60%" height="30%"> 위의 사진처럼 제가 자료를 요청한다면 첫번째 사진에 관련된 값을 두번째 사진과 같이 Json형식으로 값을 반환해줍니다. 반환된 Json 데이터에서 제게 필요한 값만을 추출해 사용할 예정입니다.<ul>
<li>title : 책의 제목</li>
<li>authors : 책의 저자</li>
<li>contents : 책의 내용</li>
<li>thumbnail : 책의 이미지</li>
</ul>
</li>
</ul>
<h3 id="-coredata"># CoreData</h3>
<ul>
<li>CRUD를 활용한 기기내의 저장공간에 데이터를 저장, 삭제, 수정, 읽기를 수행할 예정입니다.</li>
<li>이번 앱에서는 책을 선택 후 담기버튼을 활용해 내가 담은 책을 CoreData에 저장하려고 합니다.</li>
<li>그리고 최근에 선택한 책을 저장해 어떤 책을 봤었는지 상단에 보여주려고 합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift] Xcode Instruments]]></title>
            <link>https://velog.io/@jeffapd_/swift-Xcode-Instruments</link>
            <guid>https://velog.io/@jeffapd_/swift-Xcode-Instruments</guid>
            <pubDate>Thu, 26 Dec 2024 16:58:06 GMT</pubDate>
            <description><![CDATA[<p>오늘은 새롭게 알게된 Xcode의 도구에 대해서 적어보려고 한다.</p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/8761d89d-a1cf-4503-814e-02541d0ccb0b/image.png" alt=""></p>
<p>Xcode에서 제공하는 Instruments이다.</p>
<ul>
<li>앱의 성능 분석 및 디버깅 도구로이며, 메모리 누수가 일어나는지에 대해서 분석할때 사용하면 좋다.</li>
<li>메모리 누수를 확인하면서 Stack Trace도 함께 확인이 가능하다.</li>
</ul>
<p>간단하게 메모리 누수가 일어나는 코드를 작성해 Instruments에서 어떻게 보이는지 확인해보자</p>
<ul>
<li>아래의 코드는 순환참조를 발생시켜 메모리 누수가 일어나게 작성하였다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/38d69e33-4bcd-4e94-9d8f-3dddc5e1ae26/image.png" alt=""></li>
</ul>
<p>그럼 우리는 아래와 같이 앱을 run한 후 product에서 Profile을 통해서 Instrument를 실행할 수 있다. 
<img src="https://velog.velcdn.com/images/jeffapd_/post/128a930c-cad5-47e0-8e21-cb00b7892beb/image.png" alt=""></p>
<p>아래의 두 사진을 보게되면, 앱이 실행된 이후에 메모리의 누수 여부와 변동 사항이 있는지를 시간의 흐름에 따라 볼 수 있다. 어느 객체에서 누수가 일어나고 있는지 보여주고 동작이 이루어지고 누수가 일어난 포인트를 두번째 사진처럼 표시를 해줘 쉽게 확인 할 수 있다. 누수가 일어나고 변동이 있는지 계속해서 확인을 해서 알려준다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/a35bf7fa-df63-4c15-ba37-76e593d0b0a8/image.png" alt="">
<img src="https://velog.velcdn.com/images/jeffapd_/post/3ad7da4d-96ff-4859-8e00-6eaf569295d7/image.png" alt=""></p>
<p>메모리 누수뿐만 아니라 어떻게 싸이클이 되고 있는지 확인이 가능하고, 두번째 사진과 같이 어떻게 함수가 호출이 되어 call stack이 쌓여있는지도 확인할 수 있다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/762ff217-c2d8-4a93-8955-4e00299bd1a4/image.png" alt="">
<img src="https://velog.velcdn.com/images/jeffapd_/post/a3e68ba5-ab3b-4721-a81f-5fe4feb9fe58/image.png" alt=""></p>
<p>이렇게 개발자가 개발하는 과정에서 찾기 어려운 부분을 Istruments라는 도구를 통해 메모리 누수가 발생하지 않은 완전한 앱을 만들 수 있게 도와준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]ScrollView]]></title>
            <link>https://velog.io/@jeffapd_/swiftScrollView</link>
            <guid>https://velog.io/@jeffapd_/swiftScrollView</guid>
            <pubDate>Mon, 16 Dec 2024 21:13:34 GMT</pubDate>
            <description><![CDATA[<p>오늘은 ScrollView에 대해서 이야기를 해보려고 한다.
그동안 앱을 만들면서 사용해보지 않았던 스크롤뷰를 사용해보았다.</p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/12819b4c-2b48-4281-80c1-47a089ff727b/image.png" alt=""></p>
<blockquote>
<p>View안에 있는 내용을 스크롤하거나 확대가 가능한 뷰를 의미한다.</p>
</blockquote>
<ul>
<li>UIScrollView는 콘텐츠가 화면에 모두 표시되지 않을 때 사용된다. </li>
<li>사용자는 화면을 스크롤하여 추가 콘텐츠에 액세스할 수 있다. </li>
<li>주로 긴 리스트, 이미지 갤러리, 또는 줌이 필요한 콘텐츠를 표시할 때 사용된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]project_포켓몬 연락처]]></title>
            <link>https://velog.io/@jeffapd_/project%ED%8F%AC%EC%BC%93%EB%AA%AC-%EC%97%B0%EB%9D%BD%EC%B2%98</link>
            <guid>https://velog.io/@jeffapd_/project%ED%8F%AC%EC%BC%93%EB%AA%AC-%EC%97%B0%EB%9D%BD%EC%B2%98</guid>
            <pubDate>Wed, 11 Dec 2024 23:53:00 GMT</pubDate>
            <description><![CDATA[<p>이번 과제는 포켓몬 이미지를 활용한 연락처를 만드는 것이다.
이번엔 MVC패턴을 활용해 프로젝트를 진행하기로 결정했기에 View, ViewController, Model을 나누어 구현을 하였다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/7aa991db-85e0-4404-9ce3-0e67521d7546/image.gif" width="50%" height="30%"></p>
<h1 id="lv1--5">Lv.1 ~ 5</h1>
<ul>
<li>💡 Lv.1 - 연락처 화면 구현 (TableView, NavigationController)</li>
<li>💡 Lv.2 - 연락처 추가화면 구현 (TextField)</li>
<li>💡 Lv.3 - 상단바 구현 (NavigationItem)</li>
<li>💡 Lv.4 - API 연결 (Alamofire)</li>
<li>💡 Lv.5 - 데이터 저장 (CoreData)</li>
</ul>
<table>
<thead>
<tr>
<th align="center">Lv.1</th>
<th align="center">Lv.2,3</th>
<th align="center">Lv.4</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/fc750a99-5ff8-495e-a684-27446c401378/image.png" width="80%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/3fb4b834-53a5-47bc-a25c-0458b8b23021/image.png" width="80%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/31957459-19d8-4cb7-89e2-2f4fe94a0bf7/image.png" width="80%" height="30%"></td>
</tr>
</tbody></table>
<hr>
<h2 id="💡-lv1---연락처-화면-구현-tableview-navigationcontroller">💡 Lv.1 - 연락처 화면 구현 (TableView, NavigationController)</h2>
<h3 id="📌-point">📌 Point</h3>
<ul>
<li>아래 사진과 같이 NavigationViewController를 사용해주면 우리는 간단하게 사용할 수 있게 된다. 가장 먼저 보여줄 Controller를 rootViewController에 넣어주면 된다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/d200a6bc-0e46-4b8c-9560-37fced4544bb/image.png" alt=""></li>
<li>초기에는 MainView에 TableView가 들어가기에 TableView를 사용한다면 꼭 구현해줘야하는 dataSource, delegate 프로토콜들을 채택해 View에서 구현을 했다. 하지만 View는 오로지 UI를 구현하는데 역할이 집중되니 비즈니스적인 로직을 처리하거나 알 필요가 없다는걸 알게되었고, 데이터를 관리하고 View와 Data를 연결하는 ViewController에 쓰는게 적합하다고 생각해 코드를 수정해 옮겨왔다.(나중에는 ViewController가 정말 비대해지는게 느껴졌다.)</li>
</ul>
<hr>
<h2 id="💡-lv2---연락처-추가화면-구현-textfield">💡 Lv.2 - 연락처 추가화면 구현 (TextField)</h2>
<h3 id="📌-point-1">📌 Point</h3>
<ul>
<li><p>MainViewController에서 <code>추가</code>버튼을 눌렀을 시 PhoneBookViewController로 이동할 수 있게 구현</p>
<pre><code class="language-swift">rightButton.action = #selector(tappedAddButton)

@objc func tappedAddButton() {
      navigationController?.pushViewController(phoneBookViewController, animated: true)
  }
</code></pre>
</li>
</ul>
<pre><code>- 이름과 전화번호를 입력할 수 있는 TextField를 구현
  - 우리는 사용자의 입력을 받는 TextField와 TextView가 존재한다. 여기서 두개 모두 각자의 장단점을 가지고 있기에 지금 상황에 맞는 TextField를 채택하게 되었다.
  - placeholder을 통해 사용자가 직관적으로 무엇을 입력해야하는지 인식할 수 있고, 이름과 전화번호라는 짧은 텍스트 입력을 받아오기에 TextField를 사용했다.

---
## 💡 Lv.3 - 상단바 구현 (NavigationItem)
### 📌 Point
- &lt;img src=&quot;https://velog.velcdn.com/images/jeffapd_/post/2d29cb8d-f1a8-41d8-8873-7b05e0e18605/image.png&quot; width=&quot;30%&quot; height=&quot;30%&quot;&gt;
위 사진에 보시면 앱 상단에 친구목록, 연락처 추가, 생성이 있는 부분이 NavigationBar부분이다.
- NavigationBar 부분은 NavigationController를 생성하면 자동으로 생성이 된다.

### 🎯 트러블 슈팅
| &lt;img src=&quot;https://velog.velcdn.com/images/jeffapd_/post/07091c6e-a8ef-4dae-8651-04bb40a23df1/image.png&quot; width=&quot;80%&quot; height=&quot;30%&quot;&gt;  |&lt;img src=&quot;https://velog.velcdn.com/images/jeffapd_/post/53e7146f-385d-45d7-a127-a37f278aa4ff/image.png&quot; width=&quot;80%&quot; height=&quot;30%&quot;&gt; |&lt;img src=&quot;https://velog.velcdn.com/images/jeffapd_/post/ed5baa58-ef2d-4e70-9c9c-e875efe396c4/image.png&quot; width=&quot;80%&quot; height=&quot;30%&quot;&gt;
|:---:|:---:|:---:|
- 이 프로젝트 초반에는 navigationBar의 존재를 모르고 Label과 Button을 직접 만들어서 사용했었다. 그렇기에 위의 사진처럼 navigationBar부분에 걸쳐서 생성이 되어져 있는걸 볼 수 있다. 
- 검색을 통해 navigationBar을 생성해서 만들 수 있다는걸 알아 직접 생성해서 추가한 결과 navigationBar가 두개가 되어있는 모습을 발견했다.
- 더 찾아본 결과 NavigationController를 사용하면 기본적으로 제공을 해주고 접근하는거 또한 ViewController에서 간단하게 설정할 수 있어 마지막 사진처럼 제대로된 자리에 생성이 되었다.
```swift 
 navigationItem.title = &quot;친구 목록&quot;        // 가운데에 들어가는 타이틀
 navigationItem.rightBarButtonItem = rightButton  // 오른쪽에 들어갈 Item을 앞서 생성한 Button(rightButton)을 넣어주면 된다. </code></pre><hr>
<h2 id="💡-lv4---api-연결-alamofire">💡 Lv.4 - API 연결 (Alamofire)</h2>
<h3 id="📌-point-2">📌 Point</h3>
<ul>
<li><p>API 통신을 하는 방법으론 보통 3가지(URLSession, Alamofire, Moya)를 사용하는데 그 중 URLSession은 애플에서 제공해주는 Class이다. 이 URLSession을 더 편하게 사용하려고 만든 라이브러리가 Alamofire이며, 이번엔 Alamofire에 대해 알아보기 위해 사용해보았다. </p>
</li>
<li><p>우리는 API 요청을 통해 받아온 json 데이터에서 이미지가 저장된 url를 가지고 이미지를 보여주면 된다.</p>
</li>
<li><p>우선 다양한 네트워킹을 하기위해 범용적인 함수를 생성해준다.</p>
<pre><code class="language-swift">func fetchData&lt;T: Decodable&gt;(url: URL, completion: @escaping (Result&lt;T, AFError&gt;) -&gt; Void) {
      AF.request(url).responseDecodable(of: T.self) { response in
          completion(response.result)
      }
  }</code></pre>
</li>
<li><p>위에 생성한 메서드를 활용해 api 요청 함수를 생성해준다. (URLSession에서 정의해주고 조건을 설정해줘야하는 부분을 알아서 처리가 되어있고 case문으로 간단하게 구현하면 된다.)</p>
<pre><code class="language-swift">// pokemon api 요청 함수
  func fetchPokemonApi(completion: @escaping (UIImage?) -&gt; Void) {
      let urlAdress = &quot;https://pokeapi.co/api/v2/pokemon/&quot;
      let randomNum = String(Int.random(in: 1...1000))

      // 요청할 url주소를 생성
      guard let url = URL(string: urlAdress + randomNum) else {
          completion(nil)
          return
      }

      // 바로 위에서 생성한 url주소를 가지고 결과값을 받아오는 메서드
      fetchData(url: url) { [weak self] (result: Result&lt;PokemonData, AFError&gt;) in
          guard let self = self else { return }
          switch result {
          case .success(let result) :
              guard let imageUrl = URL(string: result.sprites.frontDefault) else { return }

              // 이미지가 저장된 url을 이용해 UIImage로 변환해서 completion(콜백함수)로 Image의 값을 넘겨준다.
              AF.request(imageUrl).responseData { response in
                  if let data = response.data, let image = UIImage(data: data) {
                      DispatchQueue.main.async {
                          completion(image)
                      }
                  }
              }
          case .failure(let error) :
              print(&quot;데이터 로드에 실패 \(error)&quot;)
              completion(nil)
          }
      }</code></pre>
</li>
</ul>
<hr>
<h2 id="💡-lv5---데이터-저장-coredata">💡 Lv.5 - 데이터 저장 (CoreData)</h2>
<h3 id="📌-point-3">📌 Point</h3>
<ul>
<li>데이터를 디스크에 저장하는 방법으로는 CoreData, UserDefaults가 존재한다. 간단하게 아이디, 비밀번호와 같은 데이터는 UserDefaults로 사용이 가능하지만 사진도 저장하고 추후에 더 추가될 여지가 있다고 생각해 CoreData를 사용해보았습니다.</li>
<li>기본적으로 앱을 생성하는 동시에 CoreData를 사용한다고 하면 아래와 같이 AppDelegate에 코드가 추가가 된다.<img src="https://velog.velcdn.com/images/jeffapd_/post/326496d8-9106-48ca-9f37-06b3df0a3d8f/image.png" width="80%" height="30%">
<img src="https://velog.velcdn.com/images/jeffapd_/post/415bb4eb-d138-4ab6-a7b0-17041ad480dd/image.png" width="80%" height="30%">
첫번째 사진은 AppDelegate파일에 생성된 코드이고 두번째 사진은 우리가 AppDelegate에 있는 persistentContainer을 사용하기위해 타입캐스팅을 통해서 접근하며 인스턴스에 접근하기 쉽게 변수에 랩핑을 했다.</li>
<li>싱글톤으로 선언해 여러곳에서 쉽게 persistentContainer에 접근하기가 가능하다.<pre><code class="language-swift">// 싱글톤으로 생성
  static let shared = CoreDataManger()</code></pre>
<ul>
<li>앞서 Create를 통해 저장된 데이터를 DataSource라는 구조체에 값을 넣어줘 테이블 뷰에 보여줄 수 있다. -&gt; DataSource(name: &quot;이름&quot;, phoneNumber: &quot;전화번호&quot; , profilesImage: ImageData) 형식으로 DataSource에 생성해준다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/9ffa7be3-c479-46ff-bfda-d094a6fd57e7/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="🎯-트러블-슈팅">🎯 트러블 슈팅</h3>
<ul>
<li><p>초기에는 Attribute를 name: String, phoneNumber: String, <code>profilesImage: String</code>으로 생성을 했었다. 그 후 create를 통해 테스트를 거치며, 잘 작동하는걸 확인했다.</p>
</li>
<li><p>하지만 image를 url로 저장하려고 했던 초기 계획이 아닌거 같아 <code>profilesImage의 속성을 Binary Data</code>로 변경을 했다. 그때까지는 아무런 이상이 없어보였지만 빌드를 하고나서 장문의 에러메세지를 받게 되었다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/d288fb21-1171-4547-bf60-ea7eabf45fd5/image.png" width="80%" height="30%">
아래의 에러들은 결론적으론 기존의 저장된 데이터의 형식과 현재 빌드하는 데이터의 형식이 맞지 않아서 즉, Core Data의 모델의 변경으로 빌드를 할 수 없다는 말이였다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/360c739c-6cc5-4e4c-83e0-2caaefecd93a/image.png" width="80%" height="30%">
</li>
<li><p>이 상황에서 해결방법은 1. 데이터를 초기화, 2. 자동 마이그레이션 활성화, 3. 수동으로 마이그레이션 와 같은 선택지가 주어졌다. 저의 경우에는 개발단계이기도 하고 앞서 저장한 데이터는 테스트를 위한 데이터였기에 데이터를 초기화하는 결정을 내렸다. 가장 빠르고 개발환경에서 할 수 있는 방법이다.</p>
</li>
<li><p>아래의 사진은 AppDelegate에서 보았던 persistentContainer 변수이다. 이 코드를 통해 기존에 저장되어 있던 데이터를 지우고 새롭게 변경된 값으로 저장을 할 수 있게 되었다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/a5f1f5ba-ddab-4220-97cf-132c4c0b022f/image.png" width="80%" height="30%">

</li>
</ul>
<hr>
<h1 id="lv6--8">Lv.6 ~ 8</h1>
<ul>
<li>💡 Lv.6 - 저장된 데이터 이름순으로 나열(CoreData - NSSortDescriptor)</li>
<li>💡 Lv.7 - 상세보기 화면 구현(TableView, ViewController) </li>
<li>💡 Lv.8 - 저장된 데이터 수정(CoreData)</li>
</ul>
<hr>
<h2 id="💡-lv6---저장된-데이터-이름순으로-나열coredata---nssortdescriptor">💡 Lv.6 - 저장된 데이터 이름순으로 나열(CoreData - NSSortDescriptor)</h2>
<h3 id="📌-point-4">📌 Point</h3>
<ul>
<li>사실 이미 NSSortDescriptor라는 클래스를 지원해 CoreData의 값을 fetch할때 정렬을 한 후 사용할 수 있게 된다.<pre><code class="language-swift">// NSSortDescriptor을 이용해 오름차순으로 정렬
      let fetchRequest = PhoneBook.fetchRequest()
      fetchRequest.sortDescriptors = [NSSortDescriptor(key: &quot;name&quot;, ascending: true)]    // true : 오름차순, false : 내림차순</code></pre>
</li>
<li>아래의 사진처럼 fetchRequest를 DataSource에 추가하기 전에 먼저 정렬을 마친 후에 추가하는 방식으로 구현했다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/9ffa7be3-c479-46ff-bfda-d094a6fd57e7/image.png" alt=""></li>
</ul>
<hr>
<h2 id="💡-lv7---상세보기-화면-구현tableview-viewcontroller">💡 Lv.7 - 상세보기 화면 구현(TableView, ViewController)</h2>
<h3 id="📌-point-5">📌 Point</h3>
<ul>
<li>우선 기존에 생성해 사용하던 PhoneBookViewController를 재사용하기 위해서 데이터가 없는 화면(연락처 추가 화면)과 데이터가 들어간 화면(상세보기 화면)을 구분하기위해서 Mode를 enum으로 정의해 스위칭할 수 있게 구현했다.</li>
<li>연락처 추가 버튼을 눌렀을 시 mode를 변경해주고 화면전환을 해준다. 그리고 TableView의 특정 cell을 선택했을 시 didSelectRowAt 메서드를 통해서 화면전환과 함께 해당 cell의 데이터도 함께 넘겨주며, mode를 변경해주는 방식으로 구현했다.<img src="https://velog.velcdn.com/images/jeffapd_/post/04a2f1cd-3775-4b67-b42a-eb74eb0dfe27/image.png" width="80%" height="30%">
<img src="https://velog.velcdn.com/images/jeffapd_/post/a9a796b1-01f1-4595-b2ac-7a02efc17fa3/image.png" width="80%" height="30%">
- 변경된 Mode의 값을 기준으로 View와 NavigationBar를 업데이트할 수 있도록 viewWillAppear에 구현해 화면전환시 Mode에 맞게 구현해놓았다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/596c2857-13fd-41f6-a9a3-d1002d2498a5/image.png" width="80%" height="30%">
 <img src="https://velog.velcdn.com/images/jeffapd_/post/1063e25e-43b3-4994-ad90-1360704e08b9/image.png" width="80%" height="30%">

</li>
</ul>
<h3 id="🎯-트러블-슈팅-1">🎯 트러블 슈팅</h3>
<ul>
<li><p>TableView Cell을 선택했을 시 저장되어있는 데이터를 띄워주는데 이름, 전화번호, 이미지까지 잘 받아서 보여주는데 앱 상단의 NavigationBar에 있는 title부분이 바뀌지 않는 이슈가 있었다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/57108267-5047-4c34-847f-ccc067aa0e1d/image.png" width="40%" height="30%">
</li>
<li><p><code>navigationBarSetup함수</code>에서 navigationItem.title을 미리 &quot;연락처 추가&quot;로 저장해 놓은 바람에 <code>configureUI함수</code>에서의 navigationItem.title 값을 변화를 주어도 바뀌지 않았다. 여기서 viewDidLoad에 <code>configureUI</code> 다음에 <code>navigationBarSetup</code>을 호출하게 정의해 놓아서 덮어씌워진거였다. 그래서 <code>navigationBarSetup함수</code>에서 정의해놓은 title값을 제거한 후 제대로 작동이 되었다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/3a7851e2-aa9c-4d73-9ff4-6ed33b791152/image.png" alt=""></p>
</li>
</ul>
<hr>
<h2 id="💡-lv8---저장된-데이터-수정coredata">💡 Lv.8 - 저장된 데이터 수정(CoreData)</h2>
<h3 id="📌-point-6">📌 Point</h3>
<ul>
<li>상세보기 화면을 통해 View에 진입을 하면 CoreData에 저장되어있던 데이터가 보여지는데 여기서 TextField의 값과 Image가 변경이 되고난 후 앞서 정의해 놓은 updateData메서드를 통해 다시한번 값을 수정해서 저장하도록 만들었다. DataSource의 값을 모두 받아서 업데이트가 되도록 구현했다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/8893107c-1282-4efe-b5e2-fc4743c9d965/image.png" alt="">
<img src="https://velog.velcdn.com/images/jeffapd_/post/55244a91-59a7-4a57-b903-f254361ed682/image.png" alt=""></li>
</ul>
<hr>
<h3 id="마치며">마치며..</h3>
<p>이번에 MVC패턴을 사용하고자 View와 ViewController의 역할을 분리하는 과정에서 많은 시행착오가 있었고 delegate와 클로저를 활용한 콜백함수를 구현하는 등 여러 방법으로 구현해보려고 노력했다. 물론 네트워킹과 데이터를 저장하고 다루는 부분에서 시간을 많이 소요해서 앞으로 수정할 것도 다시 공부해야할 것도 많이 남아있는 프로젝트였다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift] 순환참조]]></title>
            <link>https://velog.io/@jeffapd_/swift-%EC%88%9C%ED%99%98%EC%B0%B8%EC%A1%B0</link>
            <guid>https://velog.io/@jeffapd_/swift-%EC%88%9C%ED%99%98%EC%B0%B8%EC%A1%B0</guid>
            <pubDate>Tue, 10 Dec 2024 13:18:06 GMT</pubDate>
            <description><![CDATA[<p>오늘은 간단하게 순환참조에 대해서 알아보자</p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/9913f5cb-e2f8-41ab-96d6-d2a9a0de17ba/image.png" alt=""></p>
<p>A 가 B 를 참조하고 (A→B),
B 가 A 를 참조해서 (B→A), 서로가 서로를 참조하는 상황을 순환 참조라고 한다.
일반적으로 순환 참조는 메모리 누수를 발생시키는 대표적인 사례이다.
아래 예시를 보고 순환 참조 개념을 이해해보죠.</p>
<pre><code class="language-swift">class Person {
    var pet: Dog?
    init() {
        print(&quot;Person 클래스 생성&quot;)
    }
    deinit {
        print(&quot;Person 클래스 소멸&quot;)
    }
}

class Dog {
    var owner: Person?
    init() {
        print(&quot;Dog 클래스 생성&quot;)
    }
    deinit {
        print(&quot;Dog 클래스 소멸&quot;)
    }
}

// person rc = 1
var person: Person? = Person()
// dog rc = 1
var dog: Dog? = Dog()

// dog rc = 2
person?.pet = dog
// person rc = 2
dog?.owner = person

// person rc = 1
person = nil
// dog rc = 1
dog = nil
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]UITextField, UITextView]]></title>
            <link>https://velog.io/@jeffapd_/swiftUITextField-UITextView</link>
            <guid>https://velog.io/@jeffapd_/swiftUITextField-UITextView</guid>
            <pubDate>Mon, 09 Dec 2024 12:49:30 GMT</pubDate>
            <description><![CDATA[<p>오늘은 UITextField와 UITextView의 차이에 대해서 알아보았다.
아래의 사진은 첫번째 상자는 UITextField, 두번째 상자는 UITextView이다.</p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/1bdb52da-da2b-40ec-97eb-86a690ddb7a4/image.png" alt=""></p>
<h2 id="-uitextfield"># UITextField</h2>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/41ceb5c1-9c17-4089-a036-c2864863d28e/image.png" alt=""></p>
<blockquote>
<p>사용자가 인터페이스에서 수정 가능한 텍스트 영역을 보여주는 개체라고 되어있다.</p>
</blockquote>
<h3 id="-주요-특징"># 주요 특징</h3>
<ul>
<li>UIControl 상속</li>
<li>기본적으로 한 줄의 텍스트만 입력가능하다.</li>
<li>UITextView.text 프로퍼티의 기본 타입은 옵셔널이다.</li>
<li>placeholder를 사용할 수 있다.</li>
<li>이메일, 비밀번호, 숫자 등 특정 입력 유형에 맞는 키보드 스타일을 설정 가능하다.</li>
<li>입력이 완료되면 키보드가 자동으로 내려가는 return 키 동작 지원한다.</li>
<li>입력값 검증, 제한 등의 간단한 텍스트 입력에 적합하다.</li>
</ul>
<pre><code class="language-swift">let textField: UITextField = {
let textField = UITextField()
textField.placeholder = &quot;이름을 입력해주세요&quot;
textField.keyboardType = .default
textField.borderStyle = .roundedRect
textField.isSecureTextEntry = false
return textField
}()</code></pre>
<h2 id="-uitextview"># UITextView</h2>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/a25c50f7-3ee3-4a07-8042-06fbc3364ea8/image.png" alt=""></p>
<blockquote>
<p>여러줄로 이루어져있으며, 스크롤이 가능한 텍스트 영역이라고 되어있다.</p>
</blockquote>
<h3 id="-주요-특징-1"># 주요 특징</h3>
<ul>
<li>UIScrollView를 상속하고 있어 스크롤이 가능하다</li>
<li>여러줄 입력이 가능하다</li>
<li>UITextView.text 프로퍼티의 기본 타입은 옵셔널이 아니다</li>
<li>placeholder를 사용하지 못한다 (직접 구현 가능)</li>
<li>스타일링이 가능한 텍스트를 다룰 수 있다.</li>
<li>주로 긴 문장, 메모, 설명 등을 입력받을 때 사용한다.</li>
<li>키보드가 자동으로 내려가지 않기에 별도로 구성해 주어야한다.</li>
</ul>
<pre><code class="language-swift">let textView: UITextView = {
let textView = UITextView()
textView.text = &quot;텍스트를 입력해주세요&quot;  // placeholder (X)
textView.font = UIFont.systemFont(ofSize: 16)
textView.textColor = UIColor.darkGray
textView.isEditable = true
textView.isScrollEnabled = true
return textView
}()</code></pre>
<hr>
<p>두 컴포넌트는 텍스트를 입력받는 비슷한 기능을 제공하지만 디테일한 기능의 차이가 있기에 상황에 따라서 쓰면 좋을거 같다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Setting]CodeBase Setting]]></title>
            <link>https://velog.io/@jeffapd_/SettingCodeBase-Setting</link>
            <guid>https://velog.io/@jeffapd_/SettingCodeBase-Setting</guid>
            <pubDate>Fri, 06 Dec 2024 10:30:04 GMT</pubDate>
            <description><![CDATA[<p>현재 스토리보드 없이 코드로 구현하는 프로젝트를 진행중이다.
iOS는 Xcode에서 스토리보드와 코드베이스를 이용해 개발이 가능하다. </p>
<p>코드베이스로 구현을 하기 위해서는 가장 먼저 세팅을 해줘야한다.</p>
<h2 id="codebase-setting">codebase Setting</h2>
<h3 id="-storyboard-파일-삭제"># storyboard 파일 삭제</h3>
<ul>
<li><p>초기에 우리가 프로젝트를 생성하면 아래 사진과 같이 Main으로 된 스토리보드 파일이 존재한다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/c290828f-6613-44a6-a399-78278791d16a/image.png" alt=""></p>
</li>
<li><p>간단히 메인 파일을 지우면 아래와 같이 정말로 지울지 물어보면 우리는 쓰레기통으로 보내주면 된다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/61852187-bd07-43a5-92e6-590adde87ac0/image.png" alt=""></p>
</li>
<li><p>첫번째 사진에서 본 파일 목록들을 보면 <code>Info</code>라는 파일명을 볼 수 있다. 이 파일은 Info.plist이며, 우리는 여기서 <code>main</code>이라는 키워드를 검색해 Storyboard Name에 있는 부분을 제거해주면 된다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/fd450bd9-1863-4559-b88b-ca923dbea69b/image.png" alt=""></p>
</li>
<li><p>그 다음으로 프로젝트파일에 들어가보면 여러가지 설정하는 부분이 존재한다. 그럼 우리는 여기서 <code>Build Settings</code> 이라는 탭에 들어가서 main을 검색해본다면 <code>UIKit Main Storyboard File Base Name</code> 부분에 Main이 존재하는데 이 부분 또한 삭제를 해주면 된다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/69197a24-b9b7-4b00-be42-c064506d1e91/image.png" alt=""></p>
</li>
</ul>
<h3 id="-viewcontroller-연결"># ViewController 연결</h3>
<ul>
<li><p>우리는 스토리보드를 삭제했기에 view controller를 연결하기 위한 작업이 필요하다. 우선 SceneDelegate 파일에 들어가 코드를 추가해주면 된다. 아래의 사진에 보듯이 주석부분과 아래의 코드를 수정해주면 된다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/b19fad30-6835-4d4f-aa51-796029f46585/image.png" alt=""></p>
</li>
<li><p>아래의 코드는 윈도우 위에 올라가는 root view Controller를 지정해주는 역할을 한다.</p>
<pre><code class="language-swift">  var window: UIWindow?

  func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

      guard let windowScene = (scene as? UIWindowScene) else { return }
      let window = UIWindow(windowScene: windowScene)
      window.rootViewController = ViewController()  // ViewController를 루트 뷰 컨트롤러로 설정
      window.makeKeyAndVisible()

      self.window = window
      }</code></pre>
<p>아래에 사진과 같이 추가를 하면된다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/9b532afa-aa45-41a3-967d-700534725454/image.png" alt=""></p>
</li>
<li><p>위의 과정을 모두 마쳤다면 View Controller가 잘 연결이 되었는지 print문을 이용해 확인해보았고, 로그창에 잘 연결되었다는걸 확인할 수 있었다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/ad627d52-ce0d-4489-af57-13b5ccd55bc3/image.png" alt=""></p>
</li>
</ul>
<p>초기에 설정하고 잘 건드리지 않는 영역이다보니 이렇게 정리해 나중에 참고해야겠다는 생각에 포스팅을 하게 되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]날씨 앱]]></title>
            <link>https://velog.io/@jeffapd_/swift%EB%82%A0%EC%94%A8-%EC%95%B1</link>
            <guid>https://velog.io/@jeffapd_/swift%EB%82%A0%EC%94%A8-%EC%95%B1</guid>
            <pubDate>Thu, 05 Dec 2024 12:24:25 GMT</pubDate>
            <description><![CDATA[<p>오늘은 API를 통한 데이터를 받아와 간단한 날씨 앱을 만들어보는 시간을 가졌다.</p>
<h2 id="-api-요청"># API 요청</h2>
<p>이번 앱을 만들기 위해 날씨 데이터를 요청할 수 있는 사이트는 <code>https://openweathermap.org</code>라는 날씨 정보를 제공해주는 사이트이다. </p>
<p>여러가지 종류의 API를 제공해주는데 오늘 사용해볼 API는 2가지이다.</p>
<ul>
<li>Current Weather Data (현재의 날씨에 관한)</li>
<li>5 Day / 3 Hour Forecast (3시간 간격의 5일치 일기예보)</li>
</ul>
<h3 id="-데이터-요청-메서드-범용적인-메서드"># 데이터 요청 메서드 (범용적인 메서드)</h3>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/0e35801b-971a-4572-8906-d4c70e9ae3ef/image.png" alt=""></p>
<ul>
<li>fetchData라는 이름의 함수를 만들었다. 이 함수는 &lt;T: Decodable&gt; 이라는 제네릭을 사용해 특정한 데이터 타입만을 취급하지 않고 필요에 따른 데이터 타입을 이용해 함수에 사용할 수 있다. 현재 위의 코드는 Decodale을 채택한 어떠한 타입도 사용할 수 있다는걸 볼 수 있다. 즉, 제네릭을 이용해 범용적으로 활용하도록 함수를 만든 것이다.</li>
<li>앞선 포스팅에서 URLSession을 다뤘기에 코드에 대한 자세한 설명은 중복되기에 생략하겠습니다.</li>
</ul>
<h3 id="-current-weather-data-api"># Current Weather Data (API)</h3>
<ul>
<li><p>API를 요청하기 위한 주소로는 <code>https://api.openweathermap.org/data/2.5/weather?lat={lat}&amp;lon={lon}&amp;appid={API key}</code>이다.
여기서 필요로 하는 부분은 <code>위도(lat)</code> <code>경도(lot)</code>를 나타내는 부분과 <code>개인의 API(API key)</code>가 필요로 하다. </p>
</li>
<li><p><code>?lat={lat}&amp;lon={lon}&amp;appid={API key}</code> 앞의 주소를 제외하고 <code>?</code> 뒤에는 query라고 하며, key = value형식으로 이루어져 있고 여러 쿼리문을 연결하는 <code>&amp;</code>기호가 사용이 된다.</p>
</li>
<li><p>우리는 이런 API 요청을 하는 주소를 이용해서 요청이 이루어지는데 <code>URLComponents</code>를 이용해 쿼리문을 쉽게 추가할 수 있다. </p>
<ul>
<li><pre><code class="language-swift">var urlComponents = URLComponents(string: &quot;https://api.openweathermap.org/data/2.5/weather&quot;)
urlComponents?.queryItems = self.urlQueryItems

// 공통적으로 URL 쿼리에 들어가는 아이템을 선언
private let urlQueryItems: [URLQueryItem] = [
    URLQueryItem.init(name: &quot;lat&quot;, value: &quot;37.5&quot;),
    URLQueryItem.init(name: &quot;lon&quot;, value: &quot;126.9&quot;),
    URLQueryItem.init(name: &quot;appid&quot;, value: &quot;개인 API&quot;),
    URLQueryItem.init(name: &quot;units&quot;, value: &quot;metric&quot;)
]

// https://api.openweathermap.org/data/2.5/weather?lat=37.5&amp;lon=126.9&amp;appid=개인 API&amp;units=metric 이라는 주소가 완성이 된다.</code></pre>
</li>
<li><p>디코딩이 이루어지기 위해서 JSON형식에 맞게 구현해주어야 한다. json 데이터는 많은 정보를 주지만 우리는 모든 데이터를 사용하지 않기에 필요한 부분만 골라서 디코딩을 해주면 된다.</p>
</li>
</ul>
<table>
<thead>
<tr>
<th align="center">JSON Data</th>
<th align="center">디코딩을 할 코드</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/4186babd-efe5-4d3d-a648-e83ec324bdd3/image.png" width="80%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/9e1400cb-c201-4f27-902f-4511e4070f55/image.png" width="70%" height="30%"></td>
</tr>
</tbody></table>
<ul>
<li>아래의 사진은 Current Weather Data를 요청하기 위한 메서드이다.
위에서 미리 만들어 놓은 fetchData메서드를 이용해 API를 요청하면 된다. fetchData에서 API요청 후 데이터 값을 디코딩 해준 후 result으로 반환해 준다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/5da5b225-c2b6-478e-a0dc-724aa696f9b4/image.png" alt=""></li>
</ul>
<p>이렇게 result에 저장된 값을 이용해 아래의 현재 기온, 최저 기온, 최고 기온, 날씨 아이콘 등을 보여줄 수 있게된다. 그리고 중요한 부분은 <code>UI를 업데이트</code>를 하기위해서는 항상 <code>main 스레드</code>에서 작업이 이루어져야 하기에 우리는 <code>DisPathQueue.main</code> 을 사용해서 업데이트를 해주면 된다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/ecad3f53-2493-4028-b8e1-a5a7acee17b9/image.png" width="30%" height="30%"> 

</li>
</ul>
<h3 id="-5-day--3-hour-forecast-api"># 5 Day / 3 Hour Forecast (API)</h3>
<ul>
<li><p>앞에서 설명한 부분과 크게 다른 부분은 없고 다른 API 요청을 하기에 디코딩하는 데이터들 또한 아래와 같이 따로 만들어주어야한다.</p>
<table>
<thead>
<tr>
<th align="center">JSON Data</th>
<th align="center">디코딩을 할 코드</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/10588bbe-67fe-4fdc-a604-171db68244e6/image.png" width="70%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/9a8d45ac-3436-416d-9b70-887a80b4dae3/image.png" width="70%" height="30%"></td>
</tr>
</tbody></table>
<ul>
<li>Forecast 요청하는 API 메서드 또한 큰 차이는 없고 요청한 데이터를 디코딩한 후 어떻게 처리하는지에 차이가 있다고 보면 된다. <ul>
<li><code>DisPathQueue.main</code>에서 이루어지는 코드들은 tableView에 데이터를 보여주기위해서 저장(forecastData라는 배열을 만들고 그 변수안에 result.list값을 저장)하고 테이블 뷰를 <code>reloadData</code>를 해주는 과정이다. </li>
</ul>
</li>
<li>초기에 <code>reloadData</code>를 해주지 않아 tableView에 아무런 데이터가 보여지지 않았었다. 만약 reloadData를 해주지 않는다면 테이블 뷰는 데이터가 변경이 되었는지 감지하지 못한다. 그렇기에 현재 fetchForcastWeather메서드를 통해 새로운 데이터를 업데이트를 했지만 이 배열이 바뀐지 모른다면 비어있는 배열을 보여주게된 것이다. 
<img src="https://velog.velcdn.com/images/jeffapd_/post/c937eda6-a8bc-4b17-9a91-39a3ecfa87a0/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="-tableview-부분"># TableView 부분</h3>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/a6e461aa-130e-4068-874f-04343ef30cce/image.png" alt=""></p>
<ul>
<li>기본적으로 TableView를 사용하기 위해서는 UITableViewDelegate, UITableViewDataSource라는 프로토콜들을 채택해줘야한다. </li>
<li>채택 후에는 위의 코드와 같이 대신 작업을 해줄 ViewController에게 위임을 해주면 된다. 또한 TableView에 사용될 TableViewCell을 등록을 해줌으로써 비로소 우리가 cell을 사용하여 데이터를 보여줄 수 있게 된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/d9b6cf5a-91c2-4345-885d-377b8bf07cec/image.png" alt=""></p>
<ul>
<li>채택된 프로토콜들은 필수적으로 사용해야하는 메서드들이 존재하며, 반드시 설정을 해줘야한다.</li>
<li>필수로 구현해줘야하는 메서드는 <code>numberOfRowInSection</code>, <code>cellForRowAt</code>으로 테이블 뷰에 보여줄 셀의 갯수를 정해주고, 셀의 들어갈 구성을 정해주는 메서드들이다. 앞에서 API요청 후 받은 데이터를 <code>forcastDataItem</code>이라는 상수에 넣어서 필요한 데이터를 일기예보의 날짜와 그날의 기온을 보여주게 코드로 구현을 하였다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift] URLSession]]></title>
            <link>https://velog.io/@jeffapd_/swift-URLSession</link>
            <guid>https://velog.io/@jeffapd_/swift-URLSession</guid>
            <pubDate>Wed, 04 Dec 2024 11:41:04 GMT</pubDate>
            <description><![CDATA[<p>이번에는 URLSession에 대해서 알아보았다.</p>
<h2 id="-urlsession"># URLSession</h2>
<blockquote>
<p><em>An object that coordinates a group of related, network data transfer tasks.</em>
네트워크 데이터 전달 작업에 연관된 일련의 일을 처리하는 객체이다.
즉, swift에서 네트워크 통신 방법 중 한가지이다. URLSession은 앱과 서버간에 데이터를 주고받기 위해 HTTP를 사용하며, 네트워크 요청을 처리하는 API라고 할 수 있다.</p>
</blockquote>
<ul>
<li>iOS와 서버 간의 http 프로토콜을 지원하며 Request와 Response 구조를 가진다.</li>
<li>URLSession은 여러 개의 URLSessionTask를 생성하여 이를 통해 서버와 통신을 하고, Delegate로 네트워크의 과정을 확인하는 형태이다.</li>
<li>URLSession을 통해 iOS 앱이 실행중이지 않을 때에도 백그라운드에서 데이터를 다운로드할 수 있다.</li>
<li>URLSessionDelegate나 URLSessionTaskDelegate를 사용하면 권한이나 리다이렉션, 작업 완료에 대한 이벤트도 받을 수 있다. </li>
</ul>
<p>URLSession을 사용하기 위해서 크게 두 가지에 대해서 더 알아보아야한다.</p>
<ol>
<li>URLSessionConfiguration</li>
<li>URLSessionTask</li>
</ol>
<h3 id="-urlsessionconfiguration"># URLSessionConfiguration</h3>
<blockquote>
<p>Configuration은 환경 설정을 의미
URLSession 으로 네트워크 통신을 하되, 여러가지 커스텀한 설정들을 할 때 URLSessionConfiguration 을 이용한다.</p>
</blockquote>
<ul>
<li>.default : 기본 통신을 할때 사용한다. (쿠키와 같은 저장 객체 사용)<pre><code class="language-swift">let defaultSession = URLSession(configuration: .defualt)</code></pre>
</li>
<li>.ephemeral : 쿠키나 캐시를 저장하지 않는 정책을 가져갈 때 사용한다. (ex. 사파리의 개인정보보호 모드)<pre><code class="language-swift">let ephemeralSession = URLSession(configuration: .ephemeral)</code></pre>
</li>
<li>.background : 앱이 백그라운드에 있는 상황에서 콘텐츠 다운로드, 업로드를 할 때 사용한다.<pre><code class="language-swift">let backgroundSession = URLSession(configuration: .background)</code></pre>
</li>
</ul>
<h3 id="-urlsessiontask"># URLSessionTask</h3>
<blockquote>
<p>URLSessionTask 으로 네트워크 통신을 할 때 어떤 태스크를 수행할 것 인지 결정 가능하다.</p>
</blockquote>
<ul>
<li>URLSessionDataTask: 서버로부터 데이터를 가져오거나 서버에 데이터를 전송할 때 사용한다. (GET요청)</li>
<li>URLSessionUploadTask: 파일 업로드를 처리할 때 사용되며 백그라운드 업로드 지원된다.</li>
<li>URLSessionDownloadTask: 파일 다운로드를 처리할 때 사용되며, 백그라운드 다운로드 지원된다.</li>
</ul>
<h2 id="-사용해보기"># 사용해보기</h2>
<ul>
<li><p>사용에 앞서 <code>https://reqres.in/api/users/1</code>라는 주소를 이용해 테스트 데이터를 네트워크 통신을 하려고 한다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/a2f1ac23-9203-4082-9924-26b16e23462b/image.png" alt=""> 
<code>https://reqres.in/api/users/1</code>에 접속해보면 위와 같이 json형태의 데이터를 볼 수 있다. 우리는 이 데이터를 받아오는 동작을 해볼거다.</p>
</li>
<li><p>json형태에 맞게 데이터를 받기위해 아래의 사진과 같이 미리 구조체롤 <code>Codable</code>이라는 프로토콜을 채택해 구성하면 된다. 그리고 실제 json 데이터와 구조체의 이름이 매칭이 되지 않는다면 enum타입으로 매칭해서 바꿔서 데이터를 받아올 수 있게 해준다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/b92692e0-1cba-4b4d-9b84-7250024f7d51/image.png" alt=""></p>
</li>
<li><p>이제는 네트워크를 통신할 url을 설정해주고, 설정한 url에서 데이터를 어떤식으로 받아올지 아래와 같이 설정을 해주면 된다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/57674f5a-2c77-465b-b2aa-65d3028a3ea2/image.png" width="80%" height="30%">
</li>
<li><p>설정을 마친 후 데이터를 요청하는 일을 시킬 dataTask메서드를 구현해주면 된다. dataTask를 통해 얻어오는 (data, response, error) 3가지의 타입이 클로저로 받아온다. 
데이터의 성공하는 범위는 200번대 숫자들이기에 아래의 범위를 설정해 주었고, 성공을 했다면 json형식으로 받은 데이터를 디코딩해서 사용할 수 있게 해준다. 그리고 가장 마지막에 보이는 <code>resume</code> 키워드를 마지막에 써줘야 dataTask 메서드가 실행이 되기에 적어줘야한다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/43750b68-06f0-4a7f-86f7-7ec6adb55297/image.png" width="80%" height="30%">


</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]UserDefaults]]></title>
            <link>https://velog.io/@jeffapd_/swiftUserDefaults</link>
            <guid>https://velog.io/@jeffapd_/swiftUserDefaults</guid>
            <pubDate>Tue, 03 Dec 2024 12:45:30 GMT</pubDate>
            <description><![CDATA[<h2 id="-userdefaults"># UserDefaults</h2>
<blockquote>
<p>iOS 앱에서 데이터를 영구적으로 보관하기 위한 방식에는 여러가지가 있다. 그 중 한가지는 UserDefaults 이다.
UserDefaults는 앱이 실행되는 동안에 key-value 형태로 데이터를 저장하는 사용자의 기본 데이터베이스에 대한 인터페이스이다.
앱이 종료되더라도 사라지지 않고, 영구적으로 저장이 된다. 하지만 앱이 완전히 삭제가 된다면 데이터 또한 같이 사라진다.</p>
</blockquote>
<ul>
<li><p>간단히 말하면 </p>
<ul>
<li>디스크에 데이터를 저장할 수 있게 돕는 도구이다.</li>
<li>CoreData 보다 사용성이 간단하다.</li>
<li>key-value를 이용해 값을 저장한다.</li>
<li>Singleton 객체, 앱 전체에서 하나의 인스턴스로 사용된다.</li>
<li>대량의 데이터를 담는데는 CoreData가, 비교적 단순한 데이터를 담는 데에는 UserDefaults가 적절하다.</li>
<li>자동 로그인 여부, 아이디 저장, 환경설정에서 설정하는 설정 데이터 값(사용자가 선호하는 측정 단위나 미디어 재생 속도 등) 같은 단일 데이터 등을 UserDefaults로 담아서 보관하기도 한다.</li>
</ul>
<h2 id="-crud란"># CRUD란?</h2>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/19bd3a06-6953-4efc-bd80-2a724815b53e/image.png" alt=""></p>
<p>그럼 간단하게 CRUD에 대해 알아보고 사용해보자.</p>
<ul>
<li><p>일반적인 개발론에서 CRUD 라는 용어는 자주 사용하고 뜻은 다음과 같다.</p>
<ul>
<li>Create = 데이터 생성</li>
<li>Read = 데이터 읽기</li>
<li>Update = 데이터 업데이트 (쓰기)</li>
<li>Delete = 데이터 삭제</li>
</ul>
</li>
<li><p>전화번호 앱에서 일어나는 CRUD 를 예시로 본다면</p>
<ul>
<li>C = 새로운 전화번호를 등록한다.</li>
<li>R = 저장된 전화번호 데이터를 조회한다.</li>
<li>U = 저장된 전화번호를 수정한다. </li>
<li>D = 전화번호를 삭제한다.</li>
</ul>
</li>
</ul>
<h3 id="-create--read"># Create &amp; Read</h3>
<img src="https://velog.velcdn.com/images/jeffapd_/post/ffcea607-8eca-4044-bd91-92709e29da9f/image.png" width="80%" height="30%">

<ul>
<li>위와 같이 <code>UserDefaults.standard.set()</code> 메서드를 통해서 데이터를 <code>저장</code>할 수 있다. (Create)</li>
<li>또한 <code>UserDefaults.standard.string(forKey: &quot;&quot;)</code>을 통해 데이터의 저장된 값을 <code>읽어</code>올 수 있다. (Read)<ul>
<li>Read는 타입에 맞는 메서드를 사용해야한다.</li>
<li>String 타입 : <code>UserDefaults.standard.String(forKey: &quot;&quot;)</code></li>
<li>bool 타입 : <code>UserDefaults.standard.bool(forKey: &quot;&quot;)</code></li>
<li>Int 타입 : <code>UserDefaults.standard.integer(forKey: &quot;&quot;)</code></li>
</ul>
</li>
<li>forkey에는 앞서 Create하면서 선언한 forkey값을 사용해주면 된다.</li>
</ul>
<h3 id="-update--delete"># Update &amp; Delete</h3>
<img src="https://velog.velcdn.com/images/jeffapd_/post/92c23dc6-26cd-4a35-904f-df9cbc4e5e30/image.png" width="80%" height="30%">

<ul>
<li>위와 같이 <code>UserDefaults.standard.set()</code> 메서드를 통해서 데이터를 <code>수정</code>할 수 있다. (Update)<ul>
<li>Update는 Create와 사용법이 동일하며 데이터 위에 덮어씌운다는 느낌이다.</li>
</ul>
</li>
</ul>
<img src="https://velog.velcdn.com/images/jeffapd_/post/d2c73e6e-3ed6-4d99-ad8d-e8185c62b630/image.png" width="80%" height="30%">

<ul>
<li>UserDefaults.standard.removeObject(forKey: &quot;&quot;) 메서드를 통해서 데이터를 <code>삭제</code>할 수 있다. (Delete)<ul>
<li>forkey에는 앞서 선언한 forkey값을 사용해주면 된다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]UITapGestureRecognizer]]></title>
            <link>https://velog.io/@jeffapd_/swiftUITapGestureRecognizer</link>
            <guid>https://velog.io/@jeffapd_/swiftUITapGestureRecognizer</guid>
            <pubDate>Mon, 02 Dec 2024 12:18:10 GMT</pubDate>
            <description><![CDATA[<p>오늘은 프로젝트 도중에 탭제스처를 사용하다가 발생한 문제에 대해서 써보겠습니다.</p>
<h2 id="uitapgesturerecognizer">UITapGestureRecognizer</h2>
<blockquote>
<p>UIGestureRecognizer을 상속 받은 class이다. 
single 혹은 multi tap을 해석하는 제스처를 인식하는 class라고 한다.</p>
</blockquote>
<p>UIButton은 UIControl을 상속받으므로 별도 처리하지 않아도 이벤트를 인식한다.
하지만 ImageView나 View는 UIControl을 상속받지 않으므로 별도의 처리를 통해 이벤트를 감지하고 생성하게 만들어야한다. </p>
<h3 id="-사용하기"># 사용하기</h3>
<p>저의 경우에는 하단의 footerView를 하나 생성해서 view가 눌렸을 때 이벤트를 처리하려고 했습니다. 그래서 하나의 view를 만들고 그 view에 tapGestureRecognizer이라는 메서드를 사용했다. 
<img src="https://velog.velcdn.com/images/jeffapd_/post/bd210156-6cad-4f38-b45a-ed929fa41b6e/image.png" alt="">
제스처를 인식할 target과 인식했을 경우 어떤 이벤트를 처리할지 #selector에 지정해주면 된다. </p>
<ul>
<li>target: self.footerView -&gt; 현재 나 자신의 클래스 안에 속해 만들어진 footerView라는 UIView를 가리키는 부분이다.</li>
<li>action: #selector(footerViewTapped) -&gt; 어떤 이벤트를 실행시킬지, 사진에 보시면 @objc func footerViewTapped() 라는 함수를 실행하는 코드이다. <ul>
<li><code>#selector</code>는 Objective-C에서 사용되는 메서드를 참조할 때 사용되는 swift문법이고, <code>@objc</code>는 swift 메서드나 속성을 Objective-C 런타임에서도 사용할 수 있도록 표시하는 키워드이다.</li>
</ul>
</li>
<li>tap gesture가 <code>인식하는 기준은 frame의 크기</code>이다. </li>
</ul>
<h3 id="-트러블슈팅"># 트러블슈팅</h3>
<p>여기까지 잘 연결이 되어있는걸 확인했지만 막상 하단의 footerView를 눌렀을 때 미리 적어놓은 print문이 실행이 되지 않는 것을 발견했다. 그리고 footerView가 있음에도 View뒤에 collection뷰가 터치가 되어 움직이는 모습도 발견했다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/3971c535-e16b-42bd-abbc-75cd56bf3225/image.png" alt="">
오른쪽부터 4번째에 있는 아이콘은 Debug view Hierarchy기능을 사용할 수 있다. 이 기능은 현재 View의 상태를 확인할 수 있고 어떤식으로 컴포넌트들이 쌓여 있는지 확인을 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/5e7097e6-d782-49a0-bbcc-8b2e64c838e5/image.png" alt="">
위의 사진과 같이 뷰의 계층을 보여준다. 스토리보드에서 작업했을때 볼 수 있는 계층도이며, view의 오른쪽에 보면 보라색으로 경고창이 떠있는걸 볼 수 있다. 이 의미는 현재 View의 레이아웃이 제대로 이루어지지 않았다는걸 알려주는 경고창이다. </p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/6ffd4351-6eae-484b-a3dc-592c10085a1b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/51f3fc9f-55fc-4b92-8a65-c898bf47b4da/image.png" alt=""></p>
<p>위의 사진은 제가 마주친 문제의 부분이다. 첫번째 사진처럼 눈으로 보기에는 이상함을 느끼지 못했다. 하지만 옆모습을 보면 빨강색으로 된 footerView영역 뒤에보면 앱의 최하단까지 collection View가 내려와있는걸 볼 수 있다. 그렇다고해도 앞에 View가 있고 터치를 한다면 작동을 해야한다고 생각되지만 로그창에 print문이 찍히지 않는걸 알 수 있었다.</p>
<h3 id="-해결과정"># 해결과정</h3>
<ul>
<li><p>첫번째 시도
footerView위에 두개의 label이 올라와 있고 두개의 label들 또한 UIConrol을 상속받지 않기때문에 이벤트가 발생하지 않는다. 그리고 UIView를 상속받는 클래스들은 <code>isUserInteractionEnabled</code>라는 항목이 <code>false</code>로 설정되어 있다(UIView는 true로 활성화가 되어있음). 이는 사용자의 상호작용이 활성화가 되어있지 않다는걸 의미한다. 그래서 tapGesture를 인식하지 못하게 되는 상황이 발생한다. </p>
<ul>
<li>두개의 레이블의 <code>isUserInteractionEnabled</code>라는 항목이 <code>true</code>로 설정</li>
<li>여전히 변화가 없었다.</li>
</ul>
</li>
<li><p>두번째 시도
위에 문제점을 발견한 collectionView와 footerView의 위치에 대한 의문이 있었다. 그래서 아까 지나간 보라색 경고창을 다시 확인해보았다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/12098cce-a7ad-45d7-a302-6c7626cacf21/image.png" alt=""> height와 vertical의 제약조건이 애매하다고 나왔다. 그래서 자세히 보니 현재 footerView의 height를 보면 0이라는 걸 확인할 수 있었다.</p>
</li>
<li><p>Auto Layout을 잡기 위해서는 위치와 크기가 필수이다. height을 50을 줬더라도, 뷰의 상단과 하단 사이를 명확히 정의하지 않으면 Auto Layout은 뷰를 배치할 수 없다고 판단할 수 있었다. 그래서 제대로된 top과 bottom에 제약조건을 다시 설정해주었다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/ace120e0-7733-49cb-8e89-1129e582e057/image.png" alt="">
<img src="https://velog.velcdn.com/images/jeffapd_/post/9ad22cc0-b91a-47ed-bb37-459b3b5263e7/image.png" alt=""></p>
<ul>
<li>위와 같이 제대로 높이가 잡히니 footerView의 프레임의 높이가 잡히는걸 볼 수 있고 collectionView 또한 위로 올라간걸 확인할 수 있었다.</li>
</ul>
</li>
</ul>
<h4 id="-마치며"># 마치며</h4>
<p>이렇게 스토리보드 없이 작업을 하다보니 오토레이아웃을 잡아가는 과정에서 헷갈리고 어려움이 있었다. 하나씩 크기와 위치를 생각하며 작업을 한다면 실수를 줄일 수 있을거 같다. 이번 트러블슈팅으로 인해 탭제스처의 인식 범위에 대해서도 알게된 계기가 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]project_계산기]]></title>
            <link>https://velog.io/@jeffapd_/swiftproject%EA%B3%84%EC%82%B0%EA%B8%B0-d9mm6erj</link>
            <guid>https://velog.io/@jeffapd_/swiftproject%EA%B3%84%EC%82%B0%EA%B8%B0-d9mm6erj</guid>
            <pubDate>Thu, 21 Nov 2024 22:41:10 GMT</pubDate>
            <description><![CDATA[<p>이번 프로젝트 과제는 계산기 앱을 만드는 것이다.
보통 iOS개발에는 storyBoard방식과 codeBase방식이 존재한다. 두 가지의 방식은 각각의 장점이 존재한다. 나는 보통 storyBoard를 활용해 앱을 만들어왔기에 이번 앱은 codeBase로 만들어보았다.</p>
<p>단계를 8단계를 나눠놓아 차분히 따라가면 앱을 완성할 수 있었다. 1<del>5단계는 앱의 UI를 구현하고 6</del>8단계는 로직을 구현하는 느낌이다.</p>
<h1 id="lv1--5">Lv.1 ~ 5</h1>
<ul>
<li>💡 Lv.1 - Label을 구현하기 </li>
<li>💡 Lv.2 - Button 구현하기 (HorizontalStackView를 이용하기)</li>
<li>💡 Lv.3 - Button 구현하기 (VerticalStackView를 이용하기)</li>
<li>💡 Lv.4 - Button 구현하기 (반복되는 버튼생성을 메서드로 만들어 활용하기)</li>
<li>💡 Lv.5 - Button 구현하기 (사각형인 버튼을 원형으로 적용하기)</li>
</ul>
<table>
<thead>
<tr>
<th align="center">Lv.1</th>
<th align="center">Lv.2</th>
<th align="center">Lv.3</th>
<th align="center">Lv.4</th>
<th align="center">Lv.5</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/a1df644e-1986-42fd-9494-528eb58e1915/image.png" width="80%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/a13cd519-970d-4e70-9610-156b6ab26990/image.png" width="80%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/96002daa-73f0-4ea3-9673-c1c9af6d9000/image.png" width="80%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/1f2d648e-4b7c-4e06-92bb-42a731e35ec6/image.png" width="80%" height="30%"></td>
<td align="center"><img src="https://velog.velcdn.com/images/jeffapd_/post/b38e44fd-706e-4e70-80c9-7763a54d8413/image.png" width="70%" height="30%"></td>
</tr>
</tbody></table>
<h2 id="💡-lv1---label을-구현하기">💡 Lv.1 - Label을 구현하기</h2>
<h3 id="📌-point--📝-개발-과정">📌 Point &amp; 📝 개발 과정</h3>
<ul>
<li><p>스토리보드와 달리 코드베이스는 직접 UI 하나하나 선언해줘야 했다.</p>
<ul>
<li><pre><code class="language-swift">// Label 생성
static var label: UILabel = {
    let label = UILabel()
    label.text = result
    label.textColor = .white
    label.backgroundColor = .black
    label.textAlignment = .right
    label.font = .boldSystemFont(ofSize: 60)
    return label
}()</code></pre>
<ul>
<li>Label를 위와 같이 생성하며, Label에 필요한 조건을 같이 적어주며 생성하는 모습을 볼 수 있다. </li>
<li><pre><code class="language-swift">// NSLayoutConstraint를 사용한 오토레이아웃
 NSLayoutConstraint.activate([
     label.heightAnchor.constraint(equalToConstant: 100),
    label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
    label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30),
    label.topAnchor.constraint(equalTo: view.topAnchor, constant: 200)
  ])
</code></pre>
</li>
</ul>
<p>// SnapKit을 사용한 오토레이아웃</p>
<pre><code>ViewController.label.snp.makeConstraints {
    $0.height.equalTo(100)
    $0.leading.equalToSuperview().offset(30)
    $0.trailing.equalToSuperview().offset(-30)
    $0.top.equalToSuperview().offset(200)
}</code></pre><pre><code>- 물론 오토 레이아웃 또한 우리가 직접 설정해줘야한다. 이 부분은 애플에서 제공해주는 NSLayoutConstraint이 있고, 좀 더 간단하게 사용할 수 있게 SnapKit이라는 라이브러리를 사용할 수 있다. 위의 코드는 두 가지의 방식으로 작성한 코드이며 그냥 보기에도 코드의 길이도 사용하기에도 단순화를 한 모습을 볼 수 있다.</code></pre></li>
</ul>
</li>
</ul>
<hr>
<h2 id="💡-lv2---button-구현하기-horizontalstackview를-이용하기">💡 Lv.2 - Button 구현하기 (HorizontalStackView를 이용하기)</h2>
<h3 id="📌-point--📝-개발-과정-1">📌 Point &amp; 📝 개발 과정</h3>
<ul>
<li><p>버튼을 생성하고 생성한 버튼을 하나의 horizontalStackView에 넣어서 구현하면 된다.</p>
<ul>
<li><img src="https://velog.velcdn.com/images/jeffapd_/post/a16c952e-16c9-4eee-8391-eaea1477bc39/image.png" width="80%" height="30%"></li>
<li><p>각각의 버튼을 생성해주고 생성된 버튼을 스택뷰안에 넣는 모습을 볼 수 있다.</p>
</li>
<li><img src="https://velog.velcdn.com/images/jeffapd_/post/4bd8f394-cca7-41d0-9fa0-c66b6b82e414/image.png" width="80%" height="30%"></li>
<li><p>버튼을 생성하면서 설정해주는 반복된 코드들이 많기에 따로 메서드를 만들어 코드를 좀 간결하게 만들었다.</p>
</li>
</ul>
</li>
</ul>
<hr>
<h2 id="💡-lv3---button-구현하기-verticalstackview를-이용하기">💡 Lv.3 - Button 구현하기 (VerticalStackView를 이용하기)</h2>
<h3 id="📌-point--📝-개발-과정-2">📌 Point &amp; 📝 개발 과정</h3>
<ul>
<li><img src="https://velog.velcdn.com/images/jeffapd_/post/f767fc96-1e51-491b-90b2-5b9cf15d667d/image.png" width="80%" height="30%">
<img src="https://velog.velcdn.com/images/jeffapd_/post/d6616ecc-fc81-4128-8eba-42362ceca39d/image.png" width="80%" height="30%">
<img src="https://velog.velcdn.com/images/jeffapd_/post/34ae70a3-af4c-48af-9439-7f87be023bf6/image.png" width="80%" height="30%"></li>
<li>버튼을 하나하나 생성해서 horizontalStackView에 계산기 버튼 위치에 맞게 추가하고 추가한 각 라인의 horizontalStackView를 하나의 verticalStackView에 넣어서 계산기 입력 버튼을 구현할 수 있었다. </li>
</ul>
<h3 id="🎯-트러블-슈팅">🎯 트러블 슈팅</h3>
<ul>
<li>과연 위와 같은 방식이 최선인가? 저렇게 하나하나 선언해주는게 맞는 것인가? 라는 의문을 시작으로 아래와 같이 바꿔보았다.<ul>
<li><img src="https://velog.velcdn.com/images/jeffapd_/post/2f92efe1-00b9-4fc7-ac1b-c89f8de23334/image.png" width="80%" height="30%">
<img src="https://velog.velcdn.com/images/jeffapd_/post/d6e754dc-1e59-4b7b-ac15-789643b01e55/image.png" width="80%" height="30%"></li>
<li>계산기의 버튼들의 배치와 같은 이중 배열을 만들어 map을 활용해 배열의 String값을 순회하면서 각각의 버튼을 생성하는 코드를 만들게 되었다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="💡-lv4---button-구현하기-반복되는-버튼생성을-메서드로-만들어-활용하기">💡 Lv.4 - Button 구현하기 (반복되는 버튼생성을 메서드로 만들어 활용하기)</h2>
<h3 id="📌-point--📝-개발-과정-3">📌 Point &amp; 📝 개발 과정</h3>
<ul>
<li><p>아무래도 계산기에는 버튼이 많고 형태가 비슷하며 하는 동작이 비슷해 반복되는 코드가 많기에 버튼을 생성하는 과정에서 메서드를 이용하면 좋다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/62e98fd5-383e-4e26-a1bc-156dd86a6384/image.png" width="80%" height="30%">
</li>
<li><p>숫자 버튼과 연산자나 다른 버튼의 색상이 다르기에 생성할 때 이부분을 체크해서 생성해줘야 했다. 그래서 색상이 다른 버튼들을 하나의 배열에 모아놓고 버튼을 생성시 해당하면 주황색으로 아니라면 기본값으로 설정한 회색으로 버튼이 생성되게 하였다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/ec160bb4-dce2-4a91-bfd2-e47248757a95/image.png" width="80%" height="30%"> <img src="https://velog.velcdn.com/images/jeffapd_/post/8234e764-f106-4654-86d3-3f2377532bad/image.png" width="80%" height="30%"></p>
</li>
</ul>
<hr>
<h2 id="💡-lv5---button-구현하기-사각형인-버튼을-원형으로-적용하기">💡 Lv.5 - Button 구현하기 (사각형인 버튼을 원형으로 적용하기)</h2>
<h3 id="📌-point--📝-개발-과정-4">📌 Point &amp; 📝 개발 과정</h3>
<ul>
<li>HorizontalStackView 의 높이 = 80, VerticalStackView 의 가로 = 350, VerticalStackView 의 Spacing = 10으로 설정</li>
<li>그렇기 때문에 VerticalStackView 에 들어가는 모든 버튼은 가로 길이가 (350 - 10 * 3) / 4 = 80 이 되어 정사각형의 사이즈가 된다.</li>
<li>여기서 우리는 <code>layer.cornerRadius = 40</code>라는 코드를 버튼을 만들때 넣어주면 된다.</li>
</ul>
<hr>
<h1 id="lv6--8">Lv.6 ~ 8</h1>
<ul>
<li>💡 Lv.6 - Button을 클릭시 Label에 값 띄우기</li>
<li>💡 Lv.7 - 초기화 Button 구현하기 </li>
<li>💡 Lv.8 - 계산 Button 구현하기 (주어진 계산메서드 이용하기)</li>
</ul>
<h2 id="💡-lv6---button을-클릭시-label에-값-띄우기">💡 Lv.6 - Button을 클릭시 Label에 값 띄우기</h2>
<h3 id="📌-point--📝-개발-과정-5">📌 Point &amp; 📝 개발 과정</h3>
<ul>
<li><p>앞에서 언급 했듯이 button을 만드는 메서드를 통해 button을 생성하면 되는데 스토리보드와 달리 어떤 메서드를 실행하게 될지 <code>addTarget</code>이라는 메서드를 이용해 정의해주면 된다. </p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/9c9c99f7-6f09-4cc7-a6b4-05508adceb42/image.png" width="80%" height="30%">
</li>
<li><p>addTarget에 선택된 메서드는 @objc라는 키워드를 사용해서 정의되어야 한다. 아래에 코드에 보이는 tappedButton메서드에서 입력받은 값을 에러처리한 후 통과된 값을 Label에 띄워줄 result값에 넣어 준다. switch문을 이용해 입력된 버튼에 해당하는 작업을 수행하도록 했으며, 만약 입력시 값이 &quot;0&quot;이라면  &quot;0&quot;을 제거한 후 값을 입력받도록 구현했다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/6708b61e-29e7-4e7b-a252-d37357bd0d33/image.png" width="80%" height="30%"></li>
<li><p>그리고 아래 코드에 보이는 changeLabelValue메서드를 호출에 Label값을 변경해주도록 한다. UI의 변경은 항상 메인스레드에서 이루어져야 하기에 <code>DispatchQueue.main.async</code>라는 코드를 추가해주어야 한다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/bf43852e-5a17-4210-b65a-73795b95a67e/image.png" width="80%" height="30%">
</li>
</ul>
<hr>
<h2 id="💡-lv7---초기화-button-구현하기">💡 Lv.7 - 초기화 Button 구현하기</h2>
<h3 id="📌-point--📝-개발-과정-6">📌 Point &amp; 📝 개발 과정</h3>
<ul>
<li>switch문을 통해 입력 받은 버튼이 &quot;AC&quot;라면 값을 초기화하는 메서드를 호출해 result값을 초기화를 구현했다.<img src="https://velog.velcdn.com/images/jeffapd_/post/bde97f8e-958e-4ae6-b7c3-197f982acb4b/image.png" width="80%" height="30%">

</li>
</ul>
<hr>
<h2 id="💡-lv8---계산-button-구현하기-주어진-계산메서드-이용하기">💡 Lv.8 - 계산 Button 구현하기 (주어진 계산메서드 이용하기)</h2>
<h3 id="📌-point--📝-개발-과정-7">📌 Point &amp; 📝 개발 과정</h3>
<ul>
<li>앞서 버튼을 통해 입력 받은 값을 result값에 추가하는 방식으로 구현했으며, &quot;=&quot;버튼을 눌렀을 때는 그 전까지 입력받아온 값을 계산 메서드를 통해 계산 후 값을 반환해 주고 그 값을 Label에 띄워주게 구현했다. 계산을 마친 후에는 result값도 초기화를 해주었다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/1085485b-7fae-439d-83b4-428591981952/image.png" width="80%" height="30%"> <img src="https://velog.velcdn.com/images/jeffapd_/post/a3e4091d-1ec7-475e-8533-e5ea8bf0bb0d/image.png" width="80%" height="30%"> </li>
</ul>
<hr>
<h2 id="🎯-트러블-슈팅-1">🎯 트러블 슈팅</h2>
<ul>
<li><p>초기에는 ViewController 하나의 파일에 모든 코드를 다 넣어서 구현을 하고 있던 와중에 MVC패턴을 적용해보고 뷰를 구현하는 클래스들도 나누어 구현을 하고자 파일을 나누어서 작업을 하고 있었다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/7caaa04b-9c67-48ff-ab18-7e1eecd45e8b/image.png" width="60%" height="30%"> 
</li>
<li><p><code>문제점</code> : 계산을 해주는 메서드도 ViewController에 있었지만 따로 빼놓았더니 제대로 작동하지 않았다.
<code>해결 방안</code> : 아래 코드와 같이 계산 메서드를 프로토콜로 선언한 후 Calculate클래스에서 채택하게 한 후 ViewController에서 계산을 시킬 수 있도록 위임을 하여 해결했다.</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/21c4c487-81de-4ffe-b9f6-203cc4f3b958/image.png" width="80%" height="30%"> 
</li>
<li><p><code>문제점</code> : 버튼을 눌렀지만 미리 addTarget에서 선택한 메서드를 찾지 못하는 문제</p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/3d29d6a7-cfce-40a9-9142-dfd504937960/image.png" width="80%" height="30%"> 
처음에 설계할 당시 button을 ViewController에서 만들었지만 파일을 분리해 button을 뷰만 구현하도록 분리해놓은 것으로 문제가 시작되었다. addTarget에서 주체가 되는 즉 작업 메서드가 호출되는 개체 지정된 작업 메시지에 응답하는 객체를 검색하고 해당 객체에 메시지를 전달하는 역할을 지정하는데 이 부분에서 ViewController를 self로 받아야하는 상황이 안되는 것이였다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/8b28a750-c980-4ec6-a7d3-e3ad40cc05de/image.png" width="80%" height="30%"> 아래의 코드처럼 map으로 맵핑을 하도록 설계를 함으로써 String값만을 이용해 설정해주지만 각 버튼에 접근해서 target을 설정 해주지 못하는 상황이였다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/819500ef-149e-4390-837a-ba3a4758a269/image.png" width="80%" height="30%">
`해결 방안` : 결론적으론 설계의 오류로 인해 다시 설계를 해야한다에 도달았다... 물론 ViewController에서 맵핑으로 감싼 뷰들을 하나하나 풀어서 target을 지정해줄 순 있다고 하지만 과연 이게 올바른 방식인가? 그래서 설계에 오류가 있다는 걸 알게되었다. 그래서 이걸 뜯어 고치는 것보단 새로 다시하는게 낫다고 판단했다. 그래서 파일을 분리하지않고 우선은 완성이 목표이기에 하나의 ViewController에 코드를 모아서 작성해 해결하였다.

</li>
</ul>
<hr>
<h3 id="마치며">마치며..</h3>
<p>항상 스토리보드를 사용해 구현해왔지만 이번 기회를 통해 코드베이스로 앱을 구현해보는 좋은 기회가 되었다. 두 가지 모두 장단점이 있지만, 협업을 통해 일을 할 때는 스토리보드는 swift언어로 변환되지 않기에 코드베이스를 자주 사용한다고 하고, 외부에서 의존성을 주입하기에도 코드베이스가 더 편하다고 한다. 
이번에 새롭게 배운 내용을 적용해보려고 시도를 해보았지만 오히려 이전에 배웠던 의존성, 응집도, 결합도 등을 고려하지 않고 짧고 간결하고 클래스만 나누면 된다는 오류를 범하게 되었다. 내가 왜 이 객체를 분리하고 재사용성을 고려하고 코드를 짰는가에 대한 생각을 하지 않아 발생한 설계오류를 경험하게 되었다. 다시한번 의미있는 코드를 짜야겠다는 생각이 든 프로젝트였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]프로그래머스_의상]]></title>
            <link>https://velog.io/@jeffapd_/swift%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%9D%98%EC%83%81</link>
            <guid>https://velog.io/@jeffapd_/swift%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%9D%98%EC%83%81</guid>
            <pubDate>Wed, 20 Nov 2024 17:37:42 GMT</pubDate>
            <description><![CDATA[<h2 id="-문제--의상"># 문제 : 의상</h2>
<img src="https://velog.velcdn.com/images/jeffapd_/post/122245f4-39f1-4e6b-8a5b-d10ad577c4e2/image.png" width="80%" height="30%">

<p>문제에서 요구하는건 각 카테고리별 옷을 겹치지않게 입을 수 있는 경우의 수를 구하는거라고 생각한다. 입출력 예에서 보듯 이중 배열로 각각 의상의 이름과 카테고리를 받아온다. </p>
<img src="https://velog.velcdn.com/images/jeffapd_/post/f81d687a-01bc-4123-956f-5b0e10f30fb4/image.png" width="80%" height="30%">

<h3 id="-풀이과정"># 풀이과정</h3>
<p>우선 문제의 키워드에서 힌트를 얻을 수 있었다. 해시를 활용해 문제에 접근해서 풀면 된다.</p>
<blockquote>
<p>Hash(해시) : 데이터를 고유하게 식별하기 위해 고정된 길이의 값(해시값)을 생성하는 과정으로 Swift에서는 주로 컬렉션 타입(Set, Dictionary)에서 객체를 비교하거나, 데이터를 빠르게 검색하고 저장하기 위해 사용한다.</p>
</blockquote>
<p>그렇다. 우리는 해시테이블 구조로 된 딕셔너리를 이용하면 된다.
그럼 데이터를 그룹화하기 위해 딕셔너리를 활용해 데이터를 그룹화를 하였다.</p>
<pre><code class="language-swift">    var categoryGroups: [String: [String]] = [:]
    for cloth in clothes {
    let name = cloth[0]
    let category = cloth[1]
    categoryGroups[category, default: []].append(name)
} </code></pre>
<ul>
<li>빈 딕셔너리 Key, value배열을 생성했다.</li>
<li>입력받은 clothes를 순회하면서 key값과 value값을 딕셔너리에 추가했다.</li>
<li><code>default</code> 키워드는 만약에 값이 없는 키값이라면 빈배열을 기본값으로 제공한다는걸 의미로 안전하게 작업을 할 수 있게한다.</li>
</ul>
<p>key : 의류 카테고리
value : 각 카테고리에 소속된 의상들(배열로 구성)</p>
<p>이렇게 딕셔너리의 key값은 자동적으로 해시값을 계산해서 저장하고 있는다.
그렇기에 데이터 검색 속도가 빠르게 할 수 있고 동일한 해시값이 생겨도 내부적으로 처리하게 된다. 
<br></p>
<pre><code class="language-swift">for i in categoryGroups.keys {
    var value = 0
    value = categoryGroups[i]!.count + 1
    result *= value
}</code></pre>
<p>다음은 입을 수 있는 의상의 수를 연산하는 부분이다. 우선 앞서 데이터를 저장한 카테고리그룹의 key의 값들을 순회돌면서 각 key값에 해당하는 배열의 갯수를 구하는 코드이다.</p>
<ul>
<li>여기서 나온 계산은 예시를 들어보면 상의라는 카테고리에서 3개의 의류가 있다면, 선택하지 않는 경우를 포함해 (3 + 1) 가지 조합이 나오게 된다. </li>
<li>최종 결과는 (카테고리1 조합) * (카테고리2 조합) ... * (카테고리n 조합) - 1 (아무것도 입지 않는 경우를 제외)를 계산한다.</li>
</ul>
<h3 id="-전체-코드"># 전체 코드</h3>
<img src="https://velog.velcdn.com/images/jeffapd_/post/3bf8f8e6-439b-45e7-a742-143000bc2590/image.png" width="80%" height="30%">
아직도 내가 푼 방식이 잘 맞는지 모르겠다. 다른 분들의 풀이를 보면 많이 다르고 배울점이 많이 있었다.

]]></description>
        </item>
        <item>
            <title><![CDATA[[swift]TIL_ViewController의 생명주기]]></title>
            <link>https://velog.io/@jeffapd_/swiftTILViewController%EC%9D%98-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</link>
            <guid>https://velog.io/@jeffapd_/swiftTILViewController%EC%9D%98-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</guid>
            <pubDate>Tue, 19 Nov 2024 11:46:25 GMT</pubDate>
            <description><![CDATA[<p>오늘은 뷰컨트롤러의 생명주기에 대해 공부했다.</p>
<p>우리가 Xcode를 이용해 개발을 하려고 프로젝트를 만들었다면 아래의 사진과 같은 코드를 자주보았을 것이다. 아래의 코드에 대해서 좀 더 깊이 알아보도록 하자.</p>
<p><img src="https://velog.velcdn.com/images/jeffapd_/post/a1234f1b-b345-468c-96bd-1fc76ce7f5fa/image.png" alt=""></p>
<blockquote>
<p>생명주기(Life cycle) : 무언가 태어나고 죽는 순간까지의 주기를 떠올릴 수 있다. 여기서 무언가는 ViewController를 이야기할 수 있다. iOS에서 화면을 구성하는 요소인 ViewControler가 viewController는 뷰가 나타나고(=메모리에 할당되고부터) 사라지기(=메모리에서 해제되기)까지의 주기를 의미한다.</p>
</blockquote>
<p> <img src="https://velog.velcdn.com/images/jeffapd_/post/603b718f-1199-47a0-8a0f-f108fef43c36/image.png" alt=""></p>
<p>뷰컨트롤러의 생명주기는 위와 같이 순서와 뜻이 직관적이다.
키워드를 보고 알 수 있듯이 will(<del>할 것이다)-미래, did(</del> 했다)-과거를 의미한다고 보면된다.</p>
<blockquote>
<p>loadView(뷰컨트롤러에 뷰를 로드한다) -&gt; viewDidLoad(뷰가 로드 되었다) -&gt; viewWillAppear(뷰가 나타날 것이다) -&gt; viewDidAppear(뷰가 나타날 것이다) -&gt; viewWillDisappear(뷰가  사라질 것이다) -&gt; viewDidDisappear(뷰가 사라졌다)</p>
</blockquote>
<h3 id="-load"># Load</h3>
<ul>
<li>loadView <ul>
<li>화면에 띄워줄 View를 만드는 메서드로 View를 만들고 메모리에 올려준다.</li>
<li>일반적으로 사용자는 이 메서드를 직접 호출하면 안된다.</li>
</ul>
</li>
<li>viewDidLoad <ul>
<li>viewDidLoad메서드는 뷰의 로딩이 완료 되었을때 시스템에 의해 자동으로 호출된다.</li>
<li>화면이 처음 만들어질 때 한 번만 실행 되므로, 초기화 코드가 있을 경우 이 메서드 내부에 작성하면 된다.<h3 id="-appear"># Appear</h3>
</li>
</ul>
</li>
<li>viewWillAppear<ul>
<li>ViewContoller의 Root View가 로드된 이후에 Window의 View계층으로 더해지기 직전 호출되는 메서드이다.</li>
<li>다른 view에 갔다가 다시 돌아오는 상황에 해주고 싶은 처리는 이 메서드 내부에 작성해주면 된다.</li>
<li>viewWillAppear와 viewDidAppear사이에 constraint와 layout이 적용된다.
<img src="https://velog.velcdn.com/images/jeffapd_/post/b54851f9-26ea-42ba-b3fd-a38cd0187fc6/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li>viewDidAppear<ul>
<li>window 의 root view가 View 계층으로 더해진 직후 호출되는 메서드이다.</li>
<li>View가 나타났다는 것을 컨트롤러에게 알리는 역할을 하며, 화면에 적용될 애니메이션을 그려준다<h3 id="-disapper"># Disapper</h3>
</li>
</ul>
</li>
<li>viewWillDisapper<ul>
<li>window 의 root view가 View 계층에서 제거되기 직전 호출되는 메서드이다.</li>
<li>View가 삭제되려고 하는 것을 ViewController에 통지한다.</li>
</ul>
</li>
<li>viewDidDisapper<ul>
<li>window 의 root view가 View 계층에서 제거된 직후 호출되는 메서드이다.</li>
<li>view가 제거되었음을 알려준다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>