<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>jk2b's dev blog</title>
    <link>https://jk2b.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 7 Apr 2026 12:01:11 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>j2kb</managingEditor>
    <item>
      <title>Data Types and Algebra</title>
      <link>https://jk2b.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Swift의 데이터 타입과 대수학(Algebra)의 관계에 대해서 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 타입과 대수학이 관계가 깊다는 얘기를 우연히 접한적이 있습니다. 데이터 타입은 제가 String, Int, Bool, Long, Short, unsigned Int 등 문자열, 실수 정수 등 여러 종류의 데이터를 나타내기 위한 것이다라는 생각을 바로 할 수 있었는데요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대수학은 뭘까요??&amp;hellip;. 나무위키에 따르면 초중등교육에서는 미지수에 변수를 대입하는 기술이라고 하는데.... 그 이상의 개념은 저는 잘 모르겠어서 암튼 여기까지 하고 일단 넘어가 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구조체와 대수학&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래와 같은 구조체를 정의해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;struct Some&amp;lt;T, U&amp;gt; {
	let first: T
	let second: U
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구조체는 제네릭 타입으로 T, U를 받고 first와 second라는 2개의 필드에 각각 T, U의 타입을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 T와 U가 Boolean값이라면 Some이라는 구조체가 가질 수 있는 경우의 수는 총 4가지 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(true, true), (true, false), (false, true), (false, false)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Boolean을 T에 3가지의 경우를 가질 수 있는 열거형을 U타입에 넘기면 어떻게 될까요?&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;enum SomeEnum {
	case a
	case b
	case c
}

Some&amp;lt;Bool, SomeEnum&amp;gt;(first: true, second: .a)
Some&amp;lt;Bool, SomeEnum&amp;gt;(first: true, second: .b)
Some&amp;lt;Bool, SomeEnum&amp;gt;(first: true, second: .c)
Some&amp;lt;Bool, SomeEnum&amp;gt;(first: false, second: .a)
Some&amp;lt;Bool, SomeEnum&amp;gt;(first: false, second: .b)
Some&amp;lt;Bool, SomeEnum&amp;gt;(first: false, second: .c)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 총 6가지의 경우가 나옵니다. 2*3이죠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 U의 위치에 Void를 넣으면 어떻게 될까요?? Void는 경우의 수가 () 한 개뿐이므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2*1의 2가지 경우가 나오죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Void는 수학적으로 1을 의미한다고 해석하면 될 것 같기도 합니다?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 0은 어떻게 표현할까요??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단합니다. Never라는 키워드를 Result타입의 Failure타입에서 보신적이 있으실 거에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Failure타입에 있을 경우 발생하지 않는다는 뜻을 나타내게 되는데요. Never의 실제 구현은 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;@frozen enum Never { }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;케이스가 없는 열거형이네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 아래와 같이 U에 Never를 넘길 경우 아예 구조체 인스턴스를 만들 수 조차 없습니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;Some&amp;lt;Bool, Never&amp;gt;(first: true, second: ...???)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 잠깐 정리하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bool은 2, Void는 1, Never는 0 이네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 구조체에서는 T와 U의 경우의 수의 곱만큼 실제 구조체가 가질 수 있는 경우의 수가 존재하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;Bool, Bool&amp;gt; = 2*2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;Bool, Void&amp;gt; = 2*1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;Void, Void&amp;gt; = 1*1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;Bool, Never&amp;gt; = 2*0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;열거형과 대수학&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열거형에서는 어떻게 될까요&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;enum ExampleEnum&amp;lt;T, U&amp;gt; {
	case a(T)
	case b(U)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 열거형이 있다고 해봅시다. 그리고 구조체에서와 같이 아래의 총 4개의 열거형을 정의해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;capnproto&quot;&gt;&lt;code&gt;ExampleEnum&amp;lt;Bool, Bool&amp;gt;
ExampleEnum&amp;lt;Bool, Void&amp;gt;
ExampleEnum&amp;lt;Void, Void&amp;gt;
ExampleEnum&amp;lt;Bool, Never&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 열거형이 가질 수 있는 경우의 수는 아래와 같습니다&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;ExampleEnum&amp;lt;Bool, Bool&amp;gt;.a(true)
ExampleEnum&amp;lt;Bool, Bool&amp;gt;.a(false)
ExampleEnum&amp;lt;Bool, Bool&amp;gt;.b(true)
ExampleEnum&amp;lt;Bool, Bool&amp;gt;.b(false)
// 2+2

ExampleEnum&amp;lt;Bool, Void&amp;gt;.a(true)
ExampleEnum&amp;lt;Bool, Void&amp;gt;.a(false)
ExampleEnum&amp;lt;Bool, Void&amp;gt;.b(())
// 2+1

ExampleEnum&amp;lt;Void, Void&amp;gt;.a(())
ExampleEnum&amp;lt;Void, Void&amp;gt;.b(())
// 1+1

ExampleEnum&amp;lt;Bool, Never&amp;gt;.a(true)
ExampleEnum&amp;lt;Bool, Never&amp;gt;.a(false)
// 2+0
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조체와 다르게 곱연산이 아니라 합연산만큼 경우의 수가 있는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Optional을 예로 들어보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;enum Optional&amp;lt;A&amp;gt; {
	case none
	case some&amp;lt;A&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 none은 none(Void)로 볼 수 있을 것 같습니다. Optional.none(())와 Optional.none은 언제나 한가지의 경우만 존재하니까요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Optional&amp;lt;A&amp;gt;의 경우의 수는 1 + A의 경우의 수만큼 존재할 수 있음을 알 수 있고 이를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Optional&amp;lt;A&amp;gt; = A? = 1 + A = Void + A&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 볼 수 있겠네요&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이걸 아는게 왜 중요할까요???&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 얘기하면 실제 서비스를 만들기 위해 데이터를 모델링할 때 존재하지 않을 케이스를 만들지 않아서 불필요한 작업을 사전에 방지하는데 도움이 되기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저 정보를 API호출로 받아오는데 거기에는 이름과 나이 전화번호를 준다고 해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 전화번호는 국가코드와 국가코드 번호 그리고 숫자 번호가 있다고 해봅시다. 그리고 전화번호는 유저가 회원가입 시 입력할 수도 있고 안 할수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 모델링 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 방법&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;struct UserInfo {
	let name: String
	let age: Int
	let phoneNationalCode: String?
	let phoneNationalCodeNumber: String?
	let phoneNumber: String?
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 방법&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;struct UserInfo {
	let name: String
	let age: Int
	var phoneNumber: PhoneNumber?
}

struct PhoneNumber {
	let nationalCode: String
	let nationalCodeNumber: String
	let number: String
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 생각에는 2 번째 방법이 보다 잘 도메인을 잘 표현했다고 생각이 듭니다. 단순히 optional unwrapping의 횟수를 3번에서 1번으로 줄여서가 아니라 개념적으로 동시에 존재해야만 존재할 수 있는 3가지 개념들을 제대로 표현해낸 방법이 2번째이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 예를 또 들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대기열 기능을 만드려고 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대기열에는 다음 3가지 상태가 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;들어가기 전 ready상태&lt;/li&gt;
&lt;li&gt;대기열에 들어가서 기다리는 waiting 상태로 이 때는 내 앞에 남은 사람의 숫자 n을 같이 보내줍니다&lt;/li&gt;
&lt;li&gt;들어갈 수 있는 상태 finish&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버측에서 API Response 형상 예시를 보내왔습니다. 아래처럼 말이죠&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
	&quot;status&quot;: &quot;ready&quot;
}

{
	&quot;status&quot;: &quot;waiting&quot;,
	&quot;remaining_count&quot;: 6,
}

{
	&quot;status&quot;: &quot;finish&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 모델링하면 좋을까요??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 방법&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;enum WaitingStatus {
	case ready
	case waiting
	case finish
}

struct Wating {
	case status: WaitingStatus
	case remainingCount: Int?
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 방법&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;enum WaitingStatus {
	case ready
	case waiting(Int)
	case finish
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 2번째 방법이 좋습니다. 첫 번째 방법의 경우는 ready인데 9명이 기다리고 있다라는 상태를 가질 수 있는 가능성을 모델링에서 막아주고 있지 않기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 하는 포스팅이기 때문에 가벼운 글부터 시작해 보았는데요. 도움이 되셨을 지 모르겠네요ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://namu.wiki/w/대수학&quot;&gt;https://namu.wiki/w/대수학&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.pointfree.co/episodes/ep4-algebraic-data-types&quot;&gt;https://www.pointfree.co/episodes/ep4-algebraic-data-types&lt;/a&gt;&lt;/p&gt;</description>
      <category>swift</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/12</guid>
      <comments>https://jk2b.tistory.com/12#entry12comment</comments>
      <pubDate>Sun, 21 Jan 2024 01:06:24 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 리코쳇 로봇 swift</title>
      <link>https://jk2b.tistory.com/11</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;문제설명&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;리코쳇 로봇이라는 보드게임이 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;이 보드게임은 격자모양 게임판 위에서 말을 움직이는 게임으로, 시작 위치에서 목표 위치까지 최소 몇 번만에 도달할 수 있는지 말하는 게임입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;이 게임에서 말의 움직임은 상, 하, 좌, 우 4방향 중 하나를 선택해서 게임판 위의 장애물이나 맨 끝에 부딪힐 때까지 미끄러져 이동하는 것을 한 번의 이동으로 칩니다.&lt;span style=&quot;text-align: left;&quot;&gt;다음은 보드게임판을 나타낸 예시입니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #263747; color: #b2c0cc; text-align: left;&quot;&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #202b3d; color: #b2c0cc;&quot;&gt;&lt;code&gt;...D..R
.D.G...
....D.D
D....D.
..D....
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 &quot;.&quot;은 빈 공간을, &quot;R&quot;은 로봇의 처음 위치를, &quot;D&quot;는 장애물의 위치를, &quot;G&quot;는 목표지점을 나타냅니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 예시에서는 &quot;R&quot; 위치에서 아래, 왼쪽, 위, 왼쪽, 아래, 오른쪽, 위 순서로 움직이면 7번 만에 &quot;G&quot; 위치에 멈춰 설 수 있으며, 이것이 최소 움직임 중 하나입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;게임판의 상태를 나타내는 문자열 배열&amp;nbsp;board가 주어졌을 때, 말이 목표위치에 도달하는데 최소 몇 번 이동해야 하는지 return 하는 solution함수를 완성하세요. 만약 목표위치에 도달할 수 없다면 -1을 return 해주세요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #263747; color: #b2c0cc; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;3 &amp;le;&amp;nbsp;board의 길이 &amp;le; 100&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;3 &amp;le;&amp;nbsp;board의 원소의 길이 &amp;le; 100&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;board의 원소의 길이는 모두 동일합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;문자열은 &quot;.&quot;, &quot;D&quot;, &quot;R&quot;, &quot;G&quot;로만 구성되어 있으며 각각 빈 공간, 장애물, 로봇의 처음 위치, 목표 지점을 나타냅니다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&quot;R&quot;과 &quot;G&quot;는 한 번씩 등장합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 bfs로 풀었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이디어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시작지점에서 보드판 위에 도달이 가능한 위치는 정해져 있습니다.(아무런 장애물도 없고 보드의 끝도 아닌 곳에서 멈춰설 수는 없으니까요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 각 위치에 도달하기까지 bfs로 최단거리를 구해보자!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 시작지점과 골인지점을 찾습니다. 시작지점 찾을 때 찾는 김에 Queue에 넣었습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 장애물을 만나거나 보드판의 끝에 도달할 때까지 (아래, 위, 오른쪽, 왼쪽) 방향으로&amp;nbsp; 쭉쭉 확인하며 나아갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 도달한 위치에 해당하는 visited배열의 값과 현재 내 이동횟수의 값을 비교해서 현재 내 이동횟수가 작으면 visited를 업데이트 해주고 Queue에 도달한 위치를 넣습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 큐가 빌 때까지 반복합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 골인지점의 값을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682150916366&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func solution(_ board: [String]) -&amp;gt; Int {
    var visited = Array(repeating: Array(repeating: Int.max, count: board[0].count), count: board.count)
    var queue = [[Int]]()
    var goalI = 0
    var goalJ = 0

    for i in 0 ..&amp;lt; board.count {
        for j in 0 ..&amp;lt; board[0].count {
            let row = Array(board[i])
            if row[j] == &quot;R&quot; {
                queue.append([i,j])
                visited[i][j] = 0
            } else if row[j] == &quot;G&quot; {
                goalI = i
                goalJ = j
            }
        }
    }

    repeat {
        let item = queue.removeFirst()

        // 아래로 Y값을 증가시키며 가봅시다.
        var curI = item[0]
        var curJ = item[1]
        repeat {
            let row = Array(board[curI])
            if row[curJ] == &quot;D&quot; {
                break
            }
            curI += 1
        } while (curI &amp;lt; board.count)
        if visited[curI-1][curJ] &amp;gt; visited[item[0]][item[1]] + 1 {
            visited[curI-1][curJ] = visited[item[0]][item[1]] + 1
            queue.append([curI-1, curJ])
        }

        // 위로
        curI = item[0]
        curJ = item[1]
        repeat {
            let row = Array(board[curI])
            if row[curJ] == &quot;D&quot; {
                break
            }
            curI -= 1
        } while (curI &amp;gt;= 0)
        if visited[curI+1][curJ] &amp;gt; visited[item[0]][item[1]] + 1 {
            visited[curI+1][curJ] = visited[item[0]][item[1]] + 1
            queue.append([curI+1, curJ])
        }

        // 오른쪽으로
        curI = item[0]
        curJ = item[1]
        var row = Array(board[curI])
        repeat {
            if row[curJ] == &quot;D&quot; {
                break
            }
            curJ += 1
        } while (curJ &amp;lt; board[0].count)
        if visited[curI][curJ-1] &amp;gt; visited[item[0]][item[1]] + 1 {
            visited[curI][curJ-1] = visited[item[0]][item[1]] + 1
            queue.append([curI, curJ-1])
        }

        // 왼쪽으로
        curI = item[0]
        curJ = item[1]
        row = Array(board[curI])
        repeat {
            if row[curJ] == &quot;D&quot; {
                break
            }
            curJ -= 1
        } while (curJ &amp;gt;= 0)
        if visited[curI][curJ+1] &amp;gt; visited[item[0]][item[1]] + 1 {
            visited[curI][curJ+1] = visited[item[0]][item[1]] + 1
            queue.append([curI, curJ+1])
        }

    } while (!queue.isEmpty)

    return visited[goalI][goalJ] == Int.max ? -1 : visited[goalI][goalJ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>bfs</category>
      <category>Swift</category>
      <category>프로그래머스</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/11</guid>
      <comments>https://jk2b.tistory.com/11#entry11comment</comments>
      <pubDate>Sat, 22 Apr 2023 17:15:17 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 요격 시스템 swift</title>
      <link>https://jk2b.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문제 설명&lt;/p&gt;
&lt;div style=&quot;background-color: #263747; color: #b2c0cc; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;A 나라가 B 나라를 침공하였습니다. B 나라의 대부분의 전략 자원은 아이기스 군사 기지에 집중되어 있기 때문에 A 나라는 B 나라의 아이기스 군사 기지에 융단폭격을 가했습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;A 나라의 공격에 대항하여 아이기스 군사 기지에서는 무수히 쏟아지는 폭격 미사일들을 요격하려고 합니다. 이곳에는 백발백중을 자랑하는 요격 시스템이 있지만 운용 비용이 상당하기 때문에 미사일을 최소로 사용해서 모든 폭격 미사일을 요격하려 합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;A 나라와 B 나라가 싸우고 있는 이 세계는 2 차원 공간으로 이루어져 있습니다. A 나라가 발사한 폭격 미사일은 x 축에 평행한 직선 형태의 모양이며 개구간을 나타내는 정수 쌍 (s, e) 형태로 표현됩니다. B 나라는 특정 x 좌표에서 y 축에 수평이 되도록 미사일을 발사하며, 발사된 미사일은 해당 x 좌표에 걸쳐있는 모든 폭격 미사일을 관통하여 한 번에 요격할 수 있습니다. 단, 개구간 (s, e)로 표현되는 폭격 미사일은 s와 e에서 발사하는 요격 미사일로는 요격할 수 없습니다. 요격 미사일은 실수인 x 좌표에서도 발사할 수 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;각 폭격 미사일의 x 좌표 범위 목록&amp;nbsp;targets이 매개변수로 주어질 때, 모든 폭격 미사일을 요격하기 위해 필요한 요격 미사일 수의 최솟값을 return 하도록 solution 함수를 완성해 주세요.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한 사항&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #263747; color: #b2c0cc; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;1 &amp;le;&amp;nbsp;targets의 길이 &amp;le; 500,000&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;targets의 각 행은 [s,e] 형태입니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;이는 한 폭격 미사일의 x 좌표 범위를 나타내며, 개구간 (s, e)에서 요격해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;0 &amp;le; s &amp;lt; e &amp;le; 100,000,000&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 타겟들을 오름차순으로 정렬한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 가장 작은 end값을 가진 타겟값을 기준으로 이 end값과 다음 타겟의 start값을 비교한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. end보다 start값이  크면 미사일을 발사해서 end보다 아래에 있는 모든 타겟을 요격하고 end값을 갱신한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 계속 반복!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 반복문 돌고 나서 나머지 타겟들을 요격하기 위해 한 발 더 발사!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답&lt;/p&gt;
&lt;pre id=&quot;code_1681988498522&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func solution(_ targets:[[Int]]) -&amp;gt; Int {
    var ans = 0
    let sorted = targets.sorted(by: { $0[1] &amp;lt; $1[1] })    
    
    var end = sorted[0][1]

    for target in sorted { 
        if target[0] &amp;gt;= end {
            end = target[1]
            ans += 1
        }
    }
    return ans + 1
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩테스트</category>
      <category>Swift</category>
      <category>프로그래머스</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/10</guid>
      <comments>https://jk2b.tistory.com/10#entry10comment</comments>
      <pubDate>Thu, 20 Apr 2023 20:06:42 +0900</pubDate>
    </item>
    <item>
      <title>SwiftUI 프로젝트 처음 만들면 생성되는 파일 뜯어보기</title>
      <link>https://jk2b.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제목만 보고 뭔가 싶으실 것 같아서 뭘 얘기하고 싶은지 부가적인 설명을 해보겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성날이 토요일이기에 Saturday라는 이름으로 프로젝트를 한 번 만들었습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2023-04-15 at 8.26.57 AM.png&quot; data-origin-width=&quot;2926&quot; data-origin-height=&quot;1888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba4dTY/btsak2AhwZI/ku6ZUtCSv5OjKqKriJoWQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba4dTY/btsak2AhwZI/ku6ZUtCSv5OjKqKriJoWQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba4dTY/btsak2AhwZI/ku6ZUtCSv5OjKqKriJoWQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba4dTY%2Fbtsak2AhwZI%2Fku6ZUtCSv5OjKqKriJoWQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;615&quot; height=&quot;397&quot; data-filename=&quot;Screenshot 2023-04-15 at 8.26.57 AM.png&quot; data-origin-width=&quot;2926&quot; data-origin-height=&quot;1888&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Saturday라는 폴더아래에 3개&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- SaturdayApp.swift&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ContentView.swift&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Assets.xcassets 하나(위에 보이는 Assets이라는 이름되어 있는 거)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Preview Content라는 폴더아래에 1개가 있네요. 간단하죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Preview Assets.xcassets&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호오옥시 몰라 터미널에서 확인해보았는데 정말 4개가 전부입니다(. 한개는 현재 폴더 .. 는 상위 폴더를 의미합니다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf6oaY/btsamXrOxNt/WigfGxkchZFOUy92MyEjm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf6oaY/btsamXrOxNt/WigfGxkchZFOUy92MyEjm0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;280&quot; data-filename=&quot;Screenshot 2023-04-15 at 8.32.19 AM.png&quot; data-widthpercent=&quot;31.7&quot; width=&quot;230&quot; height=&quot;93&quot; style=&quot;width: 31.3279%; margin-right: 10px;&quot; id=&quot;kEditorPhotosEditingImage-1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf6oaY/btsamXrOxNt/WigfGxkchZFOUy92MyEjm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf6oaY%2FbtsamXrOxNt%2FWigfGxkchZFOUy92MyEjm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;690&quot; height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lxFyc/btsagQnABgw/Zj29a39lUUADmKY6Jfikz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lxFyc/btsagQnABgw/Zj29a39lUUADmKY6Jfikz1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;174&quot; data-filename=&quot;Screenshot 2023-04-15 at 8.34.15 AM.png&quot; width=&quot;230&quot; style=&quot;width: 67.5093%;&quot; data-widthpercent=&quot;68.3&quot; id=&quot;kEditorPhotosEditingImage-2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lxFyc/btsagQnABgw/Zj29a39lUUADmKY6Jfikz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlxFyc%2FbtsagQnABgw%2FZj29a39lUUADmKY6Jfikz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;924&quot; height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;xcassets??&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 오늘 다룰 주제는 아니니 간단히 언급만 하자면 xcassets는 XCode에서 사용할 각종 에셋들을 모아놓은 곳입니다. 이미지나 색깔 등등이 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;App&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 그럼 이제 살펴볼 파일은 2개 남았는데 결론부터 말하면 SaturdayApp.swift에 앱을 켰을 때의 시작화면(?)을 표시하는 부분이 있고 ContentView.swift가 그 시작화면입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 우리는 SaturdayApp.swift파일만 보면 됩니다&lt;/p&gt;
&lt;pre id=&quot;code_1681515745015&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import SwiftUI

@main                            /// 1
struct SaturdayApp: App {        /// 2
    var body: some Scene {       /// 3
        WindowGroup {            /// 4
            ContentView()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짠! 되게 간단합니다! 한 줄씩 찬찬히 음미해봅시다ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;@main&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 프로그램은 진입점이 있어야 합니다.  당연히 App도 진입점이 있는데요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템에 여기서 시작해라고 알려주는 역할을 이 @main이 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학부가 컴공이시라면 C++을 대학교에서 배울 때 아래와 같은 코드를 많이 보셨을 거에요.&lt;/p&gt;
&lt;pre id=&quot;code_1681545542334&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main(int argc, char* argv[]) {
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 요런 main() 코드가 UIKit 내부에서 불리고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2023-04-15 at 5.02.23 PM.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J9CDz/btsake2adPZ/4SHnJpx8SlZLJwuKpBokK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J9CDz/btsake2adPZ/4SHnJpx8SlZLJwuKpBokK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J9CDz/btsake2adPZ/4SHnJpx8SlZLJwuKpBokK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ9CDz%2Fbtsake2adPZ%2F4SHnJpx8SlZLJwuKpBokK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;415&quot; data-filename=&quot;Screenshot 2023-04-15 at 5.02.23 PM.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 글을 읽어보시면 아시겠지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main함수 부르지 마 시스템에서 알아서 호출하니까 대신 네 앱의 진입점을 @main로 표시하면 되&lt;/b&gt;라고 써있네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는 @UIApplicationMain이 이 역할을 했었는데 Xcode 12부터는 @main키워드도 이 역할을 수행하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 Xcode버전이 14이상이니 UIKit으로 만들던 SwiftUI로 만들던 @main키워드를 자동으로 붙여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 역할이 같은 키워드를 또 만들었는지에 대한 내용은 살짝 길어질 것 같으니 다음에 열심히 정리해보겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밑에 green1229님 블로그 링크를 타고 들어가면 설명이 나와있으니 참고하셔도 좋을 것 같아요 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;struct 앱의 이름: App&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 App은 프로토콜입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의를 보면 앱의 구조와 행동을 나타내는 타입이라고 하네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Command + 클릭을 통해 들어가보면 7만줄 가까이되는 코드가 보이는데요. 그 중 App은 요렇게 생겼습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1681547320356&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public protocol App {
    associatedtype Body : Scene

    @SceneBuilder @MainActor var body: Self.Body { get }

    @MainActor init()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;associatedtype Body : Scene&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scene프로토콜을 따르는 App 프로토콜에서 사용할 generic 타입을 정의하였네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@SceneBuilder @MainActor var body: Self.Body { get }&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단어별로 뜯어보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@SceneBuilder&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Builder&lt;/b&gt;는 뭔가를 만드는 객체를 Builder라고 하는데 그럼 Scene을 만드는 것이겠네?!라는 것을 알 수 있구&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Scene&lt;/b&gt;은 내가 유저들에게 보여주고 싶은 View 계층을 담은 컨테이너입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1681549505301&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public protocol Scene {
    associatedtype Body : Scene
    
    @SceneBuilder @MainActor var body: Self.Body { get }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;View&lt;/b&gt;는 무엇이냐?! 우리가 앱 켰을 때 화면에 보이는 Label이나 Image같은 UI요소 전부를 말합니다&lt;/p&gt;
&lt;pre id=&quot;code_1681549528375&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol View {
    associatedtype Body : View

    @ViewBuilder @MainActor var body: Self.Body { get }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2023-04-15 at 6.08.15 PM.png&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bksu7U/btsakftib9c/eKxekPJAfJPce711UYdslk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bksu7U/btsakftib9c/eKxekPJAfJPce711UYdslk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bksu7U/btsakftib9c/eKxekPJAfJPce711UYdslk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbksu7U%2Fbtsakftib9c%2FeKxekPJAfJPce711UYdslk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;311&quot; data-filename=&quot;Screenshot 2023-04-15 at 6.08.15 PM.png&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WWDC에 소개된 그림을 보면 아시겠지만 App은 Scene들로 구성되어 있고 각 Scene은 View의 계층을 담고 있네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@MainActor&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #000000; color: #f5f5f7; text-align: left;&quot;&gt;A singleton actor whose executor is equivalent to the main dispatch queue.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 공식문서에 나와있습니다. 우리가 iOS 면접볼 때 필수적으로 공부해야할 그 DispatchQueue.main&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(sync는 호출하면 안되며.. serial큐이며...)&lt;/span&gt;이 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자, MainActor가 무엇이냐? &lt;a href=&quot;https://itnext.io/swift-actors-e80ff0dc1832&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Actor&lt;/a&gt;개념을 먼저 살펴보아야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift 5.5.1부터 추가된 Concurrency(동시성)와 관련된 개념인데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시성 프로그래밍에서 발생하는 문제 data race 문제를 다루기 위해 추가되었다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actor전에는 concurrent 큐에서 작업 하나 실행할 때 flags로 barrier를 준다든가 serial큐에서 sync를 쓰던가 해서 한 변수에 동시 접근하는 것을 막아왔더랬죠. Actor에서는 Isolated access와 nonisolated access를 제공합니다. 기본값은 isolated이구요 이렇게 하면 내부적으로 알아서 data race를 막아준다고 하네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암튼 샛길에서 다시 돌아와서 본론으로 가면 main이라고 써있으니까 왠지 UI관련이 있을것 같죠? 맞습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1681551657677&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DispatchQueue.global().async { [weak self] in
    DispatchQueue.main.async {
        self?.testhLabel.text = &quot;UI 업데이트는 언제나 Main thread에서&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명을 위한 예제입니다. 위 코드를 MainActor를 사용하면 아주 예쁘게 바꿀 수 있습니다&lt;/p&gt;
&lt;pre id=&quot;code_1681551802518&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DispatchQueue.global().async { [weak self] in
    self?.updateLabel()
}

@MainActor
func updateLabel() {
    recentSearchLabel.text = &quot;요로코롬하면 보라색 버그를 보지 않을 수 있어요&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나 더 주의깊게 볼 것은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;@&lt;/span&gt;MainActor가 var body라는 프로퍼티 앞에도 붙을 수 있다는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상이 되시겠지만 &lt;b&gt;이 프로퍼티는 Main Thread에서 접근하세요&lt;/b&gt;라는 뜻입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;var Body: some Scene&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방금 위에서 언급한 View들의 계층구조를 담고 있는 그 Scene입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;WindowGroup&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681552152146&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 공식문서에 나온 거
struct WindowGroup&amp;lt;Content&amp;gt; where Content : View

// Xcode에서 Jump to Definition 눌러서 나온 거
public struct WindowGroup&amp;lt;Content&amp;gt; : Scene where Content : View {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의를 보면 WindowGroup은 Scene 프로토콜을 준수했습니다. WWDC에서는 WindowGroup을 User Interface를 관리하는 Scene이라고 말하네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS보다는 iPadOS, macOS에서 사용되는 기능이구 여러 윈도우를 띄운다던가 여러 윈도우를 하나의 탭으로 묶는다던가 하는 기능들을 제공한다네요.ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 어차피 iOS는 윈도우 하나만 띄울거니 WindowGroup대신 Window로 할 수 있나 해봤더니 왠걸.. 지원을 안하네요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2023-04-15 at 7.17.53 PM.png&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX41mu/btsal366Kju/xE240WaaNRmX0fhkz25yJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX41mu/btsal366Kju/xE240WaaNRmX0fhkz25yJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX41mu/btsal366Kju/xE240WaaNRmX0fhkz25yJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX41mu%2Fbtsal366Kju%2FxE240WaaNRmX0fhkz25yJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;738&quot; height=&quot;162&quot; data-filename=&quot;Screenshot 2023-04-15 at 7.17.53 PM.png&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window가 뭔지 더 찾아보았지만 딱히 더 쓸 것이 안나와서 여기서 나름대로 정리를 해보자면 WindowGroup은 Window들을 관리하는 기능을 담당하고 각 Window는 View 계층 을 담은 유니크한 인스턴스로 사용되는 것 같네요ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글을 쓰면서 느낀점은 너무 많은 개념들이 한 곳에 담겨있는 듯한 느낌을 받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주제도 잘못 고른 것 같고 정리도 안되고 보기도 어렵고..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 다시 개념 하나씩 하나씩 조급해하지 말고 다시 정리해야겠다는 생각이 드네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 딱히 재밌는 것이 없어서 시간도 많은데 잘됐죠 뭐ㅎㅎㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 오늘도 읽어주셔서 감사합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 및 출처&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2020/10037/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.apple.com/videos/play/wwdc2020/10037/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@abedalkareemomreyh/what-is-main-in-swift-bc79fbee741c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/@abedalkareemomreyh/what-is-main-in-swift-bc79fbee741c&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://green1229.tistory.com/265&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://green1229.tistory.com/265&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://developer.apple.com/documentation/swiftui/app/main()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://babbab2.tistory.com/180&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://babbab2.tistory.com/180&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ios-development.tistory.com/1063&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ios-development.tistory.com/1063&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://itnext.io/mainactor-in-swift-detailed-walkthrough-94044c83118b&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://itnext.io/mainactor-in-swift-detailed-walkthrough-94044c83118b&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://itnext.io/swift-actors-e80ff0dc1832&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://itnext.io/swift-actors-e80ff0dc1832&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/hcleedev/swift-actor%EB%9E%80-f8f58c68dab9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/hcleedev/swift-actor%EB%9E%80-f8f58c68dab9&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://itnext.io/thread-sanitizer-in-ios-8438ee3c8c76&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://itnext.io/thread-sanitizer-in-ios-8438ee3c8c76&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2022/10061/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.apple.com/videos/play/wwdc2022/10061/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것들 말고도 많은데.... 도저히 정리가 안됨&lt;/p&gt;</description>
      <category>SwiftUI</category>
      <category>@main</category>
      <category>app</category>
      <category>ContentView</category>
      <category>SwiftUI</category>
      <category>WindowGroup</category>
      <category>프로젝트 생성</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/9</guid>
      <comments>https://jk2b.tistory.com/9#entry9comment</comments>
      <pubDate>Sat, 15 Apr 2023 19:35:49 +0900</pubDate>
    </item>
    <item>
      <title>테스트 더블이란</title>
      <link>https://jk2b.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 더블(Test Double)이란 저자인 xUnit Test Patterns의 제라드 메스자로스(Gerard Meszaros)가 만든 용어로 테스트를 진행하기 어려울 때 대신 테스트를 진행할 수 있도록 도와주는 객체를 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 객체는 크게 5개의 종류로 나뉩니다요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2023-04-13 at 7.12.40 AM.png&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/er4Thi/btr9NEt5QPG/KmzC0Qj8oYqjTejp3wkwK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/er4Thi/btr9NEt5QPG/KmzC0Qj8oYqjTejp3wkwK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/er4Thi/btr9NEt5QPG/KmzC0Qj8oYqjTejp3wkwK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fer4Thi%2Fbtr9NEt5QPG%2FKmzC0Qj8oYqjTejp3wkwK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;184&quot; data-filename=&quot;Screen Shot 2023-04-13 at 7.12.40 AM.png&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Dummy&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Martin Fowler에 의하면 Dummy는 전달되지만 실제로는 쓰지 않을 객체를 말합니다. 그저 init할 때 parameter를 채우기 위한 용도로만 쓰이구요. 내부 구현은 하지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;protocol SomeProtocol {
	func doSomething()
	func logSomething() -&amp;gt; String
}

class DummySomeProtocol: SomeProtocol {
	func doSomething() {}
	func logSomething() -&amp;gt; String {
		return &quot;&quot;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Stub&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Martin Fowler에 의하면 Stub은 미리 정한 값(예를 들면 더미값)을 테스트 시에 그대로 반환하는 역할을 합니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;// 예제
class RecentKeywordsStub: RecentKeywordsRepository {
	let keywords = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
	func getRecentKeywords() -&amp;gt; [String] {
		return keywords
  }
}
// 테스트
func test_getRecentKeywords() {
   XCTAssertEqual(sut.getRecentKeywords(), sut.keywords)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Spy&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스텁과 같은 역할을 하며 더불어 메소드 호출 여부와 호출 횟수를 기록하는 역할을 합니다. 솔직히 스파이는 써보진 않았습니다. 이 행위를 검증하는 역할을 Mock을 만들어서 검증을 많이 하였습니다. 그래도 일단 아래와 같이 사용할 수 있다고는 하네요. 테스트 마지막 부분을 보면 호출이 정확히 되었는지를 확인하고 있습니다&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;class QuotesServiceSpy: QuotesService {
    private (set) var quoteCalls: [[String]] = []
    private let stocks: [Stock]
    private let error: Error?
    
    init(stocks: [Stock] = [], error: Error? = nil) {
        self.stocks = stocks
        self.error = error
    }
    
    func getQuotes(symbols: [String], 
                   completion: @escaping (Result&amp;lt;[Stock], Error&amp;gt;) -&amp;gt; Void) {
        quoteCalls.append(symbols)
        if let error = error {
            completion(.failure(error))
        } else {
            completion(.success(stocks))
        }
    }
}

class WatchlistViewModelTests: XCTestCase {    
    func test_getWatchlistWithSymbols_callGetQuotes() {
        let stockSymbols = [&quot;AAPL&quot;, &quot;MSFT&quot;, &quot;GOOG&quot;]
        let watchlistStore = WatchlistStoreStub(stockSymbols: stockSymbols)
        let quotesService = QuotesServiceSpy()

        let sut = WatchlistViewModel(store: watchlistStore, 
                                     service: quotesService)
        sut.getWatchlist()

				// 여기서 정확히 호출이 되었나를 확인해 테스트 통과/실패
        XCTAssertEqual(quotesService.quoteCalls[0], stockSymbols)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Fake&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Martin Fowler에 의하면 Fake 객체는 동작하는 구현을 실제로 가지고 있지만 실제 상품으로 내기보다는 테스트를 위해 간편하게 만든 구현을 가지고 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 Fake만보기의 start함수의 구현을 예로 들면 실제 걸음수를 가지고 와서 업데이트 하는 것이 아니라 1초마다 걸음 수를 올리는 것처럼 말이죠. 이렇게 하면 실제 디바이스가 아닌 시뮬레이터에서 테스트를 하더라도 걸음수가 올라가는지 테스트를 할 수 있겠죠?!&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;import Foundation

class SimulatorPedometer: Pedometer {
  var timer: Timer?
  var distance = 0.0

  func start()
    timer = Timer(timeInterval: 1, repeats: true) { _ in
      self.distance += 1
    }
    RunLoop.main.add(timer!, forMode: RunLoop.Mode.default)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Mock&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Martin Fowler에 의하면 Mock은 함수들이 원하는데로 호출되었는지를 검증할 때 쓴다고 합니다. 으음.. 저는 여기까지만 읽었을 때에는 Spy와 너무 비슷한 느낌이 드는데요..? 뭐가 다른건지 참.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 네트워크를 Mocking하는 것을 한 번 정리해보려고합니다. 맨 밑에 이미지에 각 테스트 더블들의 범위(?)를 나타낸 사진이 있는데 솔직히 저는 Mock은 행위 검증이든 상태검증이든 다 할 수 있는 것 같은 느낌이 들더라구요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 테스트 더블의 종류들의 경계가 살짝 모호한 것 같고 칼로 딱 나눠자를 수 없다는 생각이 들었는데 저는 아래 그림처럼 이해를 해보니까 조금 더 도움이 되더랍니다.ㅎㅎㅎㅎ.. 누군가에게 도움이 되었기를&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;test-double-spectrum.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx9ba5/btr9NmmZlv8/eVYJS2aWiNHk0IAhi4zI41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx9ba5/btr9NmmZlv8/eVYJS2aWiNHk0IAhi4zI41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx9ba5/btr9NmmZlv8/eVYJS2aWiNHk0IAhi4zI41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx9ba5%2Fbtr9NmmZlv8%2FeVYJS2aWiNHk0IAhi4zI41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;828&quot; height=&quot;512&quot; data-filename=&quot;test-double-spectrum.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*&lt;a href=&quot;https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/september/unit-testing-exploring-the-continuum-of-test-doubles*&quot;&gt;https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/september/unit-testing-exploring-the-continuum-of-test-doubles*&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although these types seem distinct in theory, the differences become more blurred in practice. For that reason, I think it makes sense to think of test doubles as inhabiting a continuum, as illustrated in Figure 2. - MSDN Magazine -&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 및 출처&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@pena.fernan/test-doubles-by-example-in-swift-558e9f47de52&quot;&gt;https://medium.com/@pena.fernan/test-doubles-by-example-in-swift-558e9f47de52&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hudi.blog/test-double/&quot;&gt;https://hudi.blog/test-double/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mokacoding.com/blog/swift-test-doubles/&quot;&gt;https://mokacoding.com/blog/swift-test-doubles/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.martinfowler.com/bliki/TestDouble.html&quot;&gt;https://www.martinfowler.com/bliki/TestDouble.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://codinghack.tistory.com/92&quot;&gt;https://codinghack.tistory.com/92&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sujinnaljin.medium.com/swift-mock-을-이용한-network-unit-test-하기-a69570defb41&quot;&gt;https://sujinnaljin.medium.com/swift-mock-을-이용한-network-unit-test-하기-a69570defb41&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.swiftbysundell.com/articles/mocking-in-swift/&quot;&gt;https://www.swiftbysundell.com/articles/mocking-in-swift/&lt;/a&gt;&lt;/p&gt;</description>
      <category>swift</category>
      <category>ios</category>
      <category>Swift</category>
      <category>TDD</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/8</guid>
      <comments>https://jk2b.tistory.com/8#entry8comment</comments>
      <pubDate>Thu, 13 Apr 2023 08:35:40 +0900</pubDate>
    </item>
    <item>
      <title>SwiftUI - 선언형과 명령형</title>
      <link>https://jk2b.tistory.com/6</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요 keibi입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;처음으로 인사를 하고 글을 쓰려고 하는데 소개부터 한참 시간이 걸리네요&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아무튼...&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;오늘부터 SwiftUI 관련 공부하며 정리를 시작합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;개인프로젝트를 하려고 하는데 최신 프로그래밍 언어의 흐름도 공부할 겸 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;SwiftUI와 Combine&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;으&lt;/span&gt;&lt;/span&gt;로 하기로 했거든요.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;(SwiftUI로 preview그릴 때 맥북이 힘겨워하는 게 느껴지네요 5년 썼으니 바꿀 때가 된 듯합니다.)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;맨 처음 짚고 넘어갈 것은 왜 SwiftUI인가?! 에 대해서 기존 UIKit과 비교해서 정리해보려고 합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;새로 나온 기술은 기존 기술과 다른 점이 있고 그 다른 점이 필요해서 나온 경우가 많으니까요&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;선언형과 명령형??? What과 How???&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;자료를 모으고 조사하면서 가장 먼저 정리해야겠다 싶은 것은&amp;nbsp;이 두 개념인 것 같아요.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 선언형 프로그래밍은 명령형 프로그래밍과 대비되는 개념으로&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로그램이 수행할 동작을 순차적으로 나열하는 것이 아니라&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과적으로 구현하고자 하는 것을 '명세'하는 형태로 작성&lt;/b&gt;하는 프로그래밍 방법입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;WHAT(구현하고자 하는 것을 명세) vs HOW(수행할 동작을 순차적)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이게 무슨 말이죠??&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;예시를 들어볼게요&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아내가 저에게 저녁밥을 차려야 하니 계란 10구짜리 하나 사 오라고 합니다&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;(아내 왈, 여보 간단하게 계란 장조림만 다시 해서 밥 묵자)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;명령형 : 집으로 오는 길에 왼쪽을 확인한다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; OO마트가 문을 열었는지 확인하고 들어간다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 들어가서 왼쪽 신선식품 쪽으로 걸어간다&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 신선식품 코너 옆에 있는 10구짜리 계란을 집는다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 결제할 곳으로 가지고 간다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 카드로 결제한다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 비닐에 계란을 담아서 집에 들고 간다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선언형 : 신선한 계란 10구 하나 부탁해~&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;차이가 보이시나요??&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;선언형은 무엇에 해당하는 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;'계란'&lt;/b&gt;에만 관심이 있고&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;명령형은 어떻게 계란을 살 수 있는지를 다 적는다는 차이가 있죠&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;저는 여기서 답답한 마음에 질문이 하나 생기더라고요.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아니 어쨌든 계란은 어딘가에 들려서 카드든 현금이든 내고&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;사 와야 하는데 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;필요한 다른 정보는 어디에 있는 거지??&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;답변부터 하자면&lt;span style=&quot;color: #f89009;&quot;&gt; SwiftUI가 필요한 정보를 알아서 해결해 준다!입니다&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;선언형 방식이 동작하기 위해서는 '어떻게'에 해당하는 (마트 위치나, 결제 수단 같은 것들) 것들이 추상화되어 있어야 해요&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아래와 같은 화면을 UIKit과 SwiftUI로 구현해서 차이점을 알아볼게요. 배경에 이미지와 글자만 있는 화면입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Simulator Screenshot - iPhone 14 Pro - 2023-04-10 at 20.37.10.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GeWH3/btr9mjKGKwN/qkWuOHAPsQinhqxp7hX3y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GeWH3/btr9mjKGKwN/qkWuOHAPsQinhqxp7hX3y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GeWH3/btr9mjKGKwN/qkWuOHAPsQinhqxp7hX3y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGeWH3%2Fbtr9mjKGKwN%2FqkWuOHAPsQinhqxp7hX3y0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;215&quot; height=&quot;466&quot; data-filename=&quot;Simulator Screenshot - iPhone 14 Pro - 2023-04-10 at 20.37.10.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 SwiftUI&lt;/p&gt;
&lt;pre id=&quot;code_1681126169246&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        ZStack {
            Color.purple
                .ignoresSafeArea()
            VStack {
                Image(systemName: &quot;globe&quot;)
                    .imageScale(.large)
                    .foregroundColor(.black)
                Text(&quot;Hello, world!&quot;)
                    .font(.largeTitle)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 UIKit&lt;/p&gt;
&lt;pre id=&quot;code_1681128186087&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        view.backgroundColor = .purple
        
        let imageView = UIImageView(image: UIImage(systemName: &quot;globe&quot;))
        imageView.tintColor = .white
        imageView.scalesLargeContentImage = true
        imageView.translatesAutoresizingMaskIntoConstraints = false
        
        let textLabel = UILabel()
        textLabel.translatesAutoresizingMaskIntoConstraints = false
        textLabel.text = &quot;Hello, World!&quot;
        textLabel.font = .preferredFont(forTextStyle: .largeTitle)
        textLabel.textColor = .white
        
        view.addSubview(imageView)
        view.addSubview(textLabel)
        
        NSLayoutConstraint.activate([
            imageView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
            imageView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: -20)
        ])
        NSLayoutConstraint.activate([
            textLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
            textLabel.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 20)
        ])
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;확실히 기존 UIKit은 어떻게까지 일일이 개발자가 쓰다 보니 코드의 양이 좀 더 많은 것을 볼 수 있고&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;SwiftUI는 많은 것이 미리 정의가 되어 있으니 보다 짧은 코드로 쓸 수 있는 것을 알 수 있죠&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 몇 가지 더 쓸 거 없나 끄적끄적거리다가 VStack을 뷰의 상단에 붙여보고 싶어졌어요.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 VStack이 제공하는 옵션은 딱 2개뿐이더라고요 alignment, spacing&lt;/p&gt;
&lt;pre id=&quot;code_1681128800147&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@frozen public struct VStack&amp;lt;Content&amp;gt; : View where Content : View {

    /// Creates an instance with the given spacing and horizontal alignment.
    ///
    /// - Parameters:
    ///   - alignment: The guide for aligning the subviews in this stack. This
    ///     guide has the same vertical screen coordinate for every subview.
    ///   - spacing: The distance between adjacent subviews, or `nil` if you
    ///     want the stack to choose a default distance for each pair of
    ///     subviews.
    ///   - content: A view builder that creates the content of this stack.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;확실히 많은 부분이 추상화된 만큼 직접 건드릴 수 있는 부분은 적어진 것 같아요. 그럼 이럴 땐 어떻게 하냐?!&lt;/p&gt;
&lt;pre id=&quot;code_1681128881168&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;VStack {
    Image(systemName: &quot;globe&quot;)
        .imageScale(.large)
        .foregroundColor(.white)
    Text(&quot;Hello, world!&quot;)
        .font(.largeTitle)
        .foregroundColor(.white)
    Spacer()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;요렇게 밑에 길이가 남은 공간에 따라 줄어드는 Spacer()를 활용하면 요렇게 위에 붙일 수가 있습니다.(익숙해지려면 약간의 노하우(?)같은 지식이 필요하겠네요ㅎㅎ)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Simulator Screenshot - iPhone 14 Pro - 2023-04-10 at 21.15.51.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/62Xsb/btr9n6cLxCf/SYq9v9MsMALnLnp6Iks1Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/62Xsb/btr9n6cLxCf/SYq9v9MsMALnLnp6Iks1Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/62Xsb/btr9n6cLxCf/SYq9v9MsMALnLnp6Iks1Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F62Xsb%2Fbtr9n6cLxCf%2FSYq9v9MsMALnLnp6Iks1Gk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;215&quot; height=&quot;466&quot; data-filename=&quot;Simulator Screenshot - iPhone 14 Pro - 2023-04-10 at 21.15.51.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;읽어주셔서 감사합니다!&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;언제든 잘못된 정보는 꼭 답글로 달아주시면 감사하겠습니다 ( _ _ )&lt;/p&gt;</description>
      <category>SwiftUI</category>
      <category>SwiftUI</category>
      <category>명령형</category>
      <category>선언형</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/6</guid>
      <comments>https://jk2b.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 10 Apr 2023 21:21:49 +0900</pubDate>
    </item>
    <item>
      <title>Swift, Objective-C, C++ 같이 사용하기</title>
      <link>https://jk2b.tistory.com/5</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;iOS 앱을 만들 때 모든 것을 다 Swift로 짜면 좋으련만 프로젝트를 진행하다보면 그렇지 못할 경우가 간혹 생깁니다.&lt;/p&gt;
&lt;p&gt;Web RTC를 쓴다던지 게임 엔진을 같이 사용해야 한다던지 등등..&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;회사에서 cocos2dx 게임엔진이 메탈을 지원하기 때문에 cocos2dx를 swift와 같이 사용할 일이 생겼습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 때 처음으로 C++ 소스를 Swift와 같이 사용하게 되며 공부를 하였는데 어떻게 사용할 수 있는지 그 방법을 다시 정리해보았습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;결론을 말씀드리면 C++는 &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Objective C++로 Wrapping&lt;/b&gt;&lt;/span&gt;해서 Swift에서도 쓸 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;1_YBZOl-9gdC_wghdYutAyiQ.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oE8Eo/btqDDvgGWyC/UAy8q30j7Oamcnun4VaYV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oE8Eo/btqDDvgGWyC/UAy8q30j7Oamcnun4VaYV1/img.png&quot; data-alt=&quot;Photo credit: Cecilia Humlelu&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oE8Eo/btqDDvgGWyC/UAy8q30j7Oamcnun4VaYV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoE8Eo%2FbtqDDvgGWyC%2FUAy8q30j7Oamcnun4VaYV1%2Fimg.png&quot; data-filename=&quot;1_YBZOl-9gdC_wghdYutAyiQ.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;690&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Photo credit: Cecilia Humlelu&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Swift는 Objective C(++)과 C는 직접 상호동작이 가능하지만 C++은 그렇지 못한 걸 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 순서대로 진행할 생각입니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. Xcode 프로젝트 Swift로 생성&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. C++ 파일 작성&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;3. Objective C++ Wrapper만들기&lt;/p&gt;
&lt;p&gt;4. 브릿지 헤더에 만든 Objective C++의 헤더 추가하기&lt;/p&gt;
&lt;p&gt;5. Swift에서 호출하기&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. Xcode 프로젝트 Swift로 생성&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;편한 이름으로 언어는 Swift를 선택하시고 iOS 앱 프로젝트를 생성해주세요. 저는 CppTest로 하였습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. C++ 파일 작성&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;[File] -&amp;gt; [New] -&amp;gt; [File]을 눌러(혹은&amp;nbsp;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;⌘N&lt;/span&gt;) 새로운 C++ source file을 만들어주세요.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;생성 시&lt;b&gt; Also create a header file&lt;/b&gt;을 체크하고 만들어주세요. 저는 MyCpp로 하였습니다.&lt;/p&gt;
&lt;p&gt;그러면 Bridge header를 만들 것인지 묻습니다. 만들어주세요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-24 오후 4.58.22.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;286&quot; width=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HGnue/btqDGgxUZAG/P8jf2k62QhScJAFg3leTt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HGnue/btqDGgxUZAG/P8jf2k62QhScJAFg3leTt1/img.png&quot; data-alt=&quot;브릿지 헤더를 만듭니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HGnue/btqDGgxUZAG/P8jf2k62QhScJAFg3leTt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHGnue%2FbtqDGgxUZAG%2FP8jf2k62QhScJAFg3leTt1%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-24 오후 4.58.22.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;286&quot; width=&quot;430&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;브릿지 헤더를 만듭니다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;다 만들고 나면 아래와 같이 파일들이 생성될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-25 오전 11.46.42.png&quot; data-origin-width=&quot;504&quot; data-origin-height=&quot;506&quot; width=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU33NG/btqDGHJjbEE/m21rbovHcJP7eICRYIvvq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU33NG/btqDGHJjbEE/m21rbovHcJP7eICRYIvvq0/img.png&quot; data-alt=&quot;C++ 파일 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU33NG/btqDGHJjbEE/m21rbovHcJP7eICRYIvvq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU33NG%2FbtqDGHJjbEE%2Fm21rbovHcJP7eICRYIvvq0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-25 오전 11.46.42.png&quot; data-origin-width=&quot;504&quot; data-origin-height=&quot;506&quot; width=&quot;378&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;C++ 파일 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;b&gt;*.hpp 파일을 열고 아래와 같이 입력해주세요.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1587782909871&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MyCpp.hpp

#include &amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;

class MyCpp {
public:
    MyCpp();							// 생성자
    MyCpp(const std::string &amp;amp;text);				// 변수를 입력 받는 생성자
    ~MyCpp();							// 소멸자(Destructor)

public:
    void sayHello();						// Hello world를 출력하는 함수

    void setText(const std::string &amp;amp;text);			// m_text의 getter &amp;amp; setter
    const std::string &amp;amp;getText();
    
    void setNumber(const int number);				// m_number의 getter &amp;amp; setter
    int getNumber();

private:
    std::string m_text;						// 문자열 변수
    int m_number;						// 정수값 변수
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;b&gt;*.cpp 파일을 열고 아래와 같이 입력해주세요.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1587782980567&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//  MyCpp.cpp


#include &quot;MyCpp.hpp&quot;

MyCpp::MyCpp():m_text() {}
MyCpp::MyCpp(const std::string &amp;amp;text): m_text(text) {}
MyCpp::~MyCpp() {}

void MyCpp::sayHello()
{
    std::cout &amp;lt;&amp;lt; &quot;Hello world!&quot; &amp;lt;&amp;lt; std::endl;
    
}

void MyCpp::setText(const std::string &amp;amp;text)
{
    m_text = text;
}
const std::string &amp;amp;MyCpp::getText()
{
    return m_text;
}

void MyCpp::setNumber(const int number)
{
    m_number = number;
}

int MyCpp::getNumber()
{
    return m_number;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;설명이 필요없는 아주 기본적인 각각 문자열과 정수값을 설정하고 반환하는 함수들입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. Objective C++ Wrapper만들기&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;새로운 objective-c 파일을 만들어주세요. 그리고 확장자를 &lt;b&gt;.mm&lt;/b&gt;으로 바꿉니다. 저는 이름을 CWrapper.mm로 하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;방금 만든 파일의 헤더(header)도 만들어주세요. 제 파일은 CWrapper.h입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Objective-C++을 생성하고 나서의 모습입니다. 아래쪽에 *.mm과 *.h가 하나씩 추가되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-25 오후 12.04.23.png&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;550&quot; width=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpnNWI/btqDH0BD5tZ/w4wGhwsDarZfG8a4KqMSTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpnNWI/btqDH0BD5tZ/w4wGhwsDarZfG8a4KqMSTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpnNWI/btqDH0BD5tZ/w4wGhwsDarZfG8a4KqMSTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpnNWI%2FbtqDH0BD5tZ%2Fw4wGhwsDarZfG8a4KqMSTk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-25 오후 12.04.23.png&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;550&quot; width=&quot;366&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;b&gt;*.h를 작성해 주세요&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1587783566168&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//  CWrapper.h

#import &amp;lt;Foundation/Foundation.h&amp;gt;

@interface CWrapper : NSObject
- (instancetype)initWithText:(NSString*)text;
- (void)helloWorld;

- (void)setText:(NSString*)text;
- (NSString*)getText;

- (void)setNumber:(int)number;
- (int)getNumber;
@end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;C++에 있던 것과 마찬가지로 문자열을 입력받는 상속자와 문자열, 정수값을 설정하고 반환하는 함수들을 선언하였습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;b&gt;이제 *.mm을 작성해 주세요.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;만약 복붙이 아니라 직접 입력을 하신다면 헤더 파일을 만들어서 자동완성에 함수가 뜨는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1587783948722&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#import &quot;CWrapper.h&quot;
#include &quot;MyCpp.hpp&quot;

@interface CWrapper()
@property MyCpp *cppItem;
@end
@implementation CWrapper
- (instancetype)init
{
    self = [super init];
    self.cppItem = new MyCpp();
    return self;
}
- (instancetype)initWithText:(NSString*)text
{
    self = [super init];
    self.cppItem = new MyCpp(std::string([text cStringUsingEncoding:NSUTF8StringEncoding]));
    
    return self;
}

- (void)helloWorld
{
    printf(&quot;Hello world&quot;);
}

- (void)setText:(NSString *)text
{
    self.cppItem-&amp;gt;setText(std::string([text cStringUsingEncoding:NSUTF8StringEncoding]));
}

- (NSString *)getText
{
    return [NSString stringWithUTF8String:self.cppItem-&amp;gt;getText().c_str()];
}

- (void)setNumber:(int)number
{
    self.cppItem-&amp;gt;setNumber(number);
}

- (int)getNumber
{
    return self.cppItem-&amp;gt;getNumber();
}
@end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. 브릿지 헤더에 만든 Objective C++의 헤더 추가하기&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;아래와 같이 방금 만든 Objective C++의 헤더를 추가해주세요. 이제 준비는 끝났습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;b&gt;[project name]-Bridging-Header.h&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1587784029388&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// CppTest-Bridging-Header.h

#import &quot;CWrapper.h&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;5. Swift에서 호출하기&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;b&gt;ViewController.swift&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1587784074243&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//  ViewController.swift

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let cppItem = CWrapper();

        cppItem.helloWorld()
        print(cppItem.getText())
        cppItem.setText(&quot;This is test string&quot;)
        print(cppItem.getText())

        
        let cppItem2 = CWrapper(text: &quot;Hi my name is cpp&quot;);
        print(cppItem2?.getText())
        
        cppItem2?.setNumber(33)
        print(cppItem2?.getNumber())
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;실행하면 콘솔에 다음과 같은 로그를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;(std::endl은 콘솔에서는 동작하지 않네요ㅠ ㅎㅎ)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-25 오후 12.09.43.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;540&quot; width=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cq7BpQ/btqDIooHHRQ/ccnVfaU2oUcJQtL7KUMgYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cq7BpQ/btqDIooHHRQ/ccnVfaU2oUcJQtL7KUMgYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cq7BpQ/btqDIooHHRQ/ccnVfaU2oUcJQtL7KUMgYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcq7BpQ%2FbtqDIooHHRQ%2FccnVfaU2oUcJQtL7KUMgYk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-25 오후 12.09.43.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;540&quot; width=&quot;645&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;[참조]&lt;/p&gt;
&lt;p&gt;(첫 번째 사진)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@cecilia.humlelu/using-c-c-and-objective-c-frameworks-in-swift-apps-6a60e5f71c36&quot;&gt;https://medium.com/@cecilia.humlelu/using-c-c-and-objective-c-frameworks-in-swift-apps-6a60e5f71c36&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1587784159319&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Using C, C++ and Objective-C frameworks in Swift apps&quot; data-og-description=&quot;I did a talk in try!Swift Tokyo this year about using C, C++ and Objective-C frameworks in Swift apps. The presentation was quite concise&amp;hellip;&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@cecilia.humlelu/using-c-c-and-objective-c-frameworks-in-swift-apps-6a60e5f71c36&quot; data-og-url=&quot;https://medium.com/@cecilia.humlelu/using-c-c-and-objective-c-frameworks-in-swift-apps-6a60e5f71c36&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bggD39/hyFNRgvWXr/2wxhymQWsMfKxKia1DIakk/img.png?width=1200&amp;amp;height=591&amp;amp;face=0_0_1200_591,https://scrap.kakaocdn.net/dn/KuoHr/hyFNQIGcuU/AjOJcmM36St88fHYzYBBXK/img.png?width=60&amp;amp;height=29&amp;amp;face=0_0_60_29&quot;&gt;&lt;a href=&quot;https://medium.com/@cecilia.humlelu/using-c-c-and-objective-c-frameworks-in-swift-apps-6a60e5f71c36&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@cecilia.humlelu/using-c-c-and-objective-c-frameworks-in-swift-apps-6a60e5f71c36&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bggD39/hyFNRgvWXr/2wxhymQWsMfKxKia1DIakk/img.png?width=1200&amp;amp;height=591&amp;amp;face=0_0_1200_591,https://scrap.kakaocdn.net/dn/KuoHr/hyFNQIGcuU/AjOJcmM36St88fHYzYBBXK/img.png?width=60&amp;amp;height=29&amp;amp;face=0_0_60_29');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Using C, C++ and Objective-C frameworks in Swift apps&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;I did a talk in try!Swift Tokyo this year about using C, C++ and Objective-C frameworks in Swift apps. The presentation was quite concise&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c&quot;&gt;https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1587465660693&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Importing Swift into Objective-C | Apple Developer Documentation&quot; data-og-description=&quot;Article Importing Swift into Objective-C Access Swift types and declarations from within your Objective-C codebase. OverviewYou can work with types declared in Swift from within the Objective-C code in your project by importing an Xcode-generated header fi&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c&quot; data-og-url=&quot;https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cIK4A4/hyFKkR4odM/orKNdL9WZ99KSMLi7Sj19K/img.png?width=984&amp;amp;height=632&amp;amp;face=0_0_984_632&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cIK4A4/hyFKkR4odM/orKNdL9WZ99KSMLi7Sj19K/img.png?width=984&amp;amp;height=632&amp;amp;face=0_0_984_632');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Importing Swift into Objective-C | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Article Importing Swift into Objective-C Access Swift types and declarations from within your Objective-C codebase. OverviewYou can work with types declared in Swift from within the Objective-C code in your project by importing an Xcode-generated header fi&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ekreative.com/blog/using-c-code-and-libraries-in-applications-written-in-swift/&quot;&gt;https://www.ekreative.com/blog/using-c-code-and-libraries-in-applications-written-in-swift/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1587784166390&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Using C ++ code and libraries in applications written in Swift&quot; data-og-description=&quot;Swift is a cool high-level programming language, but it&amp;rsquo;s still quite young and doesn&amp;rsquo;t have as many libraries and components as Objective-C.&quot; data-og-host=&quot;www.ekreative.com&quot; data-og-source-url=&quot;https://www.ekreative.com/blog/using-c-code-and-libraries-in-applications-written-in-swift/&quot; data-og-url=&quot;https://www.ekreative.com/blog/using-c-code-and-libraries-in-applications-written-in-swift/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BtMTy/hyFNLm4s9k/3AQrUOrKHRvmJXrdMFJ2nK/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/UUcaV/hyFNST16wD/SuEOGlIg9ktWBGf1UFfKQ0/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/2ayaO/hyFMoG1n3g/LFDzN46FgEbXhg4lPdxEYK/img.png?width=378&amp;amp;height=267&amp;amp;face=0_0_378_267&quot;&gt;&lt;a href=&quot;https://www.ekreative.com/blog/using-c-code-and-libraries-in-applications-written-in-swift/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.ekreative.com/blog/using-c-code-and-libraries-in-applications-written-in-swift/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BtMTy/hyFNLm4s9k/3AQrUOrKHRvmJXrdMFJ2nK/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/UUcaV/hyFNST16wD/SuEOGlIg9ktWBGf1UFfKQ0/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/2ayaO/hyFMoG1n3g/LFDzN46FgEbXhg4lPdxEYK/img.png?width=378&amp;amp;height=267&amp;amp;face=0_0_378_267');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Using C ++ code and libraries in applications written in Swift&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Swift is a cool high-level programming language, but it&amp;rsquo;s still quite young and doesn&amp;rsquo;t have as many libraries and components as Objective-C.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.ekreative.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>swift</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/5</guid>
      <comments>https://jk2b.tistory.com/5#entry5comment</comments>
      <pubDate>Sat, 25 Apr 2020 12:10:14 +0900</pubDate>
    </item>
    <item>
      <title>[Flutter 기초] Flutter와 Firestore 연동하기(Android studio)</title>
      <link>https://jk2b.tistory.com/3</link>
      <description>&lt;p&gt;이 글은 &lt;a href=&quot;https://www.youtube.com/watch?v=DqJ_KjFzL9I&amp;amp;t=597s&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Youtube Flutter강의&lt;/a&gt;를 번역하였습니다.&lt;/p&gt;
&lt;p&gt;기본적으로 영상의 내용과 같습니다.&lt;/p&gt;
&lt;p&gt;다만 2년 전 영상이라 그런지 그대로 따라할 경우 Multidex와 관련한 오류가 발생해 빌드가 되지 않는데 그 부분만 추가하였습니다.&lt;/p&gt;
&lt;p&gt;언젠가 다시 Firestore를 활용해 앱을 만들 때 영상을 다시 볼 수 없으니 해야할 것들만 딱 정리하도록 하겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;[참고 내용]&lt;/p&gt;
&lt;p&gt;Multidex에서 dex는 Dalvik Executable의 약어입니다. &lt;b&gt;android 앱은 기본적으로 Simple하고 Small하게 만들어지도록 되어있어서 65,536개의 Method들만 사용할 수 있도록 되어있다고 합니다.&lt;/b&gt; Method들의 수가 이 숫자를 넘어갈 경우 Multidex 오류가 발생한다고 합니다. 이 경우 앱 수준의 build.gradle에서 multiDexEnabled true를 바꿔주지 않으면 &lt;b&gt;iOS앱은 잘 되는데 Android앱은 빌드가 안되는 오류&lt;/b&gt;가 발생합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1, Firestore 저장소 만들기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;firebase에 접속하여 프로젝트를 추가합니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-01 오후 8.45.59.png&quot; data-origin-width=&quot;3345&quot; data-origin-height=&quot;1703&quot; width=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bss9IY/btqDmnjExMS/FhAnk6GpY9X9dYx0037DPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bss9IY/btqDmnjExMS/FhAnk6GpY9X9dYx0037DPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bss9IY/btqDmnjExMS/FhAnk6GpY9X9dYx0037DPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbss9IY%2FbtqDmnjExMS%2FFhAnk6GpY9X9dYx0037DPK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-01 오후 8.45.59.png&quot; data-origin-width=&quot;3345&quot; data-origin-height=&quot;1703&quot; width=&quot;645&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. Android studio에서 flutter 앱 만들기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-01 오후 8.51.22.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;1568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOJjDQ/btqDmWTAR1K/7Tj7o7C4swQYoL9kQZkvak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOJjDQ/btqDmWTAR1K/7Tj7o7C4swQYoL9kQZkvak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOJjDQ/btqDmWTAR1K/7Tj7o7C4swQYoL9kQZkvak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOJjDQ%2FbtqDmWTAR1K%2F7Tj7o7C4swQYoL9kQZkvak%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-01 오후 8.51.22.png&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;1568&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이름은 jk2b_test로 만들었습니다&lt;/p&gt;
&lt;p&gt;패키지 이름은 com.jk2b.jk2btest로 만들었습니다. 추후에 Firebase에서 앱을 등록할 때 사용합니다.&lt;/p&gt;
&lt;p&gt;따로 적지 않아도 [app]-&amp;gt;[src]-&amp;gt;[main] 밑의 AndroidManifest.xml에서 확인할 수 있습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-11 오후 1.32.07.png&quot; data-origin-width=&quot;2596&quot; data-origin-height=&quot;1316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H6O5p/btqDk7ocYaD/AkeBXmKKKOrZL2OU8T7dkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H6O5p/btqDk7ocYaD/AkeBXmKKKOrZL2OU8T7dkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H6O5p/btqDk7ocYaD/AkeBXmKKKOrZL2OU8T7dkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH6O5p%2FbtqDk7ocYaD%2FAkeBXmKKKOrZL2OU8T7dkK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-11 오후 1.32.07.png&quot; data-origin-width=&quot;2596&quot; data-origin-height=&quot;1316&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. Firebase에 iOS와 Android 앱 추가하기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;저는 jk2b-test로 만들었습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-01 오후 8.48.03.png&quot; data-origin-width=&quot;3341&quot; data-origin-height=&quot;1556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8Q0Pn/btqDkzkXOoh/cg0UFAIxzDMpXAJbaRkiY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8Q0Pn/btqDkzkXOoh/cg0UFAIxzDMpXAJbaRkiY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8Q0Pn/btqDkzkXOoh/cg0UFAIxzDMpXAJbaRkiY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8Q0Pn%2FbtqDkzkXOoh%2Fcg0UFAIxzDMpXAJbaRkiY1%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-01 오후 8.48.03.png&quot; data-origin-width=&quot;3341&quot; data-origin-height=&quot;1556&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;앱은 여러개를 추가할 수 있습니다. 먼저 iOS를 추가하겠습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-01 오후 9.15.06.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;1235&quot; width=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wVmtd/btqDj2HJbW3/VACa5cP45vKIWuFGfkRM21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wVmtd/btqDj2HJbW3/VACa5cP45vKIWuFGfkRM21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wVmtd/btqDj2HJbW3/VACa5cP45vKIWuFGfkRM21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwVmtd%2FbtqDj2HJbW3%2FVACa5cP45vKIWuFGfkRM21%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-01 오후 9.15.06.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;1235&quot; width=&quot;430&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;앱 등록 후 구성파일을 다운로드 합니다. 그리고 xcode를 엽니다(Runner.xcworkspace)&lt;/p&gt;
&lt;p&gt;방금 다운 받은 plist파일을 넣어줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-01 오후 9.16.51.png&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;953&quot; width=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dL8NCe/btqDn2sgeWu/7kdx0MDuaKWLUu82IOWHxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dL8NCe/btqDn2sgeWu/7kdx0MDuaKWLUu82IOWHxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dL8NCe/btqDn2sgeWu/7kdx0MDuaKWLUu82IOWHxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdL8NCe%2FbtqDn2sgeWu%2F7kdx0MDuaKWLUu82IOWHxK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-01 오후 9.16.51.png&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;953&quot; width=&quot;430&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음 Android를 추가합니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1607&quot; data-origin-height=&quot;1517&quot; width=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UlX6D/btqDky7oGSH/fl7gBEsY2uF17Ne20hZ1T1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UlX6D/btqDky7oGSH/fl7gBEsY2uF17Ne20hZ1T1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UlX6D/btqDky7oGSH/fl7gBEsY2uF17Ne20hZ1T1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUlX6D%2FbtqDky7oGSH%2Ffl7gBEsY2uF17Ne20hZ1T1%2Fimg.png&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1607&quot; data-origin-height=&quot;1517&quot; width=&quot;430&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;똑같이 구성파일을 다운로드 받고 몇가지 코드를 넣어줍니다. 프로젝트 수준의 build.gradle에 dependencies를 추가해주고&lt;/p&gt;
&lt;pre id=&quot;code_1586580405034&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;classpath 'com.google.gms:google-services:4.3.3'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;앱 수준의 build.gradle에 apply 구문도 추가해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1586580446361&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apply plugin: 'com.google.gms.google-services'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;defaulfConfig 부분에 multiDexEnabled 설정도 넣어줍니다&lt;/p&gt;
&lt;pre id=&quot;code_1586580607385&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;defaultConfig {
  // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
  applicationId &quot;com.jk2b.jk2btest&quot;
  minSdkVersion 16
  targetSdkVersion 28
  versionCode flutterVersionCode.toInteger()
  versionName flutterVersionName
  testInstrumentationRunner &quot;androidx.test.runner.AndroidJUnitRunner&quot;
  multiDexEnabled true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;4. Firebase에 Database 만들고 규칙 수정&lt;/p&gt;
&lt;p&gt;보통 테스트 모드로 만드는데 저는 별 이유 없이 프로덕션 모드에서 시작하였습니다. 그래서 규칙을 수정해주어야 나중에 접근이 가능합니다. 데이터베이스 지역은 일본에 있는 동아시아로 하였습니다.(아무래도 거리가 가까우니 미국보단 빠를 것 같아서)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-01 오후 9.18.20.png&quot; data-origin-width=&quot;2947&quot; data-origin-height=&quot;1452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AF1fM/btqDmncRcG0/HDSRJOV3ro4lNUZGsNYLdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AF1fM/btqDmncRcG0/HDSRJOV3ro4lNUZGsNYLdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AF1fM/btqDmncRcG0/HDSRJOV3ro4lNUZGsNYLdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAF1fM%2FbtqDmncRcG0%2FHDSRJOV3ro4lNUZGsNYLdk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-01 오후 9.18.20.png&quot; data-origin-width=&quot;2947&quot; data-origin-height=&quot;1452&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-02 오후 9.53.31.png&quot; data-origin-width=&quot;2465&quot; data-origin-height=&quot;1030&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kr3C7/btqDnxMM7Vr/HHGPrbNCMSd92Kk4PVkDXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kr3C7/btqDnxMM7Vr/HHGPrbNCMSd92Kk4PVkDXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kr3C7/btqDnxMM7Vr/HHGPrbNCMSd92Kk4PVkDXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKr3C7%2FbtqDnxMM7Vr%2FHHGPrbNCMSd92Kk4PVkDXK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-02 오후 9.53.31.png&quot; data-origin-width=&quot;2465&quot; data-origin-height=&quot;1030&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;5. 데이터 추가&lt;/p&gt;
&lt;p&gt;지금 만들앱에서는 문서이름은 필요가 없고 컬렉션 이름은 앱에서 알고 있어야합니다.&lt;/p&gt;
&lt;p&gt;컬렉션은 Test로 만들고 문서 이름은 랜덤하게 생성해주는 기능을 사용했습니다.&lt;/p&gt;
&lt;p&gt;필드에 name과 votes를 추가해줍니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-11 오후 1.54.25.png&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KAk4o/btqDn11b3UM/btLrBxLlp5qMbldgMx3VGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KAk4o/btqDn11b3UM/btLrBxLlp5qMbldgMx3VGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KAk4o/btqDn11b3UM/btLrBxLlp5qMbldgMx3VGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKAk4o%2FbtqDn11b3UM%2FbtLrBxLlp5qMbldgMx3VGK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-11 오후 1.54.25.png&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;371&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;6. 코드 작성&lt;/p&gt;
&lt;p&gt;이제 widget을 만들고 Firebase를 가져오는 코드를 만들어보겠습니다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;main.dart를 엽니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;cloud_firestore.dart를 import하고 firestore instance를 가져옵니다.&lt;/p&gt;
&lt;pre id=&quot;code_1586581060825&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

void main() =&amp;gt; runApp(MyApp());


class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() =&amp;gt; _MyAppState();
}

class _MyAppState extends State&amp;lt;MyApp&amp;gt; {
  Firestore firestore = Firestore.instance;
  @override

  Widget build(BuildContext context) {
    return MaterialApp(
      title: &quot;Band Name Test&quot;,
      theme: ThemeData.dark(),
      home: const MyHomePage(title: 'Band names'),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;화면에 보여줄 위젯을 생성합니다&lt;/p&gt;
&lt;pre id=&quot;code_1586581237151&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: StreamBuilder(
          stream: Firestore.instance.collection('Test').snapshots(),
          builder: (context, snapshot) {
            if (!snapshot.hasData) return const Text('Loading...');
            return ListView.builder(
              itemExtent: 80.0,
              itemCount: snapshot.data.documents.length,
              itemBuilder: (context, index) =&amp;gt;
                  _buildListItem(context, snapshot.data.documents[index]),
            );
          }
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;_buildListItem 메소드를 만들면 끝!&lt;/p&gt;
&lt;pre id=&quot;code_1586581286328&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  Widget _buildListItem(BuildContext context, DocumentSnapshot document) {
    return ListTile(
      title: Row(
        children: &amp;lt;Widget&amp;gt;[
          Expanded(
            child: Text(
              document['name'],
              style: Theme.of(context).textTheme.headline,
            ),
          ),
          Container(
            decoration: const BoxDecoration(
              color: Color(0xffddddff),
            ),
            padding: const EdgeInsets.all(10.0),
            child: Text(
              document['votes'].toString(),
              style: Theme.of(context).textTheme.display1,
            ),
          )
        ],
      ),
      onTap: () {
        print(&quot;should increase votes here.&quot;);
        document.reference.updateData({
          'votes': document['votes'] + 1
        });
      },
    );
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-11 오후 2.01.57.png&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCXwS8/btqDmVf6MaZ/MLzlVkcYRFWFP2kxYC6P5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCXwS8/btqDmVf6MaZ/MLzlVkcYRFWFP2kxYC6P5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCXwS8/btqDmVf6MaZ/MLzlVkcYRFWFP2kxYC6P5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCXwS8%2FbtqDmVf6MaZ%2FMLzlVkcYRFWFP2kxYC6P5K%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-11 오후 2.01.57.png&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;754&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;[관련 링크]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=DqJ_KjFzL9I&amp;amp;t=597s&quot;&gt;https://www.youtube.com/watch?v=DqJ_KjFzL9I&amp;amp;t=597s&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Multidex관련&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/android-multidex-how-deal-chris-sullivan&quot;&gt;https://www.linkedin.com/pulse/android-multidex-how-deal-chris-sullivan&lt;/a&gt;&lt;/p&gt;</description>
      <category>flutter</category>
      <category>Android</category>
      <category>Android Studio</category>
      <category>firestore</category>
      <category>Flutter</category>
      <category>MultiDex</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/3</guid>
      <comments>https://jk2b.tistory.com/3#entry3comment</comments>
      <pubDate>Sat, 11 Apr 2020 14:02:15 +0900</pubDate>
    </item>
    <item>
      <title>[Tip] Xcode PROJECT_ROOT BuildSetting에서 쓰는 변수명 출력하기</title>
      <link>https://jk2b.tistory.com/2</link>
      <description>&lt;p&gt;&lt;span&gt;터미널을 열고 프로젝트가 있는 디렉토리에서 아래 명령어를 입력해주면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;xcodebuild -project {project name}.xcodeproj -target iosSample -showBuildSettings | grep {variable name}&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1586411613413&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;xcodebuild -project test.xcodeproj -target iosSample -showBuildSettings | grep PROJECT_ROOT&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>swift</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/2</guid>
      <comments>https://jk2b.tistory.com/2#entry2comment</comments>
      <pubDate>Thu, 9 Apr 2020 14:54:41 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] 원하는 앱의 document 폴더 확인하기</title>
      <link>https://jk2b.tistory.com/1</link>
      <description>&lt;p&gt;가끔 개발을 하다보면 앱 안에서 파일을 읽고 써야하는 경우가 발생합니다&lt;/p&gt;
&lt;p&gt;iOS에서는 파일을 쓸 수 있는 곳이 정해져 있는데 그 중에 한 곳이 documents입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번 글에서는 documents를 열어 파일을 확인하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Xcode의 [Window] -&amp;gt; [Devices and Simulators]를 엽니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;802&quot; width=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpXJhP/btqDc30bb8t/PvDdKwi0OKNOl2KzgWwkk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpXJhP/btqDc30bb8t/PvDdKwi0OKNOl2KzgWwkk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpXJhP/btqDc30bb8t/PvDdKwi0OKNOl2KzgWwkk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpXJhP%2FbtqDc30bb8t%2FPvDdKwi0OKNOl2KzgWwkk0%2Fimg.png&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;802&quot; width=&quot;612&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;아래와 같은 창이 나오면 document를 보고자 하는 앱을 선택해줍니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;2446&quot; data-origin-height=&quot;2094&quot; width=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qS1S7/btqDc4ZDe6Q/k3DteDO5lADZDMd6irkdkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qS1S7/btqDc4ZDe6Q/k3DteDO5lADZDMd6irkdkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qS1S7/btqDc4ZDe6Q/k3DteDO5lADZDMd6irkdkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqS1S7%2FbtqDc4ZDe6Q%2Fk3DteDO5lADZDMd6irkdkk%2Fimg.png&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;2446&quot; data-origin-height=&quot;2094&quot; width=&quot;645&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그리고 아래쪽에 설정 모양의 아이콘을 누른 후 Download Container... 를 눌러줍니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-06 오전 8.51.11.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xpW7D/btqDec3nZDB/7kTLevghoSB4nLoI6KSGmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xpW7D/btqDec3nZDB/7kTLevghoSB4nLoI6KSGmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xpW7D/btqDec3nZDB/7kTLevghoSB4nLoI6KSGmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxpW7D%2FbtqDec3nZDB%2F7kTLevghoSB4nLoI6KSGmK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-06 오전 8.51.11.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;250&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그 다음 폴더에서 오른쪽 다운로드 받은 항목에서 마우스 오른쪽 버튼을 누른 후 패키지 내용 보기를 눌러줍니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-06 오전 8.53.05.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k171P/btqDecPR4i3/KKgqfITKzNaFLeTclyKVyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k171P/btqDecPR4i3/KKgqfITKzNaFLeTclyKVyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k171P/btqDecPR4i3/KKgqfITKzNaFLeTclyKVyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk171P%2FbtqDecPR4i3%2FKKgqfITKzNaFLeTclyKVyk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-06 오전 8.53.05.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;144&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러면 아래와 같이 AppData폴더 아래에 Documents폴더가 있는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-04-06 오전 8.50.33.png&quot; data-origin-width=&quot;2834&quot; data-origin-height=&quot;1330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEdhec/btqDeS4sGlW/EkSP0gDRjdw2KVkpkpVp1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEdhec/btqDeS4sGlW/EkSP0gDRjdw2KVkpkpVp1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEdhec/btqDeS4sGlW/EkSP0gDRjdw2KVkpkpVp1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEdhec%2FbtqDeS4sGlW%2FEkSP0gDRjdw2KVkpkpVp1k%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-04-06 오전 8.50.33.png&quot; data-origin-width=&quot;2834&quot; data-origin-height=&quot;1330&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>swift</category>
      <author>j2kb</author>
      <guid isPermaLink="true">https://jk2b.tistory.com/1</guid>
      <comments>https://jk2b.tistory.com/1#entry1comment</comments>
      <pubDate>Mon, 6 Apr 2020 20:52:22 +0900</pubDate>
    </item>
  </channel>
</rss>