<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>응애 나 잘 몰루?</title>
        <link>https://velog.io/</link>
        <description>진짜 몰루</description>
        <lastBuildDate>Mon, 24 Mar 2025 10:36:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>응애 나 잘 몰루?</title>
            <url>https://velog.velcdn.com/images/tae_uk/profile/e6c388eb-46da-4801-ba32-5a40ac63150c/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 응애 나 잘 몰루?. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/tae_uk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[눕따 개발일지 #2] Gyro Data와 CreateML를 활용한 자세 인식 삽질기]]></title>
            <link>https://velog.io/@tae_uk/%EB%88%95%EB%94%B0-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-2-Gyro-Data%EC%99%80-CreateML%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9E%90%EC%84%B8-%EC%9D%B8%EC%8B%9D-%EC%82%BD%EC%A7%88%EA%B8%B0</link>
            <guid>https://velog.io/@tae_uk/%EB%88%95%EB%94%B0-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-2-Gyro-Data%EC%99%80-CreateML%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9E%90%EC%84%B8-%EC%9D%B8%EC%8B%9D-%EC%82%BD%EC%A7%88%EA%B8%B0</guid>
            <pubDate>Mon, 24 Mar 2025 10:36:09 GMT</pubDate>
            <description><![CDATA[<h1 id="🙉-자세-인식을-하려던-이유">🙉 자세 인식을 하려던 이유</h1>
<p>눕따의 초점은 단순한 스마트폰 사용 시간이 아니라, <strong>“누운 상태에서 스마트폰을 얼마나 쓰는지”</strong>였다.</p>
<p>대부분의 사람들이 스마트폰을 가장 오래 사용하는 시간은 바로 잠들기 전, 침대에 누운 상태이기 때문이다.</p>
<blockquote>
<p>이 시점을 정확히 인식해야 진짜 습관 개선에 도움이 되는 앱이 될 수 있다고 생각했다.</p>
</blockquote>
<p>그래서 사용자의 자세를 감지하는 기능을 기획했다. 정면, 측면 등 다양한 누운 자세를 인식하고 그에 따라 피드백을 제공하는 방식을 생각하였다.</p>
<hr>
<h1 id="🙊-gyro-센서-데이터-수집-과정">🙊 Gyro 센서 데이터 수집 과정</h1>
<p>자세 인식을 구현하기 위해 가장 먼저 한 건 <strong>직접 데이터 수집용 iOS 앱을 만드는 것</strong>이었다.  </p>
<p>앱 안에는 총 9개의 버튼이 있었는데, 기본 3가지 자세인  </p>
<ul>
<li><code>정면으로 누움</code>  </li>
<li><code>측면으로 누움</code>  </li>
<li><code>기울여 누움</code>  </li>
</ul>
<p>여기에 각 자세마다  </p>
<ul>
<li><code>오른쪽으로 폰을 돌려 사용</code>  </li>
<li><code>왼쪽으로 돌려 사용</code>  </li>
<li><code>폰을 세워서 사용</code>  </li>
</ul>
<p>이라는 세부 사용 상황까지 고려해 <strong>총 9가지 상태를 버튼 하나로 라벨링하며 자이로 데이터를 수집</strong>할 수 있도록 구성했다.</p>
<p>센서 데이터는 단순 Gyro(회전) 값뿐 아니라, <strong>가속도계(accelerometer)</strong> 값도 함께 수집했다. 그리고 초기 테스트 과정에서 발생한 <strong>Gimbal Lock 현상</strong>을 개선하여 CreatML을 통한 학습이 가능하도록 스마트폰의 자이로 데이터를 수집할 수 있게 만들었다!</p>
<p>그 결과, 자세한 회전 정보를 기반으로 학습에 쓸 수 있을 만큼 <strong>신뢰도 있는 자이로 데이터 로깅 앱</strong>을 구현할 수 있었다.</p>
<p><strong>📌 초기 자이로 데이터 로깅 앱 UI 및 데이터 저장 형식</strong></p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/363f7977-dab8-4dc7-ae0a-67c13b7f1117/image.png" width="80%" height="80%"></p>

<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/e5dab1c6-0cc6-4f82-81f1-7a9230759e6a/image.png" width="80%" height="80%"></p>

<blockquote>
<p>처음엔 3개의 라벨을 사용했다가 사용자의 자세에 대한 변수가 늘어남에 따라 총 9개의 버튼을 구성하였다!</p>
</blockquote>
<p>데이터 수집을 위해 수십 번씩 직접 눕고, 방향을 바꾸고, 폰 각도를 조절하며 테스트를 반복했다.  </p>
<p>그 과정은 정말 말 그대로...</p>
<blockquote>
<p><strong>“개발자”</strong>가 아니라 <strong>“눕는 자세 테스트 전문가”</strong>에 가까웠다. 😵‍💫</p>
</blockquote>
<hr>
<h1 id="🙀-createml-학습-과정과-삽질">🙀 CreateML 학습 과정과 삽질</h1>
<p>수집한 데이터를 기반으로, Apple의 CreateML Activity Classifier를 사용해 자세 분류 모델 학습을 시도했다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/6e284a45-528b-41b0-8329-981dcacaf38b/image.png" width="80%" height="80%"></p>

<blockquote>
<p>이 라벨은 실제 앱에서 selectedPose 파라미터로 선택되어 자이로 + 가속도계 데이터를 기반으로 기록되었다.</p>
</blockquote>
<p>하지만 실제로 모델을 적용해본 결과, 일부 자세에서 예상보다 정확도가 크게 떨어지는 문제가 나타났다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/0ced66b1-4315-46d5-a714-e58af4c96737/image.png" width="80%" height="80%"></p>

<p>학습 데이터셋에 이상이 있는지 확인하기 위해 각 자세별/축별로 분리된 데이터셋을 기준으로 Precision, Recall, F1 Score를 비교했다. 그리고 동시에 학습된 모델을 기반으로 실시간 자세 추론이 가능한 테스트 앱을 활용하여 여러 각도에서 자체 추론을 실험해보았다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/b5d97907-8d2d-474b-97ae-a3db320f461c/image.png" width="80%" height="80%"></p>

<p><strong>📌 관찰된 이슈</strong></p>
<ul>
<li>정면 자세는 전체적으로 높은 정확도(0.95~1.0)를 보임</li>
<li>하지만, 측면 자세는 일부 방향에서 Precision 또는 Recall이 60~70% 수준으로 낮았음</li>
<li>특히, lying_on_left_side_left, lying_on_left_side_right는 예측 안정성이 떨어져 데이터 보강이 필요했음</li>
</ul>
<blockquote>
<p>무엇보다 문제였던 건, 학습 정확도는 높았지만 실제 사용 상황에선 전혀 쓸 수 없다는 것. </p>
</blockquote>
<p>이후, 여러 실험 끝에 측면 자세의 DataSet에 이상이 있다는 것을 인지하였고, 데이터 증강을 통한 재검증과 재생성에 대한 추가 검증 필요성을 느꼈다!</p>
<p>하지만, 이 쯤에서부터 해당 기능을 실제 서비스에 반영할 수 없다는 것을 깨닫기 시작하였다... 😢</p>
<p>CreateML 모델의 정확도가 아쉬워도, &quot;그래도 써볼 수는 있지 않을까?&quot;라는 희망은 있었다.</p>
<blockquote>
<p>그런데 진짜 문제는 바로 Apple의 보안 정책이었다.</p>
</blockquote>
<p>iOS 환경에서는 앱이 완전히 종료되거나 백그라운드 상태일 경우 자이로스코프나 가속도계 데이터를 수집할 수 없다.</p>
<p>HealthKit처럼 백그라운드에서도 일부 데이터를 읽을 수 있는 API는 있지만 자세 데이터처럼 실시간 회전값을 읽는 기능은 명백히 제한되어 있었다. 결국, CoreMotion 기반의 자세 인식은 앱이 활성화된 상태에서만 가능하다는 치명적인 제약이 있었다.</p>
<hr>
<h1 id="🥺-결국-눕따는-방향을-전환했다">🥺 결국 눕따는 방향을 전환했다...</h1>
<p>자세 인식을 통한 디지털 디톡스 앱이라는 아이디어는 흥미롭고 매력적인 기능이었지만 현실적인 한계를 넘지 못했다. 보완 가능하였지만 정확도 문제도 있었고, 무엇보다 애플 정책상 백그라운드에서 센서 접근이 불가능했다.</p>
<blockquote>
<p>그래서 결국 눕따 기능의 중심을 바꿨다.</p>
</blockquote>
<p>누워있는 자세를 인식하는 대신, 수면 전 사용에 대해서 피드백을 하기로 결정하였다! 또한, iOS의 Screen Time API를 활용해 앱 사용 시간 및 사용 앱 종류와 사용 패턴을 분석하는 구조로 전환했다.</p>
<blockquote>
<p>결과적으로 이 전환은 초기 아이디어를 버리는 것이 아니라, 현실적이고 실용적인 형태로 진화시킨 선택이었다.</p>
</blockquote>
<h1 id="🚧-삽질을-마무리하면서">🚧 삽질을 마무리하면서...</h1>
<p>자세 인식 기능 개발과 방향 전환까지, 3주 넘는 시간을 흘려보내면서 솔직히 눕따 개발 일정이 꽤 늘어난 게 아쉬웠다. </p>
<p>그래도 이번 기회를 통해 CreateML을 처음 경험해봤고, 생각보다 강력하고 정확한 도구라는 인상을 받았다. 언젠가 CreateML을 제대로 활용한 프로젝트를 기획할 수 있을 것 같다는 확신도 생겼다. </p>
<blockquote>
<p>기술적으로 하나의 기반을 익혔다는 점만으로도 이번 삽질은 나름 의미 있었다고 생각한다.</p>
</blockquote>
<p>당시, 눕따의 기능 대부분은 구현이 마무리된 상태였고 개발 방향도 명확해진 덕분에 이제 남은 건 프로토타입 완성뿐. 이제 정말 끝이 조금씩 보이기 시작했다. </p>
<blockquote>
<p>앞으로도 분명 또 삽질하겠지만.. 이번엔 꽤 의미 있는 삽질이었다고 그렇게 스스로 위안해본다 😅</p>
</blockquote>
<hr>
<p>이 글을 읽고 &quot;어..? 나 얘긴데?&quot; 싶다면
<a href="https://apps.apple.com/us/app/%EB%88%95%EB%94%B0-%EC%8A%A4%EB%A7%88%ED%8A%B8%ED%8F%B0-%EC%A4%91%EB%8F%85-%EB%B0%A9%EC%A7%80-%EC%82%AC%EC%9A%A9-%EC%8A%B5%EA%B4%80-%EA%B0%9C%EC%84%A0-%EC%95%B1/id6741762602">🛌 눕따 - 스마트폰 중독 방지 &amp; 사용 습관 개선 앱 보러가기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[눕따 개발일지 #1] 스마트폰 중독을 줄이는 디지털 디톡스 앱, 눕따의 시작]]></title>
            <link>https://velog.io/@tae_uk/%EB%88%95%EB%94%B0-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-1-%EC%8A%A4%EB%A7%88%ED%8A%B8%ED%8F%B0-%EC%A4%91%EB%8F%85%EC%9D%84-%EC%A4%84%EC%9D%B4%EB%8A%94-%EB%94%94%EC%A7%80%ED%84%B8-%EB%94%94%ED%86%A1%EC%8A%A4-%EC%95%B1-%EB%88%95%EB%94%B0%EC%9D%98-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@tae_uk/%EB%88%95%EB%94%B0-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-1-%EC%8A%A4%EB%A7%88%ED%8A%B8%ED%8F%B0-%EC%A4%91%EB%8F%85%EC%9D%84-%EC%A4%84%EC%9D%B4%EB%8A%94-%EB%94%94%EC%A7%80%ED%84%B8-%EB%94%94%ED%86%A1%EC%8A%A4-%EC%95%B1-%EB%88%95%EB%94%B0%EC%9D%98-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Sat, 22 Mar 2025 06:16:57 GMT</pubDate>
            <description><![CDATA[<h1 id="🙋♀️-왜-눕따를-만들었을까">🙋‍♀️ 왜 눕따를 만들었을까?</h1>
<p>연애 초기에 여자친구와 매일 밤 늦게까지 연락을 하며 <strong>자기 전 스마트폰 사용</strong>이 일상이 되었다. 톡을 주고받다가 어느 순간 새벽 2시, 3시가 넘어버리는 건 흔한 일이었다. 그때마다 &quot;아, 나 내일 일어나야 되는데…&quot; 싶었지만 <strong>스마트폰을 손에서 놓는 일</strong>, 생각보다 쉽지 않았다.</p>
<p>그러던 어느 날, 문득 여동생도 불을 끄고 누운 채 계속해서 스마트폰 화면을 바라보고 있는 비슷한 모습을 하고 있었다. “나만 이런 게 아니었네?”라는 생각이 들었고 주변 지인들과 가족들을 관찰하면서 확신이 생겼다.</p>
<p>그렇다면, 이 문제를 바꾸기 위해 내가 할 수 있는 건 없을까?</p>
<blockquote>
<p>그 질문이 디지털 디톡스 및 미니멀리즘 앱인 <strong>눕따</strong>라는 아이디어로 이어졌다!</p>
</blockquote>
<hr>
<h1 id="💁♀️-눕따는-어떤-앱인가">💁‍♀️ 눕따는 어떤 앱인가?</h1>
<p>&quot;눕따(NoopDda)&quot;는 “누웠다”에서 따온 이름이다. 초기 눕따의 아이디어는 사용자의 누운 상태에서 스마트폰을 사용하는 것을 감지하고 그 데이터를 분석하여 스마트폰 사용 습관 개선을 유도하는 iOS 앱이었다!</p>
<blockquote>
<p>물론 앱이 완전히 종료된 상황에서 사용자의 자이로 데이터를 수집 못하는 애플의 개인정보정책에 의해서 누운 자세를 감지하고 대응하는 시스템을 구현하지 못하였다..... 🙉</p>
</blockquote>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/0eec7bb8-746d-430f-ae5a-d20424a764e4/image.png" width="80%" height="80%"></p>

<p><strong>📌 핵심 기능</strong></p>
<ul>
<li>스마트폰을 사용하는 상황에 맞춘 피드백 제공</li>
<li>사용량에 따른 앱 차단</li>
<li>행동 패턴에 따른 맞춤형 리마인드</li>
<li>취침 전 알람 및 수면 시간 중 앱 차단</li>
</ul>
<p>기존 앱들과 달리 디지털 디톡스 기능 뿐만 아니라, 수면 전 스마트폰 사용과 취침 시간을 정해 수면 시간대를 만들어 주는 기능을 눕따의 핵심 기능으로 고려하여 기획과 설계를 진행하였다.</p>
<p><strong>📌 타겟 유저</strong></p>
<ul>
<li>수면 루틴을 개선하고 싶은 20~30대 직장인 &amp; 대학생</li>
<li>자기 전 스마트폰 사용 습관을 줄이고 싶은 사람</li>
<li>자연스러운 습관 개선 방식을 원하는 유저</li>
</ul>
<p>그리고 눕따는 나 혼자서 개발한 1인 개발 iOS 프로젝트이다! 그렇기에 빠르게 의사결정을 할 수 있다는 장점이 매우 크게 작용했던거 같다.</p>
<blockquote>
<p>초기 눕따의 기획과 디자인을 빠르게 일주일만에 완성하고, 단위기능 개발도 한 달 정도 소요된거 같다!</p>
</blockquote>
<p>MJ이라는 친구가 눕따에 합류하기 전까진 말이다... 🙊</p>
<blockquote>
<p>눕따 개발 중간부터 합류한 친구인데, MJ는 기획과 마케팅 부분에서 협업을 하여 해당 부분의 퀄리티는 더욱 향상되었지만 여전히 개발은 혼자 담당하였다...!</p>
</blockquote>
<hr>
<h1 id="🎨-figma로-그려본-눕따의-초기-ui-기획">🎨 Figma로 그려본 눕따의 초기 UI 기획</h1>
<p>초기 디자인 작업은 Figma를 활용해 진행했다. 핵심은 직관적이고, 감성적인 UI를 구현하는 것이었다. 아래의 이미지처럼 UI 구성과 그에 따른 와이터프레임을 활용하여 눕따 앱의 디자인을 간략하게 구성해보았다!</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/7b58b634-c9cc-4a09-bc7a-e2a6b5a2e95c/image.png" width="80%" height="80%"></p>

<p><strong>📌 초기 UI 컨셉</strong></p>
<ul>
<li>메인 화면</li>
<li><blockquote>
<p>원형 UI를 활용하여 현재 스마트폰 사용 상태를 시각적으로 표현</p>
</blockquote>
</li>
<li>애니메이션 효과</li>
<li><blockquote>
<p>사용자가 누우면 화면이 부드럽게 전환되며 침대 아이콘이 나타남</p>
</blockquote>
</li>
<li>피드백 페이지</li>
<li><blockquote>
<p>시간대별 스마트폰 사용 패턴을 그래프로 시각화</p>
</blockquote>
</li>
</ul>
<p>그리고 아래는 초기 UI 컨셉을 정리한 내용이다! 디자인도 혼자 구성해야했기에 빠르게 GPT를 활용하여 초안과 그에 따른 컨셉을 뽑아냈다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/67264fae-bea5-4cf0-a8f8-33f6aa687d5c/image.png" width="80%" height="80%"></p>


<p>마지막으로 와이어프레임과 초기 UI 컨셉을 종합하여 아래의 초기 눕따 디자인을 구성하였다.</p>
<p><strong>📌 초기 디자인</strong></p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/f2d37d6c-42c5-4965-a6ac-b29b6efec5a4/image.png" width="80%" height="80%"></p>

<blockquote>
<p>눕따는 더 직관적이고 감성적인 피드백 방식을 고민하며 UI를 설계했다! 🙋‍♀️</p>
</blockquote>
<hr>
<h1 id="🔍-기능-설계---단위-기능-및-ui-디자인-기반-코드-레이아웃">🔍 기능 설계 - 단위 기능 및 UI 디자인 기반 코드 레이아웃</h1>
<p>눕따는 SwiftUI 기반의 MVVM 아키텍처를 중심으로, 기능별 모듈화를 통한 구조를 적용하여 설계되었다. 눕따의 다양한 기능과 그에 따른 UI를 구현하는데 있어 해당 디자인 패턴이 편리하겠다고 판단되었고, 향후 업데이트를 통한 유지 보수 관점에서도 해당 아키텍처가 효율적이라 생각되었다!</p>
<blockquote>
<p>MVVM 패턴을 통해 UI 표현과 상태 관리 및 데이터 흐름에 집중하였고, 기능별 모듈 분리을 기반으로 확장성과 유지보수의 편리성을 확보하였다!</p>
</blockquote>
<p><strong>📌 구현 기능 정리</strong></p>
<ul>
<li>사용자 자세 분석</li>
<li><blockquote>
<p>애플 정책에 의해 구현 불가능</p>
</blockquote>
</li>
<li>리마인드 알람</li>
<li>앱 제한 기능</li>
<li>사전 온보딩 설문</li>
<li>사용 시간 및 시간대 분석 알고리즘</li>
</ul>
<p>이렇게 눕따의 요구 사항을 정의해보았다. 물론 최종 결과물과는 조금 다르게 구현되었지만, 초기 아이디어도 정리해두고 싶어 기록을 하였다!</p>
<p>이후, 이전에 디자인을 해뒀던 초기 UI을 기반으로 아래의 이미지처럼 각각의 
UI 컴포넌트를 정리하였다. 이를 통해 UI 과정 중, 큰 어려움 없이 개발을 진행할 수 있었다!</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/38ba8151-e20d-4774-b5f4-7ac7f2aa400e/image.png" width="80%" height="80%"></p>

<hr>
<h1 id="🤔-다음-삽질-일지-자세-인식과-createml-삽질기">🤔 다음 삽질 일지... 자세 인식과 CreateML 삽질기</h1>
<p>이처럼 초기 눕따의 아이디어와 요구사항을 정리해보았다! 다음엔 단위 기능 중 가장 크게 CreateML과 Gyro 수집 과정에서 삽질했던 기록에 대해서 정리해보려고 한다. 😢</p>
<p>특히, 애플 정책에 의해 앱이 완전히 종료된 상황에서 사용자 자세 추정 기능이 구현 불가능한 걸 너무 늦게 알았다. ㅠㅠ</p>
<blockquote>
<p>미리 알아보고 했으면.. 3주에서 한 달 정도의 개발 시간을 아꼈을거다.... 🙀</p>
</blockquote>
<hr>
<p>이 글을 읽고 &quot;어..? 나 얘긴데?&quot; 싶다면
<a href="https://apps.apple.com/us/app/%EB%88%95%EB%94%B0-%EC%8A%A4%EB%A7%88%ED%8A%B8%ED%8F%B0-%EC%A4%91%EB%8F%85-%EB%B0%A9%EC%A7%80-%EC%82%AC%EC%9A%A9-%EC%8A%B5%EA%B4%80-%EA%B0%9C%EC%84%A0-%EC%95%B1/id6741762602">🛌 눕따 - 스마트폰 중독 방지 &amp; 사용 습관 개선 앱 보러가기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[많이 늦은 24년 회고... 🙉🙈🙊]]></title>
            <link>https://velog.io/@tae_uk/%EB%A7%8E%EC%9D%B4-%EB%8A%A6%EC%9D%80-24%EB%85%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@tae_uk/%EB%A7%8E%EC%9D%B4-%EB%8A%A6%EC%9D%80-24%EB%85%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 15 Mar 2025 13:18:10 GMT</pubDate>
            <description><![CDATA[<h1 id="2024년-회고-졸업-도전-그리고-나만의-ios-프로젝트">2024년 회고: 졸업, 도전, 그리고 나만의 iOS 프로젝트</h1>
<p>24년 2월, 드디어 전자전기공학부 학사 졸업장을 받았다. 막상 졸업장을 받았을 때의 기쁨도 있었지만 한편으로는 앞으로의 방향에 대한 고민도 깊었다. 그 당시에, 대학원과 취준의 사이에서 고민을 했던거 같다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/7d4f4214-f919-4ff2-836e-820097c5b0c8/image.png" width="80%" height="80%"></p>

<blockquote>
<p>한동안 이 곰돌이들도 볼 일이 없을거 같다.. 🥺</p>
</blockquote>
<p>그리고 그 시기에 우연히 팀스파르타의 iOS 개발 교육을 접하게 되면서 임베디드나 3D 모델링과 완전 달라보이는 앱 개발을 접하게 된다.</p>
<hr>
<h1 id="대학-졸업-그리고-새로운-시작">대학 졸업, 그리고 새로운 시작</h1>
<p>iOS 개발 교육을 처음 접한 순간.. 시스템 반도체나 자율주행 아니면.. 임베디드 프로그래밍 등의 여러 부트캠프를 두고 왜 iOS 개발을 선택했는지는 아직도 잘 모르겠다.</p>
<blockquote>
<p>아마 iOS 앱이 임베디드와 달리 UI/UX의 디자인 요소부터 훨신 더 재밌는 요소가 많겠다는 점이 크게 느껴진거 같다.</p>
</blockquote>
<p>지금까지 해왔던 임베디드와 큰 접점이 없다고 생각하여 조금의 두려움은 있었지만, 새로운 영역에 대한 호기심이 더 컸기에 결국엔 iOS 개발 교육을 선택했던거 같다.</p>
<hr>
<h1 id="ios-개발-교육과-대학원">iOS 개발 교육과 대학원?</h1>
<p>우선 결과부터 말하면 iOS 개발 교육을 수료하긴 하였다. 물론 수업 중간부터 대학원 진학을 고민하면서 참여를 거의 하지않는 방식으로 하였지만 말이다...</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/b4f242f5-f70f-48e9-b5d3-918a0be28a90/image.png" width="80%" height="80%"></p>

<p>iOS 개발 교육 중순 쯤, 이 길이 아니라 전자나 로봇 계통의 대학원을 가는게 향후에 더 도움이 되질 않을까 싶어서 iOS 교육 강의도 수강하지 않고 대학원을 가기 위해 CV나 컨텍 메일 등의 작업만 했었다.</p>
<p>몇몇 대학원의 랩실에서 긍정적인 답을 받았고, 면접 준비만 하면 되는 상황까지 갔다. 하지만 개발이 아닌 연구 중심의 환경이 나와 맞지 않는다고 느꼈고, 결국 대학원에 진학하지 않기로 결정했다. 그 과정에서 iOS 프로젝트에도 참여하지 못해 아쉬움이 남았다...</p>
<blockquote>
<p>이렇게 iOS 개발 교육과 대학원 모두 어영부영 넘어가게 되었다.</p>
</blockquote>
<hr>
<h1 id="나만의-앱-프로젝트-눕따-시작">나만의 앱 프로젝트, &quot;눕따&quot; 시작!</h1>
<p>iOS 개발 교육과 대학원 일정 모두 끝난 이후, 혼자 1인 개발을 통한 iOS 앱 프로젝트를 기획하였다. &quot;BarBell Coach&quot;라는 이름의 운동 어플을 만들려고 했지만, UI Layout부터 단위 기능 구현 등의 여러 부분에서 어려움이 당시의 개발 스킬로는 해당 어플의 개발을 구현할 수 없겠다는 판단에 개발을 멈추게되었다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/63c01552-5fa7-4a2f-bcb2-304dde86a623/image.png" width="80%" height="80%"></p>

<p>이후, 훨신 작은 규모의 프로젝트를 기획하여 빠르게 만드는 것을 목표로 하여 여러 주제를 검토하던 중, 여자친구와 여동생의 스마트폰 사용 습관을 통해 디지털 디톡스 앱에 대한 아이디어를 떠올려 이를 개발 주제로 삼고 기획부터 시작하게 된다.</p>
<p>이렇게 &quot;눕따&quot;라는 앱이 만들어지게 된다.</p>
<blockquote>
<p>💁‍♀️ 일단 iOS 앱인 &quot;눕따&quot;는 잘 기획 및 개발되어 Apple AppStore에 출시된 상태이다!! 앞으로 눕따를 개발하면서 겪었던 과정이나 삽질에 대해서 회고? 일지?를 적어볼 생각이다!</p>
</blockquote>
<hr>
<h1 id="2024년을-돌아보며">2024년을 돌아보며</h1>
<p>iOS 교육을 들으면서 약간의 의무적?으로 작성했던 TIL을 제외하고는 거의 1년 만에 글을 적는 거 같다. 그래서 1년 치 밀린 일기를 적는 느낌도 나지만, 그사이 나름 많은 일들이 있었기에 너무 정신이 없었다.</p>
<p>2024년을 대학 졸업으로 시작했지만, 연말을 iOS 개발로 마무리하게 될 줄은 전혀 예상하지 못했다. 기존의 개발 스킬과는 전혀 다른 분야였지만, 소소하게나마 성과를 쌓아가고 있다고 느낀다. (아니면… 솔직히 좀 서운할 것 같기도… 🥲)</p>
<p>2025년은 눕따로 시작하게 되었는데, 과연 어떤 일로 한 해를 마무리하게 될지. 기대 반, 걱정 반이지만, 올해도 차근차근 나아가 보려고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Sparta Swift Team Project #1  ... Coffee Ordering App ☕️]]></title>
            <link>https://velog.io/@tae_uk/Sparta-Swift-Team-Project-1-...-Coffee-Ordering-App</link>
            <guid>https://velog.io/@tae_uk/Sparta-Swift-Team-Project-1-...-Coffee-Ordering-App</guid>
            <pubDate>Tue, 16 Apr 2024 05:05:27 GMT</pubDate>
            <description><![CDATA[<h1 id="team-project-마무리">Team Project 마무리</h1>
<p>벌써 팀 프로젝트가 끝난지 일주일이 지났다... 지난주 월요일에 다른 팀들도 모여 함께 결과 발표를 진행하였다.</p>
<p>결과부터 말하면... Coffee Ordering App에서 모든 기능을 구현하지 못하였고, 그 상태로 발표를 진행하였다. ㅠ</p>
<blockquote>
<p>이에 대한 원인도 팀원들과 함께 고민해보았다... 😥</p>
</blockquote>
<p>엿튼, 우여곡절 끝에 프로젝트는 마무리되었다... 🙈</p>
<hr>
<h1 id="coffee-ordering-app-기능-정리">Coffee Ordering App 기능 정리</h1>
<p>Coffee Ordering App의 UI를 모든 팀원이 만들어보고, 가장 적합하다고 판단되는 것을 사용하기로 하였다.</p>
<blockquote>
<p>그 결과, 팀원 중 한 분의 StoryBoard를 사용하기로 하였다!</p>
</blockquote>
<p>우선 아래의 그림처럼 StoryBoard를 구성하였고, 메뉴 항목 선택을 위해 <code>UISegmentedControl</code>을 사용하였다. 그리고 선택된 항목에 따라 메뉴를 보여주기 위해 <code>UICollectionView</code>를 사용하였고, 하단의 선택 내역을 보여주기 위해 <code>UITableView</code>를 사용하였다.</p>
<ul>
<li>Coffee Ordering App -&gt; StoryBoard<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/bc19f046-cfe0-40a8-b4cb-5decc04f9254/image.png" width="80%" height="80%"></p>

</li>
</ul>
<p>그리고 화면 중앙에 선택된 메뉴의 수와 총 금액을 출력하기 위한 <code>Label</code>을 추가하였다.</p>
<p>여기서 나는 <code>UICollectionView</code>에서 선택한 값들이 <code>UITableView</code>에 중복되어 출력되는 것이 아닌, 선택된 항목에 한하여 하나의 Cell만 출력하도록 기능을 수정하였다.</p>
<p>그리고 결제 확인에서 <code>UIAlertView</code>를 사용한 추가 알람까지 구현하였다.</p>
<p>이후, 이러한 기능에 따른 와이어프레임을 작성해보았다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/1c3f71c3-8e37-426f-a572-fd4336c9f358/image.png" width="80%" height="80%"></p>



<blockquote>
<p>필수 구현 기능</p>
</blockquote>
<ul>
<li><p>배너 : UILabel의 inspector에서 text를 변경하여 내용을 변경</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/13de4788-b355-4a4a-9a6e-58a9bad811ee/image.png" width="80%" height="80%"></p>
</li>
<li><p>카테고리 : UISegmentedControl 을 사용하여 각 segment 마다 카테고리에 맞는 메뉴를 표시</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/e23e31e9-6158-438b-8ba8-04870e58b9c4/image.png" width="80%" height="80%"></p>
</li>
<li><p>메뉴 : UICollectionView 의 각 셀에 UIImage 와 UILabel을 추가하여 메뉴 사진과 이름, 가격정보를 표시(셀의 갯수는 메뉴 데이터의 숫자대로 생성되며 한 줄에 에 2개의 셀이 배치)</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/525b637e-6ff7-4082-a33d-e53fe3e518da/image.png" width="80%" height="80%"></p>
</li>
<li><p>선택된 메뉴의 총개수와 총가격 : UICollectionViewCell이 선택될 때 작동하는 함수(didSelectItemAt)를 사용하여 tableView에서 바뀐데이터를 통해 표시</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/f0a462e5-96fd-4c67-bedc-1373b7ffed2f/image.png" width="80%" height="80%"></p>
</li>
<li><p>선택된 메뉴 리스트 : UITableView 를 활용하여 스크롤 형식으로 선택된 메뉴를 표시하였으며 UITableViewCell 에는 UIImageView와 UILabel을 활용하여 선택된 메뉴의 사진과 이름을 표시하고, UIStepper를 사용하여 선택된 메뉴의 개수를 줄이고 늘리는 기능을 추가</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/03bd30f6-0faa-49e7-8007-60c97fe2aff9/image.png" width="80%" height="80%"></p>
</li>
<li><p>결제, 취소 버튼 : 전체삭제, 결제하기 버튼 선택시 확인 알럿, 한번 더 확인을 누를 시 완료되었다는 안내창이 뜨며 테이블뷰와 총 메뉴 집계수량의 데이터가 초기화</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/12ce3d11-e2e1-43af-ae96-fd217c6794bf/image.png" width="80%" height="80%"></p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/2bc38dae-7610-4700-b36e-c2c460a5a4f8/image.png" width="80%" height="80%"></p>


</li>
</ul>
<blockquote>
<p>추가 구현 기능</p>
</blockquote>
<ul>
<li><p>LaunchScreen : App 실행 시 홈 화면으로 넘어가기 전 로딩시간동안 보여지는 런치스크린 구현</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/3e7e031c-02d3-4126-94b6-928b8447a1d6/image.png" width="80%" height="80%"></p>
</li>
<li><p>직원호출 버튼 : 버튼 선택시 확인 알럿이 생성</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/bfc0d7f5-2608-4e38-babb-d3a3afbd9700/image.png" width="80%" height="80%"></p>


</li>
</ul>
<hr>
<h1 id="coffee-ordering-app-결과">Coffee Ordering App 결과</h1>
<blockquote>
<p>완성하지 못한 기능</p>
</blockquote>
<ul>
<li>UIStepper의 메뉴 개수 추가, 감소에 따른 총개수와 총가격의 데이터 변동</li>
</ul>
<p>!youtube[FVWcMXfvIx8]</p>
<hr>
<h1 id="project를-마무리하며">Project를 마무리하며...</h1>
<p>Swift와 IOS App 개발에 대해 공부를 시작한 이후로, 첫 팀 프로젝트였다...</p>
<p>다른 팀원들도 상황은 비슷하였고, 다들 서툴렸기에 결과에 대한 미련은 없다... ㅠㅠ</p>
<p>근데, 총 개발 기간인 7일 중, 초반 3 ~ 4일을 각자 프로젝트를 구성해보고 이후 합쳐보는 걸로 진행했던 점이 문제가 되었던 것 같다...</p>
<blockquote>
<p>막상 개발해야 하는 기능에 대해 소요 시간이나 원활한 작업 분배 등을 고려하지 않고, 그냥 각자 만들면서 공부하자는 목적으로 시작했던게 독이 된 것 같다... 😭</p>
</blockquote>
<p>그리고... MVC 패턴을 적용해보지 못한 점도 아쉽게 느껴진다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/abd0bbe5-36fb-4190-b048-385c4f7ded1e/image.png" width="50%" height="50%"></p>

<p>메뉴에 대한 Data는 별도로 구성하였지만, 좀 더 세분화시키지 못하였고...</p>
<p>Controller에 대한 로직도 따로 구성하지 못하였다... ㅠ</p>
<blockquote>
<p>전반적으로 아쉬운 점이 많았다...</p>
</blockquote>
<p>그래도 첫 팀 프로젝트였기에 의미가 남달랐던거 같다. 다음 프로젝트에서는 여기서 느낀 아쉬운 점들을 보완해봐야겠다...!</p>
<blockquote>
<p>그래도 좋은 분들과 한 팀이 되었기에 개발 기간 내내, 편안하게 작업할 수 있었다!!</p>
</blockquote>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/a97a445e-4c14-45be-9e4f-a1ab4be097e7/image.png" width="80%" height="80%"></p>

<blockquote>
<p><a href="https://github.com/cafekioskproject/cafekiosk/tree/dev">Coffee Ordering App - GitHub</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #19 ... 1st Team Project - Coffee Ordering App #3]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-19-...-1st-Team-Project-Coffee-Ordering-App-3</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-19-...-1st-Team-Project-Coffee-Ordering-App-3</guid>
            <pubDate>Thu, 04 Apr 2024 14:11:14 GMT</pubDate>
            <description><![CDATA[<h1 id="시도해-본-것들">시도해 본 것들!</h1>
<p>우선 간단한 UI 구성 후, UISegmentedControl과 UIStackView를 활용하여 메뉴를 출력하는 기능을 구현해보았다!</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/c4743d2c-2344-4e99-bc27-5d69c4fd46fe/image.png" width="80%" height="80%"></p>

<p>그리고 어플 하단에 UITableView를 포함시켜, 주문한 목록을 출력시켰다!</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/64532684-c487-433e-95b5-9dd1a20a586b/image.png" width="80%" height="80%"></p>

<p>이때, 아래의 DataSet이라는 배열에 각각의 매뉴에 따른 요소들을 저장해주고 세그먼트의 값이 바뀔때 마다 출력하는 배열을 초기화 해주는 방식으로 제작하였다.</p>
<pre><code class="language-swift">  let coffeeDataSet = [
        Item(name: &quot;아메리카노&quot;, image: &quot;coffee1.jpg&quot;, price: 2500),
        Item(name: &quot;카페라떼&quot;, image: &quot;coffee2.jpg&quot;, price: 4000),
        Item(name: &quot;에스프레소&quot;, image: &quot;coffee3.jpg&quot;, price: 2000)
    ]
    let teaDataSet = [
        Item(name: &quot;케모마일티&quot;, image: &quot;tea1.jpg&quot;, price: 4000),
        Item(name: &quot;아이스티&quot;, image: &quot;tea2.jpg&quot;, price: 3500),
        Item(name: &quot;레몬티&quot;, image: &quot;tea3.jpg&quot;, price: 4000),
        Item(name: &quot;그린티&quot;, image: &quot;tea4.jpg&quot;, price: 3500)
    ]
    let cakeDataSet = [
        Item(name: &quot;블루베리치즈케익&quot;, image: &quot;cake1.jpg&quot;, price: 6000),
        Item(name: &quot;바스크 치즈케익&quot;, image: &quot;cake2.jpg&quot;, price: 5500),
        Item(name: &quot;당근 케익&quot;, image: &quot;cake3.jpg&quot;, price: 6500)
    ]
    var currentDataSet: [Item] = []</code></pre>
<blockquote>
<p>전체 코드는 나중에 Github에서 정리해볼 계획이다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #18 ... 1st Team Project - Coffee Ordering App #2]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-18-...-1st-Team-Project-Coffee-Ordering-App-2</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-18-...-1st-Team-Project-Coffee-Ordering-App-2</guid>
            <pubDate>Tue, 02 Apr 2024 13:18:34 GMT</pubDate>
            <description><![CDATA[<h1 id="오늘-시도해-본-것들">오늘 시도해 본 것들!</h1>
<p>어제에 이어 <code>UICollectionView</code>를 활용하는 화면 구성을 계속 시도해보았다.</p>
<p><code>CustomView</code>를 활용하여 하나의 디자인된 Cell를 만들어 재사용하기 위해, 어제와 유사하게 코드를 구현하였다.</p>
<pre><code class="language-swift">import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var CoffeeListSelector: UISegmentedControl!
    @IBOutlet weak var CoffeListCollection: UICollectionView!

    let coffeeDataSet = [
        [&quot;name&quot;: &quot;아메리카노&quot;, &quot;image&quot;: &quot;coffee1.jpg&quot;, &quot;price&quot;: &quot;2500&quot;],
        [&quot;name&quot;: &quot;카페라떼&quot;, &quot;image&quot;: &quot;coffee2.jpg&quot;, &quot;price&quot;: &quot;4000&quot;],
        [&quot;name&quot;: &quot;에스프레소&quot;, &quot;image&quot;: &quot;coffee3.jpg&quot;, &quot;price&quot;: &quot;2000&quot;]
    ]

    let teaDataSet = [
        [&quot;name&quot;: &quot;케모마일티&quot;, &quot;image&quot;: &quot;tea1.jpg&quot;, &quot;price&quot;: &quot;4000&quot;],
        [&quot;name&quot;: &quot;아이스티&quot;, &quot;image&quot;: &quot;tea2.jpg&quot;, &quot;price&quot;: &quot;3500&quot;],
        [&quot;name&quot;: &quot;레몬티&quot;, &quot;image&quot;: &quot;tea3.jpg&quot;, &quot;price&quot;: &quot;4000&quot;],
        [&quot;name&quot;: &quot;그린티&quot;, &quot;image&quot;: &quot;tea4.jpg&quot;, &quot;price&quot;: &quot;3500&quot;]
    ]

    let cakeDataSet = [
        [&quot;name&quot;: &quot;블루베리치즈케익&quot;, &quot;image&quot;: &quot;cake1.jpg&quot;, &quot;price&quot;: &quot;6000&quot;],
        [&quot;name&quot;: &quot;바스크 치즈케익&quot;, &quot;image&quot;: &quot;cake2.jpg&quot;, &quot;price&quot;: &quot;5500&quot;],
        [&quot;name&quot;: &quot;당근 케익&quot;, &quot;image&quot;: &quot;cake3.jpg&quot;, &quot;price&quot;: &quot;6500&quot;]
    ]

    var currentDataSet: [[String : String]] = []


    override func viewDidLoad() {
        super.viewDidLoad()

        CoffeeListSelector.addTarget(self, action: #selector(segmentedControlValueChanged(_:)), for: .valueChanged)

        CoffeListCollection.isHidden = false

        currentDataSet = coffeeDataSet
    }


    @objc func segmentedControlValueChanged(_ sender: UISegmentedControl) {
        switch sender.selectedSegmentIndex {
        case 0:
            currentDataSet = coffeeDataSet
        case 1:
            currentDataSet = teaDataSet
        case 2:
            currentDataSet = cakeDataSet
        default:
            break
        }
        CoffeListCollection.reloadData()
    }
}



extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -&gt; Int {
        return currentDataSet.count
    }


    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: &quot;cell&quot;, for: indexPath) as! CoffeeListCollectionCell

        let item = currentDataSet[indexPath.item]

        if let imageName = item[&quot;image&quot;], let image = UIImage(named: imageName) {
            cell.coffeeImage.image = image
        }

        if let name = item[&quot;name&quot;] {
            cell.coffeeLabel.text = name
        }

        return cell
    }

}

// Custom Cell
import UIKit

class CoffeeListCollectionCell: UICollectionViewCell {
    @IBOutlet weak var coffeeImage: UIImageView!
    @IBOutlet weak var coffeeLabel: UILabel!
}</code></pre>
<p>위 코드를 기반으로 StoryBoard의 UI요소와 ViewController와의 연결을 문제없이 구성하였다!</p>
<blockquote>
<p>하지만... 왜인지 UICollectionView는 동작을 하였지만, Cell은 출력되지 않았다...</p>
</blockquote>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/13bf2c66-d9da-4ca5-a8f4-18d1aff6fc62/image.png" width="80%" height="80%"></p>

<p>위 그림처럼 각각의 요소마다 <code>BackGroundColor</code>를 적용하여 결과를 확인해보니, Cell만 출력되지 않는 것을 확인할 수 있었다... 🥲</p>
<p>그래서 일단은 <code>UICollectionView</code>를 활용하는 방법은 잠시 포기하고 다른 방법을 찾아보기로 하였다.</p>
<blockquote>
<p>참고 자료 : <a href="https://ios-development.tistory.com/103">[iOS - swift] 13. 컬렉션 뷰(Collection View)</a></p>
</blockquote>
<hr>
<h1 id="uistackview로-대체해보기">UIStackView로 대체해보기!</h1>
<p><code>UICollectionView</code>가 안되더라도 과제는 구현해야 하니, <code>UIStackView</code>를 활용하여 과제를 구성해보기로 하였다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/9f6619bf-12b6-4ee1-ac80-f5ae56b1a72b/image.png" width="80%" height="80%"></p>

<p>위 그림처럼 일단은 반쯤 성공하였는데, 나중에 각 Cell마다 이미지와 가격 정보를 추가하면 될 듯하다!</p>
<blockquote>
<p>그래도 주문 가격을 저장해서 사용자에게 출력해주는 기능은 구현하였다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #17 ... 1st Team Project - Coffee Ordering App #1]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-17-...-1st-Team-Project-Coffee-Ordering-App</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-17-...-1st-Team-Project-Coffee-Ordering-App</guid>
            <pubDate>Mon, 01 Apr 2024 11:46:17 GMT</pubDate>
            <description><![CDATA[<h1 id="팀-프로젝트-시작">팀 프로젝트 시작!</h1>
<p>부트캠프가 시작되고, 처음으로 개인과제가 아닌 팀 프로젝트를 접하게 되었다!</p>
<blockquote>
<p>주제는 &quot;커피 주문 키오스크&quot;를 만드는 것이다!!!</p>
</blockquote>
<p>해당 프로젝트도 <code>필수 구현 기능</code>과 <code>선택 구현 기능</code>으로 구분되어 있으며, 혼자서 프로젝트를 빌드하는 것이 아닌 파트를 나누어 팀원과 함께 설계하는 프로젝트이기에 매우 기대된다! 🙉</p>
<hr>
<h1 id="프로젝트-요구-사항">프로젝트 요구 사항</h1>
<blockquote>
<p>필수 구현 기능</p>
</blockquote>
<ul>
<li>메인 페이지</li>
<li>상단 메뉴 카테고리 바<ul>
<li>UIStackView , UICollectionView 등을 활용</li>
</ul>
</li>
<li>메뉴 화면<ul>
<li>UICollectionView 등을 활용</li>
</ul>
</li>
<li>주문 내역 화면<ul>
<li>UITableView 등을 활용하여 화면을 구성</li>
</ul>
</li>
<li>취소하기 / 결제하기 버튼 화면</li>
</ul>
<blockquote>
<p>선택 구현 기능</p>
</blockquote>
<ul>
<li>부가 기능 추가<ul>
<li>ex. <code>돋보기</code> 와 같은 부가적인 기능 구현</li>
<li>ex. 오늘의 제일 잘 팔린 메뉴 보기</li>
<li>ex. 직원호출 → alert창 띄우기</li>
</ul>
</li>
<li>다크 모드 구현</li>
<li>페이징 기능</li>
</ul>
<blockquote>
<p>아직 시작도 안했는데... 머리가 아파온다...... 🙈</p>
</blockquote>
<hr>
<h1 id="시작하자-말자-🐶같이-멸망">시작하자 말자 🐶같이 멸망...</h1>
<p>우선 필수 구현 기능부터 설계를 하기 위해, App의 UI를 그려야 했다.</p>
<blockquote>
<p>이전에 졸업작품을 마치고 우연히 <code>Figma</code>를 접할 수 있는 기회가 있어서, 이를 통해 UI를 디자인해 보았다.</p>
</blockquote>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/38f782b5-7de7-497d-aa1a-b268b1f11edb/image.png" width="80%" height="80%"></p>

<p>이후, 이를 토대로 Xcode의 StoryBoard에 UI를 그렸다!</p>
<blockquote>
<p>이때까지만 해도 순조로웠다...</p>
</blockquote>
<p><a href="https://velog.io/@tae_uk/Sparta-Swift-Homework-2-...-ToDoList-App#lv4--lv5-">지난 과제</a>를 하며 UITableView는 어느정도 적응이 되었지만, UICollection은 처음 접하다 보니 기능 구현에 어려움이 있었다.</p>
<p>한참동안 찾아보니 UITableView와 유사한 듯 하였지만, 쉽게 구현하지 못하였다...</p>
<pre><code class="language-swift">import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var CoffeListCollection: UICollectionView!
    @IBOutlet weak var CoffeeListSelector: UISegmentedControl!


    let coffeeDataSet = [
        &quot;아메리카노&quot;: &quot;2500&quot;,
        &quot;카페라떼&quot;: &quot;4000&quot;,
        &quot;에스프레소&quot;: &quot;2000&quot;
    ]

    let teaDataSet = [
        &quot;케모마일티&quot;: &quot;4000&quot;,
        &quot;아이스티&quot;: &quot;3500&quot;,
        &quot;레몬티&quot;: &quot;4000&quot;,
        &quot;그린티&quot;: &quot;3500&quot;
    ]

    let cakeDataSet = [
        &quot;블루베리치즈케익&quot;: &quot;6000&quot;,
        &quot;바스크 치즈케익&quot;: &quot;5500&quot;,
        &quot;당근 케익&quot;: &quot;6500&quot;
    ]

    var currentDataSet: [String: String] = [:]


    override func viewDidLoad() {
        super.viewDidLoad()

        CoffeeListSelector.addTarget(self, action: #selector(segmentedControlValueChanged(_:)), for: .valueChanged)

        CoffeListCollection.isHidden = false

        currentDataSet = coffeeDataSet
    }


    @objc func segmentedControlValueChanged(_ sender: UISegmentedControl) {
        switch sender.selectedSegmentIndex {
        case 0:
            currentDataSet = coffeeDataSet
        case 1:
            currentDataSet = teaDataSet
        case 2:
            currentDataSet = cakeDataSet
        default:
            break
        }
        CoffeListCollection.reloadData()
    }
}</code></pre>
<p>그래서 UISegmentedControl만 일단 적용시켜 보았다.</p>
<p>해당 선택 요소는 StoryBoard에서 설정하였으며, 선택된 값이 0 ~ 2인지를 입력받아 <code>currentDataSet</code>라는 Dictionary 타입의 빈 배열에 알맞는 DataSet으로 초기화시킨다.</p>
<p>이때, DataSet은 총 3 종류이며 coffeeDataSet, teaDataSet, cakeDataSet으로 상품 정보와 가격을 포함하고 있다.</p>
<blockquote>
<p>UICollectionView만 해결되면, 각 Cell에 이미지를 넣어보는 작업을 진행할 예정이다!</p>
</blockquote>
<hr>
<h1 id="일단-마무리하며">일단 마무리하며...</h1>
<p>오늘 하루 실질적인 성과가 없다해도 UICollectionView을 어떤 방식으로 구현하고, 프로젝트를 전반적으로 어떻게 설계하면 되겠다는 청사진이 머리속에서 그려졌다!!</p>
<blockquote>
<p>그나마 다행일지도..</p>
</blockquote>
<p>가능하면 오늘 중으로 UICollectionView을 마무리하고, UITableView를 추가하고 싶다. 😅</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Sparta Swift Homework #2 ... ToDoList App ✏️]]></title>
            <link>https://velog.io/@tae_uk/Sparta-Swift-Homework-2-...-ToDoList-App</link>
            <guid>https://velog.io/@tae_uk/Sparta-Swift-Homework-2-...-ToDoList-App</guid>
            <pubDate>Sun, 31 Mar 2024 14:58:24 GMT</pubDate>
            <description><![CDATA[<h1 id="과제-주제-📝">과제 주제 📝</h1>
<p>iOS 입문 강의를 수강하고 배운 내용을 활용하여 ToDoList App을 만드는 것이 과제이다!</p>
<p><a href="https://velog.io/@tae_uk/Swift-Number-BaseBall-Game">이전 숫자 야구 과제</a>와 동일하게 구현해야 되는 기능으로 <code>필수 구현 기능</code>이 있고, 부가적으로 <code>선택 구현 기능</code>이 있다.</p>
<ul>
<li><p>필수 구현 기능</p>
<ul>
<li>Lv1. Todo List 화면 만들기 (TodoListViewController)<pre><code class="language-swift">  Storyboard를 활용하여 UI를 구성해주세요.
    UIButton과 UITableView을 활용하여 화면을 구성해주세요.
    할일에 대한 데이터 구성
       - 할일 데이터의 고유값인 “id (Int)”
       - 할일 제목인 “Title (String)”
       - 완료 여부를 확인할 “isCompleted (Bool)”</code></pre>
</li>
<li>Lv2. Todo 추가 및 완료 기능 구현하기 (TodoListViewController)<pre><code class="language-swift">Lv1에서 만든 Todo 추가 버튼을 이용해주세요.
UIAlertController를 활용해서 할 일 추가 UI 및 기능을 구현해보세요. 
(Closure 등을 사용해서 Action을 추가해보세요.)
버튼 혹은 UISegmentedControl을 활용, Todo의 완료 상태를 완료/미완료 상태로 변경합니다.</code></pre>
</li>
<li>Lv3. Todo 삭제 기능 구현하기 (TodoListViewController)<pre><code class="language-swift">Todo List에서 특정 Todo를 삭제할 수 있도록 화면과 기능을 자유롭게 구성해보세요.</code></pre>
</li>
</ul>
</li>
<li><p>선택 구현 기능</p>
<ul>
<li>Lv4. Todo Cell 발전시키기<pre><code class="language-swift">   Todo 데이터에 들어있는 다양한 요소들을 배치해봅니다.
   Interface Builder의 수많은 속성들을 바꾸어보세요. (코드로 바꾸어보아도 좋아요)</code></pre>
</li>
<li>Lv5. 할일 추가 등 animation이 있는 코드 구성하기<pre><code class="language-swift">   iOS에서는 기본적으로 구현해주는 animation들이 다양하게 있어요
   ex) tableview에 cell이 추가되거나 삭제되는 animation</code></pre>
</li>
</ul>
</li>
</ul>
<blockquote>
<p>위 조건들 중 현재는 필수 구현 기능인 Lv.3까지 설계하였고, Lv4와 Lv5의 구현 목표 중 일부만 구현에 성공하였다... 🥲</p>
</blockquote>
<hr>
<h1 id="lv1-🐵">Lv.1 🐵</h1>
<blockquote>
<p>필수 구현 기능</p>
<p>Storyboard를 활용하여 UI를 구성
UIButton과 UITableView을 활용하여 화면을 구성
할일에 대한 데이터 구성</p>
</blockquote>
<pre><code class="language-swift">import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var toDoListTable: UITableView!

    var data: [(id: Int, title: String, isCompleted: Bool)] = []
    var count: Int = 0


    override func viewDidLoad() {
        super.viewDidLoad()

        toDoListTable.dataSource = self
        toDoListTable.delegate = self
        toDoListTable.register(UITableViewCell.self, forCellReuseIdentifier: &quot;Cell&quot;)
    }

    @IBAction func addListButton(_ sender: Any) {
        count += 1

        data.append((id: count, title: &quot;todolist title&quot;, isCompleted: false))

        toDoListTable.reloadData()
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
        return data.count
    }

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

        let item = data[indexPath.row]
        cell.textLabel?.text = item.title

        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print(&quot;Selected: \(data[indexPath.row])&quot;)
        tableView.deselectRow(at: indexPath, animated: true)
    }
}</code></pre>
<p>우선 아래의 그림처럼 ViewController의 UI를 구성하였다!</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/2fbe1298-1ce1-4b17-91f2-fab3ec8e997d/image.png" width="80%" height="80%"></p>

<p>해당 과제에서 UITableView를 사용하기 때문에, 다음 단계에서 코드가 길어길 것을 고려하여 <code>extension</code>을 활용하여 <code>UITableViewDataSource</code>와 <code>UITableViewDelegate</code>에 대하 설정을 해주었다.</p>
<p>이때, Cell에 입력될 데이터는 <code>ViewController</code> 클래스 내부에 <code>data</code> 배열을 만들어서 활용하였다.</p>
<blockquote>
<p>해당 배열에 출력된 값의 고유번호, 출력될 내용과 완료 여부를 포함시켰다.</p>
</blockquote>
<p>여기까지가 Lv.1 단계이지만, <code>addListButton</code> 함수를 만들어 버튼을 누르면, <code>count</code>가 증가하여 고유번호가 알맞게 입력되도록 설정하였다.</p>
<hr>
<h1 id="lv2-🐶">Lv.2 🐶</h1>
<blockquote>
<p>필수 구현 기능</p>
<p>Lv.1에서 만든 Todo 추가 버튼이 동작하도록 구현
UIAlertController를 활용해서 할 일 추가 UI 및 기능을 구현
UISegmentedControl을 활용, Todo의 완료 상태를 완료/미완료 상태로 변경
할일을 나타내는 Todo의 완료/미완료 상태에 따라 UI를 변경</p>
</blockquote>
<pre><code class="language-swift">                    :

@IBAction func addListButton(_ sender: Any) {
        let alertTitle = &quot;할 일 추가&quot;
        let addTitle = &quot;추가&quot;
        let cancelTitle = &quot;취소&quot;

        let addListAlert = UIAlertController(title: alertTitle, message: nil, preferredStyle: .alert)

        addListAlert.addTextField { textField in
            textField.placeholder = &quot;할 일 입력&quot;
        }

        let cancelButton = UIAlertAction(title: cancelTitle, style: .cancel)

        let addButton = UIAlertAction(title: addTitle, style: .default) { _ in
            if let textField = addListAlert.textFields?.first, let taskTitle = textField.text, !taskTitle.isEmpty {
                self.addTask(title: taskTitle)
            }
        }

        addListAlert.addAction(addButton)
        addListAlert.addAction(cancelButton)

        self.present(addListAlert, animated: true)
    }


    func addTask(title: String) {
        count += 1
        data.append((id: count, title: title, isCompleted: false))
        toDoListTable.reloadData()
    }
}

                    :

extension ViewController: UITableViewDataSource, UITableViewDelegate {

                    :

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

        let item = data[indexPath.row]
        cell.textLabel?.text = item.title

        let taskStateSwitch = UISwitch()
        taskStateSwitch.isOn = item.isCompleted
        taskStateSwitch.addTarget(self, action: #selector(switchStateChanged(_:)), for: .valueChanged)

        cell.accessoryView = taskStateSwitch

        return cell
    }

                    :

    @objc func switchStateChanged(_ sender: UISwitch) {
        guard let cell = sender.superview as? UITableViewCell,
              let indexPath = toDoListTable.indexPath(for: cell),
              let taskTitle = cell.textLabel?.text
        else {
            return
        }

        data[indexPath.row].isCompleted = sender.isOn

        let attributedString = NSMutableAttributedString(string: taskTitle)
        if sender.isOn {
            attributedString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 1, range: NSMakeRange(0, attributedString.length))
        }
        cell.textLabel?.attributedText = attributedString
    }
}</code></pre>
<blockquote>
<p>코드 전부를 입력하기엔 양이 너무 많아 수정 및 추가된 부분만 정리하였다!</p>
</blockquote>
<p>Lv.2 요구 조건을 따르기 위해 <code>addListButton</code>에 <code>UIAlertController</code>를 추가하였으며, <code>UIAlertAction</code>을 활용하여 &quot;추가&quot; 버튼을 누를 경우 TextField에 입력한 값을 <code>addTask</code> 함수의 <code>title</code>에 저장하도록 만들었다.</p>
<blockquote>
<p>이때, <code>textField</code>에 입력된 값은 배열 형태로 저장되기에 <code>.first</code>를 통해 입력값에 접근할 수 있다!</p>
</blockquote>
<p>그리고 <code>UISwitch</code>를 사용하여 각 Cell 마다 완료 여부를 선택할 수 있는 기능을 추가하였고, 이를 구현하기 위해 <code>switchStateChanged</code> 함수를 만들었다.</p>
<p>또한 할 일을 완료했을 경우, 해당 Tilte에 밑줄이 생기도록 <code>NSMutableAttributedString</code>을 활용하였다. 해당 기능도 <code>switchStateChanged</code> 함수 내부에 있는데, <code>sender.isOn</code>를 통해 완료 여부를 파악하고 해당 동작을 하도록 설계하였다.</p>
<hr>
<h1 id="lv3-🐹">Lv.3 🐹</h1>
<blockquote>
<p>필수 구현 기능</p>
<p>Todo 삭제하기 기능 추가
Todo를 스와이프하여 삭제 - UITableView의 기능</p>
</blockquote>
<pre><code class="language-swift">                    :

extension ViewController: UITableViewDataSource, UITableViewDelegate {

                    :

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            data.remove(at: indexPath.row)

            tableView.deleteRows(at: [indexPath], with: .fade)
        }
    }

                    :
}</code></pre>
<p>Lv.3에서는 삭제 기능을 추가해야 하며, 해당 기능을 위해 <code>editingStyle == .delete</code> 조건을 활용하여 tableView 내의 Cell을 삭제시키도록 만들었다.</p>
<hr>
<h1 id="lv4--lv5-">Lv.4 &amp; Lv.5 ⁇</h1>
<blockquote>
<p>선택 구현 기능</p>
<p>Todo 데이터에 들어있는 다양한 요소들을 배치
Interface Builder의 수많은 속성들을 수정
할일 추가 등 animation이 있는 코드 구성
기타 시도해보고 싶은 기능</p>
</blockquote>
<pre><code class="language-swift">import UIKit

class ViewController: UIViewController {
    var data: [(id: Int, title: String, isCompleted: Bool, addTime: String)] = []

                    :

    func addTask(title: String) {
        let addListTime = DateFormatter()
        addListTime.dateFormat = &quot;HH:mm&quot;
        let addListTimeTrnasString = addListTime.string(from: Date())

        count += 1

        data.append((id: count, title: title, isCompleted: false, addTime: addListTimeTrnasString))

        toDoListTable.reloadData()
    }
}


extension ViewController: UITableViewDataSource, UITableViewDelegate {

                    :

@objc func switchStateChanged(_ sender: UISwitch) {

                    :

            showcompletedAlert()
        }
                    :
    }


    func showcompletedAlert() {
        let completedTitle = &quot;🎉🎉🎉&quot;
        let completedMessage = &quot;할 일을 끝냈습니다! 😝&quot;
        let doneTitle = &quot;확인&quot;

        let completedAlert = UIAlertController(title: completedTitle, message: completedMessage, preferredStyle: .alert)

        let doneButton = UIAlertAction(title: doneTitle, style: .default) { _ in
        }

        completedAlert.addAction(doneButton)

        self.present(completedAlert, animated: true)
    }
}</code></pre>
<blockquote>
<p>마지막 Lv.4 &amp; 5에서는 요구하는 모든 기능을 구현하지 못하였지만, 몇몇 기능을 구현하였기에 이를 정리해보았다!</p>
</blockquote>
<p>우선 기존 data에 <code>addTime</code>이라는 &quot;작성 시간&quot;이라는 요소를 추가하였다. 그래서 <code>addTask</code>를 살펴보면 <code>DateFormatter</code>를 통해 시간을 불러와 <code>.dateFormat = &quot;HH:mm&quot;</code>으로 24시간 형태로 시간을 변환해준 뒤, 이를 <code>addListTimeTrnasString</code>에 문자열 타입으로 저장해주었다.</p>
<p>그리고 할 일을 완료했을 때, <code>showcompletedAlert</code> 함수를 통해 <code>🎉</code> 축하 알람이 발생하도록 만들었다. 😅</p>
<blockquote>
<p>아래는 todoList App을 실행시킨 모습이다!</p>
</blockquote>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/3b5e95eb-40e8-457a-9ec3-082a06e28cde/image.PNG" width="100%" height="100%"></p>

<hr>
<h1 id="과제를-마무리하며-🔍">과제를 마무리하며... 🔍</h1>
<p>이번 과제도 어떻게 만들다보니 완성은 한 것 같다.😂</p>
<p>그나마 저번 과제에 비해 코드를 읽기 편한 것 같고 무엇보다 변수나 함수 명을 적절히 잘 작성한 것 같아 가독성이 훨씬 좋아진 것처럼 느껴진다!</p>
<p>하지만 난이도가 지난번 과제에 비해 상대적으로 매우 어려웠으며, 이로 인해 스스로 해결했다보다는 거의 대부분의 기능을 검색을 통해 만들었다... 🙈</p>
<blockquote>
<p>이러한 부분이 많이 아쉽게 느껴진다.</p>
</blockquote>
<p>그래도 지금처럼 모르는 내용을 잘 활용해서 결과물을 만들어 내는 것도 나름 의미가 있었던 것 같다!</p>
<blockquote>
<p>Github -&gt; <a href="https://github.com/Tae-Ouk/toDoList-Memo">toDoList-Memo</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #16 ... Algorithm 문제 풀이 🐻‍❄️]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-16-...-Algorithm-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-16-...-Algorithm-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Fri, 22 Mar 2024 14:40:39 GMT</pubDate>
            <description><![CDATA[<h1 id="level-1-3진법-뒤집기---68935"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/68935.%E2%80%853%EC%A7%84%EB%B2%95%E2%80%85%EB%92%A4%EC%A7%91%EA%B8%B0">[level 1] 3진법 뒤집기 - 68935</a></h1>
<ul>
<li>문제 설명
자연수 n이 매개변수로 주어집니다. n을 3진법 상에서 앞뒤로 뒤집은 후, 이를 다시 10진법으로 표현한 수를 return 하도록 solution 함수를 완성해주세요.</li>
</ul>
<pre><code class="language-swift">문제 풀이

import Foundation

func solution(_ n:Int) -&gt; Int {
    var inVal = String(n, radix: 3)
    var ansArr = inVal.map{ Int(String($0))! }
    var ansVal: Int = 0

    for i in 0..&lt;ansArr.count {
        ansVal += ansArr[i] * Int(pow(3.0, Double(i)))
    }

    return ansVal
}</code></pre>
<p>맨 처음 문제를 봤을 때, 해당 풀이와는 다르게 접근하였다.</p>
<p>입력값(10진법)을 3진법 수로 변환하여 값을 뒤집고, 이후 다시 10진법으로 변환하여 최종값을 반환하는 방법을 구상하였다.</p>
<pre><code class="language-swift">var inVal = String(n, radix: 3)
var ansArr = inVal.map{ Int(String($0))! }
ansArr.reverse()
var ansStr = Int(ansArr.map(String.init).joined())!</code></pre>
<p>위 코드에서 볼 수 있듯이 3진법 수로 변환된 값을 뒤집어 10진법으로 변환하기 전 단계까지 온 것을 볼 수 있다. </p>
<p>하지만 해당 파트에서 진전이 없어 맨 위의 풀이 방법으로 수정하였다.</p>
<p>입력값을 <code>map</code>함수를 사용하여 <code>[Int]</code> 타입으로 변환해주고, 첫번째 인덱스 값부터 3의 제곱값을 곱하여 주었다.</p>
<blockquote>
<p>그러면 굳이 입력값을 뒤집을 필요도 없기 때문이다!</p>
</blockquote>
<hr>
<h1 id="level-1-직사각형-별찍기---12969"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12969.%E2%80%85%EC%A7%81%EC%82%AC%EA%B0%81%ED%98%95%E2%80%85%EB%B3%84%EC%B0%8D%EA%B8%B0">[level 1] 직사각형 별찍기 - 12969</a></h1>
<ul>
<li>문제 설명
이 문제에는 표준 입력으로 두 개의 정수 n과 m이 주어집니다.
별(*) 문자를 이용해 가로의 길이가 n, 세로의 길이가 m인 직사각형 형태를 출력해보세요.</li>
</ul>
<pre><code class="language-swift">문제 풀이

import Foundation

let n = readLine()!.components(separatedBy: [&quot; &quot;]).map { Int($0)! }
let (a, b) = (n[0], n[1])

var ans: String = &quot;&quot;

for i in 0..&lt;a {
    ans.append(&quot;*&quot;)
}

for j in 0..&lt;b {
    print(ans)
}</code></pre>
<p>위의 풀이보다 괜찮은 방법을 찾아봤지만, 아쉽게도 식상한 방식으로 문제를 풀었다...</p>
<p><code>for</code>문을 2번 활용하여, * 문자를 <code>ans</code>에 추가하여 가로 길이를 저장해주고 세로 길이에 맞춰 여러번 출력해주었다.</p>
<blockquote>
<p>다음에 더욱 깔끔한 풀이 방법을 찾아봐야겠다.</p>
</blockquote>
<hr>
<h1 id="level-1-문자열-다루기-기본---12918"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12918.%E2%80%85%EB%AC%B8%EC%9E%90%EC%97%B4%E2%80%85%EB%8B%A4%EB%A3%A8%EA%B8%B0%E2%80%85%EA%B8%B0%EB%B3%B8">[level 1] 문자열 다루기 기본 - 12918</a></h1>
<ul>
<li>문제 설명
문자열 s의 길이가 4 혹은 6이고, 숫자로만 구성돼있는지 확인해주는 함수, solution을 완성하세요. 예를 들어 s가 &quot;a234&quot;이면 False를 리턴하고 &quot;1234&quot;라면 True를 리턴하면 됩니다.</li>
</ul>
<pre><code class="language-swift">문제 풀이

func solution(_ s:String) -&gt; Bool {
    var ansCounter: Int = 0
    var mapArr = s.map{ Int(String($0)) }

    !mapArr.contains(nil) ? (ansCounter += 1) : (ansCounter += 0)
    mapArr.count == 4 || mapArr.count == 6 ? (ansCounter += 1) : (ansCounter += 0)

    return ansCounter == 2 ? true : false
}</code></pre>
<blockquote>
<p>오늘 푼 문제 중에서 해당 문제를 가장 잘 푼 것 같다!</p>
</blockquote>
<p>우선 입력값을 <code>map</code> 함수를 활용하여, <code>[Int]</code>으로 변환하였다. 이때, 숫자가 아닌 글자가 입력되면 <code>nil</code>값이 저장된다.</p>
<p>따라서 이러한 점을 활용하여 배열 내에 <code>nil</code>이 존재하면 카운더에 1이 추가되도록 하였고, 배열의 길이가 4나 6일 때 카운더가 한번 더 동작하도록 만들었다.</p>
<p>마지막으로 카운터가 2일 때, 삼항연산자를 활용하여 true가 반환되도록 만들었다.</p>
<blockquote>
<p>해당 문제를 제출하고 지금까지 받아본적 없는 무려 12점을 받았다!!!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #15 ... Algorithm 문제 풀이 🐶]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-15-...-Algorithm-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-15-...-Algorithm-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Thu, 21 Mar 2024 14:24:35 GMT</pubDate>
            <description><![CDATA[<h1 id="level-1-행렬의-덧셈---12950"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12950.%E2%80%85%ED%96%89%EB%A0%AC%EC%9D%98%E2%80%85%EB%8D%A7%EC%85%88">[level 1] 행렬의 덧셈 - 12950</a></h1>
<ul>
<li>문제 설명
행렬의 덧셈은 행과 열의 크기가 같은 두 행렬의 같은 행, 같은 열의 값을 서로 더한 결과가 됩니다. 2개의 행렬 arr1과 arr2를 입력받아, 행렬 덧셈의 결과를 반환하는 함수, solution을 완성해주세요.</li>
</ul>
<pre><code class="language-swift">문제 풀이

func solution(_ arr1:[[Int]], _ arr2:[[Int]]) -&gt; [[Int]] {
    var sumArr = [Int]()
    var ansArr = [[Int]]()

    for i in 0..&lt;arr1.count {
        for j in 0..&lt;arr1[0].count {
            sumArr.append(arr1[i][j] + arr2[i][j])
        }
        ansArr.append(sumArr)

        sumArr = []
    }

    return ansArr
}</code></pre>
<p>가급적 고차함수를 사용하여 문제 풀이를 간단하게 작성해보고 싶었다.</p>
<p>하지만 그렇게 구현하기 위해 여러 과정을 거쳤지만, 결국엔 실패하였다...</p>
<p>그래서 단순하게 각 행렬의 인덱스 값을 더하여 <code>sumArr</code>에 저장하여 최종 답안을 저장하는 <code>ansArr</code>에 저장해주었다.</p>
<blockquote>
<p>그리고 마지막으로 <code>sumArr</code>를 다시 초기화하여 불필요한 값이 누적되지 않도록 만들었다.</p>
</blockquote>
<hr>
<h1 id="level-1-2018kakaoblindrecruitment-1차-비밀지도---17681"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/17681.%E2%80%85%EF%BC%BB1%EC%B0%A8%EF%BC%BD%E2%80%85%EB%B9%84%EB%B0%80%EC%A7%80%EB%8F%84">[level 1] 2018 KAKAO BLIND RECRUITMENT [1차] 비밀지도 - 17681</a></h1>
<pre><code class="language-swift">문제 풀이

func solution(_ n: Int, _ arr1: [Int], _ arr2: [Int]) -&gt; [String] {
    var result = [String]()

    for i in 0..&lt;n {
        var line = &quot;&quot;

        for j in 0..&lt;n {
            let index = n - 1 - j
            let bit1 = (arr1[i] &gt;&gt; index) &amp; 1
            let bit2 = (arr2[i] &gt;&gt; index) &amp; 1
            let bit = bit1 | bit2

            line += (bit == 1) ? &quot;#&quot; : &quot; &quot;
        }

        result.append(line)
    }

    return result
}</code></pre>
<p>맨 처음 문제를 풀고, 다음으로 풀 문제를 찾다가 재밌는 문제를 찾았다!</p>
<blockquote>
<p>지금까지 푼 코딩테스트 문제와 구성이 다른 것에 흥미를 느꼈다!</p>
</blockquote>
<p>위 코드를 보면 각각의 배열의 인덱스 값에 접근하여 비트 연산자를 활용한 것을 볼 수 있다.</p>
<p>그릭 연산한 비트값을 삼항연산자를 활용하여 <code>&quot;#&quot;</code>와 <code>&quot; &quot;</code>을 구분하였다.</p>
<pre><code class="language-swift">
var ansArr: [String] = []
var ans: String = &quot;&quot;

var map1 = arr1.map{ String($0, radix: 2) }
var map2 = arr2.map{ String($0, radix: 2) }

for i in 0..&lt;n {
    while map1[i].count != n {
        map1[i].insert(&quot;0&quot;, at: map1[i].startIndex)
    }
    while map2[i].count != n {
        map2[i].insert(&quot;0&quot;, at: map2[i].startIndex)
    }
}    </code></pre>
<p>처음부터 저러한 방식으로 접근한 것이 아닌, 각 배열의 값을 <code>radix</code>를 통해 2진수로 변환하여 활용하였다.</p>
<p>이후, 2진수로 변환된 값 중에서 n의 자릿수 만큼 비트 길이가 짧은 값은 ìnsert`를 활용하여 0을 추가해주었다.</p>
<blockquote>
<p>재밌어 보여 문제 풀이를 시도하였는데, 생각보다 너무 어려웠다...</p>
</blockquote>
<p>그리고 문제를 다 풀고, 다른 사람들의 풀이를 보니 아직 갈 길이 멀다는걸 느낄 수 있었다... 🙈</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #14 ... Algorithm 문제 풀이 및 간단한 Counter 앱]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-14-...-Algorithm-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%B0%8F-%EA%B0%84%EB%8B%A8%ED%95%9C-Counter-%EC%95%B1</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-14-...-Algorithm-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%B0%8F-%EA%B0%84%EB%8B%A8%ED%95%9C-Counter-%EC%95%B1</guid>
            <pubDate>Tue, 19 Mar 2024 11:53:21 GMT</pubDate>
            <description><![CDATA[<h1 id="level-1-부족한-금액-계산하기---82612"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/82612.%E2%80%85%EB%B6%80%EC%A1%B1%ED%95%9C%E2%80%85%EA%B8%88%EC%95%A1%E2%80%85%EA%B3%84%EC%82%B0%ED%95%98%EA%B8%B0">[level 1] 부족한 금액 계산하기 - 82612</a></h1>
<ul>
<li>문제 설명
새로 생긴 놀이기구는 인기가 매우 많아 줄이 끊이질 않습니다. 이 놀이기구의 원래 이용료는 price원 인데, 놀이기구를 N 번 째 이용한다면 원래 이용료의 N배를 받기로 하였습니다. 즉, 처음 이용료가 100이었다면 2번째에는 200, 3번째에는 300으로 요금이 인상됩니다.
놀이기구를 count번 타게 되면 현재 자신이 가지고 있는 금액에서 얼마가 모자라는지를 return 하도록 solution 함수를 완성하세요.
단, 금액이 부족하지 않으면 0을 return 하세요.</li>
</ul>
<pre><code class="language-swift">import Foundation

func solution(_ price:Int, _ money:Int, _ count:Int) -&gt; Int64{
    var val = (1...count).map{ $0 * price }.reduce(0, +)

    return Int64(val &gt; money ? val - money : 0)
}</code></pre>
<p>한동안 Swift 문법 강의와 과제 구현에만 신경쓰다보니, 오랜만에 알고리즘 문제를 접하였다.</p>
<blockquote>
<p>여러 강의와 과제 실습을 덕분에, 클로저와 고차함수의 사용이 더욱 편하게 느껴진다.</p>
</blockquote>
<p>문제 설명을 본 순간, 바로 <code>map</code>으로 횟수에 따른 비용을 추론하고 이후 <code>reduce</code>를 활용하여 해당 값을 모두 더하여 정수 형태로 반환하면 되겠다는 생각이 들었다!</p>
<blockquote>
<p>확실히 강의를 듣고 몇 번 실습해보니, 조금씩 성장하는걸 느낄 수 있다!</p>
</blockquote>
<p>해당 문제에서는 반환값을 <code>Int64</code> 타입으로 설정되어 있기에 <code>solution()</code> 함수의 반환값으로 문제 조건에 알맞는 답을 해당 타입으로 변환하여 반환해주었다.</p>
<hr>
<h1 id="ios-counter-app-⏱️">[iOS] Counter App ⏱️</h1>
<p>이전까지 수강하던 문법 주차가 마무리되고, 이번 주부터는 iOS 앱 개발 입문 주차가 시작되었다!</p>
<p>강의에서 Xcode를 활용하여 앱을 구현하는 과정과 방법에 대해 배울 수 있었다.</p>
<blockquote>
<p>자세한 내용은 다음에 정리해보겠다!</p>
</blockquote>
<p>강의를 다 듣고 배운 내용을 종합하여 간단한 카운더 앱 만들기라는 작은 과제가 있었다.</p>
<h2 id="✏️-app-구현-요구사항">✏️ App 구현 요구사항</h2>
<ul>
<li><p>레이아웃 요구사항</p>
<ol>
<li>UILabel이 가운데 위치하게 해 주세요.</li>
<li>UILabel을 기준으로 상단에는 감소 버튼, 아래에는 증가 버튼을 위치시켜 주세요.</li>
<li>UILabel과 UIButton사이의 간격은 16px로 설정해 주세요.</li>
<li>AutoLayout을 사용해 주세요.<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/ca8749f7-29dc-4745-97b0-08c6e1cdeb29/image.png" width="80%" height="80%"></p>
</li>
</ol>
</li>
<li><p>로직 요구사항</p>
<ol>
<li>count의 시작은 0으로 시작해 주세요.</li>
<li>감소버튼을 눌렀을 때, -1씩 감소시켜 UILabel에 표시해 주세요.</li>
<li>증가버튼을 눌렀을 때, +1씩 증가시켜 UILabel에 표시해 주세요.</li>
</ol>
</li>
</ul>
<h2 id="simple-counter-app-⏱️">simple Counter App ⏱️</h2>
<p>해당 과제도 기능 구현에 초점을 두고 설계해보았다!</p>
<p>우선 과제를 구현하기 위한 요소 중 레이아웃 요구사항을 먼저 충족시켜주었다.</p>
<ul>
<li>Xcode Swift Storyboard<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/495ffcd5-e0c8-41b2-beb3-2ba1190058dd/image.png" width="80%" height="80%"></p>

</li>
</ul>
<p>Swift Storyboard에 <code>Drag&amp;Drop</code>으로 사용할 <code>Label</code>과 <code>Button</code>을 추가해주었고, AutoLayout을 활용하여 각각 요구사항에 맞춰 배치하였다.</p>
<pre><code class="language-swift">// ViewController.swift

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var CounterLabel: UILabel!

    private var countData: Int = 0

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

    @IBAction func counterAdd(_ sender: Any) {
        self.countData += 1
        resetDisplay()
    }

    @IBAction func counterSub(_ sender: Any) {        
        self.countData -= 1
        resetDisplay()
    }

    func resetDisplay() {
        self.CounterLabel.text = &quot;\(self.countData)&quot;
    }
}</code></pre>
<p>다음으로 각 요소를 ViewController에서 기능을 만들 수 있도록, <code>Drag&amp;Drop</code>으로 <code>IBOutlet</code>과 <code>IBAction</code>을 구현하였다.</p>
<p>코드를 살펴보면 증가, 감소 버튼을 누르면 <code>countData</code>의 값이 1 씩 추가되거나 빼지는 것을 볼 수 있다.</p>
<p>그리고 해당 값을 <code>CounterLabel</code>에 문자보간법을 통해 저장하여 함수화시켰다.</p>
<blockquote>
<p>버튼을 눌러 값이 수정되면, 동시에 CounterLabel도 저장하도록 만들어 Count를 증가 또는 감소시켰다.</p>
</blockquote>
<h2 id="마무리-🛎️">마무리! 🛎️</h2>
<p>막상 강의를 듣고 있을 때는 간단해보였지만, 실제로 마우스와 키보드를 움직여 무언가 만들려고 쉽지 않아 당황했다...🥲</p>
<p>그래도 간단한 기능부터 조금씩 구현해보니 흥미가 더욱 붙는거 같다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #13 ... Swift - Number BaseBall Game ⚾️ 과제  피드백]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-13-...-Swift-Number-BaseBall-Game-%EA%B3%BC%EC%A0%9C-%ED%94%BC%EB%93%9C%EB%B0%B1</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-13-...-Swift-Number-BaseBall-Game-%EA%B3%BC%EC%A0%9C-%ED%94%BC%EB%93%9C%EB%B0%B1</guid>
            <pubDate>Mon, 18 Mar 2024 14:27:47 GMT</pubDate>
            <description><![CDATA[<h1 id="0-시작하기-앞서-📝">0. 시작하기 앞서... 📝</h1>
<p>해당 과제인 <a href="https://velog.io/@tae_uk/Swift-Number-BaseBall-Game">Number BaseBall Game ⚾️</a>을 제출한지 몇 일이 지나자, 튜너님이 과제에 대한 피드백을 전달해주셨다!</p>
<p>과제를 구현하는 1 ~ 6까지의 Level별로 commit하였는데, 이를 통해 상세히 피드백을 달아주셨고 그걸 정리해보았다.</p>
<blockquote>
<p>과제 코드 : <a href="https://github.com/Tae-Ouk/Number-BaseBall-Game/blob/main/swift_Number_BaseBall_Game/main.swift">swift Number BaseBall Game(GitHub - Tae Ouk)</a></p>
</blockquote>
<hr>
<h1 id="1-과제-마무리-후-궁금한-점">1. 과제 마무리 후, 궁금한 점</h1>
<p>과제는 <code>필수 구현 기능</code>과 <code>선택 구현 기능</code>으로 구성되어 있었는데, 모든 기능을 다 구성하여 과제를 마무리하였다.</p>
<p>각각의 기능을 모두 함수화하여, 코드의 가동성을 최대한 높였다!</p>
<blockquote>
<p>문제는 여기서 발생하였다! 🤯</p>
</blockquote>
<h2 id="값을-반환하는-함수-🤷">값을 반환하는 함수??? 🤷</h2>
<p>예를 들어, 아래의 함수를 살펴보면 반환값으로 Bool 타입을 가지는 것을 볼 수 있다.</p>
<pre><code class="language-swift">func valueComparison(_ valArr: [Int], _ inputArr: [Int]) -&gt; Bool {}</code></pre>
<p>이때, <code>valueComparison()</code> 함수 내부에서 조건문을 사용하면 모든 경우에서 Bool 타입의 반환값을 가져야 하는지 궁금했다.</p>
<blockquote>
<p>이를 원활히 해결하지 못하여 <code>func valueInput() -&gt; [Int] {}</code> 함수 내의 특정 조건에서 [0,0,0]이라는 더미값을 반환하여 문제를 해결하였다.</p>
</blockquote>
<p>물론, 코드나 기능에 이상은 없었지만 보기 좋지는 않았다... 🥲</p>
<blockquote>
<p>이러한 문제를 어떻게 깔끔하게 처리할지 좋은 방법을 찾지 못하여서, 매우 궁금하였다.</p>
</blockquote>
<hr>
<h1 id="2-튜터님의-피드백-💡">2. 튜터님의 피드백! 💡</h1>
<p>과제를 제출하고 얼마 지나지않아 피드백을 받을 수 있었는데, 답변을 보고 &quot;왜 저런 생각을 못했지??&quot;라는 의문이 생길 정도로 간단하고 깔끔하였다! </p>
<blockquote>
<p>바로 함수의 반환값을 [Int]가 아닌 <code>[Int]?</code>으로 수정하는 것이다!</p>
</blockquote>
<p>그럼 굳이 [0,0,0]이라는 더미값을 만들 필요가 없어진 것이다!!!</p>
<p><code>valueInput()</code> 함수에서 필요한 값을 반환할 때는 그냥 [Int] 타입으로 반환하면 되고, 만약 에러 처리를 통해 값을 반환할 필요가 없을 때는 <code>return nil</code>을 활용하면 간단히 해결할 수 있는 문제였다.</p>
<blockquote>
<p>지금까지 접했던 프로그래밍에서 흔하게 접할 수 있는 개념이 아니였기에, 이러한 방안을 떠올려 활용하는 것에는 무리가 있었을거 같다... 😅</p>
</blockquote>
<hr>
<h1 id="3-마무리-👻">3. 마무리! 👻</h1>
<p>사실 해당 질문에 대한 피드백 말고도 몇몇 피드백이 더 있었지만, 과제를 제출하고 검토하는 과정에서 찾은 문제들에 대한 피드백이었다.</p>
<blockquote>
<p>그래서 굳이 알고 있는 내용을 중복하고 싶지 않아, 별도로 정리하지는 않았다. </p>
</blockquote>
<pre><code class="language-swift">Lv6까지 과제를 완료하셨군요. 수고하셨습니다!
코드를 깔끔하게 잘 작성해주셨어요.
Level별로 commit이 나뉘어져 있는것도 좋았어요!
그리고 예외처리가 있는 코드도 인상깊었어고, 각 기능들이 함수로 나뉘어져 있어서 가독성이 좋았어요.

몇가지만 보완하면 더 좋은 코드가 될거 같아서 공유드릴게요 :)

첫째, 같은 값 3개를 넣어도(111) Nothing, Ball 등이 나오고 있어요. 오류메시지만 출력하면 좋을거 같아요.
둘째, valueInput 함수에서 return [0,0,0]으로 사용하셨는데 함수의 리턴값을 [Int]?로 변경하고 return nil로 사용하면 더 좋을거 같아요.
마지막으로 BaseballGame Struct을 새로운 파일로 만들어서 구현하면 좋을거 같아요.

그리고 func valueComparison(_ valArr: [Int], _ inputArr: [Int]) -&gt; Bool {} 같은경우는
해당 함수가 끝나기전에 Bool 타입의 true 혹은 false를 리턴해야 문제가 발생하지 않아요.
왜냐하면 함수를 정의할 때 Bool 타입을 반환한다고 선언했는데 Bool을 반환하지 않으면 오류가 발생하기 때문이에요.

수고하셨어요!</code></pre>
<p>다음에 찾아볼 수도 있기에, 혹시 몰라 이렇게 피드백 전체 내용을 올려두겠다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Sparta Swift Homework #1 ... Number BaseBall Game ⚾️]]></title>
            <link>https://velog.io/@tae_uk/Swift-Number-BaseBall-Game</link>
            <guid>https://velog.io/@tae_uk/Swift-Number-BaseBall-Game</guid>
            <pubDate>Fri, 15 Mar 2024 12:42:30 GMT</pubDate>
            <description><![CDATA[<h1 id="과제-주제-📝">과제 주제 📝</h1>
<p>Swift 기초, 심화 과정에서 배운 내용들을 종합하여 &quot;<strong>숫자 야구 게임</strong>&quot;을 만드는 것이 목표이다!</p>
<blockquote>
<p>구현해야 되는 기능으로 <code>필수 구현 기능</code>이 있고, 부가적으로 <code>선택 구현 기능</code>이 있다.</p>
</blockquote>
<ul>
<li><p>필수 구현 기능</p>
<ul>
<li>Lv1<ul>
<li>1 ~ 9까지의 서로 다른 임의의 수 3개를 정하고 맞추는 게임입니다.</li>
<li>정답은 랜덤으로 만듭니다.(1에서 9까지의 서로 다른 임의의 수 3자리)</li>
</ul>
</li>
<li>Lv2<ul>
<li>정답을 맞추기 위해 3자리수를 입력하고 힌트를 받습니다.</li>
<li>힌트는 야구용어인 볼과 스트라이크입니다.</li>
<li>같은 자리에 같은 숫자가 있는 경우 스트라이크, 다른 자리에 숫자가 있는 경우 볼입니다.</li>
<li>만약 올바르지 않은 입력값에 대해서는 오류 문구를 보여주세요.</li>
<li>3자리 숫자가 정답과 같은 경우 게임이 종료됩니다.</li>
</ul>
</li>
</ul>
</li>
<li><p>선택 구현 기능</p>
<ul>
<li><p>Lv3</p>
<ul>
<li>정답이 되는 숫자를 0에서 9까지의 서로 다른 3자리의 숫자로 바꿔주세요.</li>
<li>맨 앞자리에 0이 오는 것은 불가능합니다.</li>
</ul>
</li>
<li><p>Lv4</p>
<ul>
<li>프로그램을 시작할 때 안내문구를 보여주세요.<pre><code class="language-swift">환영합니다! 원하시는 번호를 입력해주세요
1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기</code></pre>
</li>
<li>1번 게임 시작하기의 경우 “필수 구현 기능” 의 예시처럼 게임이 진행됩니다.</li>
<li>정답을 맞혀 게임이 종료된 경우 위 안내문구를 다시 보여주세요.<pre><code class="language-swift">환영합니다! 원하시는 번호를 입력해주세요
1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기
1 // 1번 게임 시작하기 입력
&lt; 게임을 시작합니다 &gt;
숫자를 입력하세요
.
.
.</code></pre>
</li>
</ul>
</li>
<li><p>Lv5</p>
<ul>
<li>2번 게임 기록 보기의 경우 완료한 게임들에 대해 시도 횟수를 보여줍니다.<pre><code class="language-swift">환영합니다! 원하시는 번호를 입력해주세요
1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기
2 // 2번 게임 기록 보기 입력
</code></pre>
</li>
</ul>
<p>&lt; 게임 기록 보기 &gt;
1번째 게임 : 시도 횟수 - 14
2번째 게임 : 시도 횟수 - 9
3번째 게임 : 시도 횟수 - 12
.
.
.
```</p>
</li>
<li><p>Lv6</p>
<ul>
<li><p>3번 종료하기의 경우 프로그램이 종료됩니다.</p>
<pre><code class="language-swift">환영합니다! 원하시는 번호를 입력해주세요
1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기
3 // 3번 종료하기 입력

&lt; 숫자 야구 게임을 종료합니다 &gt;</code></pre>
</li>
<li><p>이전의 게임 기록들도 초기화됩니다.</p>
</li>
<li><p>1, 2, 3 이외의 입력값에 대해서는 오류 메시지를 보여주세요.</p>
<pre><code class="language-swift">환영합니다! 원하시는 번호를 입력해주세요
1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기
4

올바른 숫자를 입력해주세요!</code></pre>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>위의 조건에 맞게 총 Lv.6까지의 기능을 구현하여 과제를 마무리하였다!</p>
<hr>
<h1 id="설명에-앞서-💻">설명에 앞서... 💻</h1>
<p>해당 프로젝트는 Playground가 아닌 Xcode command line tool을 활용하여 구성되어 있다!</p>
<p>Xcode command line tool 을 이용하여 직접 입력값 받을 수 있기에 과제를 디버깅하기에는 매우 안성맞춤이였다.</p>
<blockquote>
<p>Xcode상단탭 - File - New - Project - macOS의 Command Line Tool로 프로젝트 생성 </p>
</blockquote>
<pre><code class="language-swift">// readLine() 함수를 이용하여 유저의 입력값 처리하기
// readLine() 함수에 대해 학습해보고 활용하기
let input = readLine()</code></pre>
<hr>
<h1 id="lv1-🐶">Lv.1 🐶</h1>
<blockquote>
<p>필수 구현 기능</p>
</blockquote>
<ul>
<li>1 ~ 9까지의 서로 다른 임의의 수 3개를 정하고 맞추는 게임입니다.</li>
<li>정답은 랜덤으로 만듭니다.(1에서 9까지의 서로 다른 임의의 수 3자리)</li>
</ul>
<p>맨 처음을 어떻게 시작할지 감이 안왔는데, 마침 기본 뼈대 코드가 제공되어서 이를 활용하였다!</p>
<pre><code class="language-swift">코드 뼈대

// main.swift 파일
// 프로젝트 생성시 자동 생성됨

let game = BaseballGame()
game.start() // BaseballGame 인스턴스를 만들고 start 함수를 구현하기


// BaseballGame.swift 파일 생성
class 혹은 struct {
    func start() {
        let answer = makeAnswer() // 정답을 만드는 함수
    }

    func makeAnswer() -&gt; Int {
        // 함수 내부를 구현하기
    }
}</code></pre>
<blockquote>
<p>BaseballGame.swift 파일 생성을 별도로 생성해서 프로젝트를 구성해라고 안내하였지만, main.swift 파일 내에서 모든 코드를 작성하였다. (꼼꼼히 읽어볼껄... 🥲)</p>
</blockquote>
<pre><code class="language-swift">// Lv.1 Number BaseBall Game

let game = BaseballGame()
game.start()


struct BaseballGame {
    func start() {
        let answer = makeAnswer()

        print(&quot;Start Number BaseBall Game&quot;)
        print(&quot;Input Your Number&quot;)

        print(makeAnswer())

        let input = readLine()!

        let IntAnswer = String(input).map{ Int(String($0))! }
        // 입력받은 input을 String -&gt; [Int]로 변환

        print(IntAnswer)
    }


    func makeAnswer() -&gt; [Int] {
        var ansMake = Array(1...9)
        var randomValues: [Int] = []

        while randomValues.count &lt; 3 {
            let randomIndex = Int.random(in: 0..&lt;ansMake.count)
            let randomNumber = ansMake[randomIndex]

            if !randomValues.contains(randomNumber) {
                randomValues.append(randomNumber)
            }
        }

        /*
        var ansMake: [Int] = []

        for i in 0...2 {
            ansMake.append(Int.random(in: 1...9))
        }
         // ⚠️ 랜덤하게 추가된 ansMake의 요소 중 중복이 발생할 수 있음 -&gt; ✅ 해결!
        */

        return randomValues
    }
}</code></pre>
<p>BaseballGame 안의 start()와 makeAnswer()를 만들어 기능을 구분하여 코드를 작성하였다.</p>
<p>먼저 makeAnswer()를 살펴보면, while 문을 사용하여 3자리의 랜덤값이 쌓일 수 있도록 만들었으며 내부에 조건문을 활용하여 이미 입력된 값이 중복되지 않도록 만들었다!</p>
<blockquote>
<p>맨 처음엔 주석처리 된 코드를 작성하였는데, 중복이 발생할 수 있다는 것을 깨닫고 바로 수정하였다.</p>
</blockquote>
<p>그리고 start()에서는 입력받은 map함수를 활용하여 입력받은 String 타입의 입력을 [Int] 타입으로 저장하였다.</p>
<hr>
<h1 id="lv2-🐵">Lv.2 🐵</h1>
<blockquote>
<p>필수 구현 기능</p>
</blockquote>
<ul>
<li>정답을 맞추기 위해 3자리수를 입력하고 힌트를 받습니다.</li>
<li>힌트는 야구용어인 볼과 스트라이크입니다.</li>
<li>같은 자리에 같은 숫자가 있는 경우 스트라이크, 다른 자리에 숫자가 있는 경우 볼입니다.</li>
<li>만약 올바르지 않은 입력값에 대해서는 오류 문구를 보여주세요.</li>
<li>3자리 숫자가 정답과 같은 경우 게임이 종료됩니다.</li>
</ul>
<p>다음으로 필수 구현 기능 중 마지막인 Lv.2 코드이다!</p>
<blockquote>
<p>아래의 Lv.3 ~ 6보다 해당 레벨에서 많은 시간이 소요되었다...</p>
</blockquote>
<pre><code class="language-swift">// Lv.2 Number BaseBall Game

let game = BaseballGame()
game.start()


struct BaseballGame {
    func start() {
        let answer = makeAnswer()
        var isCorrect = false

        print(&quot;Number BaseBall Game 시작!&quot;)
        print(&quot;숫자를 입력하세요.&quot;)

        print(answer)

        while !isCorrect {
            var IntAns = valueInput()

            if valueComparison(answer, IntAns) {
                print(&quot;정답!&quot;)
                isCorrect = true
            } else {
                print(&quot;틀렸습니다... 다시 시도해주세요&quot;)
            }
        }
    }



    func valueInput() -&gt; [Int] {
        var input = readLine()!
        if input.allSatisfy({ $0.isNumber }) {
        // 입력된 값이 숫자로 구성되어 있는지 검증
            let inputAns = input.map { Int(String($0))! }
            // 입력받은 input을 String -&gt; [Int]로 변환

            if inputAns.count &lt; 3 || inputAns.count &gt; 3 {
                print(&quot;올바르지 않은 입력값입니다 - 3가지 숫자를 입력해주세요&quot;)
            } // 입력받은 수가 총 3 자리가 맞는지 검증
            else if inputAns[0] == inputAns[1] || inputAns[1] == inputAns[2] || inputAns[0] == inputAns[2]{
                print(&quot;올바르지 않은 입력값입니다 - 서로 다른 숫자를 입력해주세요&quot;)
            } // 입력받은 수에서 중복되는 수가 있는 경우 검증
            else if inputAns.contains(0) {
                print(&quot;올바르지 않은 입력값입니다 - 0이 아닌 숫자를 입력해주세요&quot;)
            } // 입력받은 수에서 0이 있는 경우 검증
            return inputAns
        }
        else {
            print(&quot;올바르지 않은 입력값입니다 - 숫자만 입력해주세요&quot;)

            return [0,0,0]
        }
        // 숫자로 변환할 수 없는 입력에 대한 에러 처리
    }



    func valueComparison(_ valArr: [Int], _ inputArr: [Int]) -&gt; Bool {
        var ballCounter: Int = 0
        var strikeCounter: Int = 0
        var warningCounter: Int = 0

        for i in 0..&lt;valArr.count {
            if inputArr.count == 3 &amp;&amp; inputArr != [0,0,0] &amp;&amp; !inputArr.contains(0) {
            // 에러 요소가 발생시, 카운터 동작 정지 - 3자리 미만 또는 이상, 문자 입력시([0,0,0]), 0을 포함
                if valArr[i] == inputArr[i] {
                    strikeCounter += 1
                }
                else if valArr.contains(inputArr[i]) {
                    ballCounter += 1
                }
                else if !valArr.contains(inputArr[i]) {
                    warningCounter += 1
                }
            }
        } // ball &amp; strike 구분 + 일치하는 값이 없을 때, Nothing을 출력하기 위한 경고 신호 발생

        if inputArr.count == 3 &amp;&amp; inputArr != [0,0,0] &amp;&amp; !inputArr.contains(0) {
            if warningCounter != 3 {
                print(&quot;Balls: \(ballCounter), Strikes: \(strikeCounter)&quot;)
            }
            else {
                print(&quot;Nothing&quot;)
            }
        } // 입력값과 정답을 비교하여 하나도 일치하지 않을 경우, &quot;Nothing&quot; 출력

        // warningCounter != 3 ? print(&quot;Balls: \(ballCounter), Strikes: \(strikeCounter)&quot;) : print(&quot;Nothing&quot;)

        warningCounter = 0
        // 경고 초기화

        return strikeCounter == 3
    }


    func makeAnswer() -&gt; [Int] {
                    .
                    .
              Lv.1 코드와 동일
                    .
                    .
    }
}</code></pre>
<p>기존 코드에서 입력된 값에서 게임 조건인 3자리 숫자만 입력되었는지 판단하는 valueInput()와 입력된 값과 랜덤값을 비교하여 <code>힌트</code>를 출력하는 valueComparison() 함수를 추가하였다.</p>
<p>valueInput() 내부에는 많은 조건문을 볼 수 있는데, <code>isNumber</code>를 활용하여 숫자 입력 여부를 확인할 수 있고, 그 이외의 다른 입력 조건을 배제하기 위한 기능임을 알 수 있다!</p>
<blockquote>
<p>valueInput() 함수의 <code>[Int]</code>라는 리턴값이 존재하기 때문에, 어떠한 조건에서도 값을 반환해야 했다. 그래서 만약 입력값이 숫자가 아니면 <code>[0,0,0]</code>을 반환하게 되는데, 게임 기능에 영향을 미치지 않는 더미값으로 전송하였다.</p>
</blockquote>
<p>valueComparison()에서는 볼, 스트라이크와 같은 힌트 이외에도 일치하는 수가 없을 경우 &quot;Nothing&quot;을 출력하기 위해 <code>warningCounter</code>을 만들어 해당 조건에 알맞은 기능을 하도록 설계하였다.</p>
<p>마지막으로 start() 내에서 게임을 실행하고 입력값을 넣으면 생성된 랜덤값과 비교하게 된다. 하지만 한 번 비교하고 프로그램이 종료되었기에, 이를 해결하기 위해 while문을 만들어 &quot;정답&quot;이 된 상황에서만 게임이 끝나도록 만들었다.</p>
<hr>
<h1 id="lv3-🦁">Lv.3 🦁</h1>
<blockquote>
<p>선택 구현 기능</p>
</blockquote>
<ul>
<li>정답이 되는 숫자를 0에서 9까지의 서로 다른 3자리의 숫자로 바꿔주세요.</li>
<li>맨 앞자리에 0이 오는 것은 불가능합니다.</li>
</ul>
<p>lv.3에서는 코드가 길어서 가독성을 위해 해당 조건의 기능을 하는 함수만 설명하겠다!</p>
<pre><code class="language-swift">func makeAnswer() -&gt; [Int] {
    let ansMake = Array(0...9)
    var randomVal: [Int] = []

    while randomVal.count &lt; 3 {
        let randomIndex = Int.random(in: 0..&lt;ansMake.count)
        let randomNum = ansMake[randomIndex]

        if !randomVal.contains(randomNum) {
            randomVal.isEmpty ? randomVal.append(Int.random(in: 1...9)) : randomVal.append(randomNum)
                // ⚠️ 맨 처음 배열은 공백이기에 0번째 인덱스로 접근하면 에러 발생
        }
    }

        return randomVal
}</code></pre>
<p>기존 makeAnswer() 함수는 1 ~ 9 까지의 숫자를 활용한 3자리 랜덤값을 만드는 것이였다. 하지만 이번 조건에 맞추기 위해서는 <code>ansMake</code>를 0 ~ 9 까지의 배열로 만들고 랜덤값을 저장하는 배열의 0번째 인덱스만 조심하면 된다.</p>
<p>위 코드의 주석에서 볼 수 있듯이, 맨 처음 랜덤 배열을 가져왔을 때는 배열 내부가 비어있기에 <code>isEmpty</code>를 활용하여 접근하였다.</p>
<p>그리고 삼항연산자를 사용하여, 0번째 인덱스에 1 ~ 9 까지의 값을 저장해주었다. </p>
<blockquote>
<p>배열의 0번째 인덱스 값이 입력되면, 이후의 1과 2 인덱스에는 0 ~ 9 까지의 숫자가 입력되게 된다!</p>
</blockquote>
<hr>
<h1 id="lv4--6-🐯🐻🐱">Lv.4 ~ 6 🐯🐻🐱</h1>
<blockquote>
<p>선택 구현 기능</p>
<p>Lv.4</p>
</blockquote>
<ul>
<li>프로그램을 시작할 때 안내문구를 보여주세요.</li>
<li>1번 게임 시작하기의 경우 “필수 구현 기능” 의 예시처럼 게임이 진행됩니다</li>
<li>정답을 맞혀 게임이 종료된 경우 안내문구를 다시 보여주세요.<blockquote>
<p>Lv.5</p>
</blockquote>
</li>
<li>2번 게임 기록 보기의 경우 완료한 게임들에 대해 시도 횟수를 보여줍니다.<blockquote>
<p>Lv6</p>
</blockquote>
</li>
<li>3번 종료하기의 경우 프로그램이 종료됩니다.</li>
<li>이전의 게임 기록들도 초기화됩니다.</li>
<li>1, 2, 3 이외의 입력값에 대해서는 오류 메시지를 보여주세요.</li>
</ul>
<p>Lv. 4 ~ 6는 코드를 작성하는 과정에 있어, 구현해야 되는 기능이 유사하기에 한번에 설명하겠다!</p>
<pre><code class="language-swift">// Lv.2 Number BaseBall Game

var game = BaseballGame()
game.startScreen()



struct BaseballGame {
    var gameCounter: Int = 0
    var gameCounterLog: [Int] = []
    // 전역변수 gameCounterLog를 활용한 게임 기록 저장

    mutating func startScreen() {
        var isCorrect = false

        while !isCorrect {
            print(&quot;환영합니다! 원하시는 번호를 입력해주세요&quot;)
            print(&quot;1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기&quot;)

            var input = readLine()!

            if input.allSatisfy({ $0.isNumber }) {
                let inputAns = input.map { Int(String($0))! }

                if inputAns.count == 1 &amp;&amp; ( inputAns.contains(1) || inputAns.contains(2) || inputAns.contains(3) ) {


                    if inputAns[0] == 1 {
                        start()
                    } // 선택지 1번 선택시 -&gt; start() 실행

                    else if inputAns[0] == 2 {
                        if !gameCounterLog.isEmpty {
                            for i in 0..&lt;gameCounterLog.count {
                                print(&quot;\(i+1)번째 게임 : 시도 횟수 - \(gameCounterLog[i])&quot;)
                            }
                        } // ⚠️ 게임 기록이 없어 각각의 Log를 저장하는 배열이 비었을때, i에 해당하는 인덱스를 검색하면 에러 발생
                        else {
                            print(&quot;Game 기록이 없습니다...&quot;)
                        }
                    }

                    else if inputAns[0] == 3 {
                        print(&quot;&lt; 숫자 야구 게임을 종료합니다 &gt;&quot;)

                        gameCounterLog = []

                        break
                    }
                }
                else {
                    print(&quot;올바르지 않은 입력값입니다 - 보기의 숫자만 입력해주세요( 1 ~ 3 )&quot;)
                }
            }
            else {
                print(&quot;올바르지 않은 입력값입니다 - 숫자만 입력해주세요&quot;)
            }
        }

        isCorrect = true
    }



    mutating func start() {
                        .
                        .
               나머지 Lv.2 코드와 동일
                        .
                        .
        while !isCorrect {
            var IntAns = valueInput()

            if valueComparison(answer, IntAns) {
                print(&quot;정답!&quot;)

                isCorrect = true

                gameCounter += 1
                gameCounterLog.append(gameCounter)
            }
            else {
                print(&quot;틀렸습니다... 다시 시도해주세요&quot;)

                gameCounter += 1
            }

        }
    }



    func valueInput() -&gt; [Int] {
                            .
                            .
        if inputAns.count != 3 {
            print(&quot;올바르지 않은 입력값입니다 - 3가지 숫자를 입력해주세요&quot;)
        }
        else if inputAns[0] == inputAns[1] || inputAns[1] == inputAns[2] || inputAns[0] == inputAns[2]{
            print(&quot;올바르지 않은 입력값입니다 - 서로 다른 숫자를 입력해주세요&quot;)
        }
                  나머지 Lv.2 코드와 동일
                            .
                            .

    }



    func valueComparison(_ valArr: [Int], _ inputArr: [Int]) -&gt; Bool {
                                    .
                                    .
       if 조건문 -&gt; inputArr.count == 3 &amp;&amp; inputArr != [0,0,0] 으로 수정
                            나머지 Lv.2 코드와 동일
                                    .
                                    .

    }



    func makeAnswer() -&gt; [Int] {
                    .
                    .
              Lv.3 코드와 동일
                    .
                    .
    }
}</code></pre>
<p>우선 Lv.4를 구현하기 위해 startScreen()라는 함수를 만들었다. 그리고 1번을 선택하면 게임이 동작하도록, 해당 함수 내부에서 이전의 게임 실행을 담당하던 start()를 호출하도록 구현하였다.</p>
<p>다음으로 Lv.5에서 게임 기록을 보기 위해 BaseballGame 내에 <code>gameCounter</code>와 <code>gameCounterLog</code>를 만들었다.</p>
<blockquote>
<p>start() 내에서 오답과 정답이 발생할 때마다 gameCounter가 1 씩 추가되도록 만들었으며, 이를 gameCounterLog에 저장하여 게임 횟차마다의 시도 횟수를 저장할 수 있게 만들었다.</p>
</blockquote>
<p>그리고 게임 기록을 보기 위해 시작 메뉴에서 2번을 누르면, gameCounterLog에 저장된 값을 순차적으로 불러와 기록을 출력한다.</p>
<blockquote>
<p>만약 게임을 시도하지 않아 gameCounterLog가 비어있을 때 배열을 출력하려 시도하면 런타임 에러가 발생한다. 이를 해결하기 위해 <code>isEmpty</code>를 활용하여 gameCounterLog에 접근하였다. </p>
</blockquote>
<p>마지막으로 Lv.6의 프로그램 종료를 위해 startScreen() 안 while문 내의 조건문에서 3번을 선택하면 <code>break</code>가 되도록 만들었다. 또한 gameCounterLog을 <code>[]</code>으로 만들어 내부 값을 휘발시켰다.</p>
<p>또한 추가 기능인 1 ~ 3 이외의 값을 입력하면 동작하지 않도록 조건문을 활용하여 해당 기능을 구현하였다.</p>
<hr>
<h1 id="과제를-마무리-하며-🔍">과제를 마무리 하며... 🔍</h1>
<p>일단 주어진 조건에 맞추어 잘 동작하도록 마무리는 잘 하였다!</p>
<blockquote>
<p>모든 기능이 정상적으로 동작하며, 문제가 발생할 수 있는 예외 사항에서도 아무 에러 없이 잘 구현하였다!!! 🤯</p>
</blockquote>
<p>하지만 너무 기능 구현에만 초점을 두고 작성하였기에, 코드가 너무 난잡하다...</p>
<p>그래서 가독성을 중점으로 <code>switch</code>나 삼항연산자 등을 활용하여 최종 코드를 수정해보았다.</p>
<blockquote>
<p><a href="https://github.com/Tae-Ouk/Number-BaseBall-Game/blob/main/swift_Number_BaseBall_Game/main.swift">swift Number BaseBall Game(GitHub - Tae Ouk)</a></p>
</blockquote>
<p>다음 과제부터는 이러한 부분도 고려해서 코드를 작성해야겠다!</p>
<blockquote>
<p>안그러면 팀프로젝트에서 팀원들과 협업하기 어려울거 같다... 😢</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #12 ... Swift 심화 문법 정리 #2]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-12-...-Swift-%EC%8B%AC%ED%99%94-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC-2</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-12-...-Swift-%EC%8B%AC%ED%99%94-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC-2</guid>
            <pubDate>Wed, 13 Mar 2024 11:57:07 GMT</pubDate>
            <description><![CDATA[<h1 id="swift-심화-문법-강의-정리-💻-2">Swift 심화 문법 강의 정리 💻 #2</h1>
<h2 id="접근제한자">접근제한자</h2>
<p>: 다른 소스 파일이나 모듈의 코드에서 코드 일부에 대한 접근을 제한</p>
<pre><code class="language-c">[제약 적음] open &lt; public &lt; internal &lt; fileprivate &lt; private [제약 많음]</code></pre>
<blockquote>
<p>상위 요소보다 하위 요소가 더 높은 접근 수준을 가질 수 없습니다.</p>
</blockquote>
<pre><code class="language-swift">private struct Car {
      public var model: String // 🚨 에러
}

//private의 제약이 public보다 더욱 크기에 에러가 발생!</code></pre>
<pre><code class="language-c">- open             : 모든 소스 파일에서 해당 level 접근 가능
                + 모든 곳에서 서브클래싱 가능

- public         : 모든 소스 파일에서 해당 level 접근 가능 
                  + 같은 모듈 내에서만 서브클래싱 가능

- internal         : 같은 모듈 내에서만 접근 가능
                + 접근 제한자를 작성하지 않으면 `internal`로 판단

- fileprivate     : 같은 소스파일 내에서만 접근 가능

- private         : 클래스 내부에서만 접근 가능</code></pre>
<blockquote>
<p><code>fileprivate</code> -&gt; 서로 다른 클래스가 같은 하나의 소스 파일에 fileprivate로 선언되어 있다면, 두 클래스는 서로 접근할 수 있음</p>
</blockquote>
<blockquote>
<p><code>private</code> -&gt; 같은 파일 안에 있어도 서로 다른 클래스이고, private로 선언되어 있다면 두 요소는 서로 접근할 수 없음</p>
</blockquote>
<hr>
<h2 id="예외처리">예외처리</h2>
<h3 id="에러처리">에러처리</h3>
<p>: 프로그램에서 에러가 발생한 상황에 대응하고 이에 대응하는 과정</p>
<blockquote>
<p>Error 프로토콜을 채택하여 사용자 정의 에러를 정의하여 에러 대응</p>
</blockquote>
<pre><code class="language-swift">enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}</code></pre>
<h3 id="throw-do-catch">throw, do-catch</h3>
<ul>
<li><code>throws</code>는 리턴 값을 반환하기 전에, 오류가 발생하면 에러 객체를 반환</li>
<li><code>throws</code>는 오류가 발생할 가능성이 있는 메소드 제목 옆에 사용</li>
<li><code>throw</code>는 오류가 발생할 구간에서 사용</li>
</ul>
<blockquote>
<p>throw로 던진 에러를 <code>do-catch</code>문에서 처리</p>
</blockquote>
<pre><code class="language-swift">enum CustomError: Error {
    case outOfBounds
    case invalidInput(String)
} // Error 프로토콜을 활용하여 사용자가 직접 에러 정의


func processValue(_ value: Int) throws -&gt; Int {
    if value &lt; 0 {
        throw CustomError.invalidInput(&quot;Value cannot be negative&quot;)
    } else if value &gt; 100 {
        throw CustomError.outOfBounds
    }
    return value * 2
} // `throw`문을 활용하여 오류가 발생하였을 때의 동작 설정


do {
    let result = try processValue(-10)
    print(&quot;Result is \(result)&quot;)
} catch CustomError.outOfBounds {
    print(&quot;Value is out of bounds!&quot;)
} catch CustomError.invalidInput(let errorMessage) {
    print(&quot;Invalid Input: \(errorMessage)&quot;)
} catch {
    print(&quot;An error occurred: \(error)&quot;)
} // `do-catch`문을 활용하여 에러 처리
// 출력 : Invalid Input: Value cannot be negative</code></pre>
<h3 id="try--try--try">try , try? , try!</h3>
<ul>
<li><p><code>try</code></p>
<ul>
<li>에러가 발생할 수 있는 코드 블록을 표시</li>
<li>에러를 던질 수 있는 함수나 메서드를 호출할 때 사용</li>
<li>해당 코드 블록에서 발생한 에러를 잡거나 처리할 수 있음 -&gt; <code>do-catch</code></li>
</ul>
</li>
<li><p><code>try?</code></p>
<ul>
<li><code>do-catch</code> 구문 없이도 사용이 가능</li>
<li>에러 발생시 nil값을 반환</li>
<li>에러가 발생하지 않으면 리턴 값의 타입은 옵셔널로 반환</li>
</ul>
</li>
<li><p><code>try!</code></p>
<ul>
<li>에러가 발생을 하면 앱이 강제 종료</li>
<li>반환 타입은 옵셔널이 언래핑된 값이 리턴됨</li>
</ul>
</li>
</ul>
<pre><code class="language-swift">enum MyError: Error {
    case invalidInput
}


func someThrowingFunction(value: Int) throws -&gt; String {
    guard value &gt;= 0 else {
        throw MyError.invalidInput // value가 음수인 경우 에러를 던짐
    }

    return &quot;The value is \(value)&quot;
}


do {
    let result = try someThrowingFunction(value: -2)
    print(result)
} catch {
    print(&quot;Error occurred: \(error)&quot;) // 음수 값을 처리하는 에러
} // do-catch문과 try 함께 사용

// try?를 사용하여 에러 처리하기
let result1 = try? someThrowingFunction(value: 5) // 유효한 값 호출
print(result1) // Optional(&quot;The value is 5&quot;) -&gt; 옵셔널 타입으로 결과 반환

let result2 = try? someThrowingFunction(value: -2)
print(result2) // nil


let result4 = try! someThrowingFunction(value: -2)
print(result4)
// 에러가 발생할 수 있는 상황에서 try! 사용으로 인해, 에러 발생 및 앱 강제 종료</code></pre>
<hr>
<h2 id="arc와-메모리-누수">ARC와 메모리 누수</h2>
<h3 id="arcautomatic-reference-counting">ARC(Automatic Reference Counting)</h3>
<p>: Swift에서의 메모리 관리 기법 중 하나로, 객체나 인스턴스가 참조되는 횟수를 추적하여 메모리에서 해제할 시점을 결정</p>
<blockquote>
<p>참조 횟수가 <code>0</code>이 되면 해당 객체는 메모리에서 해제</p>
</blockquote>
<pre><code class="language-swift">class Person {
    let name: String
    init(name: String) {
        self.name = name
        print(&quot;\(name) is being initialized&quot;)
    }
    deinit {
        print(&quot;\(name) is being deinitialized&quot;)
    }
}

var reference1: Person?
var reference2: Person?
var reference3: Person?

reference1 = Person(name: &quot;John Appleseed&quot;) // RC: 1️⃣
// Prints &quot;John Appleseed is being initialized&quot;

reference2 = reference1 // RC: 2️⃣ 
reference3 = reference1 // RC: 3️⃣

reference1 = nil // RC: 2️⃣ -&gt; 참고했던 객체를 지우면 RC가 1씩 감소
reference2 = nil // RC: 1️⃣

reference3 = nil // RC: 0️⃣ -&gt; 해당 객체는 메모리에서 해제
// Prints &quot;John Appleseed is being deinitialized&quot;
// 프로퍼티 옵저버와 유사하게 참고 여부를 계속 확인하여 반응</code></pre>
<h3 id="강한-참조-순환strong-reference-cycle">강한 참조 순환(Strong Reference Cycle)</h3>
<p>: 두 개 이상의 인스턴스가 서로가 서로를 강한 참조일 때 발생</p>
<blockquote>
<p>강한 참조 순환: 메모리가 해제되지 않고 유지되어 메모리 누수가 발생하는 현상</p>
</blockquote>
<ul>
<li>Swift로 개발할 때에는 메모리 누수(Memory Leak)을 주의</li>
<li>참조는 디폴트로 강한 참조(Strong Reference)를 사용</li>
</ul>
<blockquote>
<p>강한 참조를 잘못 사용하면 메모리 누수(Memory Leak) 문제가 발생할 수 있음</p>
</blockquote>
<h3 id="강한-참조-순환-문제-해결-방법">강한 참조 순환 문제 해결 방법</h3>
<ol>
<li>약한 참조(Weak Reference) 사용 
: 참조되는 대상을 약하게 참조하여 순환 참조를 방지</li>
</ol>
<ul>
<li><p><code>weak</code>는 옵셔널로 선언되는 참조</p>
</li>
<li><p>참조 대상이 메모리에서 해제되면 자동으로 nil로 설정</p>
</li>
</ul>
<blockquote>
<p>두 객체가 서로를 강하게 참조하는 경우, 한쪽을 <code>weak</code>로 선언하여 순환 참조 문제를 해결</p>
</blockquote>
<pre><code class="language-swift">class Man {
    var name: String
    weak var girlfriend: Woman? 
    //weak는 클래스 내부가 아니라 사용하는 객체 앞에 붙여도 됨

    init(name: String) {
        self.name = name
    }
    deinit { print(&quot;Man Deinit!&quot;) }
}

class Woman {
    var name: String
    var boyfriend: Man?

    init(name: String) {
        self.name = name
    }
    deinit { print(&quot;Woman Deinit!&quot;) }
}


var chelosu: Man? = .init(name: &quot;철수&quot;)
var yeonghee: Woman? = .init(name: &quot;영희&quot;)

chelosu?.girlfriend = yeonghee
yeonghee?.boyfriend = chelosu


chelosu = nil
yeonghee = nil
chelosu?.girlfriend // nil -&gt; `weak`를 활용한 에러 처리</code></pre>
<ol start="2">
<li>비소유 참조(Unowned Reference)
: 참조되는 대상이 항상 유효한 경우에만 사용하며, 해당 대상이 해제될 수 있는 상황에는 사용하지 않음</li>
</ol>
<ul>
<li><p><code>unowned</code>는 옵셔널이 아닌 비소유 참조</p>
</li>
<li><p>비소유 참조는 항상 값이 있다고 가정하며, 참조하는 객체가 해제되면 런타임 에러가 발생할 수 있음</p>
</li>
</ul>
<blockquote>
<p><code>unowned</code> 참조는 참조 대상이 해제될 수 있는 경우에만 사용</p>
</blockquote>
<pre><code class="language-swift">class Man {
    var name: String
    unowned var girlfriend: Woman?

    init(name: String) {
        self.name = name
    }
    deinit { print(&quot;Man Deinit!&quot;) }
}

class Woman {
    var name: String
    var boyfriend: Man?

    init(name: String) {
        self.name = name
    }
    deinit { print(&quot;Woman Deinit!&quot;) }
}

var chelosu: Man? = .init(name: &quot;철수&quot;)
var yeonghee: Woman? = .init(name: &quot;영희&quot;)

chelosu?.girlfriend = yeonghee
yeonghee?.boyfriend = chelosu


yeonghee = nil
chelosu?.girlfriend // 에러 발생</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #11 ... Swift 심화 문법 정리 #1]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-11-...-Swift-%EC%8B%AC%ED%99%94-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC-1</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-11-...-Swift-%EC%8B%AC%ED%99%94-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC-1</guid>
            <pubDate>Tue, 12 Mar 2024 11:49:12 GMT</pubDate>
            <description><![CDATA[<h1 id="swift-심화-문법-강의-정리-💻-1">Swift 심화 문법 강의 정리 💻 #1</h1>
<h2 id="프로퍼티-옵저버">프로퍼티 옵저버</h2>
<p>: 프로퍼티를 관찰(observe)하면서 변경 사항이 발생할 때 실행</p>
<blockquote>
<p>willSet보다는 didSet이 많이 사용됨</p>
</blockquote>
<h3 id="didset">didSet</h3>
<p><code>didSet</code>은 새 값이 저장된 직후에 호출</p>
<blockquote>
<p>이전 프로퍼티의 값이 <code>oldValue</code> 로 제공</p>
</blockquote>
<h3 id="willset">willSet</h3>
<p><code>willSet</code>은 값이 저장되기 직전에 호출</p>
<blockquote>
<p>새로운 프로퍼티의 값이 <code>newValue</code> 로 제공</p>
</blockquote>
<pre><code class="language-swift">var name: String = &quot;Unknown&quot; {
    willSet {
        print(&quot;현재 이름 = \(name), 바뀔 이름 = \(newValue)&quot;)
    } // newVal은 변경 될 새로운 값
    didSet {
        print(&quot;현재 이름 = \(name), 바뀌기 전 이름 = \(oldValue)&quot;)
    } // oldVal은 변경 전 myProperty의 값
}

name = &quot;Peter&quot;
// willSet이 먼저 실행됨
// 현재 이름 = Unknown, 바뀔 이름 = Peter
// 현재 이름 = Peter, 바뀌기 전 이름 = Unknown</code></pre>
<blockquote>
<p>프로퍼티 옵저버를 활용하면 기존 프로퍼티 값을 수정될 때마다, 동시에 값이 변경된 것을 인지하고 지정된 동작을 실행</p>
</blockquote>
<hr>
<h2 id="타입-캐스팅">타입 캐스팅</h2>
<p>: 변수나 객체의 타입을 다른 타입으로 변환하는 프로세스</p>
<blockquote>
<p>타입이란?</p>
</blockquote>
<ul>
<li>기본 데이터 타입(Int, Double, Bool, String) 등</li>
<li>옵셔널(Optional)</li>
<li>커스텀 데이터 타입(struct,  class, enum, protocol 등)</li>
</ul>
<pre><code class="language-swift">// Person은 슈퍼 클래스(부모 클래스)
class Person {

}

// Student는 서브 클래스(자식 클래스) - 상속받은 클래스
class Student: Person {

}

// Teacher는 서브 클래스(자식 클래스) - 상속받은 클래스
class Teacher: Person {

}

let person: Person = Person() - 가능
let student: Student = Student() - 가능
let student: Person = Student() - 가능
let teacher: Person = Teacher() - 가능

let person: Student = Person() - 불가능
// 부모 클래스의 인스턴스를 자식 클래스 타입으로 할당할 수 없음

let student: Teacher = Student() - 불가능
//Teacher와 Student 클래스 간의 아무런 관계가 없음</code></pre>
<h3 id="is">is</h3>
<p>타입을 체크하는 연산자로, 비교 결과를 bool 타입을 반환</p>
<blockquote>
<p>타입 체킹</p>
</blockquote>
<pre><code class="language-swift">let char: Character = &quot;A&quot;

print(char is Character)
// 출력값: true
print(char is String)   
// 출력값: false</code></pre>
<h3 id="as-as-as">as, as!, as?</h3>
<p>인스턴스의 &quot;타입&quot;을 확인하거나, 해당 인스턴스를 슈퍼 클래스(부모 클래스)나 하위 클래스(자식 클래스)로 취급하는 방법</p>
<blockquote>
<p>as</p>
</blockquote>
<ul>
<li>컴파일 단계에서 캐스팅이 실행, 따라서 항상 타입 캐스팅이 성공할 경우에만 사용할 수 있음</li>
<li>캐스팅에 실패할 경우 에러가 발생</li>
<li>캐스팅하려는 타입이 같은 타입 이거나 슈퍼클래스 타입이라는 것을 알 때 <code>as</code> 연사자를 사용</li>
</ul>
<blockquote>
<p>as?</p>
</blockquote>
<ul>
<li>런타임에 캐스팅이 실행</li>
<li>성공하면 <code>옵셔널 타입</code>의 인스턴스를 반환하고, 실패하면 <code>nil</code> 을 반환</li>
<li>실패할 가능성이 있으면 as?를 사용</li>
</ul>
<blockquote>
<p>as!</p>
</blockquote>
<ul>
<li>런타임에 특정 타입으로 강제 캐스팅</li>
<li>강제 타입 캐스팅에 실패할 경우, 런타임 에러가 발생할 수 있음</li>
</ul>
<pre><code class="language-swift">class Person {
    var id = 0
    var name = &quot;name&quot;
    var email = &quot;hgk@gmail.com&quot;
}

class Worker: Person {
    // id
    // name
    // email
    var salary = 300
}

class Programmer: Worker {
    // id
    // name
    // email
    // salary
    var lang = &quot;Swift&quot;
}


// 업캐스팅 - as
let person1 = Person()
let worker1 = Worker()
let programmer1 = Programmer()

let personList = [person1, worker1, programmer1] 
// 타입을 선언하지 않았지만 Person 타입으로 인식 -&gt; 즉 업캐스팅이 되었음
personList[1].name
//personList[1].salary -&gt; Person 타입으로 보고 있기 때문에 salary에 접근하지 못함

let worker2 = Worker()
worker2.salary

let workerPerson = worker2 as Person
//workerPerson.salary -&gt; Person 타입으로 보고 있기 때문에 salary에 접근하지 못함


// 다운캐스팅 - as? / as!
// as?
let pro = programmer1 as? Programmer -&gt; 타입 변환이 될 수도 있고 안될 수도 있기 때문에 옵셔널을 리턴

if let person2 = programmer1 as? Programmer {
    person2.lang
}

if let person3 = worker1 as? Programmer {
    person3.lang
}

// as!
let pro2 = worker2 as! Programmer // Error : 타입 변환 실패시 오류</code></pre>
<hr>
<blockquote>
<p>나머지 내용은 내일 정리해보겠다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #10 ... 🍎 Swift ＆ Algorithm 🖥️]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-10-...-Swift-%EF%BC%86-Algorithm</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-10-...-Swift-%EF%BC%86-Algorithm</guid>
            <pubDate>Fri, 08 Mar 2024 12:00:59 GMT</pubDate>
            <description><![CDATA[<h1 id="level-1-없는-숫자-더하기---86051"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/86051.%E2%80%85%EC%97%86%EB%8A%94%E2%80%85%EC%88%AB%EC%9E%90%E2%80%85%EB%8D%94%ED%95%98%EA%B8%B0">[level 1] 없는 숫자 더하기 - 86051</a></h1>
<ul>
<li>문제 설명
0부터 9까지의 숫자 중 일부가 들어있는 정수 배열 numbers가 매개변수로 주어집니다. numbers에서 찾을 수 없는 0부터 9까지의 숫자를 모두 찾아 더한 수를 return 하도록 solution 함수를 완성해주세요.</li>
</ul>
<pre><code class="language-swift">문제 풀이

import Foundation

func solution(_ numbers:[Int]) -&gt; Int {
    var ans: Int = 0

    for i in 0...9 {
       ans += numbers.contains(i) != true ? i : 0
    }

    return ans
}</code></pre>
<p>맨 처음 문제를 보았을 때, 고차함수를 사용하여 간단하게 코드를 작성하고 싶었지만 위 코드처럼 조건문을 사용하여 문제를 해결하였다. </p>
<pre><code class="language-swift">문제 풀이

import Foundation

func solution(_ numbers:[Int]) -&gt; Int {
    return (0...9).filter{ !numbers.contains($0) }.reduce(0, +)
}</code></pre>
<p>우선, 문제 풀이만 구현하였고 고차함수를 사용하여 다시 수정해보았다!</p>
<hr>
<h1 id="level-1-제일-작은-수-제거하기---12935"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12935.%E2%80%85%EC%A0%9C%EC%9D%BC%E2%80%85%EC%9E%91%EC%9D%80%E2%80%85%EC%88%98%E2%80%85%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0">[level 1] 제일 작은 수 제거하기 - 12935</a></h1>
<ul>
<li>문제 설명
정수를 저장한 배열, arr 에서 가장 작은 수를 제거한 배열을 리턴하는 함수, solution을 완성해주세요. 단, 리턴하려는 배열이 빈 배열인 경우엔 배열에 -1을 채워 리턴하세요. 예를들어 arr이 [4,3,2,1]인 경우는 [4,3,2]를 리턴 하고, [10]면 [-1]을 리턴 합니다.</li>
</ul>
<pre><code class="language-swift">문제 풀이

func solution(_ arr:[Int]) -&gt; [Int] {
    var val_min = arr.min()!
    var ans = arr

    if ans.count == 1 {
        return [-1]
    }
    else {
        ans.remove(at: ans.firstIndex(of: val_min)!)
    }


    return ans
}</code></pre>
<p>해당 문제도 고차함수를 사용하여 풀이를 시도하였으나, 실패하고 조건문으로 먼저 풀어보았다... 🥲</p>
<p>해당 코드 중에서 <code>firstIndex(of: val_min)</code>의 반환값이 옵셔널 타입이여서, 이를 강제로 언랩핑하여 사용하였다.</p>
<blockquote>
<p>아마 입력된 배열에 아무것도 없으면 런타임 오류가 발생할 수 있으니까, 옵셔널 타입으로 반환되는거 같다.</p>
</blockquote>
<p>다음부터 옵셔널 타입을 다룰 때, <code>!</code>를 활용한 강제 언랩핑이 아니라 더욱 안전한 방법을 찾아보아야겠다!</p>
<hr>
<h1 id="level-1-가운데-글자-가져오기---12903"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12903.%E2%80%85%EA%B0%80%EC%9A%B4%EB%8D%B0%E2%80%85%EA%B8%80%EC%9E%90%E2%80%85%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0">[level 1] 가운데 글자 가져오기 - 12903</a></h1>
<ul>
<li>문제 설명
단어 s의 가운데 글자를 반환하는 함수, solution을 만들어 보세요. 단어의 길이가 짝수라면 가운데 두글자를 반환하면 됩니다.</li>
</ul>
<pre><code class="language-swift">문제 풀이

func solution(_ s:String) -&gt; String {
    var val = s.map{$0}
    var arr: [String] = []
    var pos: Int = val.count / 2

    if val.count % 2 == 1 {
        arr.append(String(val[pos]))
    }
    else {
        arr.append(String(val[pos-1]))
        arr.append(String(val[pos]))
    }

    let ans = arr.reduce(&quot;&quot;, +)

    return ans

}</code></pre>
<hr>
<h1 id="level-1-수박수박수박수박수박수---12922"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12922.%E2%80%85%EC%88%98%EB%B0%95%EC%88%98%EB%B0%95%EC%88%98%EB%B0%95%EC%88%98%EB%B0%95%EC%88%98%EB%B0%95%EC%88%98%EF%BC%9F">[level 1] 수박수박수박수박수박수? - 12922</a></h1>
<ul>
<li>문제 설명
길이가 n이고, &quot;수박수박수박수....&quot;와 같은 패턴을 유지하는 문자열을 리턴하는 함수, solution을 완성하세요. 예를들어 n이 4이면 &quot;수박수박&quot;을 리턴하고 3이라면 &quot;수박수&quot;를 리턴하면 됩니다.</li>
</ul>
<pre><code class="language-swift">문제 풀이

func solution(_ n:Int) -&gt; String {
    var arr: [String] = []
    var count: Int = 1

    while count &lt;= n {
        count % 2 == 1 ? arr.append(&quot;수&quot;) : arr.append(&quot;박&quot;)

        count += 1
    }

    var ans = arr.joined()

    return ans
}</code></pre>
<p>여러번 시도 끝에 고차함수를 사용하지 못하고 문제 풀이를 마무리하였다.</p>
<blockquote>
<p>이 문제도 다음에 깔끔하게 정리해야겠다!</p>
</blockquote>
<hr>
<h1 id="level-1-내적---70128"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/70128.%E2%80%85%EB%82%B4%EC%A0%81">[level 1] 내적 - 70128</a></h1>
<ul>
<li>문제 설명
길이가 같은 두 1차원 정수 배열 a, b가 매개변수로 주어집니다. a와 b의 내적을 return 하도록 solution 함수를 완성해주세요.
이때, a와 b의 내적은 a[0]<em>b[0] + a[1]</em>b[1] + ... + a[n-1]*b[n-1] 입니다. (n은 a, b의 길이)</li>
</ul>
<pre><code class="language-swift">문제 풀이

import Foundation

func solution(_ a:[Int], _ b:[Int]) -&gt; Int {
    return (0..&lt;a.count).map{ a[$0] * b[$0] }.reduce(0, +)
}</code></pre>
<p>지금까지 풀었던 문제 중에서 가장 깔끔하게 잘 푼 것 같다! map 함수를 사용하여, solution 함수의 입력으로 들어오는 a와 b 배열을 각각의 인덱스를 더하였다. 이후, reduce를 활용하여 모든 값을 더하고 해당 값을 반환하였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #9 ... 🍎 Swift ＆ Algorithm 🖥️]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-9-...-Swift-%EF%BC%86-Algorithm</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-9-...-Swift-%EF%BC%86-Algorithm</guid>
            <pubDate>Wed, 06 Mar 2024 10:53:58 GMT</pubDate>
            <description><![CDATA[<h1 id="level-1-콜라츠-추측---12943"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12943.%E2%80%85%EC%BD%9C%EB%9D%BC%EC%B8%A0%E2%80%85%EC%B6%94%EC%B8%A1">[level 1] 콜라츠 추측 - 12943</a></h1>
<ul>
<li>문제 설명
1937년 Collatz란 사람에 의해 제기된 이 추측은, 주어진 수가 1이 될 때까지 다음 작업을 반복하면, 모든 수를 1로 만들 수 있다는 추측입니다. 작업은 다음과 같습니다.</li>
</ul>
<blockquote>
<p>1-1. 입력된 수가 짝수라면 2로 나눕니다. 
1-2. 입력된 수가 홀수라면 3을 곱하고 1을 더합니다. 
2. 결과로 나온 수에 같은 작업을 1이 될 때까지 반복합니다. </p>
</blockquote>
<p>예를 들어, 주어진 수가 6이라면 6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1 이 되어 총 8번 만에 1이 됩니다. 위 작업을 몇 번이나 반복해야 하는지 반환하는 함수, solution을 완성해 주세요. 단, 주어진 수가 1인 경우에는 0을, 작업을 500번 반복할 때까지 1이 되지 않는다면 –1을 반환해 주세요.</p>
<pre><code class="language-swift">문제 풀이

func solution(_ num:Int) -&gt; Int {
    var count: Int = 0
    var val = num

    while val != 1 {
        if count &gt;= 500 {
            break
        }

        val = val % 2 == 0 ? val / 2 : (val * 3) + 1

        count += 1
    }

    return val == 1 ? count : -1
}</code></pre>
<p>앞서 풀었던 문제처럼 여러 함수를 사용하여 문제 풀이를 시도하였다.</p>
<p>num 값을 활용하여 문자열을 받는 배열을 만들어, 위 풀이의 삼항연산 결과를 배열에 입력시킬 계획이었다. 이후, 배열의 길이를 이용하여 작업의 반복 횟수를 판단하고 <code>arr.last == 1</code>으로 작업이 마무리되는 것을 확인하려고 시도하였다.</p>
<blockquote>
<p>이렇게 구상한 방법이 더욱 짧은 코드를 만들기 쉬울 듯 하였으나, 많은 시도에도 구현하기 어려워 포기하였다... 😭</p>
</blockquote>
<p>그래서 일단은 문제 풀이가 가능한 기능의 코드만 구현하였다..</p>
<blockquote>
<p>다음에 다른 방법이 생각난다면 다시 풀어볼거다!!</p>
</blockquote>
<hr>
<h1 id="level-1-서울에서-김서방-찾기---12919"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12919.%E2%80%85%EC%84%9C%EC%9A%B8%EC%97%90%EC%84%9C%E2%80%85%EA%B9%80%EC%84%9C%EB%B0%A9%E2%80%85%EC%B0%BE%EA%B8%B0">[level 1] 서울에서 김서방 찾기 - 12919</a></h1>
<ul>
<li>문제 설명
String형 배열 seoul의 element중 &quot;Kim&quot;의 위치 x를 찾아, &quot;김서방은 x에 있다&quot;는 String을 반환하는 함수, solution을 완성하세요. seoul에 &quot;Kim&quot;은 오직 한 번만 나타나며 잘못된 값이 입력되는 경우는 없습니다.</li>
</ul>
<pre><code class="language-swift">func solution(_ seoul:[String]) -&gt; String {
    var pos = seoul.firstIndex(of: &quot;Kim&quot;)

    return &quot;김서방은 \(pos)에 있다&quot;
}</code></pre>
<p>이번 문제는 다른 문제들에 비해 매우 쉬웠다! 입력되는 배열 중, &quot;KIM&quot;이라는 값만 찾으면 되기에, <code>seoul.firstIndex(of: &quot;Kim&quot;)</code>으로 해당 값과 배열의 인택스 값을 비교하여 위치를 찾았다. 그리고 해당 값을 어제 강의에서 배운 문자 보간법을 활용하여 출력해주었다.</p>
<hr>
<h1 id="level-1-음양-더하기---76501"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/76501.%E2%80%85%EC%9D%8C%EC%96%91%E2%80%85%EB%8D%94%ED%95%98%EA%B8%B0">[level 1] 음양 더하기 - 76501</a></h1>
<ul>
<li>문제 설명
어떤 정수들이 있습니다. 이 정수들의 절댓값을 차례대로 담은 정수 배열 absolutes와 이 정수들의 부호를 차례대로 담은 불리언 배열 signs가 매개변수로 주어집니다. 실제 정수들의 합을 구하여 return 하도록 solution 함수를 완성해주세요.</li>
</ul>
<pre><code class="language-swift">import Foundation

func solution(_ absolutes:[Int], _ signs:[Bool]) -&gt; Int {
    var ans: Int = 0

    for i in 0...absolutes.count-1 {
        if signs[i] == true {
            ans += absolutes[i]
        }
        else {
            ans -= absolutes[i]
        }        
    }

    print(ans)

    return ans
}
</code></pre>
<p>해당 문제도 맨 처음 문제처럼 여러 함수들과 문자열을 받는 배열을 활용하여 해결하기 위해 많은 시도를 하였다.</p>
<blockquote>
<p>하지만 위 코드에서 볼 수 있듯이 실패하였다...</p>
</blockquote>
<p>그래서 문제 조건에 알맞게 조건문을 활용하여 문제를 풀었다. 그리고 이를 조금 더 가독성을 높이기 위해 삼항연산자를 활용하여 아래의 코드로 마무리하였다.</p>
<pre><code class="language-swift">import Foundation

func solution(_ absolutes:[Int], _ signs:[Bool]) -&gt; Int {
    var ans: Int = 0

    for i in 0...absolutes.count-1 {
       (signs[i] == true) ? (ans += absolutes[i]) : (ans -= absolutes[i])
    }

    return ans
}</code></pre>
<p>아마 맨 처음 문제를 수정할 수 있는 방법을 찾게 되면, 이 문제도 배열과 여러 함수를 사용하여 풀어볼 수 있을 것이다.</p>
<blockquote>
<p>다음에 이 문제도 다시 풀어볼 것이다!!</p>
</blockquote>
<hr>
<h1 id="swift-기초-문법-강의-정리-💻-3">Swift 기초 문법 강의 정리 💻 #3</h1>
<h2 id="옵셔널과-nil">옵셔널과 nil</h2>
<h3 id="옵셔널">옵셔널</h3>
<p>옵셔널은 값이 없을 수 있는 상황에서 사용한다. 그리고 두 가지 가능성을 나타내며, 물음표 ? 를 사용하여 표시한다.</p>
<blockquote>
<p>옵셔널 타입끼리의 연산은 불가능!</p>
</blockquote>
<pre><code class="language-swift">var num1: Int? = 4</code></pre>
<blockquote>
<p>옵셔널 타입을 찾아보면 some, none case로 구성되어 있다!</p>
</blockquote>
<h3 id="nil">nil</h3>
<p>해당 값을 활용하여 옵셔널 형태의 데이터에 값이 없는 상태로 저장할 수 있다.</p>
<pre><code class="language-swift">var num: Int? = nil</code></pre>
<blockquote>
<p>Swift는 옵셔널 타입을 활용하여 nill을 처리하기에 런타임 오류를 최소하 하여 안정적이다!</p>
</blockquote>
<h3 id="옵셔널-바인딩">옵셔널 바인딩</h3>
<p>옵셔널 값이 빈값인지 존재하는지 검사한 후, 존재하는 경우 그 값을 다른 변수에 대입시켜 바인딩하는 것을 의미합니다.</p>
<blockquote>
<p>옵셔널을 사용하기 전에, 내부에 빈 값의 유무를 체크하고 언래핑 해주는 것이 강제로 언래핑하는 것보다 훨씬 안전하다.</p>
</blockquote>
<hr>
<h1 id="swift-기초-문법-강의-정리-💻-4">Swift 기초 문법 강의 정리 💻 #4</h1>
<h2 id="스택-큐">스택, 큐</h2>
<h3 id="queue">Queue</h3>
<p>First-In-First-Out (FIFO)</p>
<pre><code class="language-swift">struct Queue&lt;T&gt; {
    private var queue: [T] = []

    public var count: Int {
        return queue.count
    }

    public var isEmpty: Bool {
        return queue.isEmpty
    }

    public mutating func enqueue(_ element: T) {
        queue.append(element)
    }

    public mutating func dequeue() -&gt; T? {
        return isEmpty ? nil : queue.removeFirst()
    }
}

var queue = Queue&lt;Int&gt;()
queue.enqueue(10)
queue.enqueue(20)
queue.dequeue() // 10</code></pre>
<h3 id="stack">Stack</h3>
<p>Last-In-First-Out (LIFO)</p>
<pre><code class="language-swift">struct Stack&lt;T&gt; {
    private var stack: [T] = []

    public var count: Int {
        return stack.count
    }

    public var isEmpty: Bool {
        return stack.isEmpty
    }

    public mutating func push(_ element: T) {
        stack.append(element)
    }

    public mutating func pop() -&gt; T? {
        return isEmpty ? nil : stack.popLast()
    }
}

var stack = Stack&lt;Int&gt;()
stack.push(10)
stack.push(20)
stack.pop() // 20</code></pre>
<blockquote>
<p>이제 강의 전체 분량 중 절반보다 조금 더 들었는데, 내용을 정리하면서 동시에 강의를 수강하니까 속도기 너무 느린거 같다..</p>
</blockquote>
<p>그래서 나머지 내용들은 강의를 다 듣고 내용이 이해되면 다시 정리해보겠다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #8 ... 🍎 Swift ＆ Algorithm 🖥️]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-8-...-Swift-%EF%BC%86-Algorithm</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-8-...-Swift-%EF%BC%86-Algorithm</guid>
            <pubDate>Tue, 05 Mar 2024 11:27:29 GMT</pubDate>
            <description><![CDATA[<h1 id="level-1-두-정수-사이의-합---12912"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12912.%E2%80%85%EB%91%90%E2%80%85%EC%A0%95%EC%88%98%E2%80%85%EC%82%AC%EC%9D%B4%EC%9D%98%E2%80%85%ED%95%A9">[level 1] 두 정수 사이의 합 - 12912</a></h1>
<ul>
<li>문제 설명
두 정수 a, b가 주어졌을 때 a와 b 사이에 속한 모든 정수의 합을 리턴하는 함수, solution을 완성하세요.
예를 들어 a = 3, b = 5인 경우, 3 + 4 + 5 = 12이므로 12를 리턴합니다.</li>
</ul>
<pre><code class="language-swift">문제 풀이

func solution(_ a:Int, _ b:Int) -&gt; Int64 {
    var val: Int = 0

    if a &lt; b {
        for i in a...b {
            val += i
        }
    }
    else if b &lt; a {
        for i in b...a {
            val += i
        }
    }
    else {
        return Int64(a)
    }

    return Int64(val)
}</code></pre>
<p>맨 처음 문제를 봤을 때, 어떤 식으로 구성할지 고민하다 우선 문제 풀이대로 동작하는 위 코드를 만들었다. 조건문을 사용하여 a와 b의 크기를 구분하였고 그에 따른 연산을 추가하였다. 또한 else 문에는 a와 b가 동일할 때의 예외를 처리하기 위한 기능을 구현하였다.</p>
<blockquote>
<p>하지만 기존 코드를 더욱 효율적으로 정리해볼 수 있을거 같아 아래의 코드로 수정해보았다.</p>
</blockquote>
<pre><code class="language-swift">문제 풀이

func solution(_ a:Int, _ b:Int) -&gt; Int64 {
    var val: Int = 0

    for i in a &lt; b ? a...b : b...a {
            val += i
        }


    return a == b ? Int64(a) : Int64(val)
}</code></pre>
<p>풀이 코드를 수정하면서 신기한 것을 알게 되었다.</p>
<blockquote>
<p>Swift에서의 for 문에서 삼항연산자를 활용할 수 있었다.</p>
</blockquote>
<pre><code class="language-swift">for i in a &lt; b ? a...b : b...a {
    ~~~
}</code></pre>
<p>기존에 사용하던 C와는 달라 매우 신기하게 느껴졌다. 아마 이번 문제가 아니였다면, 몰랐을 Swift 문법인 것 같다.</p>
<hr>
<h1 id="level-1-나누어-떨어지는-숫자-배열---12910"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12910.%E2%80%85%EB%82%98%EB%88%84%EC%96%B4%E2%80%85%EB%96%A8%EC%96%B4%EC%A7%80%EB%8A%94%E2%80%85%EC%88%AB%EC%9E%90%E2%80%85%EB%B0%B0%EC%97%B4">[level 1] 나누어 떨어지는 숫자 배열 - 12910</a></h1>
<ul>
<li>문제 설명
array의 각 element 중 divisor로 나누어 떨어지는 값을 오름차순으로 정렬한 배열을 반환하는 함수, solution을 작성해주세요.
divisor로 나누어 떨어지는 element가 하나도 없다면 배열에 -1을 담아 반환하세요.</li>
</ul>
<pre><code class="language-swift">func solution(_ arr:[Int], _ divisor:Int) -&gt; [Int] {
    var val  = arr.filter{ $0 % divisor == 0 }

    val.sort(by: &lt;)

    return val.last == nil ? [-1] : val
}</code></pre>
<p>이번 문제는 생각보다 쉽게 풀렸다. 아마 <code>filter</code>를 활용하였기에 편했던 것 같다.</p>
<p>우선 입력값 중 하나인 arr 배열값을 divisor으로 나누어 각각의 요소의 나머지가 0이 되는 것만 filter를 사용하여 정렬하였다.</p>
<p>그리고 <code>print()</code>를 활용하여 val의 값을 확인해보았는데, 입력값의 순서에 대해 순차적으로 정렬되지않은 것을 볼 수 있었다.</p>
<p>그래서 이를 해결하기 위해 <code>sort(by: &lt;)</code>를 통해 올림차순으로 정렬해주었고, 마지막으로 삼항연산자를 활용하여 문제 마지막 조건을 충족시켜주었다.</p>
<hr>
<h1 id="swift-기초-문법-강의-정리-💻-1">Swift 기초 문법 강의 정리 💻 #1</h1>
<p>Sparta 내배캠에서 제공된 Swift 기초 문법 강의를 듣고 모르는 내용만 정리해보았다.</p>
<blockquote>
<p>아직까진 전반적으로 C에서 다룬 부분과 유사한 것들이 많아 쉽게 느껴진다.</p>
</blockquote>
<h2 id="xcode-playground-활용하기">Xcode Playground 활용하기</h2>
<p>아래의 코드에서 변수 ans에 대해 데이터 타입을 별도로 지정해주지 않았다.</p>
<pre><code class="language-swift">func solution(_ a:Int, _ b:Int) -&gt; Int64 {
    var val: Int = 0
    var ans = 100

     ~~~

 }</code></pre>
<p>그럼 Xcode 컴파일러가 알아서 입력된 값에 알맞는 데이터 타입을 유추하는데, 해당 타입을 알고 싶다면 변수 위에 &quot;option&quot; 키를 누르고 마우스를 올리면 아래와 같은 결과를 볼 수 있을 것이다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/tae_uk/post/50195341-f13d-4da1-85af-53f41b22f9a4/image.png" width="80%" height="80%"></p>

<h2 id="swift의-특징-🔍">Swift의 특징 🔍</h2>
<h3 id="안전성-🔧">안전성 🔧</h3>
<ul>
<li><p>타입에 엄격
(= Type Safe한 언어)</p>
<blockquote>
<p>앞서 몇 일간 Swift를 사용하여 코딩테스트 문제들을 풀어보았는데, 항상 느낀 점 중 하나가 데이터 타입에 대해 엄격하여 타입 변환과 일치에 신경을 많이 쓸 수 밖에 없었다...</p>
</blockquote>
</li>
<li><p>nil(빈 값을 의미)에 엄격</p>
<blockquote>
<ul>
<li>컴파일 시 문제가 발생할 수 있는 nil 객체를 만들거나 사용할 수 없음</li>
<li>만약 nil을 사용할 경우 ?모양의 옵셔널(Optional)을 이용하여 표시</li>
</ul>
</blockquote>
</li>
</ul>
<h2 id="프로퍼티-출력과-함수">프로퍼티, 출력과 함수</h2>
<h3 id="연산-프로퍼티-computed-property">연산 프로퍼티 (Computed Property)</h3>
<p>연산 프로퍼티는 그 때 그 때 계산한 값을 알려주기 때문에 var를 사용하며, 매번 값을 연산하여 활용하기 때문에 특정 값을 저장하지 않는다.</p>
<pre><code class="language-swift">var x = 10
var y = 20
var z = 0

var sum: Int {
    get {
        return x + y
    }
    set {
        z = x + y
    }
}

print(sum)
// 출력값: 30


var sum1: Int {
        return x + y
}

var sum2: Int {
        x + y
}</code></pre>
<p>그리고 get과 set 중 get만 필요한 경우에는 키워드 생략 가능하며, return을 지우고 사용이 가능하나 수식이 한 줄일 경우에만 사용할 수 있다.</p>
<h3 id="멀티라인-텍스트">멀티라인 텍스트</h3>
<p>여러 줄의 텍스트를 String으로 저장할 때, 아래와 같은 방법을 활용할 수 있다.</p>
<pre><code class="language-swift">var greeting =  &quot;&quot;&quot;
                Hello 
                World 
                 🌍 
                &quot;&quot;&quot;</code></pre>
<h3 id="문자-보간법">문자 보간법</h3>
<p>문자열 내에 변수나 상수의 값을 나타내고 싶을 때 사용하며, ( ) 를 사용하면 된다.</p>
<pre><code class="language-swift">let height = 185
let myInfo = &quot;My height is \(height) 🌍&quot;  
// 출력값: My height is 185 🌍</code></pre>
<h3 id="함수">함수</h3>
<p>Swift에서는 함수도 하나의 타입으로 취급될 수 있다!</p>
<blockquote>
<p>이러한 점을 활용하여 다른 함수의 매개변수로 전달하거나, 다른 함수의 반환값으로 사용할 수 있다.</p>
</blockquote>
<h3 id="메서드란-함수와-메서드의-차이는">메서드란? 함수와 메서드의 차이는?</h3>
<p>함수(Function)와 메서드(Method)는 유사한 기능을 구현하지만 몇 가지 차이점이 있다.</p>
<blockquote>
<ul>
<li>정의와 소속성</li>
</ul>
</blockquote>
<ul>
<li><p>함수
: 독립적으로 정의되며 일반적으로 전역 범위에서 정의되거나 특정 범위 내에 내장</p>
</li>
<li><p>메서드
: 클래스, 구조체, 열거형 등의 특정한 타입에 속해 있고, 해당 객체의 특정한 기능을 수행하기 위해 사용</p>
</li>
</ul>
<blockquote>
<ul>
<li>호출 방식</li>
</ul>
</blockquote>
<ul>
<li>함수
: 직접적으로 호출</li>
</ul>
<pre><code class="language-swift">addNumbers(5, 3)
// 함수 이름 뒤에 괄호를 사용하여 호출</code></pre>
<ul>
<li>메서드
: 해당하는 객체 또는 타입에 대해 호출</li>
</ul>
<pre><code class="language-swift">myObject.increment()
// 객체 또는 타입 인스턴스 뒤에 메서드 이름을 호출</code></pre>
<blockquote>
<ul>
<li>소속성</li>
</ul>
</blockquote>
<ul>
<li><p>함수
: 함수는 특정한 객체의 속성이나 상태에 접근할 수 없고, 주어진 매개변수를 받아들여 작업을 수행하고 값을 반환하는데 집중</p>
</li>
<li><p>메서드
: 객체 또는 타입의 속성에 직접적으로 접근할 수 있고, 해당 객체의 상태를 변경하거나 속성에 접근하여 작업을 수행하는 데 사용된다.</p>
</li>
</ul>
<blockquote>
<p>간단 정리 ☝️</p>
</blockquote>
<p>함수는 특정한 타입에 속하지 않으며 독립적으로 정의되고 호출</p>
<p>메서드는 특정한 타입에 속해 있으며, 해당 타입의 인스턴스에 대해 작동하고 상태를 변경할 수 있는 함수</p>
<h3 id="함수-기본-형태">함수 기본 형태</h3>
<pre><code class="language-swift">func 함수_이름(아규먼트_레이블: 파라미터_타입) -&gt; 리턴_타입 {
  // ... 코드
}
// 네이밍 컨벤션으로 카멜케이스를 사용합니다 (ex- methodName 🙆 / method_name 🙅)

ex) 
func sayHi(friend: String) {
  print(&quot;Hi~ \(friend)!&quot;)
}

sayHi(friend: &quot;영호&quot;)
// 함수의 사용</code></pre>
<blockquote>
<p>참고자료 : <a href="https://velog.io/@leyuri/%ED%91%9C%EA%B8%B0%EB%B2%95-%EC%8A%A4%EB%84%A4%EC%9D%B4%ED%81%AC-%EC%BC%80%EC%9D%B4%EC%8A%A4-%ED%8C%8C%EC%8A%A4%EC%B9%BC-%EC%BC%80%EC%9D%B4%EC%8A%A4-%EC%B9%B4%EB%A9%9C-%EC%BC%80%EC%9D%B4%EC%8A%A4#%EC%B9%B4%EB%A9%9C-%EC%BC%80%EC%9D%B4%EC%8A%A4-camel-case">표기법 - 스네이크 케이스, 파스칼 케이스, 카멜 케이스</a></p>
</blockquote>
<pre><code class="language-swift">func sayHi(to friend: String) {
  print(&quot;Hi~ \(friend)!&quot;)
}
sayHi(to: &quot;영호&quot;)
// &quot;to: &quot; -&gt; 호출 시점에 friend 사용

func sayHi(_ friend: String) -&gt; String {
  return (&quot;Hi~ \(friend)!&quot;)
}
print(sayHi(&quot;영호&quot;))
// 


// 출력값: Hi~ 영호!</code></pre>
<hr>
<h1 id="swift-기초-문법-강의-정리-💻-2">Swift 기초 문법 강의 정리 💻 #2</h1>
<h2 id="데이터-타입">데이터 타입</h2>
<h3 id="float">Float</h3>
<p>소수점을 표현하는 데이터 타입으로, 소수점 이하 6자리(32비트 부동 소수)까지 표현 가능하다.</p>
<h3 id="double">Double</h3>
<p>소수점을 표현하는 데이터 타입으로, 소수점 이하 15자리(64비트 부동 소수) 이상 표현 가능하다.</p>
<h3 id="string">String</h3>
<p>아래의 String을 선언하는 방법 이외에</p>
<pre><code class="language-swift">var emptyString: String = &quot;&quot; </code></pre>
<p>이러한 방식으로 String을 사용할 수 있다.</p>
<pre><code class="language-swift">var anotherEmptyString = String() </code></pre>
<h3 id="tuple">Tuple</h3>
<p>여러 값을 하나로 그룹화한 데이터 타입이다.</p>
<pre><code class="language-swift">let http404Error: (Int, String) = (404, &quot;Not Found&quot;)


let (justTheStatusCode, _): (Int, String) = http404Error
print(&quot;The status code is \(justTheStatusCode)&quot;)
// (justTheStatusCode, _) -&gt; (Int, String)
// justTheStatusCode -&gt; Int, _ -&gt; String
// _ =&gt; 사용하지 않음
// 따라서 Int 타입의 justTheStatusCode만 사용


http404Error.0
http404Error.1
// 튜플에 저장된 값의 순서를 활용하여 저장된 데이터에 접근


let http200Status: (Int, String) = (statusCode: 200, description: &quot;OK&quot;)
// 튜플 내 각 요소마다 이름을 지정해줄 수 있음</code></pre>
<p>위 예시에서 볼 수 있듯이 관련성 있는 2~3개 정도의 데이터를 묶어서 사용하기 편리하다.</p>
<h3 id="any">Any</h3>
<p>다양한 데이터 타입의 값을 수용할 수 있는 데이터 타입이다.</p>
<pre><code class="language-swift">var anyArray: [Any] = [1,&quot;Hi&quot;, true]

var anyValue: Any = 1000
anyValue = &quot;어떤 타입도 수용 가능&quot;
anyValue = 12345.67

// 컴파일 에러
let doubleValue: Double = anyValue
// Any 데이터 타입이 Double을 포함하는 더욱 포괄적인 개념이니 문제가 발생함</code></pre>
<blockquote>
<p>Any 데이터 형을 사용하기 위해서는 반드시 형 변환이 필요하다!!</p>
</blockquote>
<h3 id="swif에서의-타입">Swif에서의 타입</h3>
<blockquote>
<ul>
<li>기본 데이터 타입 (Built-in Data Types)
: 해당 데이터 타입은 모두 구조체로 구현되어 있음</li>
</ul>
</blockquote>
<ul>
<li><strong>Int</strong>: 정수 타입</li>
<li><strong>UInt</strong>: 부호 없는 정수 타입</li>
<li><strong>Float</strong>: 단정도 부동 소수점 숫자 타입</li>
<li><strong>Double</strong>: 배정도 부동 소수점 숫자 타입</li>
<li><strong>Bool</strong>: 불리언 타입</li>
<li><strong>String</strong>: 문자열 타입</li>
<li><strong>Character</strong>: 단일 문자 타입</li>
</ul>
<blockquote>
<ul>
<li>사용자 정의 데이터 타입 (Custom Data Types)
: 해당 데이터 타입은 개발자가 직접 타입을 정의</li>
</ul>
</blockquote>
<ul>
<li><strong>구조체(Structures)</strong>: 멤버 변수와 메서드를 포함하는 타입</li>
<li><strong>클래스(Classes)</strong>: 객체 지향 프로그래밍을 위한 참조 타입</li>
<li><strong>열거형(Enumerations)</strong>: 관련된 값들의 그룹을 정의하는 타입</li>
<li><strong>프로토콜(Protocols)</strong>: 특정 작업 또는 기능에 대한 메서드, 속성 및 요구 사항을 정의하는 타입</li>
</ul>
<blockquote>
<p>사용자 정의 데이터 타입에 대해서는 몇 번 더 읽어보기!! 🥲</p>
</blockquote>
<h2 id="swift에서의-데이터-타입">Swift에서의 데이터 타입</h2>
<h2 id="타입-안정성">타입 안정성</h2>
<p>Swift에서 데이터 타입을 엄격하게 분류하기 때문에, 코드 작성 시 데이터 타입을 명확하게 사용하게 된다. 또한 서로 다른 타입끼리의 연산이 불가능하기에 타입 안정성이 매우 높다!</p>
<h2 id="타입-형변환">타입 형변환</h2>
<p>만약 다른 데이터 타입의 변수들을 더하거나 빼는 등의 연산하고 싶다면, 하나의 통일된 데이터 타입으로 형변환을 해주어야 한다.</p>
<pre><code class="language-swift">let val1: Int = 10
let val2: Double = 10.0

val1 + Int(val2)
// Double 형의 val2를 Int 형으로 변환</code></pre>
<hr>
<h1 id="swift-기초-문법-강의-정리-💻-3">Swift 기초 문법 강의 정리 💻 #3</h1>
<h2 id="연산자-조건문과-반복문">연산자, 조건문과 반복문</h2>
<h3 id="주의점">주의점</h3>
<p>Swift는 띄어쓰기도 신경써야 하는 언어이다!</p>
<pre><code class="language-swift">a - b
// a 빼기 b

a -b
// a와 -b</code></pre>
<blockquote>
<p>컴파일 에러가 발생하지 않도록 띄어쓰기를 주의하자!! 🚨</p>
</blockquote>
<h3 id="switch">switch</h3>
<p>사용 방법은 C와 매우 유사하였지만, 몇 가지 차이점이 있어 정리해보았다.</p>
<ul>
<li><p>모든 케이스가 적용되지 않는 경우의 실행되는 <code>default</code>는 항상 마지막에 표시</p>
</li>
<li><p>fallthrough
switch 조건 중 특정 케이스에 해당되어 해당 구문이 실행된 뒤, 다음 케이스 블럭을 실행</p>
</li>
</ul>
<pre><code class="language-swift">var number = 5

switch number {
case 5:
    print(&quot;5&quot;)
    fallthrough 
    // 해당 케이스의 구문이 실행된 이후에도 무조건 다음블럭을 실행함
default:
    print(&quot;default&quot;)
}
// 출력 결과 
// 5
// default</code></pre>
<ul>
<li>열거형(enum)과 함께 사용</li>
</ul>
<pre><code class="language-swift">enum Day {
    case monday, tuesday, wednesday, thursday, friday, saturday, sunday
}

func activities(for day: Day) {
    switch day {
    case .monday:
        print(&quot;월요일: 회사 회의&quot;)
    case .tuesday:
        print(&quot;화요일: 운동 가기&quot;)
    case .wednesday:
        print(&quot;수요일: 책 읽기&quot;)
    case .thursday:
        print(&quot;목요일: 친구와 만나기&quot;)
    case .friday:
        print(&quot;금요일: 영화 보기&quot;)
    case .saturday:
        print(&quot;토요일: 쇼핑하기&quot;)
    case .sunday:
        print(&quot;일요일: 가족과 시간 보내기&quot;)
    }
}

activities(for: .monday)
activities(for: .friday)</code></pre>
<blockquote>
<p>열거형 <code>enum</code>의 사용한다는 점은 이해했으나, <code>enum</code> 이것을 처음 봐서 아직 뭔지 모르겠다...</p>
</blockquote>
<h3 id="for">for</h3>
<p>배열을 활용하여 코드를 구성할 수 있고, 아래의 예시처럼 사용할 수 있다!</p>
<pre><code class="language-swift">let students = [&quot;Tom&quot;: 2, &quot;Harry&quot;: 4, &quot;Sarah&quot;: 1]

for (name, grade) in students {
  print(&quot;\(name) 은 \(grade) 학년이야&quot;)
}

// 출력값: 
// Tom 은 2 학년이야
// Harry 은 4 학년이야
// Sarah 은 1 학년이야</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #7 ... 🍎 Swift ＆ Algorithm 🖥️]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-7-...-Swift-%EF%BC%86-Algorithm-sl8qbqvz</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-7-...-Swift-%EF%BC%86-Algorithm-sl8qbqvz</guid>
            <pubDate>Mon, 04 Mar 2024 11:16:46 GMT</pubDate>
            <description><![CDATA[<h1 id="level-1-정수-내림차순으로-배치하기---12933"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12933.%E2%80%85%EC%A0%95%EC%88%98%E2%80%85%EB%82%B4%EB%A6%BC%EC%B0%A8%EC%88%9C%EC%9C%BC%EB%A1%9C%E2%80%85%EB%B0%B0%EC%B9%98%ED%95%98%EA%B8%B0">[level 1] 정수 내림차순으로 배치하기 - 12933</a></h1>
<ul>
<li>문제 설명
함수 solution은 정수 n을 매개변수로 입력받습니다. n의 각 자릿수를 큰것부터 작은 순으로 정렬한 새로운 정수를 리턴해주세요. 예를들어 n이 118372면 873211을 리턴하면 됩니다.</li>
</ul>
<pre><code class="language-swift">문제 풀이

func solution(_ n:Int64) -&gt; Int64 {
    var val = String(Int64(n)).map{Int64(String($0))!}

    val.sort(by: &gt;)

    var ans = val.map(String.init).joined()

    return Int64(ans)!
}</code></pre>
<p>solution 함수 첫 줄에서 val을 살펴보면 map 함수를 사용한 것을 볼 수 있다. Int64 형태로 입력되는 입력값 n을 각 자릿수마다 분리하여 String 형태로 정리하였다. 이후 map 함수가 배열로 결과값을 반환하는 점을 활용하여 sort 함수를 사용하여 내림차순으로 배열을 정렬해주었다.</p>
<p>마지막으로 배열에 각 자릿수마다 분리되어 있는 val 값을 String 타입으로 초기화해주고 joined()를 활용하여 모든 값을 붙여주었다.</p>
<blockquote>
<p>진짜!! 진짜! 마지막으로 결과값 ans를 Int64로 변환하여 solution의 결과값으로 반환하였다!</p>
</blockquote>
<hr>
<h1 id="level-1-정수-제곱근-판별---12934"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12934.%E2%80%85%EC%A0%95%EC%88%98%E2%80%85%EC%A0%9C%EA%B3%B1%EA%B7%BC%E2%80%85%ED%8C%90%EB%B3%84">[level 1] 정수 제곱근 판별 - 12934</a></h1>
<ul>
<li>문제 설명
임의의 양의 정수 n에 대해, n이 어떤 양의 정수 x의 제곱인지 아닌지 판단하려 합니다.
n이 양의 정수 x의 제곱이라면 x+1의 제곱을 리턴하고, n이 양의 정수 x의 제곱이 아니라면 -1을 리턴하는 함수를 완성하세요.</li>
</ul>
<pre><code class="language-swift">문제 풀이

import Foundation

func solution(_ n:Int64) -&gt; Int64 {
    var ans: Int64 = 0
    var val: Int64 = Int64(sqrt(Double(n)))

    ans = (val*val == n) ? (val+1)*(val+1) : -1

    return ans
}</code></pre>
<p>해당 문제를 더욱 편하게 풀기 위해 제곱에 대한 sqrt 함수를 사용하였다. 이때, sqrt 함수는 입력과 출력을 Double을 기준으로 하기 때문에 위에서 볼 수 있듯이 코드를 구성하였다. </p>
<blockquote>
<p>이후, 삼항연산자를 활용하여 문제 조건에 맞는 코드를 작성하였다.</p>
</blockquote>
<hr>
<h1 id="level-1-하샤드-수---12947"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12947.%E2%80%85%ED%95%98%EC%83%A4%EB%93%9C%E2%80%85%EC%88%98">[level 1] 하샤드 수 - 12947</a></h1>
<ul>
<li>문제 설명
양의 정수 x가 하샤드 수이려면 x의 자릿수의 합으로 x가 나누어져야 합니다. 예를 들어 18의 자릿수 합은 1+8=9이고, 18은 9로 나누어 떨어지므로 18은 하샤드 수입니다. 자연수 x를 입력받아 x가 하샤드 수인지 아닌지 검사하는 함수, solution을 완성해주세요.</li>
</ul>
<pre><code class="language-swift">문제 풀이

func solution(_ x:Int) -&gt; Bool {
    var val1 = String(x).map{Int(String($0))!}
    var val2: Int = 0

    for i in 0..&lt;val1.count {
        val2 += val1[i]
    }

    return x % val2 == 0 ? true : false
}</code></pre>
<p>해당 문제도 맨 처음 문제와 비슷하게 접근하였다. val1 값을 각 자릿수마다 저장된 배열로 만들어 val2에 각 자릿수 값을 더해주었다.</p>
<p>그리고 return에서 삼항연산자를 활용하여 문제를 해결하였다.</p>
<hr>
<h1 id="내배캠-본캠프-시작-🎉">내배캠 본캠프 시작! 🎉</h1>
<p>지금까지 사전캠프 기간이였고, 저번주도 온보딩 기간으로 실질적인 프로젝트를 수행하진 않았다.</p>
<blockquote>
<p>아마 팀프로젝트를 한다고 해도, 개발 지식이 없어 진행에 어려움을 겪었을 것이다... 🥲</p>
</blockquote>
<p>하지만 오늘부터 본캠프가 시작되어 더욱 도움되는 기대가 된다!! 😋</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[📝 iOS TIL] #6 ... 🍎 Swift  ＆ Algorithm 🖥️]]></title>
            <link>https://velog.io/@tae_uk/iOS-TIL-6-...-Swift-%EF%BC%86-Algorithm</link>
            <guid>https://velog.io/@tae_uk/iOS-TIL-6-...-Swift-%EF%BC%86-Algorithm</guid>
            <pubDate>Wed, 28 Feb 2024 10:54:38 GMT</pubDate>
            <description><![CDATA[<h1 id="level-1-나머지가-1이-되는-수-찾기---87389"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/87389.%E2%80%85%EB%82%98%EB%A8%B8%EC%A7%80%EA%B0%80%E2%80%851%EC%9D%B4%E2%80%85%EB%90%98%EB%8A%94%E2%80%85%EC%88%98%E2%80%85%EC%B0%BE%EA%B8%B0">[level 1] 나머지가 1이 되는 수 찾기 - 87389</a></h1>
<ul>
<li>문제 설명
자연수 n이 매개변수로 주어집니다. n을 x로 나눈 나머지가 1이 되도록 하는 가장 작은 자연수 x를 return 하도록 solution 함수를 완성해주세요. 답이 항상 존재함은 증명될 수 있습니다.</li>
</ul>
<pre><code class="language-swift">문제 풀이

import Foundation

func solution(_ n:Int) -&gt; Int {
    var arr: [Int] = []

    for i in 1...n-1 {
        if n % i == 1 {
            arr.append(i)
        }
    }

    return arr[0]
}
</code></pre>
<p>위 문제 풀이는 우선 주어진 문제 조건에 맞게 동작하도록 구성한 것이다!</p>
<p>가장 간편하게 배열을 만든 후, 나머지가 1이 되는 값을 배열 arr에 저장되도록 하였다. 이후, 나머지가 1이 되는 값이 모여있는 배열에서 가장 작은 수가 배열 맨 앞에 있기에, arr의 0번째 인덱스에서 값을 가져와 결과값으로 반환하였다.</p>
<blockquote>
<p>위 문제 풀이를 다른 방식으로 풀 방법이 생각나 조금 바꿔보았다!</p>
</blockquote>
<pre><code class="language-swift">문제 풀이

import Foundation

func solution(_ n:Int) -&gt; Int {
    var arr: [Int] = []
    var ans: [Int] = []

    for i in 1...n-1 {
        arr.append(n % i == 1 ? i : 0)
    }

    ans = arr.filter{ $0 != 0 }

    return ans[0]
}</code></pre>
<p>맨 처음 푼 문제 풀이와 유사하게 나머지가 1이 되는 수를 구분하기 위해 </p>
<pre><code class="language-swift">for i in 1...n-1 {
        arr.append(n % i == 1 ? i : 0)
    }</code></pre>
<p>코드를 만들어 arr에 나머지가 1이 되는 수와 아닐 때의 값인 0이 저장된다.</p>
<p>이후, filter 함수를 사용하여 나머지가 1인 수만 남겨 배열을 재정리하였다.</p>
<blockquote>
<p>마지막으로 위 문제 풀이와 마찬가지로 arr의 0번째 값을 결과값으로 반환하였다.</p>
</blockquote>
<hr>
<h1 id="level-1-x만큼-간격이-있는-n개의-숫자---12954"><a href="https://github.com/Tae-Ouk/Algorithm/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12954.%E2%80%85x%EB%A7%8C%ED%81%BC%E2%80%85%EA%B0%84%EA%B2%A9%EC%9D%B4%E2%80%85%EC%9E%88%EB%8A%94%E2%80%85n%EA%B0%9C%EC%9D%98%E2%80%85%EC%88%AB%EC%9E%90">[level 1] x만큼 간격이 있는 n개의 숫자 - 12954</a></h1>
<ul>
<li>문제 설명
함수 solution은 정수 x와 자연수 n을 입력 받아, x부터 시작해 x씩 증가하는 숫자를 n개 지니는 리스트를 리턴해야 합니다. 다음 제한 조건을 보고, 조건을 만족하는 함수, solution을 완성해주세요.</li>
</ul>
<pre><code class="language-swift">문제 풀이

func solution(_ x:Int, _ n:Int) -&gt; [Int64] {
    var arr: [Int] = []

    for i in 1...n {
        arr.append(x * i)
    }    

    return arr.map{ Int64($0) }
}</code></pre>
<p>이번 문제를 풀면서 처음으로 &quot;Int64&quot; 형태를 본 것 같다. 이전부터 Swift로 문제 풀이를 하며 느낀점 중 하나로, 자료형에 대해 매우 예민한 것 같다.</p>
<blockquote>
<p>맨 처음 문제를 풀 때, 당연히 Int가 Int64의 크기를 감당할 수 있기에 별도의 형변환이 필요없을 줄 알았다...</p>
</blockquote>
<p>그래서 &quot;arr&quot;이라는 Int 형을 입력받는 빈 배열을 만들어, 문제에 대한 결과값을 배열에 담을 수 있도록 하였다.</p>
<p>이후, 최종 결과값을 map함수를 사용하여 &quot;Int64&quot; 형태로 변환한 뒤, 함수의 결과값으로 반환하였다.</p>
<hr>
<h1 id="git">Git</h1>
<blockquote>
<p>3번 이상 </p>
</blockquote>
<h2 id="git이란">Git이란?</h2>
<p>: 버전 관리 도구(형상 관리 도구)</p>
<blockquote>
<p>코드 변경점 기록</p>
</blockquote>
<h2 id="1-리눅스-명령어">1. 리눅스 명령어</h2>
<h3 id="pwdprint-working-directory">pwd(Print Working Directory)</h3>
<p>현재 작업하고 있는 디렉토리를 출력시켜줌</p>
<h3 id="lslist">ls(List)</h3>
<p>현재 위치하고 있는 디렉토리에 있는 폴더와 파일을 출력함</p>
<h3 id="ls--aall">ls -a(All)</h3>
<p>숨겨진 폴더와 파일을 포함하여 모두 출력함</p>
<h3 id="cdchange-directory">cd(Change Directory)</h3>
<p>원하는 폴더로 이동</p>
<blockquote>
<p>Ex) cd Desktop</p>
<ul>
<li><p>..
상위 폴더로 이동
Ex) cd ..</p>
</li>
<li><p>DirectoryName/DirectoryName
&quot;폴더명/폴더명&quot;으로 더 깊이 이동할 수 있음</p>
</li>
</ul>
</blockquote>
<h3 id="mkdirmake-directory">mkdir(Make Directory)</h3>
<p>현재 위치에서 폴더를 만들 수 있음</p>
<blockquote>
<p>Ex) mkdir DirectoryName</p>
</blockquote>
<h3 id="touch">touch</h3>
<p>현재 위치에서 파일을 만들 수 있음</p>
<blockquote>
<p>Ex) touch a.txt</p>
</blockquote>
<h2 id="2-github">2. Github</h2>
<h3 id="github이란">Github이란?</h3>
<p>: 코드를 백업, 공유와 협업이 가능한 온라인 코드 저장소</p>
<h2 id="3-github">3. Github</h2>
<h3 id="git-initinitialize-초기화하다">git init(initialize: 초기화하다)</h3>
<p>코드 관리를 시작하는 명령어</p>
<blockquote>
<ul>
<li>프로젝트 시작 전, 한 번만 입력하면 됨</li>
<li>정확학 프로젝트 경로에서 입력해야 됨</li>
</ul>
</blockquote>
<p>폴더 -&gt; 서비스 -&gt; 폴더에 새로운 터미널 열기</p>
<h3 id="git-add--commit">git add &amp; commit</h3>
<p>코드를 저장하는 명령어</p>
<ol>
<li><p>git add FileName</p>
<blockquote>
<p>저장 전, 저장할려는 파일 지정</p>
<ul>
<li>git add .
모든 프로젝트 지정</li>
</ul>
</blockquote>
</li>
<li><p>git commit -m &quot;Massage&quot;</p>
<blockquote>
<p>실제로 저장하는 명령어</p>
</blockquote>
</li>
</ol>
<h3 id="git-status">git status</h3>
<p>저장 여부 확인하는 명령어</p>
<h3 id="git-log">git log</h3>
<p>저장 내역을 확인하는 명령어</p>
<blockquote>
<ul>
<li>commit에서 작성한 Massage log를 모두 확인 가능</li>
<li>키보드 q = 나오기</li>
</ul>
</blockquote>
<h3 id="git-diff--reset">git diff &amp; reset</h3>
<ol>
<li><p>git diff</p>
<blockquote>
<p>코드 변경 확인</p>
</blockquote>
</li>
<li><p>git reset</p>
<blockquote>
<p>과거로 돌아가는 명령어</p>
</blockquote>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>