<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>app_shawn.log</title>
        <link>https://velog.io/</link>
        <description>iOS 개발, Flutter 개발, Swift, Dart, 42 Seoul 3기</description>
        <lastBuildDate>Thu, 28 Nov 2024 17:13:43 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>app_shawn.log</title>
            <url>https://velog.velcdn.com/images/app_shawn/profile/9edab4c0-3da8-4c31-aacc-609c40122718/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. app_shawn.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/app_shawn" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Firebase 모듈 의존성 이슈 해결하기
]]></title>
            <link>https://velog.io/@app_shawn/Firebase-%EB%AA%A8%EB%93%88-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@app_shawn/Firebase-%EB%AA%A8%EB%93%88-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 28 Nov 2024 17:13:43 GMT</pubDate>
            <description><![CDATA[<p>Firebase 관련 모듈들의 의존성 문제를 해결하면서 겪은 이슈와 해결 과정을 정리해보려고 한다.</p>
<h3 id="문제-상황">문제 상황</h3>
<p>현재 프로젝트는 다음과 같은 구조로 되어있다:</p>
<p>```swift
let project = Project(
   name: &quot;AppCraft&quot;,
   packages: [
       .remote(
           url: &quot;<a href="https://github.com/pointfreeco/swift-composable-architecture&quot;">https://github.com/pointfreeco/swift-composable-architecture&quot;</a>,
           requirement: .upToNextMajor(from: &quot;1.8.0&quot;)
       ),
       .remote(
           url: &quot;<a href="https://github.com/firebase/firebase-ios-sdk&quot;">https://github.com/firebase/firebase-ios-sdk&quot;</a>,
           requirement: .branch(&quot;main&quot;)
       )
   ],
   targets: [...]
)
이 구조에서:</p>
<p>CoreDatabase가 FirebaseFirestore를 참조
CoreAuth가 CoreDatabase와 FirebaseAuth를 참조
각 모듈이 staticFramework로 설정되어 있어서 중복 구현 에러 발생</p>
<p>Copyobjc[41982]: Class ... is implemented in both ... and ... One of the two will be used. Which one is undefined.
해결 과정</p>
<p>모든 모듈을 staticFramework에서 framework로 변경</p>
<p>swiftCopyTarget(
    name: &quot;CoreDatabase&quot;,
    platform: .iOS,
    product: .framework,  // .staticFramework에서 변경
    ...
)</p>
<p>CoreAuth와 CoreDatabase 모듈 통합</p>
<p>Firebase 관련 의존성을 한 곳에서 관리하기 위해 CoreAuth 로직을 CoreDatabase로 이동
의존성 순환 문제 해결
코드 중복 제거</p>
<p>남은 과정</p>
<p>Preview 동작 이슈는 아직 해결해야 할 문제로 남아있음</p>
<p>배운 점</p>
<p>framework와 staticFramework의 차이점</p>
<p>framework</p>
<p>동적 링크 방식
런타임에 로드됨
여러 타겟이 하나의 프레임워크를 공유
메모리 효율적
앱 크기 감소</p>
<p>staticFramework</p>
<p>정적 링크 방식
빌드 시점에 바이너리에 포함
각 타겟마다 복사본 생성
메모리/용량 증가
iOS 버전 호환성 좋음</p>
<p>Firebase같은 외부 의존성은 한 모듈에서 관리하는 것이 좋다</p>
<p>의존성 충돌 방지
코드 중복 최소화
유지보수 용이</p>
<p>이렇게 모듈 구조를 개선하니 빌드는 성공적으로 됐다. Preview 이슈는 다음 글에서 다뤄보도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[무당벌레 포커 가이드]]></title>
            <link>https://velog.io/@app_shawn/%EB%AC%B4%EB%8B%B9%EB%B2%8C%EB%A0%88-%ED%8F%AC%EC%BB%A4-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@app_shawn/%EB%AC%B4%EB%8B%B9%EB%B2%8C%EB%A0%88-%ED%8F%AC%EC%BB%A4-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Mon, 10 Jun 2024 06:34:58 GMT</pubDate>
            <description><![CDATA[<h2 id="무당벌레-포커에는-8종류의-동물카드가-8장씩">무당벌레 포커에는 8종류의 동물카드가 8장씩,</h2>
<h2 id="총-64장의-카드가-있습니다">총 64장의 카드가 있습니다.</h2>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/5d443f3f-98ae-4c47-bf1b-468d9d83d008/image.png" alt=""></p>
<h2 id="자신의-보드에">자신의 보드에,</h2>
<h2 id="한-종류의-동물카드가-4장이-되거나">한 종류의 동물카드가 4장이 되거나</h2>
<h2 id="모든-종류의-동물카드가-1장씩-깔리면">모든 종류의 동물카드가 1장씩 깔리면</h2>
<h1 id="패배합니다-😭">패배합니다. 😭</h1>
<h2 id="🕹️-게임-진행-방식">🕹️ 게임 진행 방식</h2>
<h3 id="1-총-64장의-카드를-골고루-섞어-분배합니다">1. 총 64장의 카드를 골고루 섞어 분배합니다.</h3>
<p>(손에 있는 카드는 상대에게 보이지 않습니다.)</p>
<hr>
<h3 id="2-방장-부터-손에서-상대에게-넘길-카드를-한장-고릅니다-상대에게-보여지지-않습니다">2. 방장 부터 손에서 상대에게 넘길 카드를 한장 고릅니다. (상대에게 보여지지 않습니다.)</h3>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/f1342388-92ee-4fb8-832c-8403a93a5658/image.png" alt=""></p>
<hr>
<h3 id="3-카드를-넘길-상대를-선택합니다">3. 카드를 넘길 상대를 선택합니다.</h3>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/9eb50101-55ea-411e-8aa5-4569f60bcba5/image.png" alt=""></p>
<hr>
<h3 id="4-고른-카드를-어떤-카드라고-속일까요-골라주세요">4. 고른 카드를 어떤 카드라고 속일까요? 골라주세요.</h3>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/c47a5635-d202-4768-8b95-bff94b9ddff4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/ad5c7c9b-6945-4dcd-8e73-3c45ef3dff9c/image.png" alt=""></p>
<h2 id="이-카드는-개구리-입니다-">“이 카드는 개구리 입니다 ..!”</h2>
<hr>
<h3 id="5-수비자는-이-카드가-개구리가-맞는지-아닌지를-고릅니다">5. 수비자는 이 카드가 개구리가 맞는지 아닌지를 고릅니다.</h3>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/fe1508a6-b62a-42ec-bdd0-73e280d246ea/image.png" alt=""></p>
<h2 id="이-카드는-개구리가-맞습니다-">“이 카드는 개구리가 맞습니다 !”</h2>
<hr>
<h3 id="6-공격에-성공했으면-수비자의-보드에-실패했다면-공격자의-보드에-카드가-깔립니다">6. 공격에 성공했으면 수비자의 보드에, 실패했다면 공격자의 보드에 카드가 깔립니다.</h3>
<p>위 예시에서는, 카드가 개구리가 아닌 뱀이기 때문에 수비자의 보드에 카드가 깔립니다.
<img src="https://velog.velcdn.com/images/app_shawn/post/12e7f54d-4e5b-431f-aa48-c9091064cd45/image.png" alt=""></p>
<p>뱀 카드 한장이 적립됩니다.</p>
<hr>
<h3 id="7-카드를-넘길-수도-있습니다">7. 카드를 넘길 수도 있습니다.</h3>
<p>수비자는 “맞습니다” 또는 “아닙니다” 를 통해 공격을 맞받아칠 수 있지만, </p>
<p>“카드 넘기기” 를 통해 카드의 정체를 모르는 다른 이에게 </p>
<p>공격자가 되어 카드를 넘길 수 있습니다.</p>
<h2 id="이-때-기존-수비자가-공격자로-바뀝니다">이 때, 기존 수비자가 공격자로 바뀝니다.</h2>
<h3 id="이후-다시-3번-부터-과정이-반복됩니다">이후 다시 3번 부터 과정이 반복됩니다.</h3>
<hr>
<h2 id="자신의-보드에-1">자신의 보드에,</h2>
<h2 id="한-종류의-동물카드가-4장이-되거나-1">한 종류의 동물카드가 4장이 되거나</h2>
<h2 id="모든-종류의-동물카드가-1장씩-깔리면-1">모든 종류의 동물카드가 1장씩 깔리면</h2>
<h1 id="패배합니다-😭-1">패배합니다. 😭</h1>
<h2 id="패배자는-1명-나머지는-모두-승자가-됩니다-">패배자는 1명, 나머지는 모두 승자가 됩니다 !</h2>
<h2 id="건승을-빕니다">건승을 빕니다.</h2>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/dade5885-3b35-47d2-8806-7335d11fdfe0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Compose MultiPlatform - Firebase]]></title>
            <link>https://velog.io/@app_shawn/Compose-MultiPlatform-Firebase</link>
            <guid>https://velog.io/@app_shawn/Compose-MultiPlatform-Firebase</guid>
            <pubDate>Thu, 01 Feb 2024 14:10:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/app_shawn/post/63ca1361-629d-4c3e-a516-c3eff1d22ad7/image.png" alt=""></p>
<blockquote>
<h3 id="이-글은-firebase-프로젝트-설치-후의-내용입니다">이 글은 Firebase 프로젝트 설치 후의 내용입니다.</h3>
</blockquote>
<h2 id="1-android">1. Android</h2>
<p> Compose Multiplatform 앱 (이하 CMP) 중 Android에 Firebase 프로젝트를 추가해보자.</p>
<p> 프로젝트 생성 후 좌측 상단 톱니바퀴 클릭</p>
<p> <img src="https://velog.velcdn.com/images/app_shawn/post/d956b9f1-6485-4489-ad73-480c83c4eecd/image.png" alt=""></p>
<p>내 앱 -&gt; 앱 추가 클릭 -&gt; 안드로이드 클릭</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/e07f9811-17b4-46e4-b344-ba9388202350/image.png" alt=""></p>
<p>google-services.json 다운 ( 폴더에 넣지말고 넘어가세요 )</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/ba4eaeed-80f4-479c-8933-0bc655b688f8/image.png" alt=""></p>
<p>3번 탭의 가이드를 모두 따라줍니다.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/94a220f5-5650-4905-abf9-3bdaa7e794d7/image.png" alt=""></p>
<p>App 파일 -&gt; src -&gt; androidMain -&gt; AndroidMainfest.xml 에
인터넷 권한을 줍니다.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/a6f73c02-65d9-47e0-8d3e-81fe5abac766/image.png" alt=""></p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;

    &lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt; &lt;!--This line--&gt;
    &lt;uses-permission android:name=&quot;android.permission.ACCESS_NETWORK_STATE&quot;/&gt; &lt;!--This line--&gt;

   ....
</code></pre><p>composeApp -&gt; src -&gt; androidMain -&gt; kotlin -&gt; MainActivity.kt
에서 Firebase 를 init 하면 성공</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/b46177f9-cd96-4752-9d34-ae9228094873/image.png" alt=""></p>
<pre><code>class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Firebase.initialize(this)
        setContent {
            App()
        }
    }
}</code></pre><h2 id="2-ios">2. iOS</h2>
<p>먼저 App Folder 의 iosApp를 Xcode로 실행시켜주세요.</p>
<p>Firebase -&gt; 앱 추가 를 다시 눌러서 이번엔 iOS를 추가합니다.</p>
<p>앱을 등록해야하는데, </p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/4e270df1-a70e-4645-8fb7-e412909c8b59/image.png" alt=""></p>
<p><strong>좌측 상단 iosApp</strong> 클릭, </p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/796d6452-7216-4d6b-8d1c-b97db4723a3a/image.png" alt=""></p>
<p><strong>Signing &amp; Capabilities</strong> 에서 </p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/3361ff3b-8449-4158-bfd5-6ec680d7911c/image.png" alt=""></p>
<p><strong>Bundle Identifier</strong> 를 복사해서 앱 등록시에 붙여넣기 해주세요.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/008442f3-20ac-45ae-afe2-2425b6eb4964/image.png" alt=""></p>
<p>안드로이드처럼, <strong>google-service 다운</strong> 받아서, iosApp 폴더 안에 넣어주세요.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/d383405d-14eb-40d1-b23f-56f1819022f7/image.png" alt=""></p>
<p>좌측 상단에 <strong>Add Package Dependencies</strong> 클릭</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/54814355-2ef7-4a61-a59c-a1c211ca3560/image.png" alt=""></p>
<p><a href="https://github.com/firebase/firebase-ios-sdk">https://github.com/firebase/firebase-ios-sdk</a> 
를 검색해서 Add Package 해줍니다.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/85dde9eb-b438-456e-84a8-173eed100d29/image.png" alt=""></p>
<p>이후 iOSApp.swift 파일에서 FirebaseApp.configure() 입력 후 
cmd + b 로 빌드에 성공하면 완료</p>
<pre><code>import SwiftUI
import FirebaseCore

@main
struct iOSApp: App {

    init() {
        FirebaseApp.configure()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
</code></pre><h2 id="no-such-module-firebasecore-오류-대응">No such module &#39;FirebaseCore&#39; 오류 대응</h2>
<p>이거 때문에 하루를 날렸는데,</p>
<p>먼저 </p>
<p>터미널로 iosApp 폴더까지 들어가줍니다.</p>
<pre><code>pod init 

pod install //pod 를 설치해주세요.</code></pre><p>이후 폴더를 들어가보면, <strong>iosApp.xcworkspace</strong> 가 추가되어있습니다.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/8bc90ae5-036a-490c-b099-5ec2b9f5ea2c/image.png" alt=""></p>
<p><strong>좌측 상단 iosApp</strong> 클릭, </p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/bdc57db4-b43b-4949-b4d3-666555981d7c/image.png" alt=""></p>
<p><strong>TARGETS</strong> 클릭 후 <strong>General</strong> 를 클릭해주세요.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/51025d0f-db1a-475c-ac37-2ed0e19e0f32/image.png" alt=""></p>
<p>Frameworks, Libraries, and ... 안에 + 를 누르고</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/27c3c1a2-7778-4d1f-b878-188dd181b4f4/image.png" alt=""></p>
<p>Firebase Package 안에 내용물을 다 때려넣어주세요.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/4088d6ce-c9d6-4a90-9f11-49dd0db01f05/image.png" alt=""></p>
<p>이렇게 하니 성공했습니다 :)</p>
<p>이제 진짜 개발 시작...!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[인스타그램 연동] Graph Api with Swift]]></title>
            <link>https://velog.io/@app_shawn/%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EC%97%B0%EB%8F%99-Graph-Api-with-Swift-qpnibyy6</link>
            <guid>https://velog.io/@app_shawn/%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EC%97%B0%EB%8F%99-Graph-Api-with-Swift-qpnibyy6</guid>
            <pubDate>Mon, 30 May 2022 05:41:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="나의-어플리케이션에-인스타그램-피드를-가져오자"><strong>나의 어플리케이션에 인스타그램 피드를 가져오자</strong></h3>
</blockquote>
<p>인스타그램 연동을 위해서는 몇가지 준비물이 필요하다.</p>
<ol start="0">
<li>Meta for Developers 의 테스트 앱</li>
<li>인스타그램을 연동할 어플리케이션</li>
<li>페이스북 아이디</li>
<li>인스타그램 비즈니스 계정</li>
</ol>
<blockquote>
<p>0번 테스트 앱 준비</p>
</blockquote>
<p>0번의 준비가 가장 까다롭다. 
<a href="https://developers.facebook.com/?locale=ko_KR">https://developers.facebook.com/?locale=ko_KR</a>
에 가입한 후 , 앱을 하나 만들자<img src="https://velog.velcdn.com/images/app_shawn/post/0e8f89b2-0532-4dde-9405-e853f0b7c954/image.png" alt="">
이런식으로 앱을 만들고 나면 , 테스트앱을 만들어야한다.<img src="https://velog.velcdn.com/images/app_shawn/post/8c7fdcab-cfcc-4bcf-9f01-fdb41dce3fa4/image.png" alt=""></p>
<p>일반 앱에는 권한을 줄 수 없다. 테스트앱에만 모든 권한을 줄 수 있고, 테스트앱을 만들어야만 페이스북 앱 검수를 받을 수 있다.</p>
<p>당신이 어플에 인스타그램 연동 기능을 넣겠다고 다짐한 순간
까다로운 Apple 심사와 함께 더 까다롭다고 소문난 Meta 앱 검수를 함께 진행해야한다.</p>
<p>테스트앱을 만들고 나면 <img src="https://velog.velcdn.com/images/app_shawn/post/47d34f93-4d99-43ed-b578-e7018e9c7898/image.png" alt=""></p>
<p>Instagram Basic Display, Facebook 로그인 두 제품을 선택하고 안에 앱 번들 등을 입력해줘야하는 번거로운 작업을 완료해 두어야 한다. </p>
<p>일반 앱에서는 <img src="https://velog.velcdn.com/images/app_shawn/post/fa8f0308-10e9-4186-8ca9-679f7d26ee1b/image.png" alt="">
개발모드에서 페이스북 로그인 기능을 넣어놔도 적용이 되지 않는다. 
라이브 모드로 바꿔야하는데, 인스타그램 권한이 없기 때문에 라이브모드로 바꿔도 말짱 도루묵.</p>
<p>테스트앱에서는 개발모드에서도 모든 기능이 사용가능하다. 단,내가 관리자가 아니라면,  <img src="https://velog.velcdn.com/images/app_shawn/post/6e95a84a-386a-41a2-b7af-82a4b6b25723/image.png" alt=""></p>
<p>테스터를 추가해주어야한다. </p>
<p>다른 블로그에서 흔히 보여주는 인스타그램 테스터와는 완전 다르다.
저 테스터는 &quot;페이스북 테스터&quot; 이고, <img src="https://velog.velcdn.com/images/app_shawn/post/a61fea54-0484-49dc-b21c-b1449cb882a6/image.png" alt="">
인스타 연동을 해본적도 없는 블로그에서 보여주는 테스터는 이 인스타그램 테스터이다.</p>
<p>앱에 정식으로 연동기능을 넣으려면 인스타그램 테스터는 단 한 계정도 필요가 없다. 갖다 버려도 좋다. </p>
<p>우린 페이스북 로그인을 통해 인스타그램 정보를 가져와야 하기 때문에 &quot;페이스북 테스터&quot;로 등록해야한다. 인스타그램 OAuth 로그인이라는 것은 존재하지 않는다.</p>
<hr>
<p>위 준비물이 준비 되었다면 인스타그램과 페이스북 계정의 이해가 필요하다.</p>
<blockquote>
<h3 id="인스타그램-계정--페이스북-계정-">인스타그램 계정 == 페이스북 계정 ...?</h3>
</blockquote>
<p>메타 ( 구 페이스북 ) 은 인스타그램을 인수했다. 
그래서 인스타그램 계정과 페이스북 계정이 1대 1 대응을 이룬다고 생각할 수 있지만 그렇지 않다.</p>
<p>인스타그램 계정 여러개가 하나의 페이스북 계정에 연결된다.
페이스북 계정에 연동하려면 페이스북 페이지에 연결을 해야한다.</p>
<p>자세한 과정은 <a href="https://antennagom.com/525">https://antennagom.com/525</a> 에서 확인 할 수 있다.</p>
<p>내 인스타그램 비즈니스 계정을 페이스북 아이디에 연동을 하고나면
이제 정말 연동을 할 준비가 된 것이다.</p>
<blockquote>
<h3 id="graph-api-">Graph Api ???</h3>
</blockquote>
<p>그래프 api 는 메타에서 만든 api 이다. Rest Api 의 OverFetching 또는 UnderFetching 의 단점을 보완하는 강력한 api라고 한다.
<a href="https://youtu.be/N-81mS2vldI!">니꼬 선생님의 유튜브</a>에서 자세한 설명을 확인 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/09c52095-a274-4bc5-a609-05bb77419b06/image.png" alt=""></p>
<p>아무튼 우리는 이 graph api를 활용해서 인스타그램 정보를 가져와야한다.
<a href="https://developers.facebook.com/tools/explorer?locale=ko_KR">그래프 Api 탐색기</a>를 준비하자.<br><em>( 연동이 끝나는 시점까지 계속 사용할 예정이니 오른쪽 상단 탭에 항상 하나씩 올려놓도록 하자 )</em></p>
<p>자 그럼 정말 준비물은 끝났다.
토큰을 이해하러 가보자.</p>
<blockquote>
<h1 id="fxxxing-token">Fxxxing Token</h1>
</blockquote>
<p>나는 연동을 하는 과정에서 토큰에 대한 이해가 제일 어려웠다. 공식문서에서는 토큰을 자세하게 설명해주고 있지 않다.</p>
<h3 id="1-액세스-토큰">1. 액세스 토큰</h3>
<p>액세스 토큰 혹은 OAuth Token 은 카카오톡 로그인 네이버 로그인 처럼 일반적인 써드 파티 로그인을 했을 때 발급되는 토큰이다. 이 토큰은 로그인 기능을 제공함과 동시에 다른 여러 기능에 액세스 할수 있게 도와준다.</p>
<p>토큰을 먼저 발급 받아보자.</p>
<p>Swift Package Manager 을 사용하던, Cocoapods를 사용하던 원하는 방법으로 페이스북 SDK를 프로젝트에 설치한 후 버튼을 하나 만들자.</p>
<p><a href="https://firebase.google.com/docs/auth/ios/facebook-login?hl=ko">Firebase 공식 문서</a> 에서 잘 설명해주고 있다.</p>
<p>버튼을 만들고 나면 
<img src="https://velog.velcdn.com/images/app_shawn/post/030df66a-a0e9-42e8-a6f4-0b99142b973a/image.png" alt=""></p>
<p>요놈을 임포트 해주고, </p>
<pre><code>        let manager = LoginManager()
        manager.logIn(permissions: [&quot;public_profile&quot;, &quot;instagram_basic&quot;, &quot;pages_show_list&quot;], from: self) { result, error in
            if let error = error {
                print(&quot;Process error: \(error)&quot;)
                return
            }
            guard let result = result else {
                print(&quot;No Result&quot;)
                return
            }
            if result.isCancelled {
                print(&quot;Login Cancelled&quot;)
                return
            }

        }</code></pre><p>위의 코드와 함께 로그인을 세팅해주자.
permissions 에는 우리가 사용할 인스타그램 권한을 넣어주면 된다. 
&quot;public_profile&quot;은 페이스북 정보에 대한 접근 권한.
&quot;instagram_basic&quot;은 인스타그램 정보에대한 접근 권한. ( 팔로워 수, 이름, 닉네임 등등 에 대한 권한이다)
&quot;pages_show_list&quot;는 페이스북 페이지 목록에 접근 권한이다. (페이스북 페이지에 접근이 가능해야 페이지에 연결된 인스타그램 계정에 연동이 가능하다)</p>
<p>자 이제 액세스 토큰으로 어떻게 내 인스타그램 계정을 가져올지 고민해보자.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/7f290f96-e106-423e-9a8d-b2f974d6c4ef/image.png" alt=""></p>
<p>그래프 Api 탐색기를 켜고 오른쪽 Generate Access Token을 눌러보자
<img src="https://velog.velcdn.com/images/app_shawn/post/a00d1c2d-dad5-4c1e-8f3c-73faf6d617e3/image.png" alt=""></p>
<p>다음으로 이렇게 비즈니스 계정을 선택</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/f4533b60-2815-4b1e-b222-6b904982a690/image.png" alt=""></p>
<p>다음으로 페이스북 페이지를 선택하고</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/bf72424a-aeba-4662-8c3e-e386769dc953/image.png" alt=""></p>
<p>권한을 허용해주면 액세스 토큰이 마침내 발급된다.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/ba484228-17f1-4e53-b891-35f028b84c8d/image.png" alt=""></p>
<p>발급받은 토큰을 활용하는 방법은 다음 장에서 다뤄보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[인스타그램 연동]  Graph Api with Swift ]]></title>
            <link>https://velog.io/@app_shawn/%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EC%97%B0%EB%8F%99-Graph-Api-with-Swift</link>
            <guid>https://velog.io/@app_shawn/%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EC%97%B0%EB%8F%99-Graph-Api-with-Swift</guid>
            <pubDate>Wed, 25 May 2022 14:47:59 GMT</pubDate>
            <description><![CDATA[<p>나의 인스타그램 피드를 iOS 어플리케이션으로 가져오는 어마무시한 일을 해보자.</p>
<blockquote>
<h2 id="any-problem">Any Problem?<img src="https://velog.velcdn.com/images/app_shawn/post/8fd3d23f-34bf-472c-a07a-d3220c6fdc23/image.png" alt=""></h2>
</blockquote>
<p>정말 구글에 나와있는 대부분의 인스타그램 연동글을 본 것 같다.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/a19f1670-281c-42cd-9aab-9a12647857c4/image.png" alt=""></p>
<p>여기에 나와있는 모든 글들은 제대로 연동을 하고 있지 않다. </p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/f5bd4fa1-a3cc-4910-be7c-67496a3cbdb8/image.png" alt=""></p>
<p>대부분의 경우에서 위와 같은 테스트 사용자를 만들어서 토큰을 발급하고 피드를 받아오거나 연동하는 일을 진행했을 것이다.</p>
<p>하지만 우리가 정말 인스타그램 연동을 해야하는 어플리케이션을 앱스토어에 내야한다면?</p>
<p>여기 &quot;사용자 토큰 생성기&quot; 에 나와 있는 토큰은 도대체 어디에서 발급되는 것인가?</p>
<p>이렇게 토큰을 생성하는 일도 우리 어플리케이션 안에서 이루어져야한다.</p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/357ecbe2-2aea-40e7-ab9e-6ac9f97da6ac/image.png" alt=""></p>
<p>그래서 <a href="https://developers.facebook.com/docs/facebook-login/guides/access-tokens/get-long-lived">페이스북 개발자 페이지</a>의 DOCS를 살펴본다.
정말 놀랍게도 iOS는 Objective-C 로 작성되어있다.
이것은 고사하고, 토큰이 정말 많고 어느게 어떤 토큰인지 제대로 설명하고 있지도 않다.</p>
<p>지난 한 달 간 내가 어떻게 공식 문서없이 토큰을 발급받아 나의 어플리케이션으로 인스타그램 피드를 불러오는데 성공했는지, 그 과정을 앞으로 적어보려고 한다.</p>
<p>당신이 테스터로 등록하지 않는 방법으로 인스타그램 연동을 구현하려고 했다면
나의 블로그를 찾은 것이 신의 한수가 될 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[RxSwift] - Generic 을 활용한 리펙터링]]></title>
            <link>https://velog.io/@app_shawn/RxSwift-Generic-%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%A6%AC%ED%8E%99%ED%84%B0%EB%A7%81</link>
            <guid>https://velog.io/@app_shawn/RxSwift-Generic-%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%A6%AC%ED%8E%99%ED%84%B0%EB%A7%81</guid>
            <pubDate>Thu, 21 Apr 2022 14:41:23 GMT</pubDate>
            <description><![CDATA[<p>지난 포스트에서 Generic을 활용하여 코드를 단축하는 방법에 대해 연구해보았다.
그렇다면 이번엔 정말로 긴, 긴 코드를 줄여보자. 단순히 Decode정도가 아닌.</p>
<blockquote>
<h3 id="problem">Problem</h3>
<p>&quot;피플&quot;은 Single, Observable을 정말 많이 리턴한다.</p>
</blockquote>
<p>코드를 살펴보자</p>
<pre><code>    func fetchFive() -&gt; Single&lt;[CalendarModel]&gt; {


        return Single.create() { observable in


            let urlString = &quot;\(self.baseUrl)/api/v1/account/calendar/after/test?minusDay=5&amp;accountUuid=\(UserDatabaseManager.shared.myUserModel!.uuid)&quot;

            guard let url = URL(string: urlString) else {
                observable(.error(DatabaseFetchingErrorType.urlFailedError))
                return Disposables.create()
            }

            var request = URLRequest(url: url)
            request.httpMethod = &quot;GET&quot;
            request.setValue(&quot;application/json&quot;, forHTTPHeaderField: &quot;Content-Type&quot;)
            request.setValue(UserDatabaseManager.shared.jwtToken, forHTTPHeaderField: &quot;X-AUTH-TOKEN&quot;)
            request.timeoutInterval = 10

            AF.request(request).responseData { response in
                switch response.result {
                case .success(let data):
                    if let calendarModelArray = self.decode(jsonData: data, type: [CalendarModel].self) {
                        observable(.success(calendarModelArray))
                        print(&quot;[Calendar 5] fetching 성공&quot;)
                    } else {
                        observable(.success([]))
                    }

                case .failure(let err):
                    print(&quot;🚫[Calendar 5] 에러 \(err)&quot;)
                    observable(.error(DatabaseFetchingErrorType.fetchingFailedError))
                }
            }

            return Disposables.create()
        }
    }</code></pre><p>Calendar Manager의 fetchFive라는 함수이다.
최근 5개의 CalendarModel을 리턴한다. Single 로 리턴한다.</p>
<p>다른 코드들 또한 이러한 형식을 많이 따른다. 
request에 필요한 파라미터를 담고, 리퀘스트를 디코딩해서 결과를 얻어 Bind한다.</p>
<p>그런데, 벌써 코드가 30~40줄 정도가 되는 것 같다. </p>
<p>이러한 함수가 20 ~ 30개가 되니 코드의 양은 너무 방대해져버렸다.</p>
<p>Param과 Path를 인자로 받아 원하는 타입의 Single을 리턴하는 함수를 만들어보자. </p>
<pre><code>func single&lt;T: Codable&gt;(path: String, type: T.Type, params: [String: Any]?) -&gt; Single&lt;T&gt;</code></pre><p>선언은 이렇게한다.
path 와 type, 그리고 파라미터는 없을 수도 있으니까 옵셔널로 받는다.</p>
<pre><code>            var queryParams = &quot;&quot;
            if let params = params {
                queryParams = params.map { k, v in &quot;\(k)=\(v)&quot; }.joined(separator: &quot;&amp;&quot;)
            }

            var fullPath = path.hasPrefix(&quot;http&quot;) ? path : self.baseUrl + path
            if queryParams.count &gt; 0 {
                fullPath += &quot;?&quot; + queryParams
            }</code></pre><p>파라미터에대한 처리는 위처럼 해준다.</p>
<p>&amp;로 key=value를 묶어 마지막에 path 뒤에 ? 를 붙여 fullPath를 완성한다.</p>
<pre><code>            var request = URLRequest(url: url)
            request.httpMethod = &quot;GET&quot;
            request.setValue(&quot;application/json&quot;, forHTTPHeaderField: &quot;Content-Type&quot;)
            request.setValue(UserDatabaseManager.shared.jwtToken, forHTTPHeaderField: &quot;X-AUTH-TOKEN&quot;)
            request.timeoutInterval = 10

            AF.request(request).responseData { response in
                switch response.result {
                case .success(let data):
                    if let returnData = self.decode(jsonData: data, type: type.self) {
                        observable(.success(returnData))
                        print(&quot;[\(type)] fetching 성공&quot;)

                    } else {
                        observable(.error(DatabaseFetchingErrorType.encodingFailedError))
                    }

                case .failure(let err):
                    print(&quot;🚫[\(type)] 에러 \(err)&quot;)
                    observable(.error(DatabaseFetchingErrorType.fetchingFailedError))
                }
            }
            return Disposables.create()</code></pre><p>그리고 나서는 똑같이 함수를 적어 리턴해주면 제네릭 함수가 완성된다.</p>
<blockquote>
<h3 id="결론">결론</h3>
</blockquote>
<pre><code>    func fetchFive() -&gt; Single&lt;[CalendarModel]&gt; {
        return single(path: fivePath, type: [CalendarModel].self, params: [&quot;accountUuid&quot;: UserDatabaseManager.shared.myUserModel!.uuid])
    }</code></pre><p>세 줄로 줄었다.</p>
<p>하지만 지금당장은 Single 만 사용을 해보았고, 다른 복잡한 형태의 제네릭 함수구현을 위해서는 조금 더 공부를 해보아야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[RxSwift] - Generic 을 활용한 리팩터링]]></title>
            <link>https://velog.io/@app_shawn/RxSwift-Generic-%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81</link>
            <guid>https://velog.io/@app_shawn/RxSwift-Generic-%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81</guid>
            <pubDate>Thu, 21 Apr 2022 10:11:32 GMT</pubDate>
            <description><![CDATA[<p>피플 앱 (이하 &quot;피플&quot;) 을 정신없이 만들다보니 계속 신경쓰이는 부분이 있었다. </p>
<p>&quot;피플&quot;은 10개정도의 Manager 들에 의해 BackEnd 소스들과 http 통신이 이루어진다. 가장 많은 코드를 가진 Manager은 역시 사연부분을 담당하는 DontionDatabaseManager 이다. <img src="https://velog.velcdn.com/images/app_shawn/post/b7a51c71-1e60-4596-ac88-00b4abb10497/image.png" alt=""></p>
<p>리팩터링을 진행하기 전까지 약 870줄 정도의 코드를 가진 Massive한 파일이다.</p>
<blockquote>
<h3 id="1-decode">1. Decode</h3>
</blockquote>
<p>HTTP 통신을 많이하는 앱을 만들어보았다면 Decode를 해야하는 모델이 상당히 많다는 것을 알고 있을 것이다. 디코딩을 매우 편리하게 도와주는 <a href="https://quicktype.io/">퀵타입</a>의 존재도 분명히 알고 있을 것이다. </p>
<pre><code>    private func decodeNoticeModel(of jsonData: Data) -&gt; [NoticeModel]? {
        do {
            let noticeArray = try JSONDecoder().decode([NoticeModel].self, from: jsonData)
            return noticeArray
        } catch {
            print(&quot;🚫 [공지] 디코딩 실패 !:\(error)&quot;)
            return nil
        }
    }</code></pre><p>이전까지는 이렇게 각 모델마다 디코딩을 해주는 코드를 작성해 디코딩을 해주었다. 
하지만 하나 둘 쌓이는 모델들... </p>
<p>이렇게 디코딩을 하게되면 모델 수만큼 디코딩을 해주어야 하고 그만큼 코드의 양도 방대해지는 불상사가 일어나게 된다. (지금까지는 이런 멍청한 방법을 사용해왔다.)</p>
<p>나름 중복코드를 줄이고자 10개 정도의 Manager들이 상속받고 있었던 CommonBackendType에 공통 Decode 코드를 추가해주었다.</p>
<pre><code>    func decode&lt;T: Decodable&gt;(jsonData: Data, type: T.Type) -&gt; T? {
        do {
            let data = try JSONDecoder().decode(type.self, from: jsonData)
            return data
        } catch {
            return nil
        }
    }</code></pre><p>코드를 잘 살펴보면 제네릭 선언부의 T를 제외하고도 returnType이라는 파라미터에 타입자체를 넣어주었다. 
단톡방에 물어보니 , 꺽새 안에 들어가는 T는 제네릭 함수에 T의 제약만을 걸어주는 것이지 T에 대한 제네릭 함수이다 라는 것을 알려주지 않는다고 한다.</p>
<p>예를 들어, returnType을 적지 않고 그대로 T의 옵셔널을 리턴해준다면 decode함수 선언부에서는 에러가 나지 않지만, decode를 가져다 사용하는 부분에서 분명 에러가 날 것이다. </p>
<p>generic function을 specify 하라는 에러가 나올 것이다. 
T의 타입을 정확히 규정해달라는 말이다. </p>
<blockquote>
<h3 id="bad">Bad</h3>
</blockquote>
<pre><code>    func test&lt;T: Decodable&gt;(data: Data) -&gt; T? {
        do {
            let data = try JSONDecoder().decode(T.self, from: data)
            return data
        } catch {
            return nil
        }
    }</code></pre><p>이런 함수를 사용한다고 해보자</p>
<pre><code>if let returnData = self.test(jsonData: data) </code></pre><p>data를 나쁜 함수로 디코딩을 해준다면, </p>
<p><img src="https://velog.velcdn.com/images/app_shawn/post/8532b5d9-7c2e-4927-96ad-cabb1ed458bc/image.png" alt="">
정확히 T를 추론할 수 없다고 나온다. 
(Generic paramter &#39;T&#39; could not be inferred)</p>
<p>그렇다면 타입을 직접 입력해주면 ? </p>
<pre><code>if let returnData = self.test&lt;T&gt;(jsonData: data) {</code></pre><p><img src="https://velog.velcdn.com/images/app_shawn/post/455acf41-2721-4e01-8027-3143bbb0c964/image.png" alt=""></p>
<p>Specialize 할 수 없다는 에러를 뱉는다.
(Cannot explicitly specialize a generic function)</p>
<blockquote>
<h3 id="결론">결론</h3>
</blockquote>
<p>제네릭 함수를 만들고 리턴을 제네릭으로 해야한다면 정확한 타입을 파라미터로 받아주자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] 입문하기 -2]]></title>
            <link>https://velog.io/@app_shawn/Flutter-%EC%9E%85%EB%AC%B8%ED%95%98%EA%B8%B0-2</link>
            <guid>https://velog.io/@app_shawn/Flutter-%EC%9E%85%EB%AC%B8%ED%95%98%EA%B8%B0-2</guid>
            <pubDate>Mon, 03 Jan 2022 13:52:03 GMT</pubDate>
            <description><![CDATA[<h2 id="플러터-설치">플러터 설치.</h2>
<blockquote>
<h3 id="1-개발-환경-세팅">1. 개발 환경 세팅</h3>
</blockquote>
<p>시작이 반이라고, 때려치고 싶은 부분이다.</p>
<p><a href="https://docs.flutter.dev/get-started/install/macos">https://docs.flutter.dev/get-started/install/macos</a> 플러터 홈페이지에서 플러터를 다운받고</p>
<p>Android Studio, Xcode 에서 시뮬레이터들을 설치해주자.
M1 칩이면 Android 시뮬레이터는 arm64 로 된 것들을 설치하면 된다.</p>
<p>플러터 개발은 Jetbrains 의 Intellij 나 Android Studio 를 통해 하면 된다.
안드로이드 스튜디오는 뭔가 구려보여서 Intellij 를 선택했다. ( 둘이 매우 비슷하다 )</p>
<p>plugin에서 플러터를 설치해주면 끝난다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/d36ab9aa-9c95-4c45-9018-8a7eaea60f58/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-01-03%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.36.58.png" alt=""></p>
<p>New Project 위에 Flutter 을 설치한 경로를 설정해주고, 알맞은 위치에 새로운 플러터 프로젝트를 만드는 것으로 환경설정은 끝난다.</p>
<blockquote>
<p>*<em>내 기억으로는 React-Native Cli 환경설정이 백배는 어렵다 *</em></p>
</blockquote>
<blockquote>
<h3 id="2-widget">2. Widget</h3>
</blockquote>
<p>Flutter 개발이 Swift UI 와 굉장히 유사하다고 느낀 점이 있다.
Swift 는 ViewController 라는 하나의 화면을 기준으로 개발이 이루어진다면 ( 이 또한 아키텍쳐에 따라 매우 상이하다. Ribs 아키텍쳐는 완전 다른 개념을 갖고 개발한다. )
SwiftUI 는 말 그대로 UI 를 쌓는 식으로 개발이 이루어지는데, Flutter 역시 마찬가지다.</p>
<p>Swift에 View 가 있다면 Flutter 에는 Widget이 있다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/6fc59166-939e-4324-9faf-ec00cd0c2b0f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-01-03%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.40.48.png" alt=""></p>
<p>이 페이지에는 30개가 넘는 Widget 이 쌓여있는 형태이다.</p>
<p>가장 먼저 앱을 구성하는 MaterialApp 위젯 .</p>
<p>화면을 상 중 하로 나누는 Scaffold 위젯</p>
<p>맨위에 AppBar 위젯 안에 Text위젯, IconButton 위젯</p>
<p>ListView 위젯 안에 ListTile 위젯 등등</p>
<p>모든 것을 위젯이라고 부른다.</p>
<p><del>기본 디자인이 매우 구글틱한 것은 매우 마음에 안들지만 나중에 바꿀 수 있다.</del></p>
<p>중요한 위젯들을 공부해보자.</p>
<ul>
<li><h2 id="scaffold">Scaffold</h2>
<p>화면을 상 중 하로 나눈다. 
요즘 어플리케이션들은 대부분 이러한 형태를 띈다.
맨 위에 들어갈 수 있는 AppBar,
가운데에 들어가는 Body,
맨 아래에 들어가는 BottomNavigationBar 등이 있다.</p>
</li>
<li><h2 id="sizedbox--vs-container">SizedBox  Vs Container</h2>
<p>아직은 큰 차이는 모르겠지만 Container 가 좀 더 무거운 위젯이라고 한다.
남용하면 SizedBox 를 쓰라는 Lint 가 나온다.
크기와 관련된 width, height 등을 설정하지 않으면,
SizedBox : child 에 맞춰 크기가 정해진다.
Container: 최대 크기로 늘린다.
이러한 차이가 있다.</p>
</li>
<li><h2 id="buttons">Buttons</h2>
<p>TextButton
IconButton
ElevatedButton
여러 버튼들이 있지만 이런 세개의 버튼이 있고, 각 버튼은 종류에 따라 child 가 달라진다.
TextButton 의 child 는 당연히 Text 위젯이 된다.
child 와 onPressed 함수 등을 파라미터로 넣을 수 있다.
Dart 언어는 Swift 처럼 익명함수 ( 클로저 ) 를 지원한다.
onPressed: () { function(); } 이 가능하다.</p>
</li>
<li><h2 id="appbar">AppBar</h2>
<p>화면 맨 위에 들어가는 길다란 Bar 이다.
Title : 중앙 제목
Leading : 왼쪽 아이콘
Actions: [우측 아이콘들]</p>
</li>
<li><h2 id="bottomnavigationbar">BottomNavigationBar</h2>
<p>Items 들로 구성된다. 아래에 들어가는 탭 아이콘이라고 생각하면 된다.
아이템이 하나면 에러, label 이 하나라도 비어있으면 안된다.</p>
</li>
</ul>
<blockquote>
<h3 id="3-custom-widget">3. Custom Widget</h3>
</blockquote>
<p>위에 2번에서 공부한 내용정도면 다른 위젯들을 검색해보고 공부하는데에 있어서 전혀 문제되지 않는다.</p>
<p>하지만 항상 Custom , 내 맛대로 커스텀해서 사용하는 것이 훨씬 어렵고, 결국 사용해야 하는 것은 Custom Widget이 된다.
커스텀 위젯을 공부하기전에 State라는 개념을 공부해야한다.
이것이 Native 어플리케이션 개발과 아주 큰 차이라고 생각한다.
그러면서도 아주 편리한 기능이다.
다음 포스트에서 State 에 대해 공부해보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] 입문하기 - 1]]></title>
            <link>https://velog.io/@app_shawn/Flutter-%EC%9E%85%EB%AC%B8%ED%95%98%EA%B8%B0-1</link>
            <guid>https://velog.io/@app_shawn/Flutter-%EC%9E%85%EB%AC%B8%ED%95%98%EA%B8%B0-1</guid>
            <pubDate>Mon, 03 Jan 2022 13:32:17 GMT</pubDate>
            <description><![CDATA[<h2 id="1--flutter-을-공부하게-된-이유">1.  Flutter 을 공부하게 된 이유...</h2>
<p>2020년 10월에 Android 공부를 시작으로 입문하게 된 어플리케이션 공부.
2021년 2월에는 맥북프로를 구매하며 iOS에 입문하게 되었고 6월에는 첫 어플리케이션을 배포했다.
하지만 분명 나는 아직 대학생 신분으로, 부족한 경험을 채우기 위해서는 어찌 됐든 경험을 해보는 것이 중요했고, iOS와 안드로이드를 모두 해결하기 위해 결국 12월에는 Flutter을 공부하게 됐다.</p>
<hr>
<h2 id="2-react-native-vs-flutter">2. React Native vs Flutter</h2>
<p>Meta vs Google 이라고 봐도 무방할 정도의 라인업. 
하이브리드 어플 계의 부동의 투탑이라고 생각한다.
하나를 공부하기 시작하면 다른쪽으로 넘어가는 것이 쉽지 않기에
고민을 거듭하다 결국 Flutter 로 정하게 되었다.</p>
<ul>
<li><h3 id="javascript--dart-">JavaScript ..? Dart ..?</h3>
iOS 를 공부하며 swift 로 코딩테스트를 수차례 통과해봤는데, 
코딩테스트 연습 사이트인 백준에서 골드 4 ~ 5정도의 문제나, 프로그래머스에서 2레벨정도의 문제를 풀 수 있는 능력이 있다면 ( 어떤 언어로든 ) 두 언어를 배우는 데에는 전혀 문제가 없을 것이다. 
코딩테스트를 볼 주  언어가 되지 않는다면, 어플리케이션을 만들 때 쓰이는 문법은 아주 한정적이다.</li>
</ul>
<blockquote>
<p>ReactNative 와 Flutter 을 비교하는 많은 블로그들에서 <U><strong>언어가 다르다</strong></U>  는 것을 강조하고 있지만, 이것은 다른 언어를 한번도 배워보지 않은 사람들에게 해당한다고 생각한다.
<strong>둘 다 별로 어렵지 않다.</strong></p>
</blockquote>
<hr>
<h2 id="3-react-native를-선택하지-않은-이유">3. React Native를 선택하지 않은 이유</h2>
<p>먼저 내가 React Native에 혹했던 점을 알아보자.</p>
<ul>
<li><h3 id="두-가지의-개발방법">두 가지의 개발방법</h3>
<p>Expo 를 이용한 방식과 , Cli 를 사용한 방식 두 가지가 있다.
Expo는 조금 더 초보자를 위한 개발방법, Cli 는 좀 더 숙련자를 위한 방법이다.</p>
</li>
<li><h3 id="rn--native">RN + Native</h3>
<p>JS 코드에 Native 코드를 추가해서 앱을 만들 수가 있다.</p>
</li>
</ul>
<blockquote>
<p><strong>나는 두 번째 RN + Native 이 Native앱 을 공부한 사람으로써 엄청난 어드밴티지로 다가왔다.</strong></p>
</blockquote>
<ul>
<li><h3 id="너무-오래걸리는-빌드">너무 오래걸리는 빌드</h3>
시뮬레이터 키는데 너무 오래걸리고 많이 공부해보지 않아서 그럴지도 모르지만 , 버전에 따른 에러가 엄청 많은 것 같다.</li>
</ul>
<blockquote>
<p>앱 개발자로서 고쳐야한다고 생각하지만, 나는 앱을 빌드를 정말 자주하면서 개발한다.
RN 의 빌드 속도는 ** 정말 매우, 매우 느리다. **</p>
</blockquote>
<hr>
<h2 id="4-flutter">4. Flutter</h2>
<p>구글에서 만든 Dart 언어를 사용한다.
2020년 가장 인기 없는 언어 중 하나로 뽑혔다는데, 언어는 별로 중요하지 않다고 생각한다. 개발자의 역량이 중요하지</p>
<p>앞으로 내가 포스팅하는 글에는 JetBrains 의 Intellij IDE 를 이용한다.
프로버전은 유료지만.. 기분이라도 내고 싶었다.</p>
<p>코딩애플의 강의를 들으며 입문하였고, 이 강의는 초심자를 대상으로 한 강의이지만 , 나는 한번 앱을 공부해본 사람의 입장에서 궁금했던 점을 직접 찾아 덧붙여보았다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/3cad15fe-c911-42fe-83c8-3fff2fe9ebf6/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-01-03%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.30.45.png" alt=""></p>
<blockquote>
<p>열심히 공부해보자 !</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] - AWS 와 앱 연동하기]]></title>
            <link>https://velog.io/@app_shawn/iOS-AWS-%EC%99%80-%EC%95%B1-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@app_shawn/iOS-AWS-%EC%99%80-%EC%95%B1-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 21 Nov 2021 21:33:31 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="ios에서-db로-aws를-활용해보자">iOS에서 DB로 AWS를 활용해보자</h2>
</blockquote>
<h4 id="주변--앱개발-공부를-하는-많은-사람들이-초반엔-google-cloud-platform--gcp--를-외부-db로-활용한다">주변  앱개발 공부를 하는 많은 사람들이 초반엔 Google Cloud Platform ( GCP ) 를 외부 DB로 활용한다.<img src="https://images.velog.io/images/app_shawn/post/0a5cc674-1acd-44a9-aa21-e52e38f90aaa/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-22%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%206.08.11.png" alt=""></h4>
<p>-Realtime database-</p>
<p> 나도 GCP를 1년정도 사용해본 결과 정말 어플리케이션 친화적이고 초보자들이 활용하기에 안성맞춤이라는 생각이 들었다. </p>
<p>실시간으로 눈에 보이는 데이터들의 변화과정은 어떠한 DB에서도 제공되지 않는 아주 특별한 기능이라고 생각한다. 실시간으로 수정하고, 실시간으로 확인할 수 있다.</p>
<p>하지만 얼마전 헬로비즈 ( 외주업체 ) 의 CTO 분에게 컨설팅을 받은 결과 GCP 도 정말 좋은 플랫폼이지만 결국 현업으로 뛰어들게 된다면 모두 AWS 를 활용한다는 조언을 해주셨다. 
또한, 막상 배워놔도 나쁠것이 전혀 없다고...
회사들이 AWS 를 활용하는 가장 큰 이유는 바로 &quot;돈&quot; 이다.</p>
<p>GCP 가 AWS 에 비해 가격이 현저히 높다고 한다.
사실 나같은 초보 앱 개발자들이 가격의 차이를 느낄만큼 큰 데이터를 다룰일이 없었기에 전혀 생각하지 못했던 부분이다. ( 사실 아직 무료단계에서도 벗어나지 못한 양의 데이터만 다루고 있었다 )</p>
<p>분명 앱 개발을 초보 단계에서 멈출 생각이 없기에 이번 기회에 진지하게 AWS 를 어플리케이션에 테스트로 적용해보자 라는 생각으로 공부를 시작했다. </p>
<p>공부하면서 느꼈지만 확실히 AWS 는 앱 친화적이라는 생각은 들지 않는다. HostPage 를 웹에 두고, 앱은 Sub 로 활용하는 회사들에서 많이 활용하지 않을까 싶다.</p>
<blockquote>
<h3 id="aws-cognito-를-이용하여-로그인-기능을-추가해보자">AWS Cognito 를 이용하여 로그인 기능을 추가해보자</h3>
</blockquote>
<p>사실 AWS 홈페이지에 가보면 GCP 만큼 자세하지는 않지만.. 꽤나 자세하게 로그인, 로그아웃, 회원가입 등 Auth 기능을 추가하는 방법을 알려주고 있다. 가이드라인에도 잘 나와있다. 
그런데 이상하게 우리나라 페이지에는 SwiftUI 를 이용한 가이드라인이 소개되어있다.
나는 당연히 Swift로 설명해보려고 한다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/f94c0c0a-9066-44dc-b366-d0dff0c55cc9/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-22%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%206.19.32.png" alt=""></p>
<p>AWS 가이드라인에는 간단한 Todo 앱을 AWS Cognito, DB, Storage 에 저장, 업데이트, 삭제 등을 하며 소개하고있다.</p>
<h4 id="1-userdata-클래스를-만들고-note-클래스도-만들어주자">1. UserData 클래스를 만들고 Note 클래스도 만들어주자.</h4>
<p><img src="https://images.velog.io/images/app_shawn/post/721b6677-4547-4681-8d6e-1d90aadab6b8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-22%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%206.20.37.png" alt=""></p>
<h4 id="2-터미널에서-amplify를-활용하기-위해-amplify-cli-를-설치하자">2. 터미널에서 Amplify를 활용하기 위해 amplify CLI 를 설치하자</h4>
<p><img src="https://images.velog.io/images/app_shawn/post/28fbe9da-58b5-4cc6-b996-8d70b51c54eb/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-22%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%206.22.46.png" alt=""></p>
<p>npm이 설치되어있지 않다면 다른 방법도 많다.
이 방법으로 설치되지 않는다면 무적의 sudo 를 사용하는 것을 추천한다.</p>
<h4 id="3-디렉토리로-가서-amplify-init">3. 디렉토리로 가서 Amplify init</h4>
<p><img src="https://images.velog.io/images/app_shawn/post/e7a57855-740c-435a-a9df-0af25bf06d42/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-22%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%206.24.06.png" alt=""></p>
<p>git 이나, cocoapod 과 비슷하게 amplify init 으로 초기화한다.</p>
<p>여기서 중요한 점. 처음 aws 를 이용하시는 분들은 profile 이 없다고 메세지가 나온다.</p>
<p>그럴 땐 profile을 생성해주어야한다.</p>
<p>aws configure --profile #name 을 통해 생성하고 나서 </p>
<p><a href="https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/cli-configure-quickstart.html">AWS Profile 설정 가이드라인</a>으로 가서 지역등록과 인증 키를 등록해주어야 앞으로 과정이 정상적으로 작동될 것이다.</p>
<h4 id="4-pod-init-pod-install-">4. pod init, pod install !</h4>
<p><img src="https://images.velog.io/images/app_shawn/post/520cddd1-440b-4678-a29e-09fa8a8a802a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-22%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%206.28.04.png" alt=""></p>
<p>코코아팟에 amplify 와 tools 를 넣고 인스톨해주자.</p>
<h4 id="5-json-파일을-넣어주자">5. json 파일을 넣어주자</h4>
<p>지금까지 잘 따라왔다면 아래처럼 두개의 json 파일이 생긴다. 
프로젝트 파일안에 넣어주자</p>
<p><img src="https://images.velog.io/images/app_shawn/post/276a9129-d30e-4a6d-b2bb-0c412369c05a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-22%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%206.29.28.png" alt=""></p>
<p>이 두 파일이 나오지 않았다면 AWS Profile 이 제대로 되지 않아 생기지 않았을 확률이 매우 높다.
다시 한번 위의 과정을 천천히 해보길 바란다.</p>
<h4 id="6-backend-파일-생성">6. Backend 파일 생성</h4>
<p>Backend.swift 라는 파일을 만들고 아래를 복붙 해주자.</p>
<pre><code>import UIKit
import Amplify

class Backend {
    static let shared = Backend()
    static func initialize() -&gt; Backend {
        return .shared
    }
    private init() {
      // initialize amplify
      do {
        try Amplify.configure()
        print(&quot;Initialized Amplify&quot;);
      } catch {
        print(&quot;Could not initialize Amplify: \(error)&quot;)
      }
    }
}</code></pre><h4 id="7-appdelegate-설정">7. Appdelegate 설정</h4>
<pre><code>    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool {
        // Override point for customization after application launch.

        // initialize Amplify
        let _ = Backend.initialize()

        return true
    }</code></pre><p>didFinishLaunchingWithOptions 에는 항상
뭔가가 들어간다.</p>
<hr>
<p>겉으로는 쉬워보이지만 사실 Profile 에서 엄청난 삽질을 했다는...
다음 포스트에서는 로그인과 로그아웃 기능에 대해 살펴보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] - 백준 1912 연속 합]]></title>
            <link>https://velog.io/@app_shawn/Swift-%EB%B0%B1%EC%A4%80-1912-%EC%97%B0%EC%86%8D-%ED%95%A9</link>
            <guid>https://velog.io/@app_shawn/Swift-%EB%B0%B1%EC%A4%80-1912-%EC%97%B0%EC%86%8D-%ED%95%A9</guid>
            <pubDate>Thu, 21 Oct 2021 07:02:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="연속-합-문제를-swift로-풀어보자">연속 합 문제를 Swift로 풀어보자</h2>
</blockquote>
<p><img src="https://images.velog.io/images/app_shawn/post/79e6b3c6-ad16-4648-b0fc-8b4d07201b63/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.47.19.png" alt=""></p>
<p>이 문제를 1차원 적으로 접근해보자.</p>
<p>for 문을 두번 돌려서 문제를 푼다고 생각 할 것이다.</p>
<p>예제 ( 10, -4, 3, 1, 5, 6, -35, 12, 21, -1 ) 을 예로 들면, </p>
<p>10 부터 -1 까지 돌려가며 최댓값을 저장하고,</p>
<p>-4 부터 -1 까지 돌리며 최댓값을 저장하고,</p>
<p>3 부터 -1, 1부터 -1 .... 그렇게 n * (n + 1) / 2 만큼 배열을 돌리며 최댓값을 구할 것이다.</p>
<blockquote>
<h3 id="dp를-사용해보자">dp를 사용해보자</h3>
</blockquote>
<p>한번만 for문을 돌려서 풀 수 있는 방법을 고민해보자.</p>
<p>먼저 for 문을 돌리기 전 , 모든 숫자가 음수라면, 즉 원소 중 최댓값이 음수라면 정답은 음수가 될 것이다.<img src="https://images.velog.io/images/app_shawn/post/c4c10011-d3f2-43a2-bd41-e38d32939368/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.51.38.png" alt=""></p>
<p>ans = numbers.max()! , 그리고 ans &lt; 0 이면 print(ans) 로 솔루션을 종료한다.</p>
<p>이러한 특수상황이 아닐 때를 고민해보자.</p>
<hr>
<blockquote>
<p>1</p>
</blockquote>
<ul>
<li>10</li>
<li>tmp = 10</li>
<li>ans = 21</li>
</ul>
<p>먼저 10 이 나온다.</p>
<p>10은 양수이다. 현재 ans 에는 21 ( numbers.max()!) 이 저장되어있다.
tmp 에 10 을 더해준다</p>
<hr>
<blockquote>
<p>2</p>
</blockquote>
<ul>
<li>10 -4</li>
<li>tmp = 6</li>
<li>ans = 21</li>
</ul>
<p>다음에 -4 가 나온다.
-4는 음수이다. 따라서
-4 부터 시작하는 연속 된 수들의 합은, 10부터 시작하는 연속된 수들의 합보다 클 수가 없다.
따라서 tmp 에는 -4, 10 -4  =6 중 더 큰 수인 6이 들어간다.</p>
<hr>
<blockquote>
<p>3</p>
</blockquote>
<ul>
<li>10 -4 3</li>
<li>tmp = 9</li>
<li>ans = 21</li>
</ul>
<p>다음은 3이 나온다.
10 -4 + 3 ,  -4 + 3 , 3 중 가장 큰 9 가 tmp 에 들어간다. 아직 ans 이 tmp 보다 크다.</p>
<hr>
<p>여기까지 했으면 감이 잡힌다.</p>
<p>어찌되었던 numbers[0] 부터 numbers[i] 까지의 합이 numbers[i] 보다 크다면, 
앞으로도 연속된 수의 합은 전자가 더크다.</p>
<blockquote>
<h2 id="코드">코드</h2>
</blockquote>
<p><img src="https://images.velog.io/images/app_shawn/post/9c078a5a-4892-4d25-b54f-23007a4f37bc/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.02.23.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift - 엑셀 xls 데이터를 json으로 바꿔보자]]></title>
            <link>https://velog.io/@app_shawn/Swift-%EC%97%91%EC%85%80-xls-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-json%EC%9C%BC%EB%A1%9C-%EB%B0%94%EA%BF%94%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@app_shawn/Swift-%EC%97%91%EC%85%80-xls-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-json%EC%9C%BC%EB%A1%9C-%EB%B0%94%EA%BF%94%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Wed, 06 Oct 2021 11:16:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="엑셀-데이터를-json-으로-변환해보자">엑셀 데이터를 json 으로 변환해보자</h3>
</blockquote>
<p>데이터를 다루다 보면 절대로 빠지지 않는 형식이 몇 가지 있다.
그중 하나는 xls 엑셀 데이터일 것이고 , <img src="https://images.velog.io/images/app_shawn/post/528de6b5-3bca-46ee-a913-66ed89d26e0f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-06%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.10.27.png" alt=""></p>
<p>또 하나는 json 데이터 일 것이다.</p>
<p>그렇다면 이 엑셀 데이터를 json 으로 변환하는 방법을 알아보자.</p>
<h3 id="1-xls---json-dictionary">1. xls -&gt; json Dictionary</h3>
<p>먼저 </p>
<p><a href="http://shancarter.github.io/mr-data-converter/">http://shancarter.github.io/mr-data-converter/</a></p>
<p>로 이동하자.</p>
<p>이 사이트에서, 데이터를 xls 데이터를 json Dictionary로  변환이 가능하다.
<img src="https://images.velog.io/images/app_shawn/post/08c2a7a8-0404-48f5-bc32-68f687c7308e/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-06%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.12.26.png" alt=""></p>
<h3 id="2-json-dictionary-에-맞는-클래스-형식-파악하기">2. json Dictionary 에 맞는 클래스 형식 파악하기</h3>
<p><a href="https://app.quicktype.io/">https://app.quicktype.io/</a>
로 이동해서 방금 구해온 데이터를 복사 붙여넣기를 하면, <img src="https://images.velog.io/images/app_shawn/post/1c617b8e-a757-4768-84a5-944ded4190b5/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-06%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.13.48.png" alt=""></p>
<p>어떤 식으로 클래스를 구성해야 할 지 알 수 있다.</p>
<h3 id="3-xcode-에서-jsondecoder-로-parse">3. Xcode 에서 jsonDecoder 로 parse</h3>
<pre><code>                    if let jsonData = try? String(contentsOf: url!).data(using: .utf8) {
                        let decoder = JSONDecoder()

                        let itemArr = try! decoder.decode(ItemArr.self, from: jsonData)</code></pre><p>jsonDecoder을 이용해서 typeAlias로 되어있는 Welcome으로 디코딩을 해주면 
우리가 쉽게 사용할 수 있는 WelcomeValue 라는 클래스로 변환이 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RxSwift - 로그인 시스템]]></title>
            <link>https://velog.io/@app_shawn/RxSwift-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C</link>
            <guid>https://velog.io/@app_shawn/RxSwift-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C</guid>
            <pubDate>Mon, 19 Jul 2021 07:50:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="반응형-프로그래밍으로-로그인-화면을-만들어보자">반응형 프로그래밍으로 로그인 화면을 만들어보자.</h3>
</blockquote>
<p><img src="https://images.velog.io/images/app_shawn/post/be0fd1ba-1001-4647-ae03-9af774631586/simulator_screenshot_908A8CDB-95C4-4C7E-AD6B-2438D9B085BF.png" alt=""></p>
<p>이 화면을 구현해보자.</p>
<p>Email은 &quot;@&quot; 와 &quot;.&quot; 이 들어가있어야 빨간색 점이 사라지며 Valid 되고,
password는 총 6자 이상이어야 Valid 되며 빨간 점이 사라진다.</p>
<p>그리고 Email, Password 둘다 Valid 되어 빨간점이 없어야
LogIn 버튼이 활성화 Enable 된다.</p>
<h2 id="1-rxswift-없이">1. RxSwift 없이</h2>
<p>내가 만약 RxSwift를 알지 못했더라면, 
일단 ViewController 에 TextFieldDelegate를 확장해준 후,</p>
<p><img src="https://images.velog.io/images/app_shawn/post/64955e4a-a0f3-438a-bc17-966606044853/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.08.17.png" alt=""></p>
<p>아래와 같이 textFieldDidEndEditing 에서 처리를 해줄 것 같다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/12531cb8-6864-47a4-899f-ac71b3667230/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.09.38.png" alt=""></p>
<p>Delegate 또한 ReactiveX 와 같이 어느정도 실시간 데이터 처리를 해줄 수 있기 때문.
그렇다면 LoginButton 의 Enabled 여부는 어떻게 해줄까?
<img src="https://images.velog.io/images/app_shawn/post/1a8021f3-521e-434f-9c2e-74baa47f837e/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.13.50.png" alt=""></p>
<p>역시, textFieldDidEndEditing 안에 조건을 걸어 
Email, Password 둘다 Valid 하다면 enable 해주면 된다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/b9eafea1-8820-4ec4-b165-3108bb493644/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.16.34.png" alt=""></p>
<p>Delegate을 걸어주는 것은 항상 잊지 말자!</p>
<p><img src="https://images.velog.io/images/app_shawn/post/f3321e18-76e3-470b-997e-c4da8bd12bd8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.18.40.png" alt=""></p>
<p>이렇게 하면 위처럼 어느정도 로그인이 구현되었음을 알 수 있다.</p>
<p>하지만, Reactive를 사용하지 않고 작성한 <img src="https://images.velog.io/images/app_shawn/post/9658a83c-3e6b-4cda-9ff2-cd22cdfcddb5/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.20.35.png" alt=""></p>
<p>이 코드는 치명적인 단점이 있다.</p>
<p>textFieldDidEndEditing 즉, textField의 Editing이 끝난 시점에서 함수가 실행된다는 뜻인데, Editing 이 끝난다는 것은 커서를 다른쪽으로 옮겼을 때 라는 말이 된다. 따라서 실시간처리로 볼 수 있지만 완벽한 실시간 데이터 처리라고는 할 수 없다.</p>
<p>그렇다면, TextFieldDelegate 에 더 많은 override 함수 안에서 해답을 찾을 수 있지 않을까?<img src="https://images.velog.io/images/app_shawn/post/0d882618-9397-45b7-bf8d-9821920d9bfa/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.22.56.png" alt=""></p>
<p>하고 찾아봤지만 역시 녹록치않다.</p>
<p>이렇게 우리는 RxSwift에 발을 담구게 된다.</p>
<h2 id="2-mvvm-무시-rxswift">2. MVVM 무시, RxSwift</h2>
<p>우리가 생각해야할 객체는 총 다섯 개다.
idTextField, pwTextField, idValid, pwValid, LoginButton</p>
<p>먼저 idTextField를 Observe 해보자.<img src="https://images.velog.io/images/app_shawn/post/4d32670b-e5e9-42c9-a18c-c39101a75194/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.27.51.png" alt=""></p>
<p>idField의 text 를 orEmpty 로 옵셔널을 벗겨주고, checkEmailValid 되었는지 매핑해서 뱉어준다. 그러면 ob1 은 <img src="https://images.velog.io/images/app_shawn/post/b74ad22a-d81d-435e-b353-975bc5bd2064/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.28.45.png" alt="">
Bool 타입 Observable 이 된다.
<small>distinctUntilChanged() 는 같은 값이 들어오면 무시하는 메서드이다. </small></p>
<p><img src="https://images.velog.io/images/app_shawn/post/ca6a4abe-a5c1-4bbf-8fb8-242ef33786f8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.31.03.png" alt=""></p>
<p>패스워드 역시 같게 해주고,
우리는 이 두 Observable 들을 idValidView, pwValidView에 binding 해줘야한다.<img src="https://images.velog.io/images/app_shawn/post/fcf1d453-6b3d-4e75-b692-e328b05dd294/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.33.11.png" alt=""></p>
<p>이렇게 두 Observable 을 뷰에 bind 해주었다.</p>
<p>이제 남은  LoginButton 은 두 Observable 들이 모두 true 일 때 라는 조건을 받아와야한다.
Observable 들을 Combine 해주자.<img src="https://images.velog.io/images/app_shawn/post/6eaaf314-8270-4090-8994-f4805ebd36fe/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.35.00.png" alt=""></p>
<p>이렇게 해주고 실행을 시키면, <img src="https://images.velog.io/images/app_shawn/post/69fe088b-13b8-4353-a13e-da89751e6251/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.35.54.png" alt=""></p>
<p>실시간으로 아주 잘 변하는 것을 볼 수 있다. 아름답다.
두 Textfield 의 text를 Observe 하고 있는 ob1, ob2 두 Observable 들을 idValidView 와 pwValidView 에 bind 해서 빨간점을 상황에 맞게 없애주고, 
ob1, ob2 를 세트로 묶어 LoginButton 에 bind 해서 
상황에 맞게 Enable 시켜준다.</p>
<p>ob1, ob2 는 두 개의 View에 bind 되어있다. disposed 되기 전까지는..</p>
<p><img src="https://images.velog.io/images/app_shawn/post/2b607e26-aaf7-4cb9-882c-05f81eea1bd1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.38.50.png" alt=""></p>
<p>코드를 보면 위와 같다.</p>
<p>한 함수에서 모든 것을 처리하니 나중에 더 많은 Observable들을 사용하게 되면 유지 보수가 힘들어질 것이다.</p>
<p>대표적인 어플리케이션 아키텍쳐인 MVVM 모델을 사용해서 이 코드를 정리해보자.</p>
<h2 id="3-mvvm--rxswift">3. MVVM , RxSwift</h2>
<p>ViewModel 을 만들자. 그리고 
checkEmailValid 와 checkPasswordValid 를 모두 뷰 모델로 보낸다.</p>
<p>우리가 View 에서 Input 받는 것은 idTextField 와 PasswordTextField 두개이다.
이 두 인풋값이 Valid 한지 안한지 판단은 ViewModel 에서 해준다.
<img src="https://images.velog.io/images/app_shawn/post/9e07568c-86f3-459d-973d-aba042a6d8d8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.43.49.png" alt=""></p>
<p>ViewModel 의 setEmailText , setPwText로 보내주자.<img src="https://images.velog.io/images/app_shawn/post/e2bb92ab-d31b-4b89-9aa2-1d8ab8c079e9/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.45.18.png" alt=""></p>
<p>isValid 로 email, password의 Valid 유무를 담아줬다.
이제 isValid 를 idValidView, pwValidView 에 Output으로 넣어줘야하는데 
isValid 는 Bool 타입 Observable 이 아니다. 그냥 Bool 이다.</p>
<p>외부에서 넘어온 값을 받기 위해 BehaviorSubject 로 받아주자.<img src="https://images.velog.io/images/app_shawn/post/9ea340f6-51a3-4897-9f30-9264cdc4e18c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.47.33.png" alt=""></p>
<p><img src="https://images.velog.io/images/app_shawn/post/82c57b8c-ed51-4104-9f93-845e06c910b2/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.48.05.png" alt=""></p>
<p>각 Subject 에 담아주고 View 들에 Bind 해주자.
<img src="https://images.velog.io/images/app_shawn/post/996ac098-2f6e-487b-9d4a-ece2b0708442/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.48.43.png" alt=""></p>
<p>이렇게 하면 ViewController 에서 bindInput() 함수에서 , 우리가 받아올 idText 와 pwText를 인풋받아와서
ViewModel 에서 Text가 Valid 한지 판단해준 후,</p>
<p>bindOutput() 함수에서 View들에 적용시켜주었다.</p>
<p>여기서는 Model이 딱히 필요하지 않아 사용하지 않았지만, 
View와 ViewModel 의 역할을 구분지어서 사용하게 해주었다.</p>
<p>끝!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] - RxSwift 알아보기]]></title>
            <link>https://velog.io/@app_shawn/iOS-RxSwift-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@app_shawn/iOS-RxSwift-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 13 Jul 2021 06:14:18 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="rxswift">RxSwift?</h2>
</blockquote>
<p>지금까지 벨로그에서 RxSwift 에 대해 여러 포스트를 남겼다.
유튜브 &quot;곰튀김&quot; 님의 강의를 듣고 입문을 하게 된 것인데, 강의를 들으면서 이 기술을 어디에 써먹을 수 있을까... 를 계속 생각하면서 들었다.</p>
<p>공부를 하면서 느낀 점이 있는데,
나는 지금껏 앱 개발을 공부하면서 알게 모르게 비동기적인 반응형 프로그래밍을 구현하고 있었다.
이렇게 할 수 있었던 이유는, 비동기적 프로그래밍을 구현해주는 라이브러리가 많이 나와있어서이다. 예를 들어,
<img src="https://images.velog.io/images/app_shawn/post/d4bbc0b1-b803-4e43-830a-35770685b927/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.39.52.png" alt="">
KingFisher 이라는 이미지 비동기 처리 라이브러리이다.
어느정도 캐시 처리도 해주고 있어서 내가 아주 애용하는 라이브러리인데, 
RxSwift를 공부하고 이 코드를 보면 참 닮았다는 생각이 많이 든다.
또, Firebase의 코드를 살펴보면, <img src="https://images.velog.io/images/app_shawn/post/061dc4e1-e7a5-4706-a2ba-913cbe5ab450/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.41.39.png" alt=""></p>
<p>이렇게 Firebase 역시 Observe라는 단어를 쓰는 것이 참 닮았다는 생각이 든다. Firebase의 Observe는 RxSwift의 subscribe와 비슷한 기능을 한다. </p>
<p>RxSwift를 공부해야겠다고 생각이 들었던 이유는 Firebase에서 생겼다.
위에 코드를 보면 알겠지만 <img src="https://images.velog.io/images/app_shawn/post/2bf902c5-1be6-4ef5-8e78-b818a92ce5d3/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.44.09.png" alt="">
Firebase 의 RealtimeDatabase 는 위 사진처럼 데이터 트리 형태를 띈다. 그렇다면 깊숙하게 있는 데이터를 꺼낼 때는 코드가 더 길어진다는 뜻이 된다. 그렇기에 나는 코드를 줄이기 위해 적어도 사용자의 정보 정도는 간단하게</p>
<pre><code>func getUserInfo(_ uid: String)-&gt;UserModel {
    유저의 정보를 불러오는 코드..
}</code></pre><p>정도로 만들어서 불러오고 싶었다. 
하지만 불러오는 것은 가능하고, 리턴하는 것이 불가능 하다는 것을 깨닳았다.
왜냐면 위처럼 코드를 짠다고 가정했을 때,</p>
<pre><code>func getUserInfo(_ uid: String) -&gt; UserModel {

    Database.database().reference().child(&quot;Users&quot;).child(uid!).observe(DataEventType.value) {
        DataSnapShot in 

           let user = UserModel(Json: DataSnapShot.value as? [String: Any])
    유저를 어디에서 리턴할 것인가??!!
        여기서 리턴한다면, 에러가 나온다.
        return user 불가능
    }
        그렇다고 여기서 리턴한다면,
        데이터가 user 에 담기기 까지 시간이 걸리기 때문에, 
        담기기 전에 리턴이 되어버린다.
        user = nil...
}</code></pre><p>위처럼 된다.
인터넷 통신을 통한 데이터는 확실히 느리다.
그렇다면 그 &quot;나중에 생기는 데이터&quot; 를 객체로 만들어보자.
@escaping 으로 구현해보자.</p>
<pre><code>class 나중에생기는데이터&lt;T&gt; {
    private let task: (@escaping (T) -&gt; Void) -&gt; Void

    init(task: @escaping (@escaping (T) -&gt; Void) -&gt; Void) {
        self.task = task
    }

    func 데이터가받아지면(_ f: @escaping (T) -&gt; Void) {
        task(f)
    }
}
</code></pre><p>이렇게 구현을 해볼 수 있겠다.</p>
<p>이러한 클래스를 RxSwift에서 Observable 이라고 부른다.
Observable 은 직역해보면 &quot;관찰가능한&quot; 이 된다. 아직 관찰하지 않았다.
.subscribe를 통해 &quot;관찰&quot;이 가능하다.</p>
<p>Observable 은 한번만 subscribe 가능하다.
Observable 이 subscribe 되면 Disposable 이 된다.
Disposable 은 직역하면 &quot; 사용 후 버릴 수 있는&quot; 이 된다.
아직 버리지 않았다.
.dispose() 를 통해 버릴 수 있다.</p>
<blockquote>
<h3 id="예문을-통해-알아보기">예문을 통해 알아보기</h3>
</blockquote>
<p>간단하게 url 에서 json 을 받아오는 코드를 짜보자.</p>
<pre><code>     MARK: 함수에서 async를 사용하는 방법 1
    func downloadJson(_ url: String, _ completion: @escaping (String?) -&gt; Void) {
        DispatchQueue.global().async {
            let url = URL(string: url)!
            let data = try! Data(contentsOf: url)
            let json = String(data: data, encoding: .utf8)
            DispatchQueue.main.async {
                completion(json)
            }
        }
    }
</code></pre><p>비동기로 구현해야 하기 때문에 DispatchQueue.global().async 에서 데이터를 받아온 다음 </p>
<pre><code>downloadJson(URL_NAME) { json in 
    UI 처리.
}</code></pre><p>UI처리는 DispatchQueue.main 에서 처리 해주면 끝.</p>
<p>이제 이 코드를 RxSwift로 구현해보자.</p>
<pre><code>   //MARK: 1. 비동기로 생기는 데이터를 Observable로 감싸서 리턴하는 방법

    func downloadJson(_ url: String) -&gt; Observable&lt;String?&gt; {
        return Observable.create { emitter in
            let url = URL(string: url)!
            let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
                guard error == nil else {
                    emitter.onError(error!)
                    return
                }

                if let dat = data, let json = String(data: dat, encoding: .utf8) {
                    emitter.onNext(json)
                }
                emitter.onCompleted()
            }
            task.resume()
            return Disposables.create {
                task.cancel()
            }
        }
    }</code></pre><p>나중에 생기는 데이터 , Observable 을 리턴해보자.</p>
<p>Observe.create 를 통해 Observable 을 생성하고 
UrlSession에서 json을 받아온다.
에러가 발생하면 빠져나와 Subscribe 가 끝난 Observable , Disposable을 리턴한다.
위 함수를 만들어놓고 나면,</p>
<pre><code>        let observable = downloadJson(MEMBER_LIST_URL)
        _ = observable.subscribe { event in
            switch event {
            case .next:
                메인 쓰레드에서 ui처리
            case .error:
                break
            case .completed:
                break
            }
        }</code></pre><p>앞으로는 어떠한 url 도 downloadJson으로 손쉽게 데이터를 불러올 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] - ViewController 화면전환]]></title>
            <link>https://velog.io/@app_shawn/iOS-ViewController-%ED%99%94%EB%A9%B4%EC%A0%84%ED%99%98</link>
            <guid>https://velog.io/@app_shawn/iOS-ViewController-%ED%99%94%EB%A9%B4%EC%A0%84%ED%99%98</guid>
            <pubDate>Mon, 12 Jul 2021 07:47:30 GMT</pubDate>
            <description><![CDATA[<h4 id="viewcontroller-의-화면전환-방법들에-대해-알아보자">ViewController 의 화면전환 방법들에 대해 알아보자.</h4>
<p>버튼을 하나 만들어주고, 다른 ViewController에 드래그하면</p>
<p><img src="https://images.velog.io/images/app_shawn/post/4059c9f5-35ae-4f78-b46c-9f7db5ceb40d/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.18.40.png" alt=""></p>
<p>deprecated 된것들은 제끼고
대충 Show 와 Present가 보인다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/997ee446-6b3c-4bf3-a788-d2904b14c290/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.33.50.png" alt="">
<img src="https://images.velog.io/images/app_shawn/post/8266795f-8727-425c-bf99-9d23650d0aae/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.34.08.png" alt=""></p>
<p>영어로 된 설명이다.
간단하게 말하면, Show 와 Present 의 차이는 바로 뒤에 깔린 ViewController 이다.</p>
<p>일단 우리가 흔히 아는 앱들을 켜보면, 뭔가 새로운 화면을 띄우면 그 뒤에는 그 전 화면이 있다는 것을 알고 있다. 이렇게 스택 형식으로 쌓이게 되는데, 
Show 는 moving the source viewcontroller out of the way라고 명시되어있다.
치운다는 뜻인데 어떻게 영향을 주는 지는 아직 잘 모르겠다.
반면 present 는 그대로 우리가 보이는 그대로 아래에  source viewcontroller 가 존재하고 있는 상태이다.</p>
<blockquote>
<h3 id="uitabbarcontroller-">UITabBarController ??</h3>
</blockquote>
<p>그렇다면 좀 특이한 형태의 UITabBarController 에 대해 알아보자.
TabBarController 을 생각해보면 , 서로가 서로를 present하거나, show 하는 형태가 아님을 알 수 있다. 그렇다면 어떤 구조일까?
TabBarController 에서 이미, 2개 이상의 ViewController을 present하고 있는 구조가 된다.
중요한건 이미 ViewController 들이 Present 되어있기 때문에 더이상 instantiate 을 해서는 안된다. ( 같은 코드를 사용하는 새로운 View가 등장할 것이다.)
그렇다면, TabBarController 간의 정보를 공유하기 위해서는, 모든 뷰 들의 공통점인 TabBarController로 정보를 보내서 소통해야겠다는 짐작이 든다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] - ViewController LifeCycle]]></title>
            <link>https://velog.io/@app_shawn/iOS-ViewController-LifeCycle</link>
            <guid>https://velog.io/@app_shawn/iOS-ViewController-LifeCycle</guid>
            <pubDate>Mon, 12 Jul 2021 07:15:17 GMT</pubDate>
            <description><![CDATA[<h4 id="viewcontroller의-lifecycle에-대해-알아보자">ViewController의 LifeCycle에 대해 알아보자.</h4>
<blockquote>
<h4 id="서론">서론</h4>
</blockquote>
<p>iOS가 되던, 안드로이드가 되던 앱개발에 있어서 절대 빠질 수 없는 것 중 하나가 바로 화면간 정보 전달이다. 화면을 아무리 예쁘가 만들고 멋지게 만들어내도 우리가 화면을 전환할 때, 가져가야 할 것들을 가져가지 못하면 그 새로운 화면에는 어떠한 정보도 띄울 수 없을 것이다.</p>
<p>iOS 를 독학하기 시작했을 때부터 여러 방법들을 찾아보고 구글링 해봤지만 어떠한 블로그나 사이트에서도 한번에 깔끔하게 정리해놓은 곳을 찾지 못했다. 
내가 부족한 지식이지만 정리를 해보려고한다.</p>
<h2 id="viewcontroller의-구조">ViewController의 구조</h2>
<blockquote>
<p>UIViewController 의 구조에 대해 알아보자.</p>
</blockquote>
<p>안드로이드, iOS 의 뷰에는 LifeCycle 생명주기가 존재한다.
iOS 뷰의 LifeCycle은 아래 그림과 같다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/c558c55a-d458-4aee-89b3-ccdd3151588a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.47.18.png" alt=""></p>
<p>우리가 흔히 ViewController을 만들면 기본적으로 생성되어있는 ViewDidLoad() 는 세번째 단계에 해당한다.
<img src="https://images.velog.io/images/app_shawn/post/a21af85c-2342-4aec-9232-d0c5aba96089/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.48.10.png" alt=""></p>
<p>그렇다면 LifeCycle의 Load 와 Appear은 어떤 차이가 있을까?
어플리케이션을 켜면, 어플리케이션의 첫 화면이 등장하기까지 짧은 시간이 소요된다. 그 시간동안 어플리케이션은 코드를 처리한다. 그리고 완료된 코드를 바탕으로 View를 띄운다. 
즉 View는 그의 해당하는 코드( ViewController )를 처리해준 후 보여준다. 
간단한 예로, <img src="https://images.velog.io/images/app_shawn/post/9dffe41e-fbe1-4d8a-8d46-13c7eb6b4a72/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.54.28.png" alt="">
이런 간단한 코드가 있다고 하자.
우리는 UILabel 에 This is Shawn 이라는 단어를 띄우고 싶다.
이 ViewController의 코드를 처리해주지 않으면 mainLabel 에는 This is Shawn이 나오지 않을 것이다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/4db75732-0072-44a4-8217-7aa607025ec2/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.56.45.png" alt=""></p>
<p>잘 나온 모습이다.
그렇다면 라이프사이클에서, ViewDidLoad 다음인 ViewWillAppear에서
Label에 코드를 추가해주면 어떨까?<img src="https://images.velog.io/images/app_shawn/post/1bb9e543-1b87-428b-bef6-4d3362c96217/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.58.00.png" alt="">
결과는 ?<img src="https://images.velog.io/images/app_shawn/post/06b4360a-d713-475d-b2ec-f8ab238f94e6/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.58.43.png" alt=""></p>
<p>잘 나온다. 자 그럼 이번엔 ViewDidAppear은 어떨까?<img src="https://images.velog.io/images/app_shawn/post/7a1b72d2-c232-434d-a702-14dafaee1120/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.00.36.png" alt="">
이번에도 잘 나온다.
직접 코드로 실행시켜본다면 느끼겠지만, ViewWillAppear이나, ViewDidLoad보다 훨씬 늦게 This is Shawn이 나타나는 것을 알 수 있다.</p>
<p>이처럼 , ViewController 안에 오버라이드 된 생명주기 함수들은 차례로 코드를 처리하며 뷰를 보여준다. ViewDidAppear의 text가 늦게 나타난 이유는 당연히 함수 이름처럼 View가 나타난 이후에 text를 적용시켜주기 때문이다.</p>
<p>사실, 아주 당연해 보이는 이 실험을 해본 이유는 따로있다.</p>
<p>아래처럼, ViewDidLoad에서 text를 적용시키고,
TwoViewController 을 Present 방식으로 띄워보자.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/d449ad3e-3c55-43c7-b07a-7ca9b05f3b68/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.06.28.png" alt="">
그리고, TwoViewController 안에는 main 이라는 이름의 IBOutlet UILabel 이 있다.
결과는 어떻게될까?<img src="https://images.velog.io/images/app_shawn/post/a51341d9-30b2-4c8d-a46b-21d97a9060e9/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.08.53.png" alt="">
VC.main이 없다는 에러가 뜬다. 우리는 분명 코드에 적어줬는데?</p>
<p>UI와 관련된 객체들은 다른 레퍼런스들과 좀 다른 특성을 띈다.
쓰레드도 Main쓰레드를 사용해야만하고,, 그리고
View가 Load되기 전까지는 없는 객체가 된다.
우리가 버튼을 눌렀을 때, 없는 Label의 text를 적용 시킨 꼴이 된다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/3ac48c49-36c7-4cb2-94ef-2db42b9e7170/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.11.40.png" alt=""></p>
<p>하지만 이렇게 이미 뷰가 present가 된 이후에 코드를 적어주거나, 
<img src="https://images.velog.io/images/app_shawn/post/f3cefd04-48d8-456a-a84e-75ed9cb6964a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.13.00.png" alt="">
Completion안에 넣어준다면, text가 잘 적용된 모습을 볼 수 있다.
이것도 마음에 들지 않는다면, <img src="https://images.velog.io/images/app_shawn/post/24f63a7f-4085-4230-8bd9-fb2ac5747c39/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.14.04.png" alt="">
TwoViewController 에 value 를 새로 만든다음 적용 시킨 후 
TwoViewController 의 ViewDidLoad에서 새로 적용 시켜주면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] - RxSwift 입문 (5)]]></title>
            <link>https://velog.io/@app_shawn/iOS-RxSwift-%EC%9E%85%EB%AC%B8-5</link>
            <guid>https://velog.io/@app_shawn/iOS-RxSwift-%EC%9E%85%EB%AC%B8-5</guid>
            <pubDate>Sun, 11 Jul 2021 05:49:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="subject">Subject</h2>
</blockquote>
<p>Subject에 대해 알아보자 .
<a href="http://reactivex.io/documentation/ko/subject.html">공식 홈페이지</a>를 이용해보면, </p>
<p><img src="https://images.velog.io/images/app_shawn/post/f967ecdb-06ab-4f4a-b602-c45acd0b22d0/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.04.50.png" alt=""></p>
<p>4가지의 Subject가 있다고 한다.
하나하나 살펴보자. </p>
<h3 id="1-asyncsubject">1. AsyncSubject</h3>
<p><img src="https://images.velog.io/images/app_shawn/post/c72a03b8-ee2e-4dd7-9bf1-42a1c8415b38/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.06.20.png" alt=""></p>
<p>마블을 통해 이해해보자.</p>
<p>AsyncSubject는  Observable을 통해 Subscribe 된 후 어떠한 이벤트가 발생해도 결과를 배출하지 않는다. 그러다가 Observable의 동작이 완료되고 나면 마지막 결과를 모든 Observable에 배출한다. ( 파랑색이 모든 Stream에 있다. )</p>
<p><img src="https://images.velog.io/images/app_shawn/post/7bffcf33-49fb-45fb-a227-f362838214e2/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.09.20.png" alt=""></p>
<p>또한, Observable이 오류로 인해 동작이 끝난다면, 모든 Observable에 오류값을 전달한다.</p>
<h3 id="2-behaviorsubject">2. BehaviorSubject</h3>
<p><img src="https://images.velog.io/images/app_shawn/post/bc1043cb-cfb1-466e-88dd-db2a89ee6c55/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.11.02.png" alt=""></p>
<p>옵저버가 BehaviorSubject를 Subscribe 하기 시작하면, 기본값 (분홍색) 을 배출하고, 앞으로 오는 모든 이벤트를 배출한다.
도중 다른 옵저버가 Subscribe 하면, 바로 직전의 값 (연두) 를 배출하는 것으로 보아
분홍색 이벤트가 완전 Default 값이 아닐 수도 있다고 추측할 수 있다. 바로 직전 이벤트일 수도 있다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/f76824a2-2c4e-4f3b-8183-d20b57f360b9/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.15.19.png" alt=""></p>
<p>오류로 인해 종료된다면, 에러를 반환한다. 새로 subscribe한 Observable 에도 에러를 반환한다.</p>
<h2 id="3-publishsubject">3. PublishSubject</h2>
<p><img src="https://images.velog.io/images/app_shawn/post/f09f840a-b434-4903-9f9e-882f14c4a8d2/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.16.55.png" alt=""></p>
<p>이번엔 Subscribe 했을 때 아무것도 배출하지 않고, 그다음 이벤트부터 배출하는 것을 볼 수 있다.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/5ed67837-78a1-4c07-a6b9-a09f8de58042/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.21.15.png" alt=""></p>
<p>에러가 발생했을 때도 예상이 가능하다.</p>
<h2 id="4-relaysubject">4. RelaySubject</h2>
<p><img src="https://images.velog.io/images/app_shawn/post/3dd995b3-9ea9-4d9e-b238-fcbb6aba670b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.22.04.png" alt=""></p>
<p>RelaySubject 는 Subscribe 시점과 별개로 그 전에 발생했던 모든 이벤트들을 배출한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] - RxSwift 입문 (4)]]></title>
            <link>https://velog.io/@app_shawn/iOS-RxSwift-%EC%9E%85%EB%AC%B8-4</link>
            <guid>https://velog.io/@app_shawn/iOS-RxSwift-%EC%9E%85%EB%AC%B8-4</guid>
            <pubDate>Sun, 11 Jul 2021 05:00:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="rxcocoa">RxCocoa</h3>
</blockquote>
<p>이번 포스팅에서는 RxCocoa를 이용한 로그인 시스템에 대해 알아보겠다.<img src="https://images.velog.io/images/app_shawn/post/276f3ac3-024d-4afe-a8c8-8ede04b85995/simulator_screenshot_CA49FC13-A9F2-4DA3-9C51-8A70EF6E61FB.png" alt=""></p>
<p>UI 는 다음과 같이 구성되어있다.
Email, Password Textfield 와 
형식이 다르다면 보여지는 Email, Password Valid View (빨간 점)
그리고 마지막으로
Email, password 형식이 모두 같으면 Log In이 보여지고 
아니면 Fill the Fields 가 보이는 로그인 버튼이다.</p>
<blockquote>
<h2 id="코드">코드</h2>
</blockquote>
<pre><code>        idField.rx.text.orEmpty
            .map(checkEmailValid)
            .subscribe(onNext: { s in
                self.idValidView.isHidden = s
        })
        .disposed(by: disposeBag)</code></pre><p>idField 의 형식을 체크하는 코드이다.
물론   &quot;비동기&quot; 로 이루어진다.
사실 이번 강의를 듣기 전까지 동기, 비동기에 대해 많이 들어봤지만 실제 앱에서 어떤식으로 적용이 되는지에 대해 의문이 있었다. 이번에 좀 해소가 되는듯 하다.
idField.rx.text  : idField 의 text를 비동기로 처리해주겠다는 코드이다.
.orEmpty  : filter { $0 != nil } 과 map { $0 ! } 과 같은 코드이다. 옵셔널 처리.</p>
<p>checkEmailValid 함수는 따로 작성을 해줬다.
간단히 .contain(&quot;@&quot;), .contain(&quot;.&quot;)으로...</p>
<p>그리고 subscribe으로  나오는 값을, 
Bool 형식으로 나오는 값을 self.isValidView.isHidden 에 적용시키면, 
idField의 text가 변할때 마다 비동기로! s 가 변하는 것을 알 수 있다.</p>
<blockquote>
<h4 id="q-delegate-과-비슷한데">Q. Delegate 과 비슷한데..?</h4>
</blockquote>
<p>코드를 작성하다보니 , 특히 TextField 에서는, Delegate과 매우 비슷하다는 생각이 든다. 위의 코드를 RxSwift, RxCocoa 를 사용하지 않고 작성해보자면, 일단 ViewController에 UITextfieldDelegate 를 익스텐션 해주고, TextfieldDidEndEditing에 코드를 작성해주면 될 것 같다.
그래서 구글링을 해보니<img src="https://images.velog.io/images/app_shawn/post/2cd439fa-56ef-471e-a513-220683865607/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.54.49.png" alt="">
Delegate을 통해 비동기 처리를 해준다고 한다.</p>
<p>생각해보면 CollectionView, TableView 도 Delegate 을 통해 비동기처리를 해주고 있었던 것이다. TableView 함수들이나 CollectionView 함수들에 적혀진 함수들은 자동으로 처리되고 있었고,
내가 Database로 사용하고 있었던 Firebase에서 사용하던 Observe나 ObserveSingleEvent 함수도 결국 생각해보면 비동기 처리를 해주고 있지 않았나 생각이 든다.</p>
<blockquote>
<h2 id="combine">Combine</h2>
</blockquote>
<p>idField 의 비동기처리 코드를 보았다면 어렵지 않게 pwField 비동기 처리를 해줄 수 있다.</p>
<pre><code>        idField.rx.text.orEmpty
            .map(checkEmailValid)
            .subscribe(onNext: { s in
                self.idValidView.isHidden = s
        })
        .disposed(by: disposeBag)

        pwField.rx.text.orEmpty
            .map(checkPasswordValid)
            .subscribe(onNext: { s in
                self.pwValidView.isHidden = s
        })
        .disposed(by: disposeBag)
</code></pre><p>그렇다면 이제 로그인 버튼을 생각해보자.
위의 두 Observable들이 true 일 때 , Login In 이 표시되어야 한다.</p>
<p>코드를 보면 ,</p>
<pre><code>        Observable.combineLatest(
            idField.rx.text.orEmpty.map(checkEmailValid),
            pwField.rx.text.orEmpty.map(checkPasswordValid),
        resultSelector: { s1, s2 in s1 &amp;&amp; s2 }
        )
        .subscribe(onNext: { b in
            self.loginButton.isEnabled = b
        })
        .disposed(by: disposeBag)</code></pre><p>두 옵저버블을 선언해준 다음 , returnSelector안에 담아서 subscribe 해주면 된다.
이번역시 self.loginButton.isEnabled 에 적용시키면 
비동기로 , 값이 바뀔 때 마다 , Delegate 처럼 바뀌는 Login버튼을 확인 할 수 있다.</p>
<p>그렇다면 지금까지 공부한 코드를 좀 더 간단하게 사용하는 방법을 알아보자.</p>
<blockquote>
<h2 id="simple">Simple</h2>
</blockquote>
<p>먼저, id 작성 textfield의 observable을 만들어보자.
<img src="https://images.velog.io/images/app_shawn/post/640c1bef-c497-47c8-8020-12a9812973ca/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.44.36.png" alt=""></p>
<p>이름을 idInputOb라고 해주고, idField.rx를 치면 , 
리턴 값이 Reactive형식이라고 나온다 . <small> 흥미롭다 </small>
<img src="https://images.velog.io/images/app_shawn/post/10af49b2-1e2d-49ee-aa80-ab01532fd7a7/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.47.57.png" alt="">
text까지 입력하면 ControlProperty, 
컨트롤 할 객체 라는 것 같다.<img src="https://images.velog.io/images/app_shawn/post/060e1f26-f682-49b3-aede-0fdcf09ec5d3/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.48.54.png" alt="">
우리는 Observable 을 원하기 때문에, asObservable() 을 입력해준다.<img src="https://images.velog.io/images/app_shawn/post/d57f421d-a093-47b0-9fba-fa5013ee6cc7/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.50.03.png" alt="">
그리고 빨간 점에도 옵저버블을 만들어주자.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/e8c57365-a284-459d-9066-53010727ec25/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.51.04.png" alt=""></p>
<p>비밀번호까지 완료했다.</p>
<p>이제 만든 Observable 들을 Subscribe 해주자.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/b07d496c-17bc-48ef-96b9-acf9d74ca93b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.53.46.png" alt="">
이렇게 isHidden 에 적용시켜준다.</p>
<p>이제 두 옵저버블을 Combine 해서, 로그인 버튼에 달아주자.
<img src="https://images.velog.io/images/app_shawn/post/bf604341-163a-4cdc-9658-5b7a157c3651/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.56.06.png" alt=""></p>
<p>아까 적었던 코드와 똑같은 비동기 처리를 해주는 코드가 된다.
확실히 좀 더 많은 Observable이 들어가는 코드에서 더 알아보기 편할 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] - RxSwift 입문 (3)]]></title>
            <link>https://velog.io/@app_shawn/iOS-RxSwift-%EC%9E%85%EB%AC%B8-3</link>
            <guid>https://velog.io/@app_shawn/iOS-RxSwift-%EC%9E%85%EB%AC%B8-3</guid>
            <pubDate>Sat, 10 Jul 2021 06:40:07 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="operator-활용하기">Operator 활용하기</h2>
</blockquote>
<p>지난 포스트에서 다룬 Operator들을 더 자세하게 다뤄보자.
<a href="http://reactivex.io/documentation/ko/operators.html">공식 홈페이지</a>에 잘 번역되어있는 Operator로 들어가면, <img src="https://images.velog.io/images/app_shawn/post/d5b87d3a-1c14-4ebb-a41f-42e7ec309a89/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.06.14.png" alt=""></p>
<p>다양한 Operator들에 대해 설명을 해주고 있다.
여기서 중요한 것은, Reactive 만의 특별한 설명 방식인데, <img src="https://images.velog.io/images/app_shawn/post/cfe58d87-8119-449f-bc9b-db9df769c606/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.06.59.png" alt=""></p>
<p>Just() 를 다음과 같이 구슬로 설명하고 있다.
그대로 빨간공이 내려오면 종료 (dispose) 하겠다는 의미이다.</p>
<p>First도 한번 살펴보자.
<img src="https://images.velog.io/images/app_shawn/post/9c1291f0-84da-47b5-a96b-35f28958ccda/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.08.32.png" alt=""></p>
<p>첫번째 구슬이 내려오자마자 Dispose를 한다.
이런 것들은 코드를 통해서 확인해보고 싶어진다.</p>
<pre><code>
    @IBAction func exJust2() {
        Observable.just([&quot;Hello&quot;, &quot;World&quot;])
            .subscribe(onNext: { arr in
                print(arr)
            })
            .disposed(by: disposeBag)
    }</code></pre><p>전에 다뤘던 예문 2 코드에서, 
single()를 얹어보자.</p>
<p><small>공식홈페이지에서 Single 을 클릭하니 First가 나오는 것으로 보아, 같은 기능인 것으로 보인다 </small></p>
<pre><code>    @IBAction func exJust2() {
        Observable.just([&quot;Hello&quot;, &quot;World&quot;])
            .single()
            .subscribe(onNext: { arr in
                print(arr)
            })
            .disposed(by: disposeBag)
    }</code></pre><p>그리고 subscribe를 이벤트 형태로 받아보자.</p>
<pre><code>    @IBAction func exJust2() {
        Observable.just([&quot;Hello&quot;, &quot;World&quot;])
            .single()
            .subscribe { event in
                switch event {
                case .next(let d):
                    print(&quot;next: \(d)&quot;)
                    break
                case .error(let error):
                    print(&quot;error: \(error)&quot;)
                    break
                case .completed:
                    print(&quot;completed&quot;)
                    break
                }
            }
            .disposed(by: disposeBag)
    }</code></pre><p>subscribe 이벤트는 세가지 케이스가 있는데, 
.next, .error, .completed 가 있다.
이대로 실행시켜보면 어떻게될까?<img src="https://images.velog.io/images/app_shawn/post/ec811a5c-3f68-4927-a5d6-d3b1f7e6306d/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.16.27.png" alt="">
이렇게 나온다.
Just을 통해 
Hello, World 배열이 &quot;동시에&quot; 내려왔기 때문에 한 구슬에 담겨 내려왔음을 알 수 있다.
그렇다면, Just가 아니라 From 으로 바꿔보겠다.<img src="https://images.velog.io/images/app_shawn/post/f6310211-9f3a-42af-96f5-638ad8398898/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.17.59.png" alt="">
Single() 은 하나의 구슬이 내려오지 않으면 에러를 뱉기 때문에, 
next를 통해 Hello 구슬이 내려온 뒤, 아직 World 구슬이 내려오지 않고 남아있는 것을 확인한 후 Error을 출력한다.
<img src="https://images.velog.io/images/app_shawn/post/5b3ba031-480e-40f4-b27e-3777aa56587f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.19.20.png" alt="">
World 를 지우면, </p>
<p>예상대로, Hello 이후에 Completed가 출력되는 것을 볼 수 있다.</p>
<blockquote>
<h1 id="scheduler">Scheduler</h1>
</blockquote>
<pre><code>    @IBAction func exMap3() {
        Observable.just(&quot;800x600&quot;)
            .observeOn(ConcurrentDispatchQueueScheduler(qos: .default))
            .map { $0.replacingOccurrences(of: &quot;x&quot;, with: &quot;/&quot;) }
            .map { &quot;https://picsum.photos/\($0)/?random&quot; }
            .map { URL(string: $0) }
            .filter { $0 != nil }
            .map { $0! }
            .map { try Data(contentsOf: $0) }
            .map { UIImage(data: $0) }
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { image in
                self.imageView.image = image
            })
            .disposed(by: disposeBag)
    }</code></pre><p>이렇게 observeOn(스케쥴러) 를 이용해서 해결할 수 있다.
ConcurrentDispatchQueueScheduler 로 비동기로 사진을 불러온다.
두 줄이 새로 추가됐는데, observeOn이 두줄 추가됐다.</p>
<p>윗 줄의 .observeOn 아래로 또다시 .observeOn이 나오기 전까지의 Operator 들은 비동기로 진행된다.
그러다가 다시 메인 쓰레드로 접근하게 하는 observeOn(MainScheduler.instance) 이 호출된 이후 아래의 subscribe은 메인에서 진행된다. (UI 이기 때문에 메인에서!)</p>
<p>실행시켜보면, 이미지가  불러오는중에도 드래그가 잘 되는 것을 확인 할 수 있다.
Main에서 이미지를 Url 에서 불러오는 것이 아니라, 
Thread1 에서 이미지를 받아오고 있다.
Main Thread에 있는 UI들은 어떠한 제약도 받지 않는다.</p>
<p>굳이 구역을 정해서 비동기화를 쓰지 않고, subscribe 시작부터 전체 비동기화를 하고 싶다면, 아무곳에나 .subscribeOn(ConcurrentDispatchQueueScheduler)을 넣어주면 된다.
하지만 , observeOn(MainThread) 는 꼭 넣어줘야한다. UI를 꼭 다뤄야하기 때문,....</p>
<blockquote>
<h1 id="side-effect">Side-Effect</h1>
</blockquote>
<p>Side effect란 간단히 말하면, 현재 함수 밖에 영향을 주는 코드를 말한다.</p>
<pre><code>    @IBAction func exMap3() {
        Observable.just(&quot;800x600&quot;)
            .observeOn(ConcurrentDispatchQueueScheduler(qos: .default))
            .map { $0.replacingOccurrences(of: &quot;x&quot;, with: &quot;/&quot;) }
            .map { &quot;https://picsum.photos/\($0)/?random&quot; }
            .map { URL(string: $0) }
            .filter { $0 != nil }
            .map { $0! }
            .map { try Data(contentsOf: $0) }
            .map { UIImage(data: $0) }
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { image in
                self.imageView.image = image
            })
            .disposed(by: disposeBag)
    }</code></pre><p>이 코드에서는 self.imageView.image = image 코드가
Side-Effect를 주고 있다.
Side-Effect를 사용하기 좋은 곳이 딱 두 곳 있는데, 
이 코드의 .subscribe가 있고,
아래의 Do 가 있다.</p>
<blockquote>
<h3 id="do">Do</h3>
<p>Do 함수를 살펴보자</p>
</blockquote>
<p><img src="https://images.velog.io/images/app_shawn/post/ec6a63a4-585c-4db3-a600-ad1575b16b36/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.00.35.png" alt="">
오메나
여러 switch - case 문을 활용하기 좋아보이는 상황들이 있는데, 
알맞은 상황에 맞춰서 side-effect를 주면 된다.
다음 포스트에서는 RxCocoa에 대해 알아본다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] - RxSwift 입문 (2)]]></title>
            <link>https://velog.io/@app_shawn/iOS-RxSwift-%EC%9E%85%EB%AC%B8-2</link>
            <guid>https://velog.io/@app_shawn/iOS-RxSwift-%EC%9E%85%EB%AC%B8-2</guid>
            <pubDate>Fri, 09 Jul 2021 07:49:45 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@app_shawn/iOS-RxSwift-%EC%9E%85%EB%AC%B8-1">RxSwift 입문(1)</a> 에서 DisposeBag까지 알아보았다.</p>
<p>이번에는 Operator들에 대해서 공부해보려고 한다.</p>
<h2 id="operator">Operator</h2>
<p><a href="http://reactivex.io/documentation/ko/operators.html">공식 홈페이지 문서</a>에 보면 Operator 에 대한 설명이 뭐라뭐라 나와있다. 하지만 솔직히 말하자면, Operator을 처음 접하는 내 입장에서는 정확하게 이해하기 어렵다.</p>
<p>그냥 예문을 통해서 Operator가 무엇인지 살펴보자.</p>
<h3 id="예문-1">예문 1</h3>
<pre><code>    @IBAction func exJust1() {
        Observable.just(&quot;Hello World&quot;)
            .subscribe(onNext: { str in
                print(str)
            })
            .disposed(by: disposeBag)
    }</code></pre><p>Operator 중 Just 에 대한 예문이다.
Just는 () 안에 객체를 그대로 가져온다.
결과는<img src="https://images.velog.io/images/app_shawn/post/f6a80a3c-820f-4b2d-ae41-5c39012568bb/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.24.01.png" alt="">
로 예상 가능하다.</p>
<h3 id="예문-2">예문 2</h3>
<pre><code>    @IBAction func exJust2() {
        Observable.just([&quot;Hello&quot;, &quot;World&quot;])
            .subscribe(onNext: { arr in
                print(arr)
            })
            .disposed(by: disposeBag)
    }</code></pre><p>Just를 Array에 적용시켰다.
<img src="https://images.velog.io/images/app_shawn/post/73cb475f-db24-4e56-92e0-ccd0fd2fb61a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.24.51.png" alt="">
크게 다르지 않다.</p>
<h3 id="예문-3">예문 3</h3>
<pre><code>    @IBAction func exFrom1() {
        Observable.from([&quot;RxSwift&quot;, &quot;In&quot;, &quot;4&quot;, &quot;Hours&quot;])
            .subscribe(onNext: { str in
                print(str)
            })
            .disposed(by: disposeBag)
    }</code></pre><p>이번엔 From에 대해 알아보자.</p>
<p><img src="https://images.velog.io/images/app_shawn/post/cfaa6e96-6a4f-4489-b69f-836b99e3a087/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.25.42.png" alt=""></p>
<p>결과가 다음과 같은 것으로 보아
Just와 달리 From은 Array 안에 객체를 각각 받아오는 것으로 유추할 수 있다.</p>
<h3 id="예문-4">예문 4</h3>
<pre><code>    @IBAction func exMap1() {
        Observable.just(&quot;Hello&quot;)
            .map { str in &quot;\(str) RxSwift&quot; }
            .subscribe(onNext: { str in
                print(str)
            })
            .disposed(by: disposeBag)
    }</code></pre><p>** <em> Mapping </em> **
여기서 .map 이 나온다.
Swift 언어로 알고리즘 공부를 하다가 Programmers 에서 문제를 풀기 시작했었는데, 해답을 보니 짧고 간단한 코드들은 모두 Map() 이라는 함수가 들어갔던 것으로 기억한다.
Just 의 &quot;Hello&quot; 를
map 에서 str로 받아와 in &quot;(str) RxSwift&quot; 에 적용 시킨 후
subscribe 에서 print 해준다.</p>
<blockquote>
<p><em>Q1. 코드 끝에 disposed를 없애면 어떻게 될까?</em></p>
</blockquote>
<p>없애기 전.
<img src="https://images.velog.io/images/app_shawn/post/eae36039-0238-4aea-b806-ee67807ba7ed/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.30.32.png" alt=""></p>
<p>없앤 후.
<img src="https://images.velog.io/images/app_shawn/post/5bb497f9-1900-4ff2-bbde-cd4499853ef1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.31.25.png" alt=""></p>
<p>결과는 차이가 없지만 
코드에서, <img src="https://images.velog.io/images/app_shawn/post/820b0372-02c2-4736-b57e-56364fb3706a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.32.04.png" alt="">
에러가 나오는데,
.subscribe를 사용하고 나면 dispose 되었을 때 이벤트를 적어주라는 경고이다.
일단은 이 에러가 나오는 것 말고는 큰 차이는 없다.
더 공부하다보면 나올 것 같다.</p>
<blockquote>
<p><em>Q2. .subscribe를 사용하지 않고 구현해보면?</em></p>
</blockquote>
<p>먼저 subscribe를 제거하고 코드를 짜 보았다.</p>
<pre><code>    @IBAction func exMap1() {
        Observable.just(&quot;Hello&quot;)
            .map { str in &quot;\(str) RxSwift&quot; }
            .map { str in print(str) }
    }</code></pre><p>결과로는 아무것도 print 되지 않는다.</p>
<p>다음으로는, print를 subscribe가 아니라 map 에서 해줬다.</p>
<pre><code>    @IBAction func exMap1() {
        Observable.just(&quot;Hello&quot;)
            .map { str in &quot;\(str) RxSwift&quot; }
            .map { str in print(str) }
            .subscribe(onNext: { Void in

            })
            .disposed(by: disposeBag)
    }</code></pre><p><img src="https://images.velog.io/images/app_shawn/post/6e3452ff-bf03-48bc-b41f-b3fe8aa85721/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.36.55.png" alt="">
출력이 정상적으로 되는 것을 볼 수 있다.</p>
<p>위의 두 가지 호기심으로 알 수 있었던 것은
Observable 은 항상 끝에 subscribe로 끝나야 하는 것 같다.
그리고 가면 갈 수록 SwiftUI와 굉장히 비슷하다는 생각이 든다.</p>
<h3 id="예문-5">예문 5</h3>
<pre><code>    @IBAction func exMap2() {
        Observable.from([&quot;with&quot;, &quot;shawn&quot;])
            .map { $0.count }
            .subscribe(onNext: { str in
                print(str)
            })
            .disposed(by: disposeBag)
    }</code></pre><p>from으로 받아온 array 전체 ( $0 ) 를 .count 해서 출력한다.</p>
<h3 id="예문-6">예문 6</h3>
<pre><code>    @IBAction func exFilter() {
        Observable.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
            .filter { $0 % 2 == 0 }
            .subscribe(onNext: { n in
                print(n)
            })
            .disposed(by: disposeBag)
    }</code></pre><p>from으로 받아온 array 중 .filter 안에 true 인것만 
.subscribe 의 n 안에 넣어준다.
2, 4, 6, 8, 10이 출력된다.</p>
<h3 id="예문-7">예문 7</h3>
<pre><code>    @IBAction func exMap3() {
        Observable.just(&quot;800x600&quot;)
            .map { $0.replacingOccurrences(of: &quot;x&quot;, with: &quot;/&quot;) }
            .map { &quot;https://picsum.photos/\($0)/?random&quot; }
            .map { URL(string: $0) }
            .filter { $0 != nil }
            .map { $0! }
            .map { try Data(contentsOf: $0) }
            .map { UIImage(data: $0) }
            .subscribe(onNext: { image in
                self.imageView.image = image
            })
            .disposed(by: disposeBag)
    }</code></pre><p>제일 중요한 예문인데, 
800x600 에서 x 를 /로 바꾼다. ( replacingOccurrences 를 이용 )
이미지 url 에서 800/600 사이즈의 이미지를 찾아오는 코드인 것 같다.
url 을 URL 로 바꾸고,
nil 이 아닌 것을 filter 해주고 <small>(예외처리 인 것 같다 )</small>
UIImageview에 넣기 위해 Data로 바꿔주고 이미지를 띄워주면 끝.</p>
<blockquote>
<h3 id="whats-new">What&#39;s New?</h3>
</blockquote>
<p>앱 개발자들을 필요로하는 스타트업들을 찾아보니, 대부분이 iOS Android를 함께 만들기 위해 ReactiveNative 같은 하이브리드 앱 개발자들을 많이 찾는다. (결국 회사가 성장함에 따라 네이티브로 바뀌겠지만)
애플 또한 SwiftUI로  갈아타게끔 하려는 추세인 지금
반응형 프로그래밍을 제대로 아는것이 너무 중요한 것 같다.
오늘 배운 7가지 예문들을 보면
SwiftUI 와 매우 비슷하게 흘러가면서 <img src="https://images.velog.io/images/app_shawn/post/188b613f-cd3b-4684-be9f-778db11d48d4/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-09%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.48.27.png" alt="">
<small>ㅊㅊ:  리액티브 공식 홈페이지 </small></p>
<p>Observable Stream이라는게 뭔지 감을 잡을 수 있었다.</p>
]]></description>
        </item>
    </channel>
</rss>