<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Origogi</title>
    <description>Origogi&apos;s blog.</description>
    <link>https://origogi.github.io/</link>
    <atom:link href="https://origogi.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 21 Sep 2024 14:28:53 +0000</pubDate>
    <lastBuildDate>Sat, 21 Sep 2024 14:28:53 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>[iOS][Swift] ARC (Automatic Reference Counting) 정리</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/9b9bbd57-859f-473b-81b2-178a9d7d69e6&quot; alt=&quot;banner&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;서론&quot;&gt;서론&lt;/h2&gt;

&lt;p&gt;해당 포스트는 ARC에 대해서 정리한 포스트입니다.&lt;/p&gt;

&lt;h2 id=&quot;arc-automatic-reference-counting&quot;&gt;ARC (Automatic Reference Counting)&lt;/h2&gt;

&lt;p&gt;ARC 는 Swift 에서 사용되는 메모리 관리 방식이다. 기본적인 원리는 객체를 소유또는 참조하는 모든 객체의 수를 추적하여 객체가 더 이상 필요하지 않을 때 메모리에서 해제하는 방식이다.
예를 들어 객체 A를 어떤 변수에 할당하면 A의 참조 카운트가 1 증가하고, 또 객체 A를 다른 변수에 할당하면 참조 카운트가 1 증가한다. 만약 변수가 nil 이 되거나 다른 객체를 참조하게 되면 참조 카운트가 1 감소한다. 참조 카운트가 0이 되면 메모리에서 해제된다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;deinit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Person 객체가 메모리에서 해제됨&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Person이라는 객체가 생성됨과 동시에 a 변수에 할당되어 Person 객체의 참조 카운트가 1 증가&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Person 객체의 참조 카운트가 1 증가&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Person 객체의 참조 카운트가 1 감소&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Person 객체의 참조 카운트가 1 감소, Person 객체가 메모리에서 해제되면서 deinit 메소드가 호출됨&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;strong-reference&quot;&gt;Strong Reference&lt;/h2&gt;

&lt;p&gt;일반적은 객체 참조 방식이다. 객체를 어떤 변수가 참조하게 되면 참조 카운트가 1 증가하고, 변수가 nil 이 되거나 다른 객체를 참조하게 되면 참조 카운트가 1 감소한다.
위 예제에서 사용한 방식이 Strong Reference 이다.&lt;/p&gt;

&lt;h2 id=&quot;weak-reference&quot;&gt;Weak Reference&lt;/h2&gt;

&lt;p&gt;객체를 참조하지만 객체의 참조 카운트를 증가시키지 않는 방법이 존재한다. 이를 Weak Reference 라고 한다. 
변수 키워드 앞에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;weak&lt;/code&gt; 또는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unowned&lt;/code&gt; 키워드를 사용하여 객체를 참조할 수 있다.&lt;/p&gt;

&lt;p&gt;어떻게 보면 참조 카운트를 임의로 증가시키지 않아서 무언가 위험하고 버그가 발생할 것 같고 사용하면 안되는 것처럼 보일수 있다.
하지만 iOS Framework 에서는 많은 곳에서 사용되고 있다. iOS Framework 에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Delegate 패턴&lt;/code&gt; 또는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;클로져&lt;/code&gt; 을 많이 사용하는데 이때 Delegate 객체를 참조할 때 Weak Reference 를 적극적으로 사용한다.&lt;/p&gt;

&lt;h3 id=&quot;delegate-패턴&quot;&gt;Delegate 패턴&lt;/h3&gt;

&lt;p&gt;Delegate 패턴은 객체의 행동을 다른 객체에게 위임하는 패턴이다. 객체 A가 객체 B에게 자신의 행동을 위임하고, 객체 B는 객체 A의 행동을 대신 수행한다.
이때 객체 A는 객체 B를 생성하면서 B를 참조하고 있고 객체 B의 생성자에 객체 A의 참조를 전달하는 방식이다.&lt;/p&gt;

&lt;p&gt;여기서 객체 참조에 대해서 특별한 처리를 하지 않으면 객체 A와 객체 B는 서로 참조하고 있기 때문에 참조 카운트가 1 증가하고 두 객체는 메모리에서 해제되지 않는다. 이러한 문제를 해결하기 위해 Delegate 객체를 참조할 때 Weak Reference 를 사용한다.&lt;/p&gt;

&lt;h2 id=&quot;sample-code-strong-reference&quot;&gt;Sample Code (Strong Reference)&lt;/h2&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;apartment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Apartment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;deinit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is being deinitialized&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Apartment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tenant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;deinit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Apartment &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is being deinitialized&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;john&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unit4A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Apartment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;john&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;John Appleseed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;unit4A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Apartment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드는 Person과 Apartment 객체가 있고 Person john은 Apartment의 unit4A를 참조하고 있고, Apartment unit4A는 Person john을 참조하고 있다. 이때 객체 Person과 Apartment는 서로 참조하고 있고 또 변수에서 각각 참조하고 있기 때문에 각각 참조 카운트는 2이다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;724&quot; alt=&quot;image&quot; src=&quot;https://docs.swift.org/swift-book/images/org.swift.tspl/referenceCycle02@2x.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;이 때 john과 unit4A의 변수에 nil 를 할당하면 어떻게 될까?&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;john&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;unit4A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;단순히 생각하면 객체를 외부에서 직접 참조하는 변수가 없기 때문에 메모리에서 해제가 될 것 같고 실제로 해제가 되어야 한다. 만약 여기서 해제가 되지 않는다면 해당 객체는 메모리에서 살아있지만 실제로 접근할 수 없는 상태 즉 메모리 누수가 발생한다.&lt;/p&gt;

&lt;p&gt;위 코드를 실행해보면 안타깝게도 메모리에서 해제되지 않는다. 이유는 서로 참조하고 있기 때문에 참조 카운트가 1이 남아있기 때문이다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;724&quot; alt=&quot;image&quot; src=&quot;https://docs.swift.org/swift-book/images/org.swift.tspl/referenceCycle03@2x.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;위 그림과 같이 Person 객체와 Apartment는 살아있지만 프로그램에서 접근할수 없는 상태가 된다. 즉 개발자 또는 프로그램이 객체를 컨트롤 할 수 없고 메모리에서 자리만 차지하고 있는 상태, 즉 메모리 누수가 발생한 것이다.&lt;/p&gt;

&lt;p&gt;위와 같은 상황은 swift 환경 뿐만 아니라 다른 언어에서도 발생할 수 있는 문제이다. 이러한 문제 때문에 엄격하게 상호 참조를 허용하지 않는다. 하지만 iOS Framework 에서는 Delegate 패턴을 많이 사용하는데 Delegate 패턴에서는 서로 참조하는 경우가 많다. 이런 경우에는 어떻게 해야할까?&lt;/p&gt;

&lt;p&gt;위 문제를 해결하기 위해 약한 참조(Weak Reference)를 사용한다.&lt;/p&gt;

&lt;p&gt;위 Person, Apartment 예제 코드에서 Apartment 를 다음과 같이 수정해보자.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Apartment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// var tenant: Person?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tenant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;deinit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Apartment &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is being deinitialized&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;weak 키워드를 사용하여 Person 객체를 약한 참조를 하도록 하였다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;724&quot; alt=&quot;image&quot; src=&quot;https://docs.swift.org/swift-book/images/org.swift.tspl/weakReference01@2x.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;앞에서 설명했듯이 weak 를 사용하면 레퍼런스 카운트는 증가하지 않는다고 하였다.
따라서 Person 객체의 참조 카운트는 1 이 되고 Apartment 객체의 참조 카운트는 2 이다.&lt;/p&gt;

&lt;p&gt;조금 이해하기 쉽게 설명하자면 위 그림에서 각 객체를 가리키는 굵은 화살표 갯수를 보면 알수 있다. Person 객체를 가리키는 화살표는 1개이고 Apartment 객체를 가리키는 화살표는 2개이다.&lt;/p&gt;

&lt;p&gt;여기서 john에 nil 을 할당하면 어떻게 될까?&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;john&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Person 객체의 참조 카운트가 1 감소하고 0 이 되면서 Person 객체가 메모리에서 해제된다. 그렇게 되면 Apartment 객체의 tenant 변수에 저장된 값은 어떻게 될까? 바로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt; 이 된다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;724&quot; alt=&quot;iShot_2024-08-30_12 04 34&quot; src=&quot;https://github.com/user-attachments/assets/d44bb579-c128-4182-9511-d5969bc486d8&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;여기서 핵심은 tenant 변수에 따로 nil 을 할당하지 않았는데도 자동으로 nil 이 된다는 것이다. 
과연 이게 가능한 것일까?&lt;/p&gt;

&lt;h2 id=&quot;weak-로-선언된-변수는-어떻게-동작하는가&quot;&gt;weak 로 선언된 변수는 어떻게 동작하는가?&lt;/h2&gt;

&lt;p&gt;weak 로 선언된 변수는 다음과 같은 특징을 가진다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;weak 로 선언된 변수는 참조 카운트를 증가시키지 않는다.&lt;/li&gt;
  &lt;li&gt;weak 로 선언된 변수는 참조하고 있는 객체가 메모리에서 해제되면 자동으로 nil 이 된다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;여기서 2 번째 특징이 중요하다.&lt;/p&gt;

&lt;h2 id=&quot;weak-참조와-nil-할당&quot;&gt;weak 참조와 nil 할당&lt;/h2&gt;

&lt;p&gt;weak 로 선언된 변수가 참조하는 객체가 메모리에서 해제될 때, 해당 weak 변수는 자동으로 nil로 설정됩니다. 이 과정은 Swift 런타임 시스템에 의해 자동으로 처리된다.&lt;/p&gt;

&lt;p&gt;구체적으로는&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Swift 런타임&lt;/strong&gt; : Swift의 런타임 시스템이 이 작업을 담당합니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ARC&lt;/strong&gt; : Swift는 ARC를 사용하여 메모리를 관리합니다. ARC는 객체의 참조 카운트를 추적하고 관리한다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;해제 과정&lt;/strong&gt; :
    &lt;ul&gt;
      &lt;li&gt;객체의 참조 카운트가 0이 되면 메모리에서 해제된다.&lt;/li&gt;
      &lt;li&gt;이 때, &lt;strong&gt;Swift 런타임&lt;/strong&gt; 은 해당 객체를 참조하고 있는 모든 weak 변수를 nil로 설정한다. 이 과정을 &lt;strong&gt;Zeroing Weak Reference&lt;/strong&gt; 라고도 한다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;swift의-weak-참조-관리-메커니즘&quot;&gt;Swift의 weak 참조 관리 메커니즘&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Side Table :
    &lt;ul&gt;
      &lt;li&gt;Swift 런타임은 각 객체에 대한 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;side table&lt;/code&gt; 이라는 추가적인 메모리 구조를 유지한다.&lt;/li&gt;
      &lt;li&gt;이 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;side table&lt;/code&gt; 은 해당 객체에 대한 weak 참조들의 정보를 저장한다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Weak 참조 등록 :
    &lt;ul&gt;
      &lt;li&gt;weak 참조가 생성되면, Swift 런타임은 해당 객체의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;side table&lt;/code&gt; 에 weak 참조를 등록한다.&lt;/li&gt;
      &lt;li&gt;이 등록 과정은 자동으로 이루어진다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;객체 해제 과정 :
    &lt;ul&gt;
      &lt;li&gt;객체의 참조 카운트가 0이 되면, Swift 런타임은 해당 객체의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;side table&lt;/code&gt; 을 확인한다.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;side table&lt;/code&gt; 에 등록된 weak 참조들을 모두 nil로 설정한다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;동시성 처리 :
    &lt;ul&gt;
      &lt;li&gt;Swift 런타임은 weak 참조의 동시성 문제를 처리하기 위해 적절한 동시성 제어 메커니즘을 사용한다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;최적화 :
    &lt;ul&gt;
      &lt;li&gt;Swift 런타임은 이 과정을 매우 효율적으로 수행하도록 최적화되어 있다.&lt;/li&gt;
      &lt;li&gt;대량의 weak 참조를 처리하는 경우에도 성능 저하가 발생하지 않도록 설계되어 있다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;unowned-참조&quot;&gt;unowned 참조&lt;/h2&gt;

&lt;p&gt;약한 참조를 위해서 weak 키워드 외에도 unowned 키워드를 사용할 수 있다. 위 예제에서 weak 키워드를 사용한 것을 unowned 키워드로 변경해보자.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Apartment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 생성자에서 tenant 를 초기화 해주어야 한다.&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tenant&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;     
      &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tenant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tenant&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// var tenant: Person?&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// weak var tenant: Person? &lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;unowned&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tenant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Person&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;deinit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Apartment &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is being deinitialized&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;정상적으로 메모리 누수가 발생하지 않는다. unowned 키워드는 weak 키워드와 비슷하지만 차이점이 존재한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;unowned 참조는 기본적으로 non-optional 이다.&lt;/li&gt;
  &lt;li&gt;weak 참조는 optional operator(?)를 사용하여 값을 접근하지만, unowned 참조는 non-optional 이기 때문에 optional operator를 사용하지 않는다.&lt;/li&gt;
  &lt;li&gt;Swift 런타임은 unowned 참조가 nil이 되는 경우를 처리하지 않는다.&lt;/li&gt;
  &lt;li&gt;따라서 unowned 참조가 nil이 될 수 있고 unowned 참조를 접근하면 런타임 에러가 발생한다.&lt;/li&gt;
  &lt;li&gt;unowned 참조는 객체의 수명이 unowned 참조보다 길어야 한다는 것을 보장해야 한다.&lt;/li&gt;
  &lt;li&gt;성능상으로는 unowned 참조를 사용했을 때 weak 참조를 했을 때 보다 앱의 성능에 대해서 약간 이점이 있다. 왜냐하면 Zeroing Weak Reference 가 발생하지 않기 때문이다.&lt;/li&gt;
  &lt;li&gt;안정성 측면에서는 weak 참조를 사용하는 것이 더 안전하다.&lt;/li&gt;
  &lt;li&gt;경우에 따라서는 unowned 참조를 사용하는 것이 더 좋을수 있다. 명시적으로 nil 이 될 수 없는 경우에 사용함으로써 만약 런타임 에러가 발생한다면 이는 어딘가 잘못된 처리가 있음을 명확하게 알 수 있다. 보통은 에러가 최대한 발생하지 않도록 코드를 작성하는 것이 좋지만, 경우에 따라서는 에러가 발생하는 것이 더 좋을 수도 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/&quot;&gt;ARC 공식문서&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://velog.io/@wonhee010/Zeroing-Object-Life-Cycle&quot;&gt;Zeroing &amp;amp; Object Life Cycle (feat: Side Table)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 30 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/ios/ARC/</link>
        <guid isPermaLink="true">https://origogi.github.io/ios/ARC/</guid>
        
        <category>iOS</category>
        
        <category>Swift</category>
        
        
        <category>iOS</category>
        
      </item>
    
      <item>
        <title>[iOS][Swift] UiKit 정리 - Navigation</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/416bbea5-f706-413f-b1ad-b321ead319c4&quot; alt=&quot;banner&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;서론&quot;&gt;서론&lt;/h2&gt;

&lt;p&gt;UiKit 를 공부하면서 햇갈리는 부분을 정리해보았습니다.&lt;/p&gt;

&lt;h2 id=&quot;화면-전환&quot;&gt;화면 전환&lt;/h2&gt;

&lt;p&gt;ViewController에서 새로운 ViewController로 push를 하기 위해서는 아래와 같이 사용합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;moveNext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;secondVC&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instantiateViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;withIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SecondVC&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 네비게이션 컨트롤러를 통해 전체 화면으로 전환&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;navigationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pushViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondVC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;다만, 이 경우에는 해당 ViewController가 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NavigationController &lt;/code&gt;에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Embed&lt;/code&gt; 되어있어야 합니다. 만약 되어 있지 않으면 Modal 로 띄워야 합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;moveNext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;secondVC&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storyboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instantiateViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;withIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SecondVC&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;secondVC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modalTransitionStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coverVertical&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondVC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;여기서 modal은 전체 화면을 다 가리기 보다는 일부만 가리는 형태로 띄워집니다. 그리고 push 는 전체 화면을 가리는 형태로 띄워집니다.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;모달&lt;/th&gt;
      &lt;th&gt;전체 화면&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/f691863f-0125-4689-a312-8a79fe1cced9&quot; alt=&quot;iShot_2024-08-19_21 16 41&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/bb4077af-8ec3-43a2-be1e-66d8c8789c1a&quot; alt=&quot;iShot_2024-08-19_21 20 19&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;모달 방식은 다른 말로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Present&lt;/code&gt; 방식이라고도 합니다.&lt;/p&gt;

&lt;h2 id=&quot;이전-화면으로-이동&quot;&gt;이전 화면으로 이동&lt;/h2&gt;

&lt;p&gt;그리고 push나 modal로 띄운 ViewController는 아래와 같이 back을 할 수 있습니다.&lt;/p&gt;

&lt;h3 id=&quot;push-된-viewcontroller-back&quot;&gt;Push 된 ViewController back&lt;/h3&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;navigationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;popViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;modal-로-띄운-viewcontroller-back&quot;&gt;Modal 로 띄운 ViewController back&lt;/h3&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;navigation-controller&quot;&gt;Navigation Controller&lt;/h2&gt;

&lt;p&gt;내비게이션 컨트롤러는 뷰컨트롤러의 특별한 종류입니다. 내비게이션 컨트롤러는 뷰컨트롤러를 스택에 저장하고, 스택에 저장된 뷰컨트롤러를 관리한다. 그리고 앱 내비게이션을 표시해줄수 있는 내비게이션 바가 내장되어 있습니다.&lt;/p&gt;

&lt;p&gt;내비게이션 컨트롤러는 뷰 컨트롤러의 계층적 구조를 관리하는 역할이기 때문에, 직접 컨텐츠를 담고 화면을 구성하지 않습니다. 대신 다른 뷰 컨트롤러를 포함하고 그 뷰 컨트롤러를 관리합니다.&lt;/p&gt;

&lt;h3 id=&quot;root-view-controller&quot;&gt;Root View Controller&lt;/h3&gt;

&lt;p&gt;내비게이션 컨트롤러의 루트 뷰 컨트롤러는 내비게이션 컨트롤러의 스택에 추가된 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;첫 번째&lt;/code&gt; 뷰 컨트롤러입니다. 루트 뷰 컨트롤러는 내비게이션 컨트롤러가 표시하는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;첫 번째 화면&lt;/code&gt;이 됩니다.&lt;/p&gt;

&lt;h3 id=&quot;navigation-bar&quot;&gt;Navigation Bar&lt;/h3&gt;

&lt;p&gt;내비게이션 바는 내비게이션 컨트롤러의 뷰 계층 구조에 포함되어 있습니다. 아래 화면에 표시되는 뒤로가기 버튼 즉 내비게이션 바는 개발자가 직접 추가된 것이 아닌 내비게이션 컨트롤러가 자동으로 추가한 것입니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;200&quot; alt=&quot;iShot_2024-08-19_21 48 48&quot; src=&quot;https://github.com/user-attachments/assets/e04ae63b-6982-4c02-b570-382ff8915e97&quot; /&gt;
&lt;/div&gt;
</description>
        <pubDate>Mon, 19 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/ios/ui-kit-1/</link>
        <guid isPermaLink="true">https://origogi.github.io/ios/ui-kit-1/</guid>
        
        <category>iOS</category>
        
        <category>Swift</category>
        
        <category>Combine</category>
        
        
        <category>iOS</category>
        
      </item>
    
      <item>
        <title>[iOS][Swift] Combine 정리 (4) - Networking</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/2e217acb-c4ae-4bb8-8747-f85e5d15a6e5&quot; alt=&quot;banner&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;서론&quot;&gt;서론&lt;/h2&gt;

&lt;p&gt;해당 포스트는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Combine&lt;/code&gt; 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Networking&lt;/code&gt; 처리에 대해 정리한 내용입니다.&lt;/p&gt;

&lt;h2 id=&quot;networking&quot;&gt;Networking&lt;/h2&gt;

&lt;p&gt;아래는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URLSession&lt;/code&gt; 을 이용하여 서버로 부터 데이터를 받고 json 데이터를 디코딩하고 AnyPublisher 로 반환하는 코드입니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;userId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchPosts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnyPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URLSession&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dataTaskPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://jsonplaceholder.typicode.com/posts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(\&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchPosts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;receiveValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;datataskpublisher&quot;&gt;dataTaskPublisher()&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URLSession.shared.dataTaskPublisher(for: URL)&lt;/code&gt; 는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URLSession&lt;/code&gt; 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dataTaskPublisher(for: URL)&lt;/code&gt; 을 이용하여 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URL&lt;/code&gt; 로 부터 데이터를 받아오는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt; 를 생성합니다.&lt;/p&gt;

&lt;h3 id=&quot;erasetoanypublisher&quot;&gt;eraseToAnyPublisher()&lt;/h3&gt;

&lt;p&gt;dataTaskPublisher() 의 리턴값은 단순한 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt; 타입이 아닌 좀 더 복잡한 타입입니다. 하지만 데이터를 수신하는 쪽에서는 복잡한 타입이 아닌 단순한 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt; 타입을 원합니다. 이때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eraseToAnyPublisher()&lt;/code&gt; 를 사용하여 복잡한 타입을 단순한 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt; 타입으로 변환합니다.&lt;/p&gt;

&lt;p&gt;만약 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eraseToAnyPublisher()&lt;/code&gt; 를 사용하지 않으면 아래와 같은 타입의 Publisher 가 리턴됩니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchPosts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Publishers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Publishers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Decode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;URLSession&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;DataTaskPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 실제로는 더 복잡할 수도 있음&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;따라서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eraseToAnyPublisher()&lt;/code&gt; 를 호출하여 사용하기 단순한 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyPublisher&lt;/code&gt; 타입으로 변환합니다.&lt;/p&gt;

&lt;h2 id=&quot;error-handling&quot;&gt;Error Handling&lt;/h2&gt;

&lt;p&gt;아래는 네트워크 요청 중 발생하는 에러를 처리하는 코드입니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;userId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NetworkError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;badServerResponse&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchPosts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnyPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URLSession&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dataTaskPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://jsonplaceholder.typicode.com/posts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tryMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;retries&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NetworkError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;badServerResponse&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//            guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                throw NetworkError.badServerResponse&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//            }&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//            return data&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;retry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchPosts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;receiveValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;tryMap 연산자를 이용해서 예외를 발생시키고 retry 연산자를 이용해서 지정된 횟수만큼 재시도합니다. 그리고 completion 을 받아서 에러를 처리합니다.&lt;/p&gt;

&lt;h2 id=&quot;두-개의-publisher네트워크-요청를-합치기&quot;&gt;두 개의 Publisher(네트워크 요청)를 합치기&lt;/h2&gt;

&lt;p&gt;두 개의 Publisher 를 합치는 방법은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CombineLatest&lt;/code&gt; 연산자를 사용하면 됩니다.&lt;/p&gt;

&lt;p&gt;아래 코드는 두 개의 Publisher 를 합치는 예제입니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MovieResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Decodable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Decodable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CodingKeys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CodingKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Title&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;searchTerm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnyPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://www.omdbapi.com/?s=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;searchTerm&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;page=2&amp;amp;apiKey=564727fa&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Invalid URL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URLSession&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dataTaskPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(\&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MovieResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(\&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Publishers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;CombineLatest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetchMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;batman&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;superman&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;receiveCompletion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;receiveValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;batmanMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;supermanMovies&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Batman Movies: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;batmanMovies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Superman Movies: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;supermanMovies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;fetchMovies() 함수를 이용해서 두 개의 Publisher 를 생성하고 CombineLatest 연산자를 이용해서 두 개의 Publisher 를 합칩니다. 그리고 두 개의 Publisher 가 모두 값을 방출하면 receiveValue 클로저가 호출됩니다.&lt;/p&gt;
</description>
        <pubDate>Wed, 14 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/ios/swift-combine-5/</link>
        <guid isPermaLink="true">https://origogi.github.io/ios/swift-combine-5/</guid>
        
        <category>iOS</category>
        
        <category>Swift</category>
        
        <category>Combine</category>
        
        
        <category>iOS</category>
        
      </item>
    
      <item>
        <title>[iOS][Swift] Combine 정리 (3) - Subject</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/2e217acb-c4ae-4bb8-8747-f85e5d15a6e5&quot; alt=&quot;banner&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;서론&quot;&gt;서론&lt;/h2&gt;

&lt;p&gt;해당 포스트는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Combine&lt;/code&gt; 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt; 에 대해 정리한 내용입니다.&lt;/p&gt;

&lt;h2 id=&quot;subjects&quot;&gt;Subjects&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt; 는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt; 이자 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subscriber&lt;/code&gt; 입니다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt; 는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt; 로서 데이터를 발행할 수 있고, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subscriber&lt;/code&gt; 로서 데이터를 받을 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt; 는 다음과 같은 종류가 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;passthroughsubject&quot;&gt;PassthroughSubject&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PassthroughSubject&lt;/code&gt; 는 가장 일반적인 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt; 입니다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PassthroughSubject&lt;/code&gt; 는 데이터를 받으면 그대로 다시 발행합니다. 그리고 초기값을 가질 수 없습니다. 그리고 구독을 한 시점 이후에 발행된 데이터만 받을 수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Combine&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PassthroughSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// nothing happens&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Received value: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//1&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;currentvaluesubject&quot;&gt;CurrentValueSubject&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CurrentValueSubject&lt;/code&gt; 는 초기값을 가질 수 있는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt; 입니다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CurrentValueSubject&lt;/code&gt; 는 가장 최근의 값을 저장하고, 새로운 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subscriber&lt;/code&gt; 가 구독을 시작하면 가장 최근의 값을 전달합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;subscription&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 구독을 시작 한 시점에 가장 최근 값인 0을 받는다.&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Received value: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;custom-subject&quot;&gt;Custom Subject&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt; 를 상속받아서 커스텀 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt; 를 만들 수 있습니다.&lt;/p&gt;

&lt;p&gt;아래 코드는 짝수만 발행하는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EvenNumberSubject&lt;/code&gt; 를 만드는 예제입니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;EvenSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Subject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;typealias&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;wrapped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;initialValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;evenInitialValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;initialValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;wrapped&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evenInitialValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;wrapped&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Subscribers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Completion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;wrapped&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;subscriber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Subscriber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Failure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;wrapped&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;subscriber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subscriber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;subscription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Subscription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;wrapped&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;subscription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subscription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;evenSubject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;EvenSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;initialValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evenSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 최초 구독하자마자 2를 수신&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Received value: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


 &lt;span class=&quot;n&quot;&gt;evenSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 3은 짝수가 아니므로 무시&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;evenSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 4를 수신&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;evenSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 5는 짝수가 아니므로 무시&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 12 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/ios/swift-combine-4/</link>
        <guid isPermaLink="true">https://origogi.github.io/ios/swift-combine-4/</guid>
        
        <category>iOS</category>
        
        <category>Swift</category>
        
        <category>Combine</category>
        
        
        <category>iOS</category>
        
      </item>
    
      <item>
        <title>[iOS][Swift] Combine 정리 (3) - 예외 처리</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/2e217acb-c4ae-4bb8-8747-f85e5d15a6e5&quot; alt=&quot;banner&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;서론&quot;&gt;서론&lt;/h2&gt;

&lt;p&gt;해당 포스트는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Combine&lt;/code&gt; 의 예외 처리에 대해 정리한 내용입니다.&lt;/p&gt;

&lt;h3 id=&quot;trymap&quot;&gt;tryMap&lt;/h3&gt;

&lt;p&gt;일반적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt; 는 예외를 던지지 못합니다. 아래 코드에서 컴파일 에러가 발생합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operationFailed&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pub&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// 컴파일 에러!!! No exact matches in call to instance method &apos;map&apos;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operationFailed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;예외처리를 위해서는 map 대신 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tryMap&lt;/code&gt; 을 사용해야합니다. 추가적으로 예외를 받아서 처리해야하는 코드를 작성해야합니다.&lt;/p&gt;

&lt;h3 id=&quot;catch&quot;&gt;catch&lt;/h3&gt;

&lt;p&gt;예외가 발생했을 시 예외처리를 위한 가장 기본적인 방법은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;catch&lt;/code&gt; 연산자를 사용하는 것입니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operationFailed&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pub&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tryMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operationFailed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Just&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 예외가 발생하면 예외를 처리하고 새로운 값을 방출해야한다. 그리고 새로운 값은 방출되지 않는다.&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Value: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;replaceerror&quot;&gt;replaceError&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replaceError&lt;/code&gt; 연산자는 예외가 발생했을 때 대체 값을 방출합니다. catch 연산자보다 더 간단하게 사용할 수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pub&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tryMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operationFailed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;replaceError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// -1 을 방출하고 Publisher 를 종료한다.&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;catch&lt;/code&gt; 와 동일한 결과를 반환합니다.&lt;/p&gt;

&lt;h3 id=&quot;retry&quot;&gt;retry&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retry&lt;/code&gt; 연산자는 예외가 발생했을 때, 지정된 횟수만큼 재시도합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pub&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tryMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operationFailed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;retry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 반드시 completion 을 받아야합니다.&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;receiveValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// failure(__lldb_expr_87.CustomError.operationFailed) , 두번 재시도했지만 실패&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;retry 를 사용할때 반드시 sink 에서 completion 을 받아야합니다. 그렇지 않으면 컴파일 에러가 발생합니다.&lt;/p&gt;
</description>
        <pubDate>Sun, 11 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/ios/swift-combine-3/</link>
        <guid isPermaLink="true">https://origogi.github.io/ios/swift-combine-3/</guid>
        
        <category>iOS</category>
        
        <category>Swift</category>
        
        <category>Combine</category>
        
        
        <category>iOS</category>
        
      </item>
    
      <item>
        <title>[iOS][Swift] Combine 정리 (2) - 변환 연산자, 결합 연산자</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/2e217acb-c4ae-4bb8-8747-f85e5d15a6e5&quot; alt=&quot;banner&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;서론&quot;&gt;서론&lt;/h2&gt;

&lt;p&gt;해당 포스트는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Combine&lt;/code&gt; 에 대해 정리한 두 번째 포스트입니다.&lt;/p&gt;

&lt;h2 id=&quot;변환-연산자&quot;&gt;변환 연산자&lt;/h2&gt;

&lt;p&gt;대표적인 변환 연산자는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatMap&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge&lt;/code&gt; 등이 있습니다.&lt;/p&gt;

&lt;h3 id=&quot;map&quot;&gt;Map&lt;/h3&gt;

&lt;p&gt;가장 기본적인 변환 연산자로, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로부터 전달된 값을 변환합니다.
정수형인 정수 값을 받아서 2배로 변환할수 있고 심지어 정수형이 아닌 다른 타입으로 변환할 수도 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 2, 4, 6, 8, 10&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Value is &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Value is 1, Value is 2, Value is 3, Value is 4, Value is 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;flatmap&quot;&gt;FlatMap&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로부터 전달된 값을 다른 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로 변환합니다.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; 은 단일 값을 반환하지만, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatMap&lt;/code&gt; 은 여러 값 즉 새로운 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;를 반환합니다.
하지만 sink로 값을 받을 때는 단일 값으로 받습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;namePublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Alice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Bob&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Charlie&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;flatMapPublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;namePublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flatMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hi, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;flatMapPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;merge&quot;&gt;Merge&lt;/h3&gt;

&lt;p&gt;여러 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;를 하나의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로 변환합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;merged&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;filter&quot;&gt;Filter&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로부터 전달된 값을 필터링합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 2, 4  짝수만 출력&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;compactmap&quot;&gt;CompactMap&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로부터 전달된 값을 변환하고, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;이 아닌 값만 반환합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;four&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compactMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 1, 2, 3, 5  &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &quot;four&quot;는 Int로 변환할 수 없기 때문에 제외됩니다.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;결합-연산자&quot;&gt;결합 연산자&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Combine&lt;/code&gt; 에서는 여러 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;를 결합하는 연산자가 있습니다. 대표적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Zip&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CombineLatest&lt;/code&gt; 등이 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;combinelatest&quot;&gt;CombineLatest&lt;/h2&gt;

&lt;p&gt;여러 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로부터 최신 값을 결합합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;combinedPublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;combineLatest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combinedPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Received values: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;publisher1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;publisher2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Received values: 1, 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Received values: 3, 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Received values: 3, 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;추가로 서로 결합하는 Publisher 가 서로 다른 타입이라도 결합이 가능합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;combinedPublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;combineLatest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combinedPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Received values: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Received values: 1, A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;zip&quot;&gt;Zip&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CombineLatest&lt;/code&gt; 와 비슷하지만, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로부터 전달된 값이 모두 있을 때만 값을 전달합니다. 즉 두 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt; 중 하나라도 값이 없으면 값을 전달하지 않습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;combinedPublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;combineLatest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combinedPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Received values: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;publisher1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// publisher2에 값이 없기 때문에 출력되지 않음&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;publisher2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// publisher1, publisher2 모두 값이 있기 때문에 출력됨&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Received values: 1, 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Received values: 3, 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;추가로 Publishers 에 확장함수를 이용해서 Zip을 생성할수 있고 3 개 이상의 Publisher 를 결합할 수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;zipPub&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Publishers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pub1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pub2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;zipPub&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Publishers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Zip3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pub1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pub2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pub3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;switchtolatest&quot;&gt;SwitchToLatest&lt;/h3&gt;

&lt;p&gt;switchToLatest()는 Combine 프레임워크에서 사용되는 연산자로, 여러 퍼블리셔를 다룰 때 유용하게 사용됩니다.&lt;/p&gt;

&lt;p&gt;동작 원리를 살펴보자면&lt;/p&gt;

&lt;p&gt;먼저 Publisher 가 하나 존재하고 이를 Outer Publisher 가 있다고 가정합니다. Outer Publisher 는 일반적인 값이 아닌 또 다른 Publisher 를 방출합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;outer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PassthroughSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;AnyPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이제 Outer Publisher 가 방출하는 Publisher 를 Inner Publisher 라고 부르겠습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;inner1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;inner2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CurrentValueSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이제 Outer Publisher 가 Inner Publisher 를 방출하도록 설정합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;AnyPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inner1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이제 SwitchToLatest 연산자를 사용하여 Inner Publisher 를 구독하고 Inner Publisher 가 방출하는 값을 출력합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;switchToLatest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;만약 outer 에서 switchToLastest() 를 사용하지 않고 바로 sink 를 사용하게 되면 Inner publisher 의 값을 구독하지 않고 publisher 자체를 구독하게 됩니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// AnyPublisher&amp;lt;Int, Never&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;그리고 inner1 에 새로운 값을 전달하면 switchToLatest() 는 새로운 값을 출력합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;switchToLatest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;inner1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;다음 새로운 inner publsiher 를 변경해보겠습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;switchToLatest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;AnyPublsiher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inner2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// inner2 로 변경이 되고 inner1 은 무시됩니다. 그리고 inner2 의 값이 출력됩니다.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;inner2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 4&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;inner1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// outer 는 이미 inner2 로 변경되었기 때문에 inner1 의 값은 출력되지 않습니다.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;switchToLatest()를 사용자 입력에 활용하는 좋은 예는 검색 기능을 구현할 때입니다. 사용자가 검색어를 입력할 때마다 네트워크 요청을 보내야 한다고 가정해 보겠습니다. 이 경우 사용자가 빠르게 입력을 변경할 수 있기 때문에, 이전 검색어에 대한 요청은 취소하고 가장 최근의 검색어에 대한 요청만 처리해야 합니다. switchToLatest()를 사용하면 이러한 동작을 쉽게 구현할 수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Combine&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 검색어 입력을 받는 퍼블리셔&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;searchTextPublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PassthroughSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 네트워크 요청을 시뮬레이션하는 함수&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnyPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Never&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 실제 네트워크 요청 대신 1초 후에 결과를 반환하는 퍼블리셔&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Just&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Results for &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RunLoop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 검색어가 변경될 때마다 새로운 네트워크 요청을 생성하는 퍼블리셔&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;searchResultsPublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchTextPublisher&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;switchToLatest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 가장 최근의 검색 결과만 구독&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;receiveValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 사용자가 입력하는 시뮬레이션&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;searchTextPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Swift&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;searchTextPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SwiftUI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;searchTextPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Combine&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 이 경우 &quot;Combine&quot;에 대한 검색 결과만 출력됩니다.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Thu, 08 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/ios/swift-combine-2/</link>
        <guid isPermaLink="true">https://origogi.github.io/ios/swift-combine-2/</guid>
        
        <category>iOS</category>
        
        <category>Swift</category>
        
        
        <category>iOS</category>
        
      </item>
    
      <item>
        <title>[iOS][Swift] Combine 정리 (1) - 소개, Key Concepts, 사용 예시</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/2e217acb-c4ae-4bb8-8747-f85e5d15a6e5&quot; alt=&quot;banner&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;서론&quot;&gt;서론&lt;/h2&gt;

&lt;p&gt;해당 포스트는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Combine&lt;/code&gt; 에 대해 정리한 내용입니다.&lt;/p&gt;

&lt;h2 id=&quot;combine-이란&quot;&gt;Combine 이란?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Combine&lt;/code&gt;은 Swift에서 비동기 및 이벤트 기반 코드를 작성하는 데 사용되는 프레임워크입니다.
Combine은 비동기 및 이벤트 기반 코드를 보다 간소화되고 읽기 위운 방식으로 처리하도록 설계되었습니다.&lt;/li&gt;
  &lt;li&gt;애플은 WWDC 2019 에 첫 공개되었고 반응형 프로그래밍을 간소화시키는 것이 목표입니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;key-concepts&quot;&gt;Key Concepts&lt;/h2&gt;

&lt;p&gt;Publisher, Subscriber, Operator, Subject&lt;/p&gt;

&lt;h3 id=&quot;publisher&quot;&gt;Publisher&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;는 값을 생성하고 전달하는 객체입니다.&lt;/li&gt;
  &lt;li&gt;대표적인 예 : 센서, API 호출, 사용자 입력 등&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;subscriber&quot;&gt;Subscriber&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subscriber&lt;/code&gt;는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로부터 값을 받아 처리하는 객체입니다.&lt;/li&gt;
  &lt;li&gt;데이터 스트림을 구독하고, 값을 받아 처리합니다.&lt;/li&gt;
  &lt;li&gt;대표적인 예 : UI 컴포넌트, Data Processors&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;operator&quot;&gt;Operator&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Operator&lt;/code&gt;는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;로부터 전달된 값을 변환하거나, 조작하는 객체입니다.&lt;/li&gt;
  &lt;li&gt;예제 : &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;combineLatest&lt;/code&gt; 등&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;subject&quot;&gt;Subject&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt;는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt;와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subscriber&lt;/code&gt;의 역할을 모두 수행할 수 있는 객체입니다.&lt;/li&gt;
  &lt;li&gt;동시에 여러 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subscriber&lt;/code&gt;에게 값을 전달할 수 있습니다. (Multicast)&lt;/li&gt;
  &lt;li&gt;예제 : 실시간 데티어 스트리밍, 사용자 인터페이스 업데이트&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;combine-장점&quot;&gt;Combine 장점&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;비동기 코드를 간소화하고, 읽기 쉽게 만들어줍니다.&lt;/li&gt;
  &lt;li&gt;쉬운 데이터 조작&lt;/li&gt;
  &lt;li&gt;확장성과 응답성이 뛰어납니다. (이벤트가 발생하면 구독자는 이벤트를 수신한 다음 사용자 인터페이스를 업데이트할 수 있습니다.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;combine-사용-예시&quot;&gt;Combine 사용 예시&lt;/h2&gt;

&lt;h3 id=&quot;simple-example&quot;&gt;Simple Example&lt;/h3&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Combine&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIKit&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Just&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello Combine&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;cancellable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Just는 값을 한 번만 발행하는 Publisher입니다.&lt;/li&gt;
  &lt;li&gt;sink 함수는 Subscriber를 생성하고, 값을 받아 처리합니다. 값을 받아 처리하 기 위해 sink 함수에 클로저를 전달합니다.&lt;/li&gt;
  &lt;li&gt;sink 함수의 리턴값은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyCancellable&lt;/code&gt; 객체입니다. 이 객체를 통해 구독을 취소할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;array-example&quot;&gt;Array Example&lt;/h3&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;doublePublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;doublePublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;참고로 모든 데이터가 소비되면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyCancellable&lt;/code&gt; 객체가 자동으로 해제됩니다.&lt;/p&gt;

&lt;h3 id=&quot;timer-example&quot;&gt;Timer Example&lt;/h3&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;timerPublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;every&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;common&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;timerPublisher&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autoconnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Timer.publish&lt;/code&gt;는 주어진 간격으로 타이머를 생성하는 Publisher입니다.&lt;/li&gt;
  &lt;li&gt;every 매개변수는 타이머의 간격을 나타냅니다.&lt;/li&gt;
  &lt;li&gt;on 매개변수는 타이머가 실행되는 스레드를 나타냅니다. 여기서 .main은 메인 스레드를 나타냅니다. 즉 해당 클로저에서 ui 업데이트가 가능합니다.&lt;/li&gt;
  &lt;li&gt;in 매개변수는 타이머가 실행되는 모드를 나타냅니다. .common과 .default가 있지만 차이점은 없습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;connect-vs-autoconnect&quot;&gt;connect() vs autoconnect()&lt;/h4&gt;

&lt;p&gt;connect() 함수는 Publisher를 수동으로 연결하는 함수입니다. 이 함수를 호출해야만 Publisher가 값을 발행합니다. 반면 autoconnect() 함수는 Subscriber가 생기면 자동으로 Publisher를 연결하는 함수입니다.&lt;/p&gt;

&lt;p&gt;모든 publisher 에서 사용할수 없으며, connectable publisher 에서만 사용할 수 있습니다.
대표적인 connectable publisher로는 Timer가 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TimerPublisher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ConnectablePublisher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;선택 기준&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;connect(): 세밀한 제어가 필요하거나, 여러 구독자를 설정한 후 동시에 시작해야 하는 경우&lt;/li&gt;
  &lt;li&gt;autoconnect(): 간단한 사용 사례, 즉시 시작이 필요한 경우&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;사용 예시&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;connect()&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;every&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;common&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 나중에 원하는 시점에 시작&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;autoconnect()&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;every&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;common&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autoconnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 바로 시작&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;storein-set-메소드&quot;&gt;.store(in: &amp;amp;set) 메소드&lt;/h2&gt;

&lt;p&gt;Combine 프레임워크를 사용하다 보면 자주 마주치는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;store(in:)&lt;/code&gt; 메서드에 대해 알아보겠습니다. 이 메서드는 구독(subscription) 관리에 매우 유용한 도구입니다.&lt;/p&gt;

&lt;h3 id=&quot;storein-메서드란&quot;&gt;store(in:) 메서드란?&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;store(in:)&lt;/code&gt; 메서드는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyCancellable&lt;/code&gt; 객체를 지정된 컬렉션에 저장하는 Combine의 유틸리티 메서드입니다. 주로 구독의 수명 주기를 관리하고 메모리 누수를 방지하는 데 사용됩니다.&lt;/p&gt;

&lt;h3 id=&quot;주요-특징&quot;&gt;주요 특징&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;목적&lt;/strong&gt;: 구독의 수명 주기 관리 및 메모리 누수 방지&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;작동 방식&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyCancellable&lt;/code&gt; 객체를 지정된 컬렉션(주로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set&amp;lt;AnyCancellable&amp;gt;&lt;/code&gt;)에 추가&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;자동 취소&lt;/strong&gt;: 컬렉션이 해제될 때 저장된 모든 구독도 자동으로 취소됨&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyCancellable&lt;/code&gt; 은 deinit 될 때 자동으로 구독을 취소하는 객체입니다. 따라서 cacellable 프로퍼티가 deinit 될 때, 구독도 자동으로 취소됩니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;참고&lt;/strong&gt;:
&lt;a href=&quot;https://velog.io/@kimscastle/iOSCombine%EC%9D%98-Cancellable-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%951&quot;&gt;[iOS]Combine의 Cancellable 딥다이브(1)&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;사용-예시&quot;&gt;사용 예시&lt;/h3&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@main&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MyApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancellables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;AnyCancellable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIDevice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orientationDidChangeNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;currentOrientation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIDevice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orientation&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Orientation changed to &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentOrientation&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancellables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Scene&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;WindowGroup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;여기서 store() 를 사용하지 않으면, NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification) 의 구독이 즉시 해제되어, sink 클로저가 호출되지 않습니다.&lt;/p&gt;

&lt;h3 id=&quot;장점&quot;&gt;장점&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;코드 간결성 : 구독 취소 로직을 별도로 작성할 필요 없음&lt;/li&gt;
  &lt;li&gt;자동 관리 : 뷰 컨트롤러 들이 해제될 때, 저장된 모든 구독도 자동으로 취소됨&lt;/li&gt;
  &lt;li&gt;다중 구독 관리 : 여러 개의 구독을 한 번에 관리할 수 있음&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;예외-처리&quot;&gt;예외 처리&lt;/h2&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NumberError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operationFailed&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;numberPublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;doublePublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tryMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NumberError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operationFailed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NumberError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;subscriber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doublePublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tryMap&lt;/code&gt;은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;과 유사하지만, 클로저가 에러를 던질 수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tryMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;@escaping&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Publishers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TryMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;catch&lt;/code&gt;는 에러를 처리하고, 새로운 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Publisher&lt;/code&gt; 를 반환합니다. 그리고 남은 Publisher 는 버려지고 catch 블록에서 반환된 Publisher 가 대신 사용됩니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;아니면&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;catch를&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;사용하지&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;않고&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sink에서&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;에러를&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;처리할&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;수도&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;있습니다&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;```&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;swift&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;numberPublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;doublePublisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tryMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NumberError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operationFailed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;subscriber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doublePublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Completion: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;receiveValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Value: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드를 실행 한 결과는 다음과 같습니다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Value: 2
Value: 4
Value: 6
Completion: failure(__lldb_expr_31.NumberError.operationFailed)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 07 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/ios/swift-combine-1/</link>
        <guid isPermaLink="true">https://origogi.github.io/ios/swift-combine-1/</guid>
        
        <category>iOS</category>
        
        <category>Swift</category>
        
        
        <category>iOS</category>
        
      </item>
    
      <item>
        <title>진정한 시니어 개발자란?</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;400&quot; alt=&quot;image&quot; src=&quot;https://github.com/user-attachments/assets/d651ed04-49e4-4e39-9379-f69a6dd8d031&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;시니어-개발자가-되기-위한-조건-또는-스킬&quot;&gt;시니어 개발자가 되기 위한 조건 또는 스킬&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;엉망인 코드를 읽을 수 있는 능력&lt;/li&gt;
  &lt;li&gt;엉망인 코드를 고칠 수 있는 능력&lt;/li&gt;
  &lt;li&gt;엉망인 코드를 작성하지 않고 또 다른 사람이 엉망인 코드를 작성하지 않도록 지도할 수 있는 능력&lt;/li&gt;
  &lt;li&gt;정시에 퇴근&lt;/li&gt;
  &lt;li&gt;Automation Testing&lt;/li&gt;
  &lt;li&gt;단순 반복 작업 자동화&lt;/li&gt;
  &lt;li&gt;CI/CD&lt;/li&gt;
  &lt;li&gt;Clean Code&lt;/li&gt;
  &lt;li&gt;Architecture Principle &amp;amp; Design Pattern&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;시니어-개발자가-되기-전-흔히-하는-실수&quot;&gt;시니어 개발자가 되기 전 흔히 하는 실수&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;나쁜 환경(회사)에서 학습
    &lt;ul&gt;
      &lt;li&gt;나쁜 환경에서는 나쁜 습관을 배울 수 밖에 없다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;자동화된 테스트 코드를 작성하지 않음
    &lt;ul&gt;
      &lt;li&gt;테스트 코드를 작성하는 시간이 없다고 생각하지만, 테스트 코드를 작성하지 않으면 더 많은 시간을 낭비하게 된다.&lt;/li&gt;
      &lt;li&gt;테스트 코드를 작성하지 않으면 절대 프로젝트를 성공적으로 마칠 수 없다.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;You can only go fast if you go well.&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;생산적인 CI/CD 파이프라인을 통해서 자동화된 Test Suite 를 확보해야 한다.&lt;/li&gt;
      &lt;li&gt;자동화가 없어도 더 빠르게 진행할 수 있다고 믿는 개발자는 실제로 더 느리게 진행하게 된다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Tue, 06 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/etc/senior-developer/</link>
        <guid isPermaLink="true">https://origogi.github.io/etc/senior-developer/</guid>
        
        
        <category>etc</category>
        
      </item>
    
      <item>
        <title>[Flutter] E2E Test (2) - CI에 통합하기</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;800&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/ae4e0834-0d5f-41a1-9693-b489076e2677&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;이번 포스팅에서는 E2E Test를 CI에 적용하는 방법에 대해 알아보겠습니다.
여기서는 E2E Test를 지원하기 위해 기본 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;integration_test&lt;/code&gt; 패키지를 사용하지 않고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;patrol&lt;/code&gt; 패키지를 사용하고 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;patrol&lt;/code&gt; 패키지에 대해서 알아보시려면 &lt;a href=&quot;https://origogi.github.io/flutter/E2E-Test-1/&quot;&gt;E2E Test (1) - Patrol 패키지 사용하기&lt;/a&gt; 포스팅을 참고해주세요.&lt;/p&gt;

&lt;h2 id=&quot;환경&quot;&gt;환경&lt;/h2&gt;

&lt;h3 id=&quot;ci&quot;&gt;CI&lt;/h3&gt;

&lt;p&gt;Flutter CI 를 지원하는 여러 서비스 중에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Github Actions&lt;/code&gt; 를 이용해서 설정을 진행하겠습니다.&lt;/p&gt;

&lt;h3 id=&quot;test-환경&quot;&gt;Test 환경&lt;/h3&gt;

&lt;p&gt;CI 환경에서 테스트를 진행할 때 고려할 수 있는 몇 가지 옵션이 있습니다:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;시뮬레이터&lt;/li&gt;
  &lt;li&gt;Firebase Test Lab&lt;/li&gt;
  &lt;li&gt;AWS Device Farm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 중 시뮬레이터 사용은 추천하지 않습니다. 실제로 시뮬레이터를 이용한 테스트는 속도가 느리고, 예기치 못한 오류로 인해 테스트가 제대로 수행되지 않는 경우가 많기 때문입니다.&lt;/p&gt;

&lt;p&gt;본 포스트에서는 Firebase Test Lab을 사용하여 테스트하는 방법에 초점을 맞추겠습니다.&lt;/p&gt;

&lt;h2 id=&quot;firebase-test-lab&quot;&gt;Firebase Test Lab&lt;/h2&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;
&lt;img width=&quot;400&quot; alt=&quot;image&quot; src=&quot;https://i.ytimg.com/vi/4_ZEEX1x17k/maxresdefault.jpg&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;Firebase Test Lab은 Google Cloud의 일부로서, 개발자가 다양한 기기와 구성에서 자신의 Android 및 iOS 애플리케이션을 테스트할 수 있게 해주는 클라우드 기반의 앱 테스팅 서비스입니다. 이 서비스를 사용하면 개발자는 실제 기기를 소유하고 있지 않아도, 여러 기기와 운영 체제 버전에서 앱의 성능과 안정성을 테스트하고 문제를 식별할 수 있습니다.&lt;/p&gt;

&lt;p&gt;Firebase Test Lab의 주요 기능은 다음과 같습니다:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;다양한 기기와 환경에서의 테스트: Firebase Test Lab은 실제 기기 뿐만 아니라 에뮬레이터와 시뮬레이터를 통해 여러 안드로이드와 iOS 기기 및 버전에서 앱을 테스트할 수 있는 환경을 제공합니다.&lt;/li&gt;
  &lt;li&gt;테스트 결과 및 로그: 테스트 실행 후, Firebase Test Lab은 자세한 결과, 스크린샷, 비디오, 로그를 제공하여 개발자가 문제를 쉽게 식별하고 수정할 수 있게 해줍니다.&lt;/li&gt;
  &lt;li&gt;통합과 접근성: Firebase Test Lab은 Firebase 및 Google Cloud 플랫폼과 밀접하게 통합되어 있어, CI/CD 파이프라인과 같은 다른 개발 도구와 쉽게 연동할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;github-actions-workflow-구성&quot;&gt;Github Actions Workflow 구성&lt;/h2&gt;

&lt;p&gt;다음 단계로는 Github Actions Workflow를 구성하는 방법에 대해 알아보겠습니다.&lt;/p&gt;

&lt;p&gt;먼저 전체적인 flow는 아래와 같습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;

&lt;img width=&quot;900&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/efe0c99d-efcd-4100-988a-fa9a5be4b620&quot; /&gt;

&lt;/div&gt;

&lt;h3 id=&quot;준비&quot;&gt;준비&lt;/h3&gt;

&lt;p&gt;Github Actions 에서 Firebase Test Lab 과 통신하기 위해서는 GCP를 통해서 생성된 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service-account.json&lt;/code&gt; 파일이 필요하고 해당 파일은 Github Actions의 Secret에 미리 저장해두어야 합니다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service-account.json&lt;/code&gt; 파일을 생성하는 방법은 아래 링크를 참고해주세요.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://velog.io/@jsoh/CI-%EC%84%A4%EC%A0%95-%EC%A4%91-firebase-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B3%84%EC%A0%95-json-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C&quot;&gt;CI 설정 중 firebase 서비스 계정 json 다운로드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;그리고 Firebase Test Lab 의 결과값을 저장하기 위해서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket&lt;/code&gt; 이 필요합니다. 여기서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket&lt;/code&gt; 은 Firebase Console 에서 Storage 에서 생성할 수 있습니다.
기존의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket&lt;/code&gt; 을 사용하거나 새로 생성하여 사용하시면 됩니다.
저는 새로운 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bucket&lt;/code&gt; 을 생성하여 사용하는 것을 추천드립니다.&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;
&lt;img width=&quot;400&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/55634b91-830f-4742-9bef-b8067d935273&quot; /&gt;
&lt;/div&gt;

&lt;h3 id=&quot;android&quot;&gt;Android&lt;/h3&gt;

&lt;p&gt;먼저 Android 같은 경우 아래와 같이 Workflow를 구성하였습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;

&lt;img width=&quot;674&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/fcb4ed3e-071a-4df5-b3ef-fb2f4ed86364&quot; /&gt;

&lt;/div&gt;

&lt;p&gt;해당 yaml 파일은 아래와 같습니다.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Run Flutter E2E (android) test&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;e2e&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SLACK_MESSAGE_TITLE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Flutter E2E Test on ${{ matrix.os }} ${{ matrix.os_version }}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TESTS_EXIT_CODE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ steps.tests_step.outputs.TESTS_EXIT_CODE }}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;URL_TO_DETAILS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ steps.tests_step.outputs.URL_TO_DETAILS }}&lt;/span&gt;
    
    &lt;span class=&quot;na&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 테스트를 하고자 하는 디바이스 설정&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Android&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;API&apos;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;device_model&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;oriole&apos;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;os_version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33&apos;&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# 1. 코드 체크아웃&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v3&lt;/span&gt;  

      &lt;span class=&quot;c1&quot;&gt;# 2.1 Flutter 설치&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;subosito/flutter-action@v2&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;stable&apos;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 2.2. 의존성 설치&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install dependencies&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;flutter pub get&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 2.3. Gradle wrapper 생성&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Generate Gradle wrapper&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;flutter build apk --config-only&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 3.1. Patrol CLI 설치&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set up Patrol CLI&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dart pub global activate patrol_cli&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 3.2. Patrol 빌드&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;patrol build android&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;patrol build android&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 4.1. service-account.json 인증&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Authenticate to Google Cloud&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;google-github-actions/auth@v1&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;credentials_json&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;${{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;secrets.GCP_STAGE_SERVICE_ACCOUNT_JSON&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&apos;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 4.2. gcloud 설정&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set up Cloud SDK&lt;/span&gt;   
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;google-github-actions/setup-gcloud@v1&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 5. Firebase Test Lab 에 APK 업로드 및 테스트 실행&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Upload APKs to Firebase Test Lab and wait for tests to finish&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tests_step&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ANDROID_DEVICE_MODEL&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ matrix.device_model }}&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ANDROID_DEVICE_VERSION&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ matrix.os_version }}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;set +e&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;set -euo pipefail&lt;/span&gt;
         
          &lt;span class=&quot;s&quot;&gt;# Firebase Test Lab 실행 및 결과 저장&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;output=$(set -euo pipefail &amp;amp;&amp;amp; \&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;gcloud firebase test android run \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--type instrumentation \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--app build/app/outputs/apk/debug/app-debug.apk \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--test build/app/outputs/apk/androidTest/debug/ap-debug-androidTest.apk \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--device model=&quot;$ANDROID_DEVICE_MODEL&quot;,version=&quot;$ANDROID_DEVICE_VERSION&quot;,locale=en,orientation=portrait \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--timeout 10m \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--results-bucket=&quot;&amp;lt;버킷 이름 입력&amp;gt;&quot; \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--use-orchestrator \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--environment-variables clearPackageData=true 2&amp;gt;&amp;amp;1)&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;TESTS_EXIT_CODE=$?&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;set -e&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;# Extract the last link using grep, tail, and sed, and write it to Github Summary&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;link=&quot;$(echo &quot;$output&quot; | grep -o &apos;https://[^ ]*&apos; | tail -1 | sed &apos;s/\[//;s/\]//&apos;)&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;echo &quot;[Test details on Firebase Test Lab]($link) (Firebase members only)&quot; &amp;gt;&amp;gt; &quot;$GITHUB_STEP_SUMMARY&quot;&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;echo &quot;URL_TO_DETAILS=$link&quot; &amp;gt;&amp;gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;echo &quot;TESTS_EXIT_CODE=$TESTS_EXIT_CODE&quot; &amp;gt;&amp;gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;exit $TESTS_EXIT_CODE&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;# 6. Slack 메시지 전송&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;call_send_slack_message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;  
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Notify on Slack&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./.github/workflows/send_slack_message.yml&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;e2e&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;always()&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TESTS_EXIT_CODE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ needs.e2e.outputs.TESTS_EXIT_CODE }}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SLACK_MESSAGE_TITLE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ needs.e2e.outputs.SLACK_MESSAGE_TITLE }}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;URL_TO_DETAILS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ needs.e2e.outputs.URL_TO_DETAILS }}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secrets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;inherit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 workflow 에서 slack 메시지 전송 부분은 별도의 파일로 분리하여 사용하였습니다.
slack 메시지 전송은 필수가 아니기 때문에 slack 메시지 전송 상세 워크플로우는 생략하겠습니다.&lt;/p&gt;

&lt;h3 id=&quot;ios&quot;&gt;iOS&lt;/h3&gt;

&lt;p&gt;iOS 같은 경우 아래와 같이 Workflow를 구성하였습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;

&lt;img width=&quot;767&quot; alt=&quot;image&quot; src=&quot;https://github.com/subosito/flutter-action/assets/35194820/8a0c0638-371f-4a75-907b-1902a08d8081&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;iOS 의 경우 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Code Signing&lt;/code&gt; 이 필요하기 때문에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fastlane&lt;/code&gt; 을 사용하여 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Code Signing&lt;/code&gt; 을 진행하였습니다.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Run Flutter E2E (ios) test&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;e2e&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;macos-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SLACK_MESSAGE_TITLE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Flutter E2E Test on ${{ matrix.os }} ${{ matrix.os_version }}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TESTS_EXIT_CODE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ steps.tests_step.outputs.TESTS_EXIT_CODE }}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;URL_TO_DETAILS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ steps.tests_step.outputs.URL_TO_DETAILS }}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# 테스트를 하고자 하는 디바이스 설정&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;device_model&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;iphone14pro&apos;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;os_version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;16.6&apos;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;iOS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# 1. 코드 체크아웃&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v3&lt;/span&gt; 
    
      &lt;span class=&quot;c1&quot;&gt;# 2.1 Flutter 설치&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;subosito/flutter-action@v2&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;stable&apos;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 2.2. 의존성 설치&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install dependencies&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;flutter pub get&lt;/span&gt;
    
      &lt;span class=&quot;c1&quot;&gt;# 3.1 Ruby 설치 (Fastlane 사용을 위해)&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install Ruby&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ruby/setup-ruby@v1&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ruby-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2.7.2&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;bundler-cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        
      &lt;span class=&quot;c1&quot;&gt;# 3.2 bundle 설치 (Fastlane 사용을 위해)&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install bundle&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;cd ios&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;bundle config path vendor/bundle&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;bundle install --jobs 4 --retry 3&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 4. Fastlane 사용하여 Certificate, Provisioning Profile 설치&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# 5. Fastlane 사용하여 Xcode Code Signing 변경&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Sign iOS app&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;maierj/fastlane-action@v3.0.0&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;lane&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;code_sign_developemnt&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;subdirectory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ios&lt;/span&gt;
      
      &lt;span class=&quot;c1&quot;&gt;# 6.1 Patrol CLI 설치&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set up Patrol CLI&lt;/span&gt;   
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dart pub global activate patrol_cli&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 6.2 Patrol 빌드&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;patrol build ios&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;patrol build ios&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 7.1 service-account.json 인증&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Authenticate to Google Cloud&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;google-github-actions/auth@v1&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;credentials_json&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;${{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;secrets.GCP_STAGE_SERVICE_ACCOUNT_JSON&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&apos;&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 7.2 gcloud 설정&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set up Cloud SDK&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;google-github-actions/setup-gcloud@v1&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# 8. Firebase Test Lab 에 APK 업로드 및 테스트 실행&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Upload iOS App to Firebase Test Lab and wait for tests to finish&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tests_step&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IOS_DEVICE_MODEL&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ matrix.device_model }}&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;IOS_DEVICE_VERSION&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ matrix.os_version }}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;set +e&lt;/span&gt;

            &lt;span class=&quot;s&quot;&gt;# iOS 테스트 준비 및 Firebase Test Lab 실행&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;output=$(cd build/ios_integ/Build/Products &amp;amp;&amp;amp; \&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;rm -f ios_tests.zip &amp;amp;&amp;amp; \&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;zip -r ios_tests.zip Release-iphoneos/*.app *.xctestrun &amp;amp;&amp;amp; \&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;cd - &amp;amp;&amp;amp; \&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;gcloud firebase test ios run \&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;--type xctest \&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;--test &quot;build/ios_integ/Build/Products/ios_tests.zip&quot; \&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;--device model=&quot;$IOS_DEVICE_MODEL&quot;,version=&quot;$IOS_DEVICE_VERSION&quot;,locale=en_US,orientation=portrait \&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;--timeout 10m \&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;--results-bucket=&quot;&quot;&amp;lt;버킷 이름 입력&amp;gt;&quot; 2&amp;gt;&amp;amp;1)&lt;/span&gt;

            &lt;span class=&quot;s&quot;&gt;TESTS_EXIT_CODE=$?&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;set -e&lt;/span&gt;

            &lt;span class=&quot;s&quot;&gt;# 출력 및 링크 추출&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;echo &quot;$output&quot;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;link=&quot;$(echo &quot;$output&quot; | grep -o &apos;https://[^ ]*&apos; | tail -1 | sed &apos;s/\[//;s/\]//&apos;)&quot;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;echo &quot;[Test details on Firebase Test Lab]($link) (Firebase members only)&quot; &amp;gt;&amp;gt; &quot;$GITHUB_STEP_SUMMARY&quot;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;echo &quot;URL_TO_DETAILS=$link&quot; &amp;gt;&amp;gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;echo &quot;TESTS_EXIT_CODE=$TESTS_EXIT_CODE&quot; &amp;gt;&amp;gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;exit $TESTS_EXIT_CODE&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;# 9. Slack 메시지 전송&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;call_send_slack_message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;  
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Notify on Slack&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./.github/workflows/send_slack_message.yml&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;e2e&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;always()&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TESTS_EXIT_CODE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ needs.e2e.outputs.TESTS_EXIT_CODE }}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SLACK_MESSAGE_TITLE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ needs.e2e.outputs.SLACK_MESSAGE_TITLE }}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;URL_TO_DETAILS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ needs.e2e.outputs.URL_TO_DETAILS }}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secrets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;inherit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Code Signing 을 위한 Fastlane 은 내부적으로 Fastlane match 를 사용하였습니다.
Fastlane match 를 사용하면 Code Signing 관리를 쉽게 할 수 있습니다.
Fastlane match 은 설정이 조금 복잡하고 어렵기 때문에 해당 포스팅에서는 Fastlane match 설정에 대해서는 다루지 않겠습니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Fastlane match 를 통해 Code Signing 을 진행할 때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;development&lt;/code&gt; 타입 인증서와 provisioning profile 를 사용하는 것을 권장드립니다. 그 이유는 E2E Test binary 를 실행하기 위해서는 provisioning profile의 entitlements에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get-task-allow&lt;/code&gt;가 포함되어 있어야 하며 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;development&lt;/code&gt; 타입 인증서와 provisioning profile 에는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get-task-allow&lt;/code&gt;가 포함되어 있기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;실행-결과&quot;&gt;실행 결과&lt;/h3&gt;

&lt;p&gt;위와 같이 Workflow를 구성하고 실행하면 아래와 같이 Firebase Test Lab 에서 테스트가 진행되고 결과가 저장됩니다.&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;

&lt;img width=&quot;400&quot; alt=&quot;iShot_2024-02-13_11 12 36&quot; src=&quot;https://github.com/subosito/flutter-action/assets/35194820/d32ada47-5ea1-45bb-b028-bad72fe7f960&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;테스트 유형에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XCTest&lt;/code&gt; 는 iOS 테스트를 의미하고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;도구 작동&lt;/code&gt; 은 Android 테스트를 의미합니다.&lt;/p&gt;

&lt;p&gt;상세 결과를 확인하고 싶다면 항목 하나를 클릭하면 아래와 같이 상세 결과(로그, 실행 동영상)를 확인할 수 있습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;

&lt;img width=&quot;500&quot; alt=&quot;iShot_2024-02-13_11 15 19&quot; src=&quot;https://github.com/subosito/flutter-action/assets/35194820/8f899600-ab9b-4a63-bb28-9e0ab21f9ad5&quot; /&gt;
&lt;img width=&quot;500&quot; alt=&quot;iShot_2024-02-13_11 15 40&quot; src=&quot;https://github.com/subosito/flutter-action/assets/35194820/cd70c62f-6c3b-4da6-9c44-50d0c39aae44&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;마무리&quot;&gt;마무리&lt;/h2&gt;

&lt;p&gt;Patrol 패키지를 사용하여 E2E Test를 CI에 적용하는 방법에 대해서 알아보았습니다.
위에 정리된 워크플로우에 대해서는 각각의 상황에 맞게 수정하여 사용하시면 됩니다.&lt;/p&gt;

&lt;p&gt;처음 이 방법을 도입할 때는 여러 오류와 문제에 직면할 수 있습니다. 하지만, 이러한 문제를 해결하고 나면, CI/CD 파이프라인을 구축하는 과정이 매우 즐거운 경험이 될 것입니다.
혹시나 문제가 발생하거나 궁금한 점이 있으시면 언제든지 댓글로 남겨주세요.&lt;/p&gt;

&lt;p&gt;그리고 다음 포스팅에서는 아래와 같은 내용을 다루도록 하겠습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Patrol를 이용하여 Google Login 같은 외부 서비스와의 상호작용 및 테스트&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;감사합니다.&lt;/p&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://firebase.google.com/docs/test-lab&quot;&gt;Firebase Test Lab&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pub.dev/packages/patrol&quot;&gt;Patrol&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Tue, 13 Feb 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/flutter/e2e-test-2/</link>
        <guid isPermaLink="true">https://origogi.github.io/flutter/e2e-test-2/</guid>
        
        <category>Flutter</category>
        
        <category>Dart</category>
        
        
        <category>Flutter</category>
        
      </item>
    
      <item>
        <title>[Flutter] E2E Test (1) - Test 소개 및 Patrol package 적용하기</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;800&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/ae4e0834-0d5f-41a1-9693-b489076e2677&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;위 포스트에서는 Flutter E2E 자동화 테스트를 적용해보는 내용에 대해서 다루고 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;1-e2eend-to-end-test-란&quot;&gt;1. E2E(End to End) Test 란?&lt;/h2&gt;

&lt;p&gt;E2E(End-to-End) 테스트는 앱이 실제 사용 환경에서 예상대로 작동하는지 확인하기 위해 전체 시스템을 테스트하는 과정입니다. 이는 실제 기기와 서버를 사용하여 앱의 시작부터 끝까지(한 ‘End’에서 다른 ‘End’까지) 모든 기능을 검사합니다. 전통적으로는 수동으로 수행되었지만, 시간 소모가 크고 오류 가능성이 높은 단점이 있습니다.&lt;/p&gt;

&lt;p&gt;이를 극복하기 위해, E2E 테스트의 자동화가 중요해졌습니다. 자동화는 테스트의 일관성을 높이고 시간을 절약하며, 오류 가능성을 줄여줍니다.&lt;/p&gt;

&lt;p&gt;특히, 지속적인 통합 및 배포(CI/CD) 파이프라인에서 중요한 역할을 하며, 각 릴리스가 사용자가 요구하는 높은 품질과 신뢰성을 충족하도록 돕습니다.&lt;/p&gt;

&lt;p&gt;Flutter와 같은 플랫폼에서는 integration_test 패키지를 통해 E2E 테스트를 자동화할 수 있으며, 이는 앱의 모든 부분이 실제 조건에서 정상적으로 작동하는지 확인하는 데 유용합니다.&lt;/p&gt;

&lt;h3 id=&quot;e2e-test-vs-integration-test-vs-instrument-test&quot;&gt;E2E Test vs Integration Test vs Instrument Test&lt;/h3&gt;

&lt;p&gt;해당 포스트에서 E2E Test 와 Integration Test 용어를 혼재하고 있지만 둘은 다른 의미를 가지고 있습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;E2E Test&lt;/strong&gt;: 앱의 전체적인 기능과 성능을 검증하는 테스트로, 사용자의 관점에서 앱의 동작을 검증합니다. 이는 앱의 시작부터 끝까지(Client 에서 Server까지) 모든 기능을 검사합니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Integration Test&lt;/strong&gt;: 앱의 여러 부분이 함께 작동하는지 확인하는 테스트로, 서로 다른 부분들이 함께 잘 작동하는지 확인합니다. 이는 특정 기능이 다른 기능과 잘 통합되는지 확인하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;그리고 또 &lt;strong&gt;Instrument Test&lt;/strong&gt; 라는 용어가 있습니다. 해당 테스트의 정의는 아래와 같습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Instrument Test&lt;/strong&gt;: 앱의 UI와 같은 인터페이스를 테스트하는 테스트로, 실제 디바이스나 에뮬레이터에서 실행되는 테스트를 의미합니다. 사용자 인터페이스와 애플리케이션의 통합된 부분이 예상대로 동작하는지 검증합니다. 주로 앱의 성능과 사용자 인터랙션을 포함한 전반적인 기능을 확인합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;해당 포스트에서 소개하는 테스트는 엄밀히 따지면 &lt;strong&gt;Instrument Test&lt;/strong&gt; 라고 할 수 있으며 추가로 다른 종단(Server)의 동작 확인도 같이 커버가 되기 때문에 &lt;strong&gt;E2E Test&lt;/strong&gt; 영역에도 속할 수 있습니다.&lt;/p&gt;

&lt;p&gt;만약 서버를 Simulator 나 Mocking 을 통해서 테스트를 진행한다면 해당 테스트는 오로지 &lt;strong&gt;Instrument Test&lt;/strong&gt; 에 속하게 됩니다.&lt;/p&gt;

&lt;p&gt;flutter에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;integration_test&lt;/code&gt; 이라는 패키지 명을 사용하고 있어서 혹시 오해가 있을 수 있으니 참고하시기 바랍니다.&lt;/p&gt;

&lt;h3 id=&quot;e2e-자동화-test-한계점&quot;&gt;E2E 자동화 Test 한계점&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;매우 복잡한 시나리오인 경우 테스트 코드 자체를 작성하는 것이 실제 개발하는 것보다 더 어렵고 시간이 오래 걸릴수가 있습니다.&lt;/li&gt;
  &lt;li&gt;다양한 예외처리 (예를 들어 서버가 실패하는 시나리오) 를 검증하기 어렵습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;e2e-자동화-test를-적용해야-하는-타겟&quot;&gt;E2E 자동화 Test를 적용해야 하는 타겟&lt;/h3&gt;

&lt;p&gt;앱의 모든 기능에 대해서 E2E 자동화 테스트를 적용하는 것은 현실적으로 어려울 수 있습니다. 따라서, E2E 자동화 테스트를 적용할 때에는 다음과 같은 기준을 고려하여 적용하는 것이 좋습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;중대한 핵심 기능: E2E 테스트는 시스템의 가장 중요하고 핵심적인 기능들, 특히 실패 시 심각한 결과를 초래할 수 있는 기능들에 초점을 맞춰야 합니다. 이러한 기능들은 시스템의 안정성과 사용자 경험에 직접적인 영향을 미치기 때문에, 신중하게 테스트되어야 합니다.&lt;/li&gt;
  &lt;li&gt;개발 완료 및 UI 변경 가능성이 낮은 기능: 개발이 완전히 마무리된 기능들, 특히 추후에 UI 시나리오나 기능 자체에 대한 변경 가능성이 낮은 기능들은 E2E 테스트의 이상적인 대상입니다. 이러한 기능들은 테스트 스크립트를 자주 수정할 필요가 없어, 테스트의 유지 관리 부담을 줄일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-flutter-sample-app-에-e2e-test-적용하기&quot;&gt;2. Flutter Sample app 에 E2E Test 적용하기&lt;/h2&gt;

&lt;p&gt;먼저 아래 간단한 예제를 통해서 E2E Test 를 적용해보겠습니다.&lt;/p&gt;

&lt;h3 id=&quot;sample-app-소개-및-코드&quot;&gt;Sample App 소개 및 코드&lt;/h3&gt;

&lt;p&gt;아래 플로팅 버튼을 누르면 카운터가 증가하는 간단한 앱을 예제로 사용하겠습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;

&lt;img width=&quot;408&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/d4ca6b4c-ec18-4700-8e79-ba37206fe10c&quot; /&gt;
&lt;/div&gt;

&lt;details&gt;
&lt;summary&gt;Dart Code&lt;/summary&gt;
&lt;div&gt;

    &lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;package:flutter/material.dart&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyApp&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StatelessWidget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@override&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Widget&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BuildContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MaterialApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;title:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Counter App&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;home:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyHomePage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;title:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Counter App Home Page&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyHomePage&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StatefulWidget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyHomePage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@override&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyHomePage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_MyHomePageState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;_MyHomePageState&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyHomePage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_incrementCounter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;_counter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@override&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Widget&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BuildContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Scaffold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;appBar:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;title:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;body:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;child:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;mainAxisAlignment:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MainAxisAlignment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;children:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;[&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;&apos;You have pushed the button this many times:&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$_counter&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;nl&quot;&gt;style:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;textTheme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;headlineMedium&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;floatingActionButton:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FloatingActionButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Provide a Key to this button. This allows finding this&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// specific button inside the test suite, and tapping it.&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;key:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;increment&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;onPressed:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_incrementCounter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;tooltip:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Increment&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;child:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Icons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/div&gt;
&lt;/details&gt;

&lt;h3 id=&quot;integrate-test-패키지-추가&quot;&gt;integrate test 패키지 추가&lt;/h3&gt;

&lt;p&gt;Integrate test 를 사용하기 위해서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;integration_test&lt;/code&gt; 패키지를 추가해야 합니다.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;dev_dependencies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;flutter_test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;sdk&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;flutter&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;integration_test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;sdk&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;flutter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;sample-app-e2e-test-코드&quot;&gt;Sample App E2E Test 코드&lt;/h3&gt;

&lt;p&gt;해당 앱에 대한 E2E Test 코드는 아래와 같습니다.&lt;/p&gt;

&lt;details&gt;
&lt;summary&gt;Dart Code&lt;/summary&gt;
&lt;div&gt;

    &lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;package:flutter/material.dart&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;package:flutter_test/flutter_test.dart&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;package:integration_test/integration_test.dart&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;package:introduction/main.dart&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;IntegrationTestWidgetsFlutterBinding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ensureInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 테스트 코드의 최상단에 해당 코드를 호출합니다.&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;end-to-end test&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;testWidgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;tap on the floating action button, verify counter&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// 최상위 위젯을 로드 합니다.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;// 첫 화면에서 텍스트 0을 찾습니다.&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findsOneWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;// 플로팅 액션 버튼을 찾습니다. 여기서 Key 값은 위젯의 Key 값과 동일해야 합니다.&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fab&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;byKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;increment&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;// 플로팅 액션 버튼을 탭합니다.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;// 화면을 갱신합니다.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;// 텍스트 1을 찾습니다.&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;1&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findsOneWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/div&gt;
&lt;/details&gt;

&lt;h3 id=&quot;e2e-test-실행&quot;&gt;E2E Test 실행&lt;/h3&gt;

&lt;p&gt;아래 명령어를 수행하면 현재 컴퓨터에 연결된 디바이스나 에뮬레이터에서 E2E Test 가 실행됩니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flutter &lt;span class=&quot;nb&quot;&gt;test &lt;/span&gt;integration_test/app_test.dart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;flutter-integration_test-패키지-설명&quot;&gt;Flutter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;integration_test&lt;/code&gt; 패키지 설명&lt;/h2&gt;

&lt;p&gt;flutter integration test 를 이해하기 위해서는 아래 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tester&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Finder&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assertion&lt;/code&gt;&lt;/strong&gt; 에 대한 이해가 필요합니다.&lt;/p&gt;

&lt;h3 id=&quot;tester&quot;&gt;Tester&lt;/h3&gt;

&lt;p&gt;Flutter에서의 테스트 과정을 이해하는 데 있어, 실제 사용자나 테스터가 단말기를 조작하는 행위를 모방하는 것이 핵심입니다. 이를 위해 Flutter 테스트 프레임워크는 사용자의 상호작용을 재현할 수 있는 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Teseter&lt;/code&gt;&lt;/strong&gt; 를 제공합니다. 특히, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;화면 탭(tap)&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;화면 스크롤(scroll)&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;텍스트 입력(TextField)&lt;/code&gt; 과 같은 기본적인 사용자 상호작용을 시뮬레이션할 수 있는 API들이 포함되어 있습니다.&lt;/p&gt;

&lt;p&gt;또한, Flutter 테스트에서는 위젯의 상태 변화와 애니메이션 처리를 위한 중요한 API들이 제공됩니다:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pumpWidget()&lt;/code&gt;&lt;/strong&gt; 은 테스트 환경에서 특정 위젯을 초기화하고 렌더링하는 데 사용됩니다. 이는 앱이 시작될 때 runApp()에 의해 실행되는 주 위젯을 설정하는 과정과 유사합니다. 이 메서드를 통해, 테스트하고자 하는 위젯을 테스트 트리의 루트로 설정할 수 있습니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pump()&lt;/code&gt;&lt;/strong&gt; 는 위젯 트리에 단일 프레임을 트리거하는 데 사용되며, 선택적으로 특정 시간 동안 대기할 수 있습니다. 예를 들어, pump(Duration(seconds: 1))을 호출하면, 위젯 트리가 업데이트되고 1초 동안 대기한 후 다음 코드 라인이 실행됩니다. 이는 애니메이션 진행이나 UI 변화가 한 단계씩 진행될 때 유용합니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pumpAndSettle()&lt;/code&gt;&lt;/strong&gt; : 애니메이션과 같은 비동기 작업들이 완료될 때까지 여러 번 프레임을 트리거합니다. 이는 애니메이션이나 다른 비동기 이벤트들이 완료되고 위젯 트리가 안정화될 때까지 반복적으로 프레임을 생성합니다. 기본적으로, 이 메서드는 100ms 간격으로 프레임을 생성하며, 최대 300번 시도한 후에 멈춥니다. 애니메이션이나 비동기 이벤트들이 모두 완료될 때까지 기다리는 상황에서 매우 유용합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;finder&quot;&gt;Finder&lt;/h3&gt;

&lt;p&gt;Flutter 테스트에서 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt;&lt;/strong&gt;는 위젯을 찾는 데 사용되는 다양한 방법들을 제공합니다. &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt;&lt;/strong&gt;를 통해 테스트하려는 위젯을 정확하게 식별하고 검증할 수 있습니다. 다음은 몇 가지 일반적인 위젯 찾기 방법들입니다:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;텍스트로 찾기&lt;/strong&gt;: &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.text&lt;/code&gt;&lt;/strong&gt;를 사용하여 특정 텍스트를 포함하는 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Text&lt;/code&gt;&lt;/strong&gt; 위젯을 찾습니다. 예를 들어, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.text(&apos;Hello&apos;)&lt;/code&gt;&lt;/strong&gt;는 화면에 ‘Hello’라는 텍스트를 포함하는 모든 위젯을 찾습니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;키로 찾기&lt;/strong&gt;: &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.byKey&lt;/code&gt;&lt;/strong&gt;를 사용하여 특정 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Key&lt;/code&gt;&lt;/strong&gt;를 가진 위젯을 찾습니다. 예를 들어, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.byKey(Key(&apos;uniqueKey&apos;))&lt;/code&gt;&lt;/strong&gt;는 ‘uniqueKey’라는 키를 가진 위젯을 찾습니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;타입으로 찾기&lt;/strong&gt;: &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.byType&lt;/code&gt;&lt;/strong&gt;를 사용하여 특정 타입의 위젯을 찾습니다. 예를 들어, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.byType(IconButton)&lt;/code&gt;&lt;/strong&gt;는 모든 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IconButton&lt;/code&gt;&lt;/strong&gt; 위젯을 찾습니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;아이콘으로 찾기&lt;/strong&gt;: &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.byIcon&lt;/code&gt;&lt;/strong&gt;을 사용하여 특정 아이콘을 가진 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Icon&lt;/code&gt;&lt;/strong&gt; 위젯을 찾습니다. 예를 들어, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.byIcon(Icons.add)&lt;/code&gt;&lt;/strong&gt;는 ‘add’ 아이콘을 가진 위젯을 찾습니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;위젯 특성으로 찾기&lt;/strong&gt;: &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.byWidgetPredicate&lt;/code&gt;&lt;/strong&gt;를 사용하여 특정 조건을 만족하는 위젯을 찾습니다. 예를 들어, 특정 위젯이 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Container&lt;/code&gt;&lt;/strong&gt;이고 특정 색상을 가지는지 확인할 수 있습니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;애니메이션으로 찾기&lt;/strong&gt;: &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.byAnimation&lt;/code&gt;&lt;/strong&gt;을 사용하여 특정 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Animation&lt;/code&gt;&lt;/strong&gt;을 가진 위젯을 찾습니다. 예를 들어, 특정 애니메이션 컨트롤러를 사용하는 위젯을 찾을 수 있습니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;조건을 충족하는 첫 번째/마지막 위젯 찾기&lt;/strong&gt;: &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.first&lt;/code&gt;&lt;/strong&gt;나 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find.last&lt;/code&gt;&lt;/strong&gt;를 사용하여 조건을 충족하는 첫 번째나 마지막 위젯을 찾습니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;assertion&quot;&gt;Assertion&lt;/h3&gt;

&lt;p&gt;Flutter 테스트 프레임워크에서는 다양한 종류의 assertion을 제공합니다. 이들은 테스트 중에 특정 조건이 충족되는지 확인하는 데 사용됩니다. 여기 몇 가지 자주 사용되는 assertion들을 소개합니다:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;findsOneWidget&lt;/strong&gt;: &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect&lt;/code&gt;&lt;/strong&gt; 함수와 함께 사용될 때, 이 assertion은 정확히 하나의 위젯이 찾아지기를 기대합니다. 예를 들어, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(find.text(&apos;Hello&apos;), findsOneWidget);&lt;/code&gt;&lt;/strong&gt;은 ‘Hello’라는 텍스트를 포함하는 위젯이 정확히 하나만 존재해야 함을 검증합니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;findsNothing&lt;/strong&gt;: 이 assertion은 주어진 조건을 만족하는 위젯이 하나도 없어야 함을 검증합니다. 예를 들어, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(find.text(&apos;Goodbye&apos;), findsNothing);&lt;/code&gt;&lt;/strong&gt;은 ‘Goodbye’라는 텍스트를 포함하는 위젯이 없어야 함을 의미합니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;findsWidgets&lt;/strong&gt;: 이 assertion은 하나 이상의 위젯이 찾아지기를 기대합니다. 예를 들어, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(find.byType(TextButton), findsWidgets);&lt;/code&gt;&lt;/strong&gt;는 하나 이상의 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TextButton&lt;/code&gt;&lt;/strong&gt; 위젯이 존재해야 함을 검증합니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;findsNWidgets&lt;/strong&gt;: 특정 수의 위젯이 찾아지기를 기대합니다. 예를 들어, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(find.byIcon(Icons.add), findsNWidgets(3));&lt;/code&gt;&lt;/strong&gt;는 ‘add’ 아이콘을 가진 위젯이 정확히 세 개 존재해야 함을 의미합니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;기본-integrated-test-package의-한계&quot;&gt;기본 Integrated test package의 한계&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Native 요소와의 상호 작용 한계: 예를 들어, 퍼미션 팝업&lt;/strong&gt;: 일부 네이티브 요소, 특히 시스템 권한 요청과 같은 팝업은 E2E 테스트 도구로 직접 상호 작용하는 것이 불가능합니다.&lt;/li&gt;
  &lt;li&gt;서버 응답 대기 시간 추정 필요성: 서버와의 통신 과정에서 발생하는 로딩 시간을 고려하여, 테스트에서 얼마나 오랫동안 응답을 기다려야 할지를 예측하고 설정해야 합니다. 이는 테스트의 신뢰성과 실행 시간에 직접적인 영향을 미칩니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pump&lt;/code&gt;&lt;/strong&gt; 및 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pumpAndSettle&lt;/code&gt;&lt;/strong&gt; 사용에 대한 혼란: 특정 액션 후에 위젯 업데이트를 기다리기 위해 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pump&lt;/code&gt;&lt;/strong&gt; 또는 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pumpAndSettle&lt;/code&gt;&lt;/strong&gt;을 호출해야 하는데, 어느 것을 사용해야 할지 결정하는 것이 혼란스러울 수 있습니다. 이 둘은 위젯 상태의 변화를 처리하는 방식에서 차이가 있으며, 적절한 선택이 중요합니다.&lt;/li&gt;
  &lt;li&gt;위젯 상호 작용의 복잡성: 위젯과 상호 작용하기 위해 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finder&lt;/code&gt;&lt;/strong&gt;를 사용하여 위젯을 찾고, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assertion&lt;/code&gt;&lt;/strong&gt;을 통해 실제로 존재하는지 확인한 후, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tap&lt;/code&gt;&lt;/strong&gt;과 같은 액션 API를 호출하고, 이후에 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pump&lt;/code&gt;&lt;/strong&gt; 또는 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pumpAndSettle&lt;/code&gt;&lt;/strong&gt;을 호출하는 과정은 번거롭고 가독성이 떨어질 수 있습니다. 이러한 절차는 테스트 코드의 복잡성을 증가시킵니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;patrol-라이브러리-도입-및-장점&quot;&gt;Patrol 라이브러리 도입 및 장점&lt;/h2&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;345&quot; alt=&quot;iShot_2024-02-04_09 33 17&quot; src=&quot;https://github.com/leancodepl/patrol/assets/35194820/e87d0682-cc8c-4f93-b0b1-892b2de86523&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;Patrol 라이브러리는 기본 integrated test package의 문제점을 해결하기 위해 특별히 설계된 혁신적인 도구입니다. 이 라이브러리의 주요 목적은 개발자들이 앱의 전체적인 기능과 성능을 보다 효율적으로 검증할 수 있도록 지원하는 것입니다. Patrol을 사용함으로써, 테스트 작성과 관리의 복잡성이 대폭 감소합니다.&lt;/p&gt;

&lt;h3 id=&quot;patrol-라이브러리의-주요-장점&quot;&gt;Patrol 라이브러리의 주요 장점&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;네이티브 요소와의 상호 작용 가능&lt;/strong&gt;: Patrol을 사용하면 네이티브 요소와의 상호 작용이 가능해져, 이전에는 테스트하기 어려웠던 시나리오들을 다룰 수 있게 됩니다.&lt;/li&gt;
  &lt;li&gt;서버 응답 대기 시간 설정의 용이성: Patrol에서는 서버 응답을 기다리는 시간을 사용자가 직접 설정할 수 있습니다. 설정된 시간은 최대 대기 시간으로 작용하며, 응답이 빠르게 도착하면 즉시 다음 테스트 단계로 넘어갑니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pump&lt;/code&gt;&lt;/strong&gt; 및 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pumpAndSettle&lt;/code&gt;&lt;/strong&gt;의 간소화: Patrol은 내부적으로 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pump&lt;/code&gt;&lt;/strong&gt; 및 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pumpAndSettle&lt;/code&gt;&lt;/strong&gt;를 적절히 호출합니다. 따라서 개발자는 이러한 함수를 직접 호출할 필요가 거의 없어지며, 테스트 코드 작성이 더 간편해집니다.&lt;/li&gt;
  &lt;li&gt;코드 간소화 및 가독성 향상: Patrol은 특정 위젯과의 상호 작용을 위한 복잡한 코드를 단순화합니다. 이를 통해 가독성이 높고 직관적인 코드 작성이 가능해지며, 불필요한 보일러플레이트 코드가 대폭 줄어듭니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;patrol-설치&quot;&gt;Patrol 설치&lt;/h3&gt;

&lt;p&gt;Patrol 라이브러리를 사용하기 위해서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pubspec.yaml&lt;/code&gt; 파일에 추가하는 것 뿐만 아니라 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;patrol cli&lt;/code&gt; 도 설치해야 합니다. 설치 방법 및 셋팅은 &lt;a href=&quot;https://patrol.leancode.co/&quot;&gt;Patrol 공식 웹사이트&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;

&lt;h3 id=&quot;patrol-finder&quot;&gt;Patrol Finder&lt;/h3&gt;

&lt;p&gt;Patrol 라이브러리에서 가장 장점중 하나가 기존 Package 보다 향상된 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Widget finder&lt;/code&gt;&lt;/strong&gt; 를 통해서 직관적으로 짧은 코드로 구현이 가능하다는 점입니다.&lt;/p&gt;

&lt;p&gt;예시를 하나 들어서 살펴보겠습니다.
만약 버튼 하나를 찾아서 클릭하는 테스트 코드를 작성한다고 가정해보겠습니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fab&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;byKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;increment&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드를 Patrol에서는 아래와 같이 간단하게 작성이 가능합니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;#increment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Patrol 라이브러리에서 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$&lt;/code&gt;&lt;/strong&gt; 기호는 매우 중요한 역할을 합니다. 이 기호는 기존의 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Finder&lt;/code&gt;,&lt;/strong&gt; &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tester&lt;/code&gt;&lt;/strong&gt; 그리고 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assertion&lt;/code&gt;&lt;/strong&gt; 기능을 통합한 것으로, 위젯을 찾고 상호 작용하는 데 사용됩니다. 이러한 통합은 테스트 코드를 간소화하고 가독성을 높이는 데 크게 기여합니다.&lt;/p&gt;

&lt;p&gt;또한, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt;&lt;/strong&gt; 기호는 위젯의 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key&lt;/code&gt;&lt;/strong&gt; 값을 지정하는 데 사용됩니다. 예를 들어, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#increment&lt;/code&gt;&lt;/strong&gt;는 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key&lt;/code&gt;&lt;/strong&gt; 값이 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;increment&lt;/code&gt;&lt;/strong&gt;인 위젯을 찾는 데 사용됩니다. 이는 특정 위젯을 식별하고 테스트 코드에서 참조하기 쉽게 만들어 줍니다.&lt;/p&gt;

&lt;p&gt;Patrol 라이브러리의 사용성과 간소화된 코드 구조는 아래 예제를 통해 더욱 명확하게 이해할 수 있습니다.&lt;/p&gt;

&lt;p&gt;SignUp 화면에서 Email, Name, Password 입력 후 SignUp 버튼을 클릭하는 테스트 코드를 작성한다고 가정해보겠습니다.&lt;/p&gt;

&lt;p&gt;먼저 기본 Integrated test 패키지를 사용한 코드입니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;testWidgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;signs up&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WidgetTester&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AwesomeApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enterText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;byKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;emailTextField&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;charlie@root.me&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enterText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;byKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;nameTextField&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;Charlie&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enterText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;byKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;passwordTextField&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;ny4ncat&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;byKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;termsCheckbox&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;byKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;signUpButton&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Welcome, Charlie!&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findsOneWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;그 다음 Patrol 라이브러리를 사용한 코드입니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;patrolWidgetTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;signs up&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PatrolTester&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpWidgetAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AwesomeApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;#emailTextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enterText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;charlie@root.me&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;#nameTextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enterText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Charlie&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;#passwordTextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enterText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;ny4ncat&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;#termsCheckbox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;#signUpButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Welcome, Charlie!&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;waitUntilVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;단순 코드를 비교해도 코드량이 줄어들고 코드 자체가 매우 직관적으로 변화하는 것을 확인할 수 있습니다.&lt;/p&gt;

&lt;h3 id=&quot;native-요소와의-상호-작용&quot;&gt;Native 요소와의 상호 작용&lt;/h3&gt;

&lt;p&gt;Patrol에서는 네이티브 요소와의 상호 작용이 가능해져, 이전에는 테스트하기 어려웠던 시나리오들을 다룰 수 있게 됩니다. 예를 들어, 퍼미션 팝업과 같은 네이티브 요소들을 테스트하는 것이 가능해집니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;patrolTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;demo&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PatrolIntegrationTester&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pumpWidgetAndSettle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AwesomeApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// prepare network conditions&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;native&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enableCellular&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;native&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;disableWifi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// toggle system theme&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;native&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enableDarkMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// handle native location permission request dialog&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;native&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;selectFineLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;native&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;grantPermissionWhenInUse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// tap on the first notification&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;native&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;openNotifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;native&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tapOnNotificationByIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 OS 레벨의 상호작용 뿐만 아니라 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Google Login&lt;/code&gt; 같이 외부 서비스와의 상호작용도 가능합니다.
해당 내용은 다음 포스트에서 자세히 다루도록 하겠습니다.&lt;/p&gt;

&lt;h2 id=&quot;실제-product-에-patrol-라이브러리-적용해보기&quot;&gt;실제 Product 에 Patrol 라이브러리 적용해보기&lt;/h2&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;
&lt;img src=&quot;https://bdiscover.kakaobrain.com/meta/og-image.jpg&quot; width=&quot;50%&quot; /&gt;

&lt;/div&gt;

&lt;p&gt;먼저 제가 속해있는 팀에서 개발 중인 앱, B^ Discover에 대해 소개드리겠습니다. 이 앱은 AI 기술을 활용하여 사용자가 입력한 텍스트(Prompt)를 바탕으로 다양한 멀티미디어 콘텐츠를 생성해 주는 서비스를 제공합니다. 사용자의 아이디어를 시각적 형태로 변환해주어, 창의적인 작업을 더 쉽고 효율적으로 만들어줍니다.&lt;/p&gt;

&lt;p&gt;관심 있으신 분들은 다음 링크를 통해 다운로드 받으실 수 있습니다&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;
&lt;a href=&quot;https://apps.apple.com/us/app/b-discover-ai-video-maker/id6443811926&quot;&gt;
  &lt;img alt=&quot;app store&quot; srcset=&quot;/assets/icons/app-store.svg 1x, /assets/icons/app-store.svg 2x&quot; src=&quot;/assets/icons/app-store.svg&quot; decoding=&quot;async&quot; data-nimg=&quot;intrinsic&quot; width=&quot;200px&quot; /&gt;
&lt;/a&gt;
&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.kakaobrain.bdiscover&quot;&gt;
  &lt;img alt=&quot;google play&quot; srcset=&quot;/assets/icons/google-play.svg 1x, /assets/icons/google-play.svg 2x&quot; src=&quot;/assets/icons/google-play.svg&quot; decoding=&quot;async&quot; data-nimg=&quot;intrinsic&quot; width=&quot;200px&quot; /&gt;
&lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;그리고 저는 해당 앱 프로젝에서 Patrol 라이브러리를 적용해 보았으며, 이를 통해 앱의 성능과 사용자 경험을 한층 더 향상시키고자 하였습니다.&lt;/p&gt;

&lt;h3 id=&quot;테스트-시나리오-구성&quot;&gt;테스트 시나리오 구성&lt;/h3&gt;

&lt;p&gt;사용자가 앱을 실행하고, 특정 텍스트를 입력하고, 이를 바탕으로 AI가 이미지를 생성하는 과정을 테스트하고자 합니다.&lt;/p&gt;

&lt;p&gt;구체적인 시나리오와 테스트 코드는 아래와 같습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://github.com/Origogi/leetcode/assets/35194820/ce13d7a7-5a53-4856-b6c1-9e951dd3d6f2&quot; width=&quot;70%&quot; /&gt;

&lt;/div&gt;

&lt;h3 id=&quot;최종-실행-결과&quot;&gt;최종 실행 결과&lt;/h3&gt;

&lt;p&gt;위 시나리오와 같이 테스트 코드를 작성하고 Patrol 라이브러리를 적용한 결과, 아래와 같이 테스트가 성공적으로 실행되었습니다.
아래는 실제로 Patrol 라이브러리를 적용해서 실행되는 것을 캡처한 화면입니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://github.com/Origogi/leetcode/assets/35194820/c2e4b731-40e7-4b15-a662-8aca31062fe5&quot; /&gt;

&lt;/div&gt;

&lt;h2 id=&quot;마치며&quot;&gt;마치며&lt;/h2&gt;

&lt;p&gt;Fluttter E2E Test 를 작성할 때 기본 패키지보다는 Patrol 라이브러리를 사용하는 것이 훨씬 효율적이고 가독성이 좋다는 것을 알 수 있었습니다.
위 시나리오에 대해서 기본 패키지로 구현하게 된다면 훨씬 더 많은 코드(250줄 정도)를 작성해야 하고 그리고 또한 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pump&lt;/code&gt;&lt;/strong&gt; 및 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pumpAndSettle&lt;/code&gt;&lt;/strong&gt; 을 잘못 사용하게 되면 테스트가 실패할 수도 있습니다. 그래서 디버깅을 하고 오류를 찾는 과정이 매우 고통스웠습니다.&lt;/p&gt;

&lt;p&gt;하지만 Patrol 라이브러리를 사용하게 되면 훨씬 더 간단하고 직관적인 코드(100줄 미만)를 작성할 수 있었습니다. 그리고 또한 네이티브 요소와의 상호작용이 가능해져서 테스트하기 어려웠던 시나리오들을 다룰 수 있게 됩니다.&lt;/p&gt;

&lt;p&gt;요약을 하자면&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Patrol 라이브러리를 사용하면 훨씬 더 간단하고 직관적인 코드를 작성할 수 있습니다.&lt;/li&gt;
  &lt;li&gt;네이티브 요소와의 상호작용이 가능해져서 테스트하기 어려웠던 시나리오들을 다룰 수 있게 됩니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;그리고 다음 포스팅에서는 아래와 같은 내용을 다루도록 하겠습니다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Patrol를 이용하여 Google Login 같은 외부 서비스와의 상호작용 및 테스트&lt;/li&gt;
  &lt;li&gt;E2E Test 과정을 Github actions 의 CI에 적용하는 방법&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.flutter.dev/cookbook/testing/integration/introduction&quot;&gt;An introduction to integration testing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://patrol.leancode.co/&quot;&gt;Patrol&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 03 Feb 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/flutter/e2e-test-1/</link>
        <guid isPermaLink="true">https://origogi.github.io/flutter/e2e-test-1/</guid>
        
        <category>Flutter</category>
        
        <category>Dart</category>
        
        
        <category>Flutter</category>
        
      </item>
    
      <item>
        <title>[iOS] iOS 배포 시 Certification, Provisioning Profile 정리</title>
        <description>&lt;h2 id=&quot;서론&quot;&gt;서론&lt;/h2&gt;

&lt;p&gt;해당 포스트는 iOS app 을 빌드 과정 및 배포 과정에서 사용되는 Certification, Provisioning Profile 에 대해 정리한 내용입니다. 특히 CI/CD 환경에서 iOS 앱을 빌드할 때 Certification, Provisioning Profile 을 어떻게 셋팅하는지 알아보도록 하겠습니다.&lt;/p&gt;

&lt;h2 id=&quot;코드-서명이란&quot;&gt;코드 서명이란?&lt;/h2&gt;

&lt;p&gt;iOS에서 코드 서명(Code Signing)이 필요한 이유는 여러 가지가 있습니다. 이는 주로 보안, 신뢰성 및 사용자 경험과 관련이 있습니다:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;보안 강화: 코드 서명은 앱이 변조되지 않았으며 신뢰할 수 있는 소스에서 왔음을 확인합니다. 이는 악성 소프트웨어가 시스템을 해치는 것을 방지하는 데 도움이 됩니다.&lt;/li&gt;
  &lt;li&gt;신뢰성 보장: 애플은 개발자가 등록하고 고유한 인증서를 통해 앱을 서명하도록 요구함으로써, 개발자가 그들의 앱에 대한 책임을 질 것임을 보장합니다. 이는 사용자에게 해당 앱이 신뢰할 수 있는 개발자에 의해 생성되었다는 확신을 줍니다.&lt;/li&gt;
  &lt;li&gt;App Store 정책 준수: iOS 앱은 Apple의 App Store를 통해 배포됩니다. App Store에 앱을 제출하기 위해서는 코드 서명이 필요합니다. 이는 애플이 앱의 품질과 보안을 관리하고, 사용자에게 안전하고 신뢰할 수 있는 앱만 제공하도록 하는 데 중요한 역할을 합니다.&lt;/li&gt;
  &lt;li&gt;시스템 무결성 및 안정성 유지: 코드 서명은 앱이 iOS 시스템의 기본 보안 정책과 호환되도록 보장합니다. 이는 앱이 시스템의 안정성이나 다른 앱의 기능에 부정적인 영향을 미치지 않도록 합니다.&lt;/li&gt;
  &lt;li&gt;사용자 개인정보 보호: 코드 서명을 통해 애플은 개발자가 iOS 장치의 보안 기능을 우회하거나 사용자의 개인 정보를 부적절하게 수집하는 앱을 배포하지 못하도록 방지합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;코드-서명-과정&quot;&gt;코드 서명 과정&lt;/h2&gt;

&lt;p&gt;iOS에서의 코드 서명 과정은 인증서(Certification)와 프로비저닝 프로파일(Provisioning Profile) 두 가지 주요 요소를 이용합니다. 이 과정은 대략 다음과 같이 진행됩니다:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;개발자 인증서 생성 및 등록: 개발자는 먼저 Apple Developer Program에 등록합니다. 그 후, 개발자는 자신의 Mac에서 고유한 공개 키/개인 키 쌍을 생성합니다. 이 개인 키는 개발자만이 알고 있어야 합니다. 공개 키는 Apple에 제출되어 개발자 인증서(Certificate)를 생성하는 데 사용됩니다. 이 인증서는 앱이 해당 개발자에 의해 개발되었음을 증명하는 역할을 합니다.&lt;/li&gt;
  &lt;li&gt;프로비저닝 프로파일 생성: 프로비저닝 프로파일은 특정 앱, 특정 디바이스, 그리고 특정 개발자 인증서에 대한 정보를 포함합니다. 이 프로파일은 애플의 개발자 웹사이트에서 생성되며, 앱이 설치될 수 있는 디바이스와 이를 개발할 수 있는 인증서를 명시합니다.&lt;/li&gt;
  &lt;li&gt;앱 서명: 개발자는 앱을 빌드하고, 자신의 개인 키를 사용하여 앱에 서명합니다. 이 과정에서 개발자의 인증서와 프로비저닝 프로파일이 사용됩니다. 서명된 앱은 프로비저닝 프로파일에 명시된 디바이스에서만 실행될 수 있으며, 해당 인증서로 인증된 개발자에 의해 생성된 것으로 인식됩니다.&lt;/li&gt;
  &lt;li&gt;앱 배포 및 검증: 서명된 앱은 이제 배포될 준비가 되었습니다. 사용자가 앱을 설치할 때, iOS는 앱의 서명을 검증하여 인증서의 유효성과 프로비저닝 프로파일의 정보가 일치하는지 확인합니다. 이 검증 과정을 통해 앱의 신뢰성과 안전성이 확보됩니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;인증서-certification&quot;&gt;인증서 (Certification)&lt;/h2&gt;

&lt;p&gt;인증서는 개발자의 신원을 확인하고, 애플리케이션의 코드가 안전하게 서명되었음을 보증하는 데 사용됩니다.
인증서는 목적에 따라서 다음과 같이 나뉩니다.&lt;/p&gt;

&lt;h3 id=&quot;개발-인증서development-certificate&quot;&gt;개발 인증서(Development Certificate):&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;목적: 개발 중인 앱을 테스트하기 위해 사용됩니다.&lt;/li&gt;
  &lt;li&gt;사용: 이 인증서를 사용하여 서명된 앱은 등록된 테스트 디바이스에서만 실행될 수 있습니다.&lt;/li&gt;
  &lt;li&gt;특징: 개발 인증서는 개별 개발자 또는 개발 팀에 할당됩니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;배포-인증서distribution-certificate&quot;&gt;배포 인증서(Distribution Certificate):&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;목적: 앱 스토어에 앱을 배포하거나 Firebase App Distribution 배포를 위해 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;프로비저닝-프로파일-provisioning-profile&quot;&gt;프로비저닝 프로파일 (Provisioning Profile)&lt;/h2&gt;

&lt;h3 id=&quot;프로비저닝-프로파일의-역할&quot;&gt;프로비저닝 프로파일의 역할&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;디바이스 식별: 프로비저닝 프로파일은 특정 앱이 특정 iOS 디바이스에서 실행될 수 있도록 합니다. 이를 통해 개발자는 앱을 특정 디바이스에 설치하고 테스트할 수 있습니다.&lt;/li&gt;
  &lt;li&gt;앱 서명: 개발자가 앱에 서명하는 데 사용되는 인증서를 포함합니다. 이는 앱이 해당 개발자에 의해 만들어졌음을 보증합니다.&lt;/li&gt;
  &lt;li&gt;앱 실행 권한 관리: 프로비저닝 프로파일은 앱이 어떤 환경(개발 또는 배포)에서 실행될 수 있는지 정의합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;프로비저닝-프로파일의-유형&quot;&gt;프로비저닝 프로파일의 유형&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;development
    &lt;ul&gt;
      &lt;li&gt;앱을 개발하고 테스트하는 동안 사용됩니다.&lt;/li&gt;
      &lt;li&gt;특정 개발자 계정과 연결된 디바이스에서만 앱을 실행할 수 있도록 합니다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;appstore
    &lt;ul&gt;
      &lt;li&gt;앱을 App Store 나 TestFlight 에 배포할 때 사용됩니다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ad-hoc
    &lt;ul&gt;
      &lt;li&gt;앱을 특정 디바이스에 배포할 때 사용됩니다.&lt;/li&gt;
      &lt;li&gt;특정 개발자 계정과 연결된 디바이스에서만 앱을 실행할 수 있도록 합니다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;enterprise
    &lt;ul&gt;
      &lt;li&gt;기업이 자체 개발한 iOS 애플리케이션을 그들의 직원이나 구성원에게 배포할 때 사용됩니다.&lt;/li&gt;
      &lt;li&gt;Apple의 공식 App Store를 거치지 않고, 기업 내부에서만 사용되는 애플리케이션을 직접 관리하고 배포할 수 있습니다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;요약&quot;&gt;요약&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/format:webp/1*utY_Oy4GHDTqohQFqjRKQw.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;fastlane-을-이용한-ios-build&quot;&gt;Fastlane 을 이용한 iOS Build&lt;/h3&gt;

&lt;p&gt;CI/CD 를 이용할 때 보통 Fastlane 을 이용하여 iOS 앱을 빌드하고 배포하는 경우가 많습니다. Fastlane 을 이용하여 iOS 앱을 빌드하는 과정에서 Certification, Provisioning Profile 을 어떻게 사용하는지 알아보도록 하겠습니다.&lt;/p&gt;

&lt;p&gt;CI/CD 를 이용하여 iOS 앱을 서명할 때는 보통 수동으로 설정하고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match&lt;/code&gt; 를 이용하여 코드 서명을 진행합니다. 여기서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match&lt;/code&gt; 에 대해서는 해당 포스트에서 다루지 않겠습니다.&lt;/p&gt;

&lt;p&gt;제가 match 를 통해서 관리하는 Certification, Provisioning Profile 은 아래와 같습니다.&lt;/p&gt;

&lt;p&gt;&lt;img width=&quot;347&quot; alt=&quot;image&quot; src=&quot;https://gist.github.com/assets/35194820/852f4f7f-847e-492b-8fc6-05d49e0c609e&quot; /&gt;&lt;/p&gt;

&lt;p&gt;인증서는 development, distribution 나누어져 있고, 프로비저닝 프로파일은 adhoc, appstore, development 로 나누어져 있습니다.&lt;/p&gt;

&lt;p&gt;참고로 아래 Fastlane code는 중요한 부분만 간략하게 작성한 것이며, 실제로는 더 많은 코드가 있습니다. 자신의 환경에 맞게 코드를 작성하시면 됩니다.&lt;/p&gt;

&lt;p&gt;앱의 번들 아이디는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.sample.app&lt;/code&gt; 이라고 가정하겠습니다.&lt;/p&gt;

&lt;h4 id=&quot;1-firebase-app-distribution-배포&quot;&gt;1. Firebase App Distribution 배포&lt;/h4&gt;

&lt;p&gt;Firebase App Distribution 을 이용하여 iOS 앱을 배포하는 경우는 사용되는 인증서와 프로비저닝 프로파일은 아래와 같습니다.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;인증서&lt;/th&gt;
      &lt;th&gt;프로비저닝 프로파일&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;distribution&lt;/td&gt;
      &lt;td&gt;adhoc&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;추가적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enterprise&lt;/code&gt; 프로비저닝 프로파일을 사용할 수도 있습니다.&lt;/p&gt;

&lt;p&gt;Fastlane 은 아래와 같이 구성합니다.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;adhoc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;app_identifier: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;com.sample.app&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;update_code_signing_settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;use_automatic_signing: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;path: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Runner.xcodeproj&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;code_sign_identity: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;iPhone Distribution&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;update_target_provision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;target: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Runner&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;ss&quot;&gt;config: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Release&apos;&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;build_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;export_method: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ad-hoc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;firebase_app_distribution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;2-testflight-or-appstore-배포&quot;&gt;2. TestFlight or Appstore 배포&lt;/h4&gt;

&lt;p&gt;Firebase App Distribution 을 이용하여 iOS 앱을 배포하는 경우는 사용되는 인증서와 프로비저닝 프로파일은 아래와 같습니다.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;인증서&lt;/th&gt;
      &lt;th&gt;프로비저닝 프로파일&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;distribution&lt;/td&gt;
      &lt;td&gt;appstore&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Fastlane 은 아래와 같이 구성합니다.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;appstore&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;app_identifier: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;com.sample.app&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;update_code_signing_settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;use_automatic_signing: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;path: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Runner.xcodeproj&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;code_sign_identity: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;iPhone Distribution&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;update_target_provision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;target: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Runner&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;ss&quot;&gt;config: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Release&apos;&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;build_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;export_method: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;app-store&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;upload_to_testflight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;3-firebase-test-lab-배포&quot;&gt;3. Firebase Test Lab 배포&lt;/h4&gt;

&lt;p&gt;Firebase Test Lab 을 이용해서 E2E Test 또는 Integration Test 를 진행하는 시나리오에 대해서 알아보겠습니다.
이때 XCode 에서는 XCTest 을 위한 Runner 가 추가로 존재하고 해당 번들 아이디는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.sample.app.RunnerUITests&lt;/code&gt; 라고 가정하겠습니다.&lt;/p&gt;

&lt;p&gt;&lt;img width=&quot;201&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/75332875-bfda-4109-bda1-b99ce7b492c5&quot; /&gt;&lt;/p&gt;

&lt;p&gt;그리고 사용되는 인증서와 프로비저닝 프로파일은 아래와 같습니다.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;인증서&lt;/th&gt;
      &lt;th&gt;프로비저닝 프로파일&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;development&lt;/td&gt;
      &lt;td&gt;development&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;sr&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;주의할&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;점은&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;XCode&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;에&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;사용되는&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;번들&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;아이디는&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RunnerUITests&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;이지만&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;실제&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provisioning&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;profile&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;에는&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RunnerUITests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xctrunner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;를&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;사용해야&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;합니다&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;development&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;app_identifier: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;com.sample.app&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;com.sample.app.RunnerUITests.xctrunner&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;  
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;update_code_signing_settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;use_automatic_signing: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;path: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Runner.xcodeproj&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;code_sign_identity: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;iPhone Distribution&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;update_target_provision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;target: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Runner&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;ss&quot;&gt;config: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Release&apos;&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;update_target_provision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;target: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;RunnerUITests&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;ss&quot;&gt;config: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Release&apos;&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;sr&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;XCTestRunner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;를&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;빌드합니다&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;자세한&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;것은&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;아래&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;링크를&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;참고하세요&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;https&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:/&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firebase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ios&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xctest?hl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ko&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#project&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# xcodebuild -project PATH/TO/YOUR_WORKSPACE/YOUR_PROJECT.xcodeproj \&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#    -scheme YOUR_SCHEME \&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#    -derivedDataPath FOLDER_WITH_TEST_OUTPUT \&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#    -sdk iphoneos build-for-testing&lt;/span&gt;

&lt;span class=&quot;sr&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;XCTesetRunner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;를&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zip&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;파일로&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;압축합니다&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# cd build/ios_integ/Build/Products&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# zip -r ios_tests.zip Release-iphoneos/*.app *.xctestrun&lt;/span&gt;

&lt;span class=&quot;sr&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firebase&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lab&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;에&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;업로드&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# gcloud firebase test ios run \&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	--type xctest \&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	--test &quot;build/ios_integ/Build/Products/ios_tests.zip&quot; \&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	--device model=&quot;$IOS_DEVICE_MODEL&quot;,version=&quot;$IOS_DEVICE_VERSION&quot;,locale=en_US,orientation=portrait \&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	--timeout 10m \&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	--results-bucket= Firebase Storage 이름 \&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;reference&quot;&gt;Reference&lt;/h3&gt;

&lt;p&gt;https://sujinnaljin.medium.com/ios-certificate-%EC%99%80-provisioning-profile-e1b9455e8a51&lt;/p&gt;

</description>
        <pubDate>Wed, 24 Jan 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/ios/ios-cert-provision/</link>
        <guid isPermaLink="true">https://origogi.github.io/ios/ios-cert-provision/</guid>
        
        <category>iOS</category>
        
        <category>CI/CD</category>
        
        <category>Fastlane</category>
        
        
        <category>iOS</category>
        
      </item>
    
      <item>
        <title>2023년 회고</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;816&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211184286-e54aab45-2379-48f2-be67-dd2329206cb8.png&quot; /&gt;

&lt;/div&gt;

&lt;p&gt;해당 포스트는 2023년 한 해를 되돌아보며, 그간의 경험과 성과를 정리하고 리뷰하는 내용을 담고 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;1-job&quot;&gt;1. Job&lt;/h2&gt;

&lt;p&gt;저는 현직에서 Flutter 개발자로 재직 중입니다. Flutter 라는 강력한 프레임워크를 활용해 사용자 친화적이면서도 기술적으로 앞선 애플리케이션을 개발하는 것이 제 주업무입니다.&lt;/p&gt;

&lt;p&gt;현재 개발 중인 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B^ Discover&lt;/code&gt; 는 AI를 활용하여 이미지, 캐릭터, 영상을 생성하는 앱입니다. 이 앱은 Flutter 프레임워크를 사용하여 개발되었으며, iOS 및 Android 플랫폼에서 사용할 수 있습니다. ‘B^ Discover’는 사용자가 AI 기술을 통해 쉽고 빠르게 창의적인 콘텐츠를 만들 수 있게 도와줍니다.&lt;/p&gt;

&lt;p&gt;앱의 다운로드 링크는 아래와 같습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;
&lt;a href=&quot;https://apps.apple.com/us/app/b-discover-ai-video-maker/id6443811926&quot;&gt;
  &lt;img alt=&quot;app store&quot; srcset=&quot;/assets/icons/app-store.svg 1x, /assets/icons/app-store.svg 2x&quot; src=&quot;/assets/icons/app-store.svg&quot; decoding=&quot;async&quot; data-nimg=&quot;intrinsic&quot; width=&quot;200px&quot; /&gt;
&lt;/a&gt;
&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.kakaobrain.bdiscover&quot;&gt;
  &lt;img alt=&quot;google play&quot; srcset=&quot;/assets/icons/google-play.svg 1x, /assets/icons/google-play.svg 2x&quot; src=&quot;/assets/icons/google-play.svg&quot; decoding=&quot;async&quot; data-nimg=&quot;intrinsic&quot; width=&quot;200px&quot; /&gt;
&lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;특히 올해는 저희 팀에게 매우 의미 있는 시간이었습니다. 저희는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B^ Discover 2.0&lt;/code&gt; 이라는 대규모 업데이트를 진행했으며, 그 결과 연말에는 AI 우수상’을 수상했습니다. 이러한 성과는 저희 팀원들의 노력 덕분이었고, 저도 그 노력의 일부분을 담당했음에 감사하게 생각합니다.&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;margin: 50px 25px 50px 25px;&quot;&gt;
&lt;img width=&quot;450&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/a6b0368d-ed28-4129-ae07-e2aff1088ad0&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;그리고 Flutter 외에도 B^ Discover 서비스의 백엔드 부분 개발도 진행하였습니다.&lt;/p&gt;

&lt;p&gt;B^ Discover 서비스 백엔드 영역은 Cloud Functions 를 사용하고 있었습니다. 초기에는 JavaScript로 개발되어 있었으나, 서비스의 규모가 커지면서 JavaScript만으로는 한계에 부딪혔습니다.&lt;/p&gt;

&lt;p&gt;이에 따라, 우리 팀은 JavaScript 코드를 TypeScript로 마이그레이션하는 중대한 결정을 내렸습니다. TypeScript는 더 강력한 타입 시스템과 개발 시 오류를 줄일 수 있는 기능을 제공함으로써, 서비스의 확장성과 유지보수성을 크게 향상시켰습니다.&lt;/p&gt;

&lt;h2 id=&quot;2-자격증&quot;&gt;2. 자격증&lt;/h2&gt;

&lt;p&gt;근 제가 근무하는 회사에서는 주로 Google Cloud Platform(GCP)을 사용하고 있습니다. 개발 과정에서 GCP의 다양한 서비스를 활용하면서 자연스럽게 GCP에 대해서 관심을 가지게 되었습니다. 이러한 경험을 바탕으로 GCP에 대한 더 깊은 지식을 쌓고자 했고, 결국 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GCP Associate Cloud Engineer&lt;/code&gt; 자격증을 취득하게 되었습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;350&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/212814438-0c52c7d6-8712-4f11-8b87-adb6473efe3c.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GCP Associate Cloud Engineer&lt;/code&gt; 자격증 취득 후기는 아래 링크를 참고 바랍니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://origogi.github.io/gcp/GCP-ACE-%ED%9B%84%EA%B8%B0/&quot;&gt;GCP Associate Cloud Engineer 자격증 취득 후기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-개발-공부&quot;&gt;3. 개발 공부&lt;/h2&gt;

&lt;p&gt;올해 저는 아래와 같은 강의와 도서를 통해 개발 공부를 진행했습니다.&lt;/p&gt;

&lt;h3 id=&quot;31-강의&quot;&gt;3.1. 강의&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://courses.codewithandrea.com/p/flutter-animations-masterclass-full-course?_ga=2.79493968.51951189.1676973498-2003730414.1676973498&quot;&gt;Flutter Animations Masterclass - Complete Package&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://academy.dream-coding.com/courses/typescript/&quot;&gt;타입스크립트·객체지향 프로그래밍&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://academy.dream-coding.com/courses/node&quot;&gt;노드로 배우는 백엔드 A-Z&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.inflearn.com/course/%ED%95%A8%EC%88%98%ED%98%95_ES6_%EC%9D%91%EC%9A%A9%ED%8E%B8/dashboard&quot;&gt;함수형 프로그래밍과 JavaScript ES6+ 응용편&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;32-개발-관련-도서&quot;&gt;3.2. 개발 관련 도서&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Gof의 디자인 패턴&lt;/li&gt;
  &lt;li&gt;함수형 코딩&lt;/li&gt;
  &lt;li&gt;HTTP 완벽 가이드&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;33-교양-도서&quot;&gt;3.3 교양 도서&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;데일 카네기 인간관계론&lt;/li&gt;
  &lt;li&gt;이와타씨에게 묻다&lt;/li&gt;
  &lt;li&gt;유난한 도전&lt;/li&gt;
  &lt;li&gt;조엘 온 소프트웨어&lt;/li&gt;
  &lt;li&gt;육각형 개발자&lt;/li&gt;
  &lt;li&gt;개발자 원칙&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;4-2023-년-정리&quot;&gt;4. 2023 년 정리&lt;/h2&gt;

&lt;h3 id=&quot;41-향상-된-점&quot;&gt;4.1. 향상 된 점&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JavaScript&lt;/code&gt; 를 중급 정도로 사용할 수 있게 되었습니다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeScript&lt;/code&gt; 를 처음 사용해봤고 초중급 정도로 사용할 수 있게 되었습니다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node.js&lt;/code&gt; 를 처음 사용해봤고 간단한 스크립트를 작성할 수 있게 되었습니다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Github Actions&lt;/code&gt; 를 사용하여 CI/CD를 구축할 수 있게 되었습니다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iOS&lt;/code&gt; 앱 빌드 과정을 이해하게 되었습니다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fastlane&lt;/code&gt; 을 사용하여 iOS 앱 배포 과정을 자동화할 수 있게 되었습니다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GCP&lt;/code&gt; 에 대한 기본적인 지식을 쌓을 수 있었습니다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수형 프로그래밍&lt;/code&gt; 에 대해서 심화된 지식을 쌓았으며 실제 Product에 적용하여 복잡한 코드를 직관적이고 단순하게 리팩토링을 적용한 경험이 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;42-아쉬운-점&quot;&gt;4.2. 아쉬운 점&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Android&lt;/code&gt; 를 거의 손 놓고 있었습니다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Android&lt;/code&gt; 를 조금 더 공부해야겠습니다.&lt;/li&gt;
  &lt;li&gt;육아/업무를 병행 하면서 건강 관리를 소홀히 했습니다.(특히 몸무게) 앞으로는 건강 관리에 더 신경써야겠습니다.&lt;/li&gt;
  &lt;li&gt;블로그 포스팅을 거의 하지 못했습니다. 앞으로는 블로그 포스팅을 좀 더 자주 해야겠습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-2024-년-목표&quot;&gt;5. 2024 년 목표&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iOS&lt;/code&gt; 에 대해서 공부, 특히 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Swift Ui&lt;/code&gt; 를 사용해서 사이드 프로젝트를 진행&lt;/li&gt;
  &lt;li&gt;한 달에 1회 이상 블로그 포스팅&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flutter Integrate Test&lt;/code&gt; 를 적용해보기&lt;/li&gt;
  &lt;li&gt;한 달에 1권 이상의 도서 읽기&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;6-마치며&quot;&gt;6. 마치며&lt;/h2&gt;

&lt;p&gt;2023년은 제게 많은 변화를 가져온 해였습니다. 그 중에서도 가장 큰 변화는 아이가 태어난 것이었습니다. 육아는 상상 이상으로 육체적, 정신적으로 도전적인 일이었고, 육아, 업무, 개인적 성장 등 어느 하나 확실히 잘하고 있다는 느낌이 들지 않아 우울함을 많이 느꼈습니다. 하지만 시간이 지나면서 아기도 저도 성장하며 서서히 서로 적응하고 있습니다.&lt;/p&gt;

&lt;p&gt;올해는 많은 목표를 세우기보다는 몇 가지 확실한 목표에 집중하고자 합니다. 특히 건강 관리에 더 많은 신경을 쓰려고 합니다. 그리고 열심히 일하고, 열심히 공부하고, 열심히 살아가는 모습을 아이에게 보여주고 싶습니다.&lt;/p&gt;

&lt;p&gt;끝으로 2024년도 저에게 많은 변화가 있을 것이라고 생각합니다. 그 변화를 기대하며, 2023년을 마무리하겠습니다.&lt;/p&gt;
</description>
        <pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/etc/2023%EB%85%84-%ED%9A%8C%EA%B3%A0/</link>
        <guid isPermaLink="true">https://origogi.github.io/etc/2023%EB%85%84-%ED%9A%8C%EA%B3%A0/</guid>
        
        
        <category>etc</category>
        
      </item>
    
      <item>
        <title>[Flutter] Github actions 에서 CCache 를 적용해서 iOS 빌드 속도를 개선해 보기</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;982&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211177673-7ec90307-162d-4696-89b3-6054415d2db5.png&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;이번 포스트에서는 Github Actions 을 이용한 Flutter CI/CD workflow에서 Ccache 를 적용하여 ios 빌드 성능을 개선하였던 내용에 대해서 다루고 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;1-문제-인식&quot;&gt;1. 문제 인식&lt;/h2&gt;

&lt;p&gt;Ccache 를 적용하기 앞서서 먼저 아래 포스트를 참고하시면 좋을 것 같습니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://junsukim.dev/flutter/2023-01-06-github-actions-ios/&quot;&gt;Flutter Github actions 에서 iOS 빌드/배포 속도 개선&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;위 포스트를 보면 iOS 빌드 속도를 줄이기 위해 &lt;a href=&quot;https://github.com/invertase/firestore-ios-sdk-frameworks&quot;&gt;firebase-prebuilt library&lt;/a&gt;를 사용하고 있었는데요 Firebase 버전을 최신으로 올리면서 해당 Library를 사용하게 되면 앱이 빌드가 안되는 문제가 발견되었습니다. 이 문제는 아직 수정이 되지 않고 있습니다. 해당 문제는 아래 링크를 참고하시면 좋을 것 같습니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://github.com/invertase/firestore-ios-sdk-frameworks/issues/79&quot;&gt;any version 10.17.0 or higher: Error (Xcode): Undefined symbol&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;2-local-환경에서-ccache-적용&quot;&gt;2. Local 환경에서 Ccache 적용&lt;/h2&gt;

&lt;p&gt;Ccache 는 C/C++ 컴파일러를 위한 캐시 시스템으로 컴파일된 오브젝트 파일을 캐시에 저장하여 다음 빌드 시에 재사용할 수 있도록 해줍니다. 이를 통해서 빌드 시간을 줄일 수 있습니다.&lt;/p&gt;

&lt;h3 id=&quot;1-ccache-설치&quot;&gt;1. Ccache 설치&lt;/h3&gt;

&lt;p&gt;brew 를 통해서 Ccache 를 설치합니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;ccache
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;설치가 완료되면 아래 명령어를 통해서 Ccache 가 잘 설치되었는지 확인합니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ccache &lt;span class=&quot;nt&quot;&gt;-V&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-환경-변수-추가&quot;&gt;2. 환경 변수 추가&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bash_profile&lt;/code&gt; 나 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt; 파일에 아래와 같이 환경 변수를 추가합니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/homebrew/opt/ccache/libexec:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-심볼릭-링크-걸기&quot;&gt;3. 심볼릭 링크 걸기&lt;/h3&gt;

&lt;p&gt;아래와 같이 심볼릭 링크를 걸어줍니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;which ccache&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; /usr/local/bin/gcc
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;which ccache&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; /usr/local/bin/g++
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;which ccache&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; /usr/local/bin/cc
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;which ccache&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; /usr/local/bin/c++
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;which ccache&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; /usr/local/bin/clang
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;which ccache&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; /usr/local/bin/clang++
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-설정-확인&quot;&gt;4. 설정 확인&lt;/h3&gt;

&lt;p&gt;아래 명령어를 통해서 Ccache 설정이 잘 되었는지 확인합니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;which gcc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;정상적으로 설정이 되었다면 아래와 같이 출력이 됩니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; which gcc
/opt/homebrew/opt/ccache/libexec/gcc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;5-iospodfile-수정&quot;&gt;5. ios/Podfile 수정&lt;/h3&gt;

&lt;p&gt;ios/Podfile 에 아래와 같이 스크립트를 추가합니다.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;post_install&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;installer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;installer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;generated_projects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build_configurations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build_settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;CC&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clang&quot;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build_settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;LD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clang&quot;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build_settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;CXX&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clang++&quot;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build_settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;LDPLUSPLUS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;clang++&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;6-빌드-시간-측정&quot;&gt;6. 빌드 시간 측정&lt;/h3&gt;

&lt;p&gt;아래 명령어를 통해서 빌드 시간을 측정합니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flutter build ipa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;첫 번째 빌드 시간은 아래와 같이 327,777ms 가 소요되었습니다. 그리고 ccache -s 명령어를 통해서 캐시 효율을 확인해보면 0% 입니다.
왜냐하면 첫 번째 빌드이기 때문에 아직 cache 가 생성되지 않았기 때문입니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;flutter ipa&quot;&lt;/span&gt; took 327,777ms.

 ccache &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;
Cacheable calls:    2655 / 2655 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;100.0%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  Hits:                0 / 2655 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; 0.00%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    Direct:            0
    Preprocessed:      0
  Misses:           2655 / 2655 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;100.0%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Local storage:
  Cache size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;GiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:  0.6 /  5.0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;11.18%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  Hits:                0 / 2655 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; 0.00%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  Misses:           2655 / 2655 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;100.0%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;두 번째 빌드 시간은 아래와 같습니다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s2&quot;&gt;&quot;flutter ipa&quot;&lt;/span&gt; took 130,970ms.

 ccache &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;
Cacheable calls:    5310 / 5310 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;100.0%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  Hits:             2655 / 5310 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;50.00%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    Direct:         2655 / 2655 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;100.0%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    Preprocessed:      0 / 2655 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; 0.00%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  Misses:           2655 / 5310 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;50.00%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Local storage:
  Cache size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;GiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:  0.6 /  5.0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;11.18%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  Hits:             2655 / 5310 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;50.00%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  Misses:           2655 / 5310 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;50.00%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;시간이 절반으로 줄어들었습니다. 그리고 ccache -s 명령어를 통해서 캐시 효율을 확인해보면 50% 입니다. 즉, 캐시가 잘 생성되었다는 것을 알 수 있습니다. 50%인 이유는 두 번째 빌드 시에는 첫 번째 빌드 시에 생성된 캐시를 사용하기 때문입니다.&lt;/p&gt;

&lt;p&gt;자 이제 Github Actions 에서 Ccache 를 적용해보도록 하겠습니다.&lt;/p&gt;

&lt;h2 id=&quot;3-github-actions-에서-ccache-적용&quot;&gt;3. Github Actions 에서 Ccache 적용&lt;/h2&gt;

&lt;h3 id=&quot;1-워크플로우-구성&quot;&gt;1. 워크플로우 구성&lt;/h3&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Update env  // Ccache 환경 변수 추가&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;echo &quot;/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH&quot; &amp;gt;&amp;gt; $GITHUB_PATH&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;echo &quot;CCACHE_SLOPPINESS=clang_index_store,file_stat_matches,include_file_ctime,include_file_mtime,ivfsoverlay,pch_defines,modules,system_headers,time_macros&quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;echo &quot;CCACHE_FILECLONE=true&quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;echo &quot;CCACHE_DEPEND=true&quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;echo &quot;CCACHE_INODECACHE=true&quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install Ccache  // Ccache 설치&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hendrikmuhs/ccache-action@v1.2&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$-v2&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;max-size&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;5G&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build ipa  // 빌드&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;flutter build ipa&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-확인&quot;&gt;2. 확인&lt;/h3&gt;

&lt;p&gt;workflow 를 실행하면 최초 빌드 시 캐시가 없기 때문에 시간이 오래 걸리지만 두 번째 빌드 시에는 캐시를 사용함으로써 빌드 시간이 단축되었습니다.
그리고 workflow 의 출력을 통해 Ccahe의 캐시 효율을 확인해볼수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;img width=&quot;500&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/7e24836a-8a0b-41fd-9c03-c6decffe3ced&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;3-결과&quot;&gt;3. 결과&lt;/h3&gt;

&lt;p&gt;Ccache 를 적용한 결과 빌드 시간은 약간 유동적이긴 하지만 분명히 빌드 시간을 크게 줄이는 데 성공했습니다.&lt;/p&gt;

&lt;h4 id=&quot;ccache-적용-전&quot;&gt;Ccache 적용 전&lt;/h4&gt;

&lt;p&gt;&lt;img width=&quot;273&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/64388354-1b49-494c-ae2a-481e3588625d&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;ccache-적용-후&quot;&gt;Ccache 적용 후&lt;/h4&gt;

&lt;p&gt;&lt;img width=&quot;273&quot; alt=&quot;image&quot; src=&quot;https://github.com/Origogi/leetcode/assets/35194820/b20a8ac6-0b77-4196-b928-bf148982b857&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://ccache.dev/&quot;&gt;Ccache&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/invertase/firestore-ios-sdk-frameworks/discussions/82&quot;&gt;Using ccache instead of this pod&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dev.to/leehack/optimize-flutter-ios-build-using-ccache-2oi2&quot;&gt;Optimize Flutter iOS Build using ccache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 07 Dec 2023 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/flutter/github-actions-ios-ccache/</link>
        <guid isPermaLink="true">https://origogi.github.io/flutter/github-actions-ios-ccache/</guid>
        
        <category>Flutter</category>
        
        <category>Dart</category>
        
        
        <category>Flutter</category>
        
      </item>
    
      <item>
        <title>[Dart] Dart 3.0 Alpha 정리</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;982&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/214710504-80d3e001-daa0-45eb-8302-042f54f70ee9.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;해당 포스트는 Dart 3.0 에 추가되는 New feature를 정리한 포스트입니다.&lt;/p&gt;

&lt;h2 id=&quot;1-multiple-returns&quot;&gt;1. Multiple Returns&lt;/h2&gt;

&lt;p&gt;Dart 2.x 에서는 여러 개의 값을 리턴하기 위해서 아래 예제 처림 data class 선언해서 사용했어야 합니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;UserInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;UserInfo&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;age&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;하지만 Dart 3.0 에서는 아래와 같이 여러개의 값을 리턴이 가능하여 더이상 data class 객체를 선언하지 않아도 됩니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;age&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(String, int)&lt;/code&gt; 처럼 N 개의 Data 를 담고 있는 타입을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Records&lt;/code&gt; 라고 합니다.&lt;/p&gt;

&lt;h3 id=&quot;what-are-records&quot;&gt;What are records?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;First Class : &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Records&lt;/code&gt; 는 일급 객체로 취급하여 함수에 인자로 넘기기, 변수에 할당하기 등 다양한 연산이 가능하다.&lt;/li&gt;
  &lt;li&gt;Object : 객체로 취급이 된다.&lt;/li&gt;
  &lt;li&gt;Value semantics : == , hashcode 가 자동으로 define 되어 동등성 체크가 바로 가능하다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;record-destructuring&quot;&gt;Record destructuring&lt;/h3&gt;

&lt;p&gt;Records 을 아래와 같이 바로 분해해서 변수에 할당하는 것도 가능합니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-control-flow-in-argument-lists&quot;&gt;2. Control Flow in Argument Lists&lt;/h3&gt;

&lt;p&gt;위젯에서 상태에 따라서 속성이나 child widget 을 부여할 때 3항 연산자를 주로 사용하게 됩니다.
아래 예제는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hasNextStep&lt;/code&gt; 이라는 상태에 따라서 속성을 추가할지 아니면 null 로 set 할지를 결정하는 코드입니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ListTile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;leading&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Icons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;weekend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Welcome&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasNextStep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;subtitle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasNextStep&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Tap to Advance.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;onTap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasNextStep&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;advance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;trailing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasNextStep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Icons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;다만 이런 코드가 많을 수록 가독성이 안 좋아지기 마련입니다.
하지만 Dart 3.0 에서는 이러한 부분이 개선되어 Widget 안에 if 문을 사용하여 좀 더 가독성있는 코드를 구현이 가능해집니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ListTile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;leading&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Icons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;weekend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Welcome&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasNextStep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasNextStep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;subtitle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Tap to Advance.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;onTap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;advance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;trailing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Icons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-json-destructuring&quot;&gt;3. JSON Destructuring&lt;/h2&gt;

&lt;p&gt;일반적으로 JSON 내에서 내가 원하는 값을 파싱하기 위해서는 아래와 같이 코드를 작성하게 되며
그리고 원하는 값은 한번에 하나 씩만 가져올수 있었습니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Lily&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;dart 3.0 에서는 한번에 여러개의 값을 파싱이 가능합니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Lily&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]};&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-usable-switches&quot;&gt;3. Usable Switches&lt;/h2&gt;

&lt;p&gt;dart 2.x 의 switch 문은 단순히 비교하는 값과 일치하는 label 로 점프를 해주는 기능만 제공하였습니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;charCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;slash:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nextCharCode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;skipComment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;charCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;star:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;plus:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;minus:&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;charCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;charCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;charCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digit9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;invalid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;dart 3.0 에서는 switch 문에 아래와 같이 다양한 기능이 추가가 되어 가독성을 향상 시킬수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;charCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slash&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextCharCode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;slash:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 조건을 더 추가 할수 있다.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;skipComment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;star&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;minus:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// case 를 여러 개로 묶어서 한줄로 표현이 가능하다.&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;charCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digito&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;digit9:&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;// 조건문 을 사용할수 았다.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;그리고 switch 문 자체에 값을 return 이 가능해집니다. 따라서 switch 문의 실행 결과를 변수에 바로 할당이 가능합니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;charCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;slash&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextCharCode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;skipComment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; 
  &lt;span class=&quot;n&quot;&gt;slash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;star&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;charCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digit9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Switch 문과 JSON 파싱하는 코드를 결합하여 JSON 유효성 검사 및 데이터 파싱을 너무나 쉽게 할수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Lily&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;User &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$age&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; years old.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Unknown message&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;4-next-steps&quot;&gt;4. Next Steps&lt;/h2&gt;

&lt;p&gt;현재 Dart 3.0 은 alpha 버전이며 23년 중반에 stable 로 출시를 목표로 하고 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=KhYTFglbF2k&quot;&gt;Bringing pattern matching to Dart&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/flutter/dart-3.0/</link>
        <guid isPermaLink="true">https://origogi.github.io/flutter/dart-3.0/</guid>
        
        <category>Flutter</category>
        
        <category>Dart</category>
        
        
        <category>Flutter</category>
        
      </item>
    
      <item>
        <title>GCP Associate Cloud Engineer 자격증 취득 후기</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;400&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/212814438-0c52c7d6-8712-4f11-8b87-adb6473efe3c.png&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;0-들어가며&quot;&gt;0. 들어가며&lt;/h2&gt;

&lt;p&gt;Cloud 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C&lt;/code&gt; 자도 모르는 프론트엔드 개발자가 Google Associate Cloud Engineer (ACE) 자격증을 취득한 후기를 남겨봅니다.&lt;/p&gt;

&lt;h2 id=&quot;1-gcp-자격증-소개&quot;&gt;1. GCP 자격증 소개&lt;/h2&gt;

&lt;p&gt;먼저 GCP 란 Google Cloud Platform 의 약자로 Cloud Bigtech 3 대장중 하나입니다. Cloud 3 대장이라 하면 아래와 같습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AWS&lt;/li&gt;
  &lt;li&gt;MS Azure&lt;/li&gt;
  &lt;li&gt;Google Cloud Plateform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;위 각 Cloud 플랫폼마다 Cloud 관련 지식을 인증하는 자격증이 있으며 Cloud 세부 분야에 대해서 자격증이 존재합니다. 그 중 GCP 는 아래와 같은 가격증이 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;img width=&quot;500&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/212504422-5731ab78-b7f8-43f8-adab-91ef547f7603.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;그 중에 저는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Associate Cloud Engineer&lt;/code&gt; 등급의 자격증을 취득했습니다.&lt;/p&gt;

&lt;h2 id=&quot;2-gcp-ace-시험-방식&quot;&gt;2. GCP-ACE 시험 방식&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;장소 : On Site/Remote 선택, Remote 를 할 경우 시험비가 할인이 됩니다.&lt;/li&gt;
  &lt;li&gt;시간 : 2시간&lt;/li&gt;
  &lt;li&gt;문항 : 50문제/객관식&lt;/li&gt;
  &lt;li&gt;준비물 : 여권, 웹캠(카메라가 달린 노트북으로 추천)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-공부-기간&quot;&gt;3. 공부 기간&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;약 1달&lt;/li&gt;
  &lt;li&gt;매일 2시간 씩 꾸준히 공부&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;4-시작하게-된-계기&quot;&gt;4. 시작하게 된 계기&lt;/h2&gt;

&lt;p&gt;현재 제가 재직중에 있는 회사에서 GCP 를 사용하고 있습니다. 제가 주로 하는 업무가 MVP, PoC 형태의 서비스를 개발하다보니 팀원 규모도 작고 빨리 빨리 개발해보고 다른 걸 또 시도하다보니 프론트엔드/백엔드의 룰이 엄격하게 나누어 있지 않습니다. 그러다 보니 저도 GCP 를 조금씩 보게 되었는데요,&lt;/p&gt;

&lt;p&gt;GCP 를 처음 접했을 때 수 많은 메뉴를 보고 멘붕(?) 를 경험했습니다. 그래서 GCP에 대해서 조금 자세히 알고 싶어서 GCP를 공부하게 되었고 추가적으로 공부 했던 지식에 대해서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Milestone&lt;/code&gt; 를 남기고 자 GCP 자격증 취득을 목표로 하였습니다.&lt;/p&gt;

&lt;h2 id=&quot;5-공부-방법&quot;&gt;5. 공부 방법&lt;/h2&gt;

&lt;h3 id=&quot;강의&quot;&gt;강의&lt;/h3&gt;

&lt;h4 id=&quot;coursera-&quot;&gt;Coursera ⭐⭐⭐⭐⭐&lt;/h4&gt;

&lt;p&gt;기본적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coursera&lt;/code&gt; 강의를 들으면서 기본적인 GCP 에서 제공하는 서비스에 대해서 숙지하는 것이 좋습니다.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.coursera.org/professional-certificates/cloud-engineering-gcp&quot;&gt;Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;처음 등록하고 1주일 동안 무료이니 1주일 동안 빡세게 듣는 것을 추천 드립니다.&lt;/p&gt;

&lt;h4 id=&quot;youtube-&quot;&gt;Youtube ⭐⭐⭐⭐&lt;/h4&gt;

&lt;p&gt;그리고 두 번째로 Youtube 강의 중에 아래 강의도 추천드립니다.&lt;/p&gt;

&lt;p&gt;&lt;img width=&quot;979&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/212505276-f032af5d-042f-4c60-b26b-afceb6d74e9c.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=jpno8FSqpc8&quot;&gt;Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GCP에 대해서 요약이 매우 잘 되어 있습니다. 총 20시간이라서 부담스럽긴 합니다. 
해당 강의는 각 잡고 보기보다는 출퇴근 시간에 버스나 지하철에서 보는 것을 추천드립니다. 저는 주로 재택 근무라서 집에서 워킹 패드를 하면서 봤는데 시간이 정말 잘 갑니다 :)&lt;/p&gt;

&lt;p&gt;강의를 보면 GCP 에서 실습을 하는데 만약 공부 시간이 충분하면 실습을 따라하는 게 좋지만 시간이 없다면 실습을 패스하는 것을 추천드립니디.&lt;/p&gt;

&lt;h3 id=&quot;dump-문제-풀기&quot;&gt;Dump 문제 풀기&lt;/h3&gt;

&lt;p&gt;자격증을 취득하기 위해서 위 강의만 듣는 것으로는 부족하기에 아래 언급된 Dump를 풀면서 연습하는 것이 중요합니다.&lt;/p&gt;

&lt;h4 id=&quot;examtopics-&quot;&gt;Examtopics ⭐⭐⭐⭐⭐&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://www.examtopics.com/exams/google/associate-cloud-engineer/view/&quot;&gt;Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;약 100문제 정도를 무료로 볼수 있습니다. 
해당 덤프의 문제가 실제 GCP 시험 문제와 가장 비슷하기에 여러번 풀면서 확실히 숙지하는 것을 추천 드립니다.&lt;/p&gt;

&lt;p&gt;문제는 정답이라고 표기한 것이 80% 이상이 오답입니다. 그래서 저는 아래 방법으로 해결했습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.naver.com/kroa/222235702688&quot;&gt;블로그&lt;/a&gt;에서 확인하기
    &lt;ul&gt;
      &lt;li&gt;어떤 분이 친절히 풀이를 정리했습니다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt; 활용하기
    &lt;ul&gt;
      &lt;li&gt;문제를 그대로 복붙하면 정답 및 풀이를 자세히 설명합니다. 이 방법도 추천드립니다.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Discussion 확인하기&lt;/li&gt;
  &lt;li&gt;GCP Document 확인하기&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cloud-gru-&quot;&gt;Cloud Gru ⭐⭐⭐&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://learn.acloud.guru/dashboard&quot;&gt;Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;회원 가입을 하면 덤프 문제 및 강의도 1 주일 동안 무료로 제공합니다. 다만 문제 난이도가 실제 시험보다 높은 편이라 시간이 없으면 굳이 볼 필요가 없습니다.&lt;/p&gt;

&lt;h4 id=&quot;gcp-sample-문제-&quot;&gt;GCP Sample 문제 ⭐⭐⭐⭐&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://cloud.google.com/certification/cloud-engineer&quot;&gt;Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;20 문제 샘플 문제를 제공합니다. 문제 난이도는 실제 시험과 비교했을 때 살짝 더 어려운 거 같습니다.&lt;/p&gt;

&lt;h4 id=&quot;gcp-examquestionscom-&quot;&gt;gcp-examquestions.com ⭐⭐⭐&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://gcp-examquestions.com/gcp-associate-cloud-engineer-practice-exam-part-1/&quot;&gt;Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;쉬운 문제도 있지만 너무 어려운 문제도 섞여 있습니다. 공부 시간이 별로 없다면 패스해도 무방합니다.&lt;/p&gt;

&lt;h2 id=&quot;5-시험-방식&quot;&gt;5. 시험 방식&lt;/h2&gt;

&lt;p&gt;시험 시작하기 10 분전에 미리 공유된 링크에 접속하면 버튼이 활성화 되면서 버튼을 누르게 되면 시험을 시작하게 되는데 그 전에 상담원과 챗으로 신분증과 카메라로 주위환경을 보여달라고 요청을합니다.&lt;/p&gt;

&lt;p&gt;시험 시간은 총 2시간이고 카메라로 관찰을 하기 때문에 함부러 자리를 이탈하면 안됩니다. 만약 화장실이 급하다면 상담원에게 요청을 하면 5분정도 휴식 시간을 주어집니다.&lt;/p&gt;

&lt;p&gt;시험 문제를 제출하게 되면 아래와 같이 Pass/Fail 여부를 알수 있어서 좋았습니다. 그리고 최종 확인은 10일 내로 알려준다고 합니다.(부정 행위가 있는지 확인 하는 듯)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/212519499-e2ad058c-5045-45b6-9edf-ff8f6c5f0667.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;저 같은 경우 4일 정도 소요가 됬습니다.&lt;/p&gt;

&lt;h2 id=&quot;6-시험-후기&quot;&gt;6. 시험 후기&lt;/h2&gt;

&lt;p&gt;저는 10년 이상의 프론트엔드(모바일 App) 개발자로 쭈욱 일해왔습니다. 이 전 회사같은 경우 규모가 꽤 큰 편이라서 백엔드 개발자 또는 팀이 따로 존재하였고 그래서 저는 백엔드/인프라 분야에 볼 일도 없었고 별로 흥미도 없었습니다.&lt;/p&gt;

&lt;p&gt;하지만 최근에 스타트업이 붐을 일으키면서 많은 회사에서 인프라를 기존 On-premise 방식이 아닌 Cloud로 사용하면서 저도 Cloud에 대해서 배워야 겠다는 생각이 많이 들었습니다.&lt;/p&gt;

&lt;p&gt;마지막으로 자격증을 땄다고 해서 제가 당장 GCP 를 잘 쓸수 있다고 생각하지는 않습니다. 하지만 제가 얻게 된 성취는 다음과 같습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Infra/Cloud 에 대한 전반적인 지식 습득&lt;/li&gt;
  &lt;li&gt;GCP 콘솔을 볼 때 헤매거나 멘붕하지 않게됨&lt;/li&gt;
  &lt;li&gt;새로운 문제나 요구사항를 마주칠 때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;이때 GCP 어떤 서비스를 이용하면 되겠다&lt;/code&gt; 라는 인사이트 획득&lt;/li&gt;
  &lt;li&gt;인프라/백엔드 개발자와 커뮤니케이션을 더 잘할수 있을거 같음&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/gcp/GCP-ACE-%ED%9B%84%EA%B8%B0/</link>
        <guid isPermaLink="true">https://origogi.github.io/gcp/GCP-ACE-%ED%9B%84%EA%B8%B0/</guid>
        
        
        <category>GCP</category>
        
      </item>
    
      <item>
        <title>2022년 회고</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;816&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211184286-e54aab45-2379-48f2-be67-dd2329206cb8.png&quot; /&gt;

&lt;/div&gt;

&lt;p&gt;해당 포스트는 2022년 한 해를 돌아보며 정리 및 리뷰를 해보았습니다.&lt;/p&gt;

&lt;h2 id=&quot;1-job&quot;&gt;1. Job&lt;/h2&gt;

&lt;h3 id=&quot;good-bye-11번가&quot;&gt;Good bye 11번가&lt;/h3&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;673&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211182322-5cbcb380-289e-423e-b8b4-ad8c5af7fd60.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;2020.02에 입사하였고 2022.02 월에 퇴사를 하였습니다.&lt;/p&gt;

&lt;p&gt;11번가에서는 11번가 Android app 개발자로 근무하였고 기억에 남는 프로젝트는 11번가 아마존 런칭이였습니다.
말도 많도 탈도 많았지만 그래도 무사히 출시하여 개인적으로 매우 뿌듯하였습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;600&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211182363-2d4e1c5a-224f-4f9f-8450-b1203273c801.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;개인적으로 같은 팀원 분들도 모두 괜찮았고 워라벨도 좋아서 매우 만족하면서 일을 다녔는데요 조금 과장해서 저는 월요병을 겪어보지 않고 즐겁게 회사에 출근하였습니다.&lt;/p&gt;

&lt;p&gt;다만 중간에 코로나 때문에 입사 한 지 얼마 안되서 재택을 하는 바람에 팀원들과 깊이 있게 교류를 하지 못한 점은 개인적으로 아쉬웠습니다.&lt;/p&gt;

&lt;h3 id=&quot;hello-kakaobrain&quot;&gt;Hello Kakaobrain&lt;/h3&gt;

&lt;div align=&quot;center&quot;&gt;

&lt;img width=&quot;569&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211182418-32623819-583d-4713-8761-e715a16e653a.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;22.03 에 입사를 하여 현재까지 근무를 하고 있습니다.&lt;/p&gt;

&lt;p&gt;카카오브레인에 대해서 조금 생소할수 있는데요 저 또한 헤드헌터한테 소개받았을 때 카카오에 그런 회사가 있었나?? 라고 갸우뚱했었거든요&lt;/p&gt;

&lt;p&gt;카카오브레인에 대해서 간략히 소개하면 &lt;strong&gt;AI&lt;/strong&gt; 관련 분야를 전문적으로 연구하는 회사입니다. 그래서 그런지 회사내에서는 정말 똑똑하신 석*박사님이 참 많습니다.&lt;/p&gt;

&lt;p&gt;원래는 주로 연구 위주의 회사였는데 22년 부터는 AI를 활용한 서비스를 개발하는 것으로 기조가 바뀌면서 AI 전문가 뿐만 아니라 저 같이 어플리케이션 개발자를 채용을 하였고 운이 좋게도 제가 채용이 된 것이죠~&lt;/p&gt;

&lt;p&gt;현재 저는 기존 Android 개발이 아닌 Flutter 개발자로 커리어를 변경하였습니다.&lt;/p&gt;

&lt;p&gt;제가 카카오브레인에 이직을 하게 된 주요 원인은 Android 외에 Flutter 로 커리어를 확장하고 싶었고 마침 카카오브레인에서 Flutter 관련 개발자를 찾고 있었기에 냉큼 지원을 했었습니다.&lt;/p&gt;

&lt;p&gt;지금 생각해보면 역시 이직은 운/타이밍이 70%인거 같습니다. :)&lt;/p&gt;

&lt;p&gt;다음은 2022년 카카오 브레인에서 제가 개발한 앱은 다음과 같습니다.&lt;/p&gt;

&lt;h4 id=&quot;remy-2206-v10-release&quot;&gt;Remy (22.06 v1.0 Release)&lt;/h4&gt;

&lt;div align=&quot;center&quot;&gt;

&lt;img width=&quot;853&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211182668-210c95a5-3177-4ea6-b427-8d1e58b9db5b.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;Remy 는 제가 입사하고 처음으로 출시한 앱입니다.&lt;/p&gt;

&lt;p&gt;Remy 는 영어학습을 도와주는 앱으로 사람이 영어를 학습할 때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;청킹&lt;/code&gt; 이라는 사람이 쉽게 인식할수 있는 단위로 잘라서 영어를 읽어서 학습 능률을 극대화 시킵니다.&lt;/p&gt;

&lt;p&gt;Flutter 기반으로 개발이 되었으며 제가 Flutter 를 이용해서 처음으로 상용화한 서비스라 그런지 애착이 가는 앱입니다.&lt;/p&gt;

&lt;h4 id=&quot;b-discover-2210-v10-release&quot;&gt;B^ Discover (22.10. v1.0 Release)&lt;/h4&gt;

&lt;div align=&quot;center&quot;&gt;

&lt;img width=&quot;947&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211183033-bc411c20-c654-40c2-9e8c-12da56de3968.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;B^ Discover 는 문장(Text) 를 입력하면 해당 문장을 분석해서 알맞은 그림으로 그려주는 앱입니다.&lt;/p&gt;

&lt;p&gt;이러한 서비스를 &lt;strong&gt;T2I(Text To Image)&lt;/strong&gt; 라고 하며 내부적으로 Karlo 라는 카카오브레인에서 자체 개발한 AI 모델을 사용합니다.&lt;/p&gt;

&lt;p&gt;해당 앱은 마찬가지로 Flutter 기반으로 개발되었습니다.&lt;/p&gt;

&lt;p&gt;현재 저는 B^ Discover 앱을 고도화 및 신규 기능을 개발하고 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;2-기술-스택&quot;&gt;2. 기술 스택&lt;/h2&gt;

&lt;p&gt;현재 커리어가 Android 에서 Flutter 로 변경을 하였기 때문에 주로 Flutter 위주로 기술 스택을 향상 시켰습니다. 하지만 Android 에 대해서 완전 손을 놓기 보다는 감각(?) 을 유지하기 위해서 Compose 를 공부하고 있습니다.&lt;/p&gt;

&lt;p&gt;그리고 회사 업무 외에 사이드 프로젝트로 웹을 주로 공부를 했습니다. 지금은 웹 쪽이 시들시들 해지고 그대신 현재 Cloud Computing 을 공부하고 있고 GCP 자격증을 준비 중입니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Flutter
    &lt;ul&gt;
      &lt;li&gt;Riverpod&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Android
    &lt;ul&gt;
      &lt;li&gt;Compose&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Web
    &lt;ul&gt;
      &lt;li&gt;React&lt;/li&gt;
      &lt;li&gt;NextJS&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Github Actions&lt;/li&gt;
  &lt;li&gt;Cloud Computing
    &lt;ul&gt;
      &lt;li&gt;GCP&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-life&quot;&gt;3. Life&lt;/h2&gt;

&lt;h3 id=&quot;모각코&quot;&gt;모각코&lt;/h3&gt;

&lt;p&gt;저는 김포에 한 아파트에 거주하고 있는데요 아파트 내에서 개발에 관심을 가지고 있는 입주민과 함께 22.04 월부터 지금까지 꾸준히 모각코 모임을 가지고 있습니다.&lt;/p&gt;

&lt;p&gt;모각코 멤버는 개발자 뿐만 아니라 다양한 직업을 가진 사람들이 참여하고 있습니다. 이 중에 가장 눈에 띄는 분은 스타트업 CEO 이신데 노코드에 관심을 가지고 노코드를 열심히 공부하고 있습니다.&lt;/p&gt;

&lt;p&gt;모각코를 통해 다양한 사람들과 이야기를 하면서 개발 외적으로도 많은 것을 배울수 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;4-2023-년-목표&quot;&gt;4. 2023 년 목표&lt;/h2&gt;

&lt;h3 id=&quot;gcp-자격증&quot;&gt;GCP 자격증&lt;/h3&gt;

&lt;p&gt;원래는 22년 12월에 따는 것이 목표였지만 12월에 회사일이 너무 바빠서 결국 이루지 못하였습니다. 올해 1월에 취득을 목표로 하고 있습니다.&lt;/p&gt;

&lt;h3 id=&quot;ios&quot;&gt;iOS&lt;/h3&gt;

&lt;p&gt;Flutter 를 개발하면서 iOS 분야 지식에 대한 필요성이 많이 느껴졌습니다. 그래서 올 해 iOS 에 좀 더 깊게 공부해서 iOS 로 앱을 하나 개발하는 것이 목표입니다.&lt;/p&gt;

&lt;h3 id=&quot;오프라인-밋업-발표&quot;&gt;오프라인 밋업 발표&lt;/h3&gt;

&lt;p&gt;올해 다양한 오프라인 밋업을 참여하면서 저도 한번 발표를 하고 싶어졌습니다. Flutter 전문가가 되기 위해서는 대중 앞에서 내가 아는 지식을 설명하고 발표를 할수 있어야 생각합니다. 그래서 올 해 Flutter 관련해서 오프라인 밋업 행사에서 발표를 하는 것이 목표입니다.&lt;/p&gt;
</description>
        <pubDate>Sun, 08 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/etc/2022%EB%85%84-%ED%9A%8C%EA%B3%A0/</link>
        <guid isPermaLink="true">https://origogi.github.io/etc/2022%EB%85%84-%ED%9A%8C%EA%B3%A0/</guid>
        
        
        <category>etc</category>
        
      </item>
    
      <item>
        <title>[Flutter] Github actions 에서 iOS 빌드/배포 속도 개선</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;982&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211177673-7ec90307-162d-4696-89b3-6054415d2db5.png&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;이번 포스트에서는 Github Actions 을 이용한 Flutter CI/CD 구축 중에 iOS 빌드/배포 속도를 어떻게 개선했는지에 대해서 정리를 하였습니다.&lt;/p&gt;

&lt;h2 id=&quot;1-문제-인식&quot;&gt;1. 문제 인식&lt;/h2&gt;

&lt;p&gt;먼저 저는 현업에서 개발 중인 Flutter App 을 Github actions 를 이용하여 CI/CD 환경을 구축하는 업무를 진행하였고 거의 완성 되었을 때 iOS 빌드/배포 소요 시간이 너무 느리다는 것을 파악했습니다.&lt;/p&gt;

&lt;p&gt;먼저 안드로이드 같은 경우 빌드/배포까지 평균 10분 정도 소요가 됨으로 그럭저럭 쓸만한 성능을 내고 있습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;779&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211176021-c370b94c-2b97-48ac-83b7-448f5a794a18.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;하지만 iOS 같은 경우 빌드/배포까지 50분~60분 정도 소요가 되었습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;953&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211177441-96f7d6ac-9cff-4cdd-a1b8-09bddbcd35fc.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;앱 빌드/배포하는데 1시간이나 걸리게 되면 개발자의 편의성을 위해서 CI/CD 를 구축하는게 의미가 없어집니다.
따라서 이 문제를 해결하기 위해 많은 시행 착오를 겪었습니다.&lt;/p&gt;

&lt;h2 id=&quot;2-flutter-ios-빌드배포-분석&quot;&gt;2. Flutter iOS 빌드/배포 분석&lt;/h2&gt;

&lt;p&gt;먼저 iOS 빌드/배포 성능을 개선하기 위해서는 iOS 빌드/배포를 알아야 합니다.&lt;/p&gt;

&lt;p&gt;보통 Flutter CI/CD는 아래와 같이 구축을 하게 됩니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;940&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211176536-b15f64c4-97ad-4f2f-982a-124136a71658.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;여기서 시간이 가장 소요되는 것은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flutter build ios&lt;/code&gt; 부분입니다. 총 32분이 소요되었습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;906&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211176879-37b0036c-d500-4dc6-bfdb-691cb2eea0f8.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flutter build ios&lt;/code&gt; 를 보면 내부적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pod install&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode build&lt;/code&gt; 두 과정을 수행하게 됩니다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pod install&lt;/code&gt; 은 약 10분 그리고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode build&lt;/code&gt; 은 약 22분이 소요가 되었습니다.&lt;/p&gt;

&lt;p&gt;그래서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flutter build ios&lt;/code&gt; 이 부분를 개선해보도록 하겠습니다.&lt;/p&gt;

&lt;h2 id=&quot;3-개선-작업-1&quot;&gt;3. 개선 작업 (1)&lt;/h2&gt;

&lt;p&gt;먼저 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pod install&lt;/code&gt; 과정을 개선해보겠습니다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pod install&lt;/code&gt; 은 ios 를 빌드할 때 필요한 외부라이브러리를 다운받고 설치하는 과정입니다. 여기서 사용되는 대표적인 외부 라이브러리는 Firebase 관련 라이브러리입니다.
그중 Firestore 관련 라이브러리가 시간을 어머어마하게 잡아먹습니다.&lt;/p&gt;

&lt;p&gt;그래서 해결 방법은 Pre-build 된 Firestore 라이브러리를 직접 사용하는 것입니다.
아래와 같이 ios/Podfile 에 Pre-build 된 Firestore 라이브러리를 사용하도록 스크립트를 추가합니다.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;target &apos;Runner&apos; do&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;pod &apos;FirebaseFirestore&apos;, :git =&amp;gt; &apos;https://github.com/invertase/firestore-ios-sdk-frameworks.git&apos;, :tag =&amp;gt; &apos;10.3.0&apos;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;// ~~~&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;주의할 점은 Firestore tag 는 pubspec.yaml 의 firestore 의 버전에 따라 달라집니다. 꼭 버전을 확인해서 알맞은 tag 를 사용하도록 합니다.&lt;/p&gt;

&lt;p&gt;좀 더 자세한 내용은 아래 링크를 확인 바랍니다.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://codewithandrea.com/tips/speed-up-cloud-firestore-xcode-builds/&quot;&gt;How to speed-up Cloud Firestore Xcode builds on your Flutter apps&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;3-개선-작업-2&quot;&gt;3. 개선 작업 (2)&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flutter build ios&lt;/code&gt; 를 실행하면 내부적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode build&lt;/code&gt; 를 수행하게 되는데 문제는 Fastlane에서 xcode build 를 다시 수행하기 때문에 먼저 수행되는  xcode build 는 수행할 필요가 전혀 없습니다. 따라서 이 과정을 스킵하기 위해서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flutter build ios&lt;/code&gt; 를 수행할 때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--config-only&lt;/code&gt; 를 붙입니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;flutter build ios –flavor prod –release –config-only –no-codesign -t lib/dev/main.dart&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;4-결과&quot;&gt;4. 결과&lt;/h2&gt;

&lt;p&gt;위 개선 작업을 적용한 결과 아래와 같이 iOS 빌드/배포 시간이 18분 정도로 약 30분 정도 시간을 단축하였습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img width=&quot;955&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/35194820/211177631-38c271c1-076e-4c8f-95c5-ba8e670a3856.png&quot; /&gt;
&lt;/div&gt;
</description>
        <pubDate>Fri, 06 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/flutter/github-actions-ios/</link>
        <guid isPermaLink="true">https://origogi.github.io/flutter/github-actions-ios/</guid>
        
        <category>Flutter</category>
        
        <category>Dart</category>
        
        
        <category>Flutter</category>
        
      </item>
    
      <item>
        <title>[리팩토링] YAGNI</title>
        <description>&lt;h2 id=&quot;yagni&quot;&gt;YAGNI&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;You Ain’t Gonna Need It&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;do-it&quot;&gt;Do it&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;깨끗하게&lt;/li&gt;
  &lt;li&gt;변경이 쉽게&lt;/li&gt;
  &lt;li&gt;유지보수 용이&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;do-not-it&quot;&gt;Do not it&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;당장 필요하지 않는 기능&lt;/li&gt;
  &lt;li&gt;사용하지 않는 기능&lt;/li&gt;
  &lt;li&gt;지나치게 미래지향적&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/89649360&quot;&gt;리팩터링 2판&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 30 Jul 2022 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/refactoring/YAGNI/</link>
        <guid isPermaLink="true">https://origogi.github.io/refactoring/YAGNI/</guid>
        
        
        <category>refactoring</category>
        
      </item>
    
      <item>
        <title>[리팩토링] Chapter 3. 코드에서 나는 악취</title>
        <description>&lt;h2 id=&quot;코드에서-나는-악취&quot;&gt;코드에서 나는 악취&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;냄새 나면 당장 갈아라&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;코드에서 악취가 난다는 기준은 아래와 같이 정리할수 있다.&lt;/p&gt;

&lt;h3 id=&quot;31-기이한-이름&quot;&gt;3.1 기이한 이름&lt;/h3&gt;

&lt;p&gt;함수, 모듈, 변수, 클래스 등은 그 이름만 보고도 각각이 무슨 일을 하고 어떻게 사용해야 하는지 명확히 알수 있도록 신경 써서 이름을 지어야 한다.&lt;/p&gt;

&lt;p&gt;이름에 관련한 리팩터링은 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;함수 선언 바꾸기&lt;/li&gt;
  &lt;li&gt;변수 이름 바꾸기&lt;/li&gt;
  &lt;li&gt;필드 이름 바꾸기&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;32-중복-코드&quot;&gt;3.2 중복 코드&lt;/h3&gt;

&lt;p&gt;중복 코드 관련 리팩터링은 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;함수 추출하기&lt;/li&gt;
  &lt;li&gt;문장 슬라이드&lt;/li&gt;
  &lt;li&gt;메서드 올리기 : 같은 부모로부터 파생된 서브 클래스들에 코드가 중복되어 있다면 중복된 코드를 부모의 메서드로 정의하는 것&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;33-긴-함수&quot;&gt;3.3 긴 함수&lt;/h3&gt;

&lt;p&gt;함수가 길수록 사람들은 함수를 이해하기 힘들다.&lt;/p&gt;

&lt;p&gt;긴 함수 관련 리팩터링 작업은 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;함수 추출하기 : 99% 는 이 작업을 수행한다.&lt;/li&gt;
  &lt;li&gt;조건문 분해하기 &amp;gt; case 문 마다 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수 추출하기&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;조건부 로직을 다향성으로 바꾸기&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;반복문 쪼개기&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;34-긴-매개-변수-목록&quot;&gt;3.4 긴 매개 변수 목록&lt;/h3&gt;

&lt;p&gt;매개변수 목록이 길어질수록 코드를 이해하기 어렵다.&lt;/p&gt;

&lt;p&gt;이와 관련 리팩터링 작업은 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;매개 변수를 질의 함수로 바꾸기&lt;/li&gt;
  &lt;li&gt;객체 통째로 넘기기&lt;/li&gt;
  &lt;li&gt;매개변수 객체 만들기&lt;/li&gt;
  &lt;li&gt;여려 함수를 클래스로 묶기&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;35-전역-데이터&quot;&gt;3.5 전역 데이터&lt;/h3&gt;

&lt;p&gt;전역 데이터는 코드베이스 어디에서든 건드를 수 있고 값을 누가 바꿨는지 찾아낼 메커니즘이 없다는 게 문제다.&lt;/p&gt;

&lt;p&gt;이와 관련한 리팩터링 작업은 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;변수 캡슐화 하기 : 데이터를 수정하는 부분을 쉽게 찾을 수 있고 접근을 통제할수 있게 된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;36-가변-데이터&quot;&gt;3.6 가변 데이터&lt;/h3&gt;

&lt;p&gt;데이터를 변경했더니 예상치 못한 결과나 골치 아픈 버그로 이어지는 경우가 종종 있다. 코드의 다른 곳에서는 다른 값을 기대한다는 사실을 인식하지 못한 채 수정해버리면 프로그램이 오작동을 한다.&lt;/p&gt;

&lt;p&gt;이와 관련한 리팩터링 작업은 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;변수 캡슐화하기&lt;/li&gt;
  &lt;li&gt;질의 함수와 변경 함수 분리하기&lt;/li&gt;
  &lt;li&gt;세터 제거하기&lt;/li&gt;
  &lt;li&gt;참조를 값으로 바꾸기 : 내부 필드를 직접 수정하지 말고 구조체를 통째로 교체&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;37-뒤엉킨-변경&quot;&gt;3.7 뒤엉킨 변경&lt;/h3&gt;

&lt;p&gt;코드를 수정할 때는 시스템에서 고쳐야 할 딱 한 군데를 찾아서 그 부분만 수정할 수 있기를 바란다. 이렇게 할 수 없다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;뒤엉킨 변경&lt;/code&gt; 과 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;산탄총 수술&lt;/code&gt; 중 하나가 풍긴다.&lt;/p&gt;

&lt;p&gt;뒤엉킨 변경은 단일 책임 원칙이 제대로 지켜지지 않을 때 나타난다.&lt;/p&gt;

&lt;p&gt;이와 관련한 리팩터링 작업은 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;단계 쪼개기&lt;/li&gt;
  &lt;li&gt;함수 옮기기&lt;/li&gt;
  &lt;li&gt;함수 추출하기&lt;/li&gt;
  &lt;li&gt;클래스 추출하기&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;38-산탄총-수술&quot;&gt;3.8 산탄총 수술&lt;/h3&gt;

&lt;p&gt;산탄총 수술은 뒤엉킨 변경과 비슷하면서도 정반대다.&lt;/p&gt;

&lt;p&gt;이 냄새는 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때 풍긴다. 변경할 부분이 코드 전반에 퍼져 있다면 찾기도 어렵고 꼭 수정해야 할 곳을 지나치기 쉽다.&lt;/p&gt;

&lt;p&gt;이와 관련한 리팩터링 작업은 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수 옮기기&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;필드 옮기기&lt;/code&gt; 로 한 모듈에 묶어둔다.&lt;/li&gt;
  &lt;li&gt;여러 함수를 클래스로 묶기&lt;/li&gt;
  &lt;li&gt;어설프게 분리된 로직을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수 인라인하기&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;클래스 인라인하기&lt;/code&gt; 를 통해 하나로 묶는다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;39-기능-편애&quot;&gt;3.9 기능 편애&lt;/h3&gt;

&lt;p&gt;기능 편애는 어떤 함수가 자기가 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터와 상호 작용할 일이 더 많을 때 풍기는 냄새다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;함수를 상호 작용할 모듈로 이동시키면 된다(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수 옮기기&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;310-데이터-뭉치&quot;&gt;3.10 데이터 뭉치&lt;/h3&gt;

&lt;p&gt;데이터 항목은 서로 뭉치는 경향이 있다. 이렇게 몰려다니는 데이터 뭉치는 보금자리를 따로 마련해줘야 한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;데이터 뭉치를 찾아서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;클래스 추출하기&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;매개변수 객체 만들기&lt;/code&gt; 나 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;객체 통째로 넘기기&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;311-기본형-집착&quot;&gt;3.11 기본형 집착&lt;/h3&gt;

&lt;p&gt;자신에게 주어진 문제에 딱 맞는 기초 타입(화폐, 좌표, 구간 등)을 직접 정의하기를 몹시 꺼리는 사람이 많다.
예를 들어 전화번호를 단순히 문자 집합으로만 표현하기에는 아쉬움이 많다. 최소한 사용자에게 보여줄 때는 일관된 형식으로 출력해주는 기능이라도 갖추어야 한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;기본형을 객체로 바꾸기&lt;/li&gt;
  &lt;li&gt;타입 코드를 서브클래스로 바꾸기&lt;/li&gt;
  &lt;li&gt;조건부 로직을 다형성으로 바꾸기&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;312-반복되는-switch-문&quot;&gt;3.12 반복되는 switch 문&lt;/h3&gt;

&lt;p&gt;switch 문은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;조건부 로직을 다형성으로 바꾸기&lt;/code&gt; 로 최대한 리팩토링을 해야한다.&lt;/p&gt;

&lt;h3 id=&quot;313-반복무&quot;&gt;3.13 반복무&lt;/h3&gt;

&lt;p&gt;반복문을 파이프라인으로 바꿔서 리팩토링을 해보자&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;filter, map, ..&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;314-성의-없는-요소&quot;&gt;3.14 성의 없는 요소&lt;/h3&gt;

&lt;p&gt;본문 코드를 그대로 쓰는 것과 다름 없는 함수나 실질적으로 메서드가 하나뿐인 클래스들을 리팩토링을 통해 제거하자&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;함수 인라인하기&lt;/li&gt;
  &lt;li&gt;클래스 인라인하기&lt;/li&gt;
  &lt;li&gt;계층 합치기&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;315-추측성-일반화&quot;&gt;3.15 추측성 일반화&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;나중에 필요할 거야&lt;/code&gt; 라는 생각으로 당장은 필요 없는 모든 종류의 후킹 포인트와 특이 케이스 처리 로직을 작성해둔 코드에서 풍긴다.
실제로 사용하게 되면 다향이지만, 그렇지 않는다면 쓸데없는 낭비일 뿐이다. 당장 걸리적거리는 코드는 눈 앞에서 치워버리자.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;하는 일이 거의 없는 추상 클래스는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;계층합치기&lt;/code&gt; 로 제거&lt;/li&gt;
  &lt;li&gt;함수 인라인, 클래스 인라인&lt;/li&gt;
  &lt;li&gt;사용되지 않는 매개변수는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수 선언 바꾸기&lt;/code&gt; 로 제거&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;316-임시-필드&quot;&gt;3.16 임시 필드&lt;/h3&gt;

&lt;p&gt;간혹 특정 상황에서만 값이 설정되는 클래스도 있다. 하지만 객체를 가져올 때는 당연히 모든 필드가 채워져 있으리라 기대하는 게 보통이라, 이렇게 임시 필드를 갖도록 작성하면 코드를 이해하기 어렵다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;클래스 추출하기&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수 옮기기&lt;/code&gt; 를 통해 임시 필드들과 관련된 코드를 모조리 새 클래스에 몰아 넣는다.&lt;/li&gt;
  &lt;li&gt;임시 필드들이 유효한지를 확인한 후 동작하는 조건부 로직이 있을 수 있는데, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;특이 케이스 추가하기&lt;/code&gt; 로 필드들이 유효하지 않을 때를 위한 대안 클래스를 만들어서 제거할수 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;317-메시지-체인&quot;&gt;3.17 메시지 체인&lt;/h3&gt;

&lt;p&gt;메시지 체인은 클라이언트가 한 객체를 통해 다른 객체를 얻은 뒤 방금 얻은 객체에 또 다른 객체를 요청하는 식으로, 다른 객체를 요청하는 작업이 연쇄적으로 이어지는 코드를 말한다. 이는 클라이언트가 객체 내비게이션 구조에 종속됐음을 의미한다. 그래서 내비게이션 중단 단계를 수정하면 클라이언트 코드도 수정되어야 한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;위임 숨기기&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수 추출하기&lt;/code&gt; 로 결과 객체를 사용하는 코드 일부를 따로 빼낸 다음 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수 옮기기&lt;/code&gt; 로 체인을 숨길 수 있는지 살펴보자.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;318-중개자&quot;&gt;3.18 중개자&lt;/h3&gt;

&lt;p&gt;객체의 대표적인 기능 하나로, 캡슐화가 있다. 캡슐화하는 과정에서 위임이 자주 활용이 되는데, 하지만 지나치면 문제가 된다. 클래스가 제공하는 메서드 중 절반이 다른 클래스에 구현을 위임하고 있다면 어떤가?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;중개자 제거하기&lt;/code&gt; 를 활용하여 실제로 일을 하는 객체와 직접 소통하게 하자&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;319-내부자-거래&quot;&gt;3.19 내부자 거래&lt;/h3&gt;

&lt;p&gt;모듈 사이의 데이터 거래가 많으면 결합도가 높아진다. 일이 돌아가게 하려면 거래가 이뤄질 수 밖에 없지만, 그 양을 최소로 줄이고 모두 투명하게 처리해야 한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;은밀히 데이터를 주고받는 모듈들이 있다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;함수 옮기기&lt;/code&gt; 와 필드 옮기기 기법으로 떼어놓아서 사적으로 처리하는 부분을 줄인다.&lt;/li&gt;
  &lt;li&gt;여러 모듈이 같은 관심사를 공유한다면 공통 부분을 정식으로 처리하는 제 3의 모듈을 만들거나 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;위임 숨기기&lt;/code&gt; 를 이용한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;320-거대한-클래스&quot;&gt;3.20 거대한 클래스&lt;/h3&gt;

&lt;p&gt;한 클래스가 너무 많은 일을 하려다 보면 필드 수가 상당히 늘어난다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;클래스 추출하기&lt;/li&gt;
  &lt;li&gt;슈퍼 클래스 추출하기&lt;/li&gt;
  &lt;li&gt;타입 코드를 서브 클래스로 바꾸기&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;321-서로-다른-인터페이스의-대안-클래스&quot;&gt;3.21 서로 다른 인터페이스의 대안 클래스&lt;/h3&gt;

&lt;h3 id=&quot;322-데이터-클래스&quot;&gt;3.22 데이터 클래스&lt;/h3&gt;

&lt;p&gt;데이터 클래스란 데이터 필드와 게터/세터 메서드로만 구성된 클래스를 말한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;public 필드가 있다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;레코드 캡슐화하기&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;변경하면 안 되는 필드는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;세터 제거하기&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;323-상속-포기&quot;&gt;3.23 상속 포기&lt;/h3&gt;

&lt;p&gt;서브 클래스는 부모로부터 메서드와 데이터를 물려받는다. 하지만 부모의 유산을 원치 않거나 필요 없다면 어떻게 해야 할까? 수 많은 유산 중에 관심 있는 몇 개만 받고 끝내려는 경우는 얾마든지 있을 수 있다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;서브 클래스를 위임&lt;/code&gt; 으로 바꾸기나 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;슈퍼클래스를 위임&lt;/code&gt; 으로 바꾸기 를 활용해서 상속 메커지즘에서 벗어나보자.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;324-주석&quot;&gt;3.24 주석&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;주석을 남겨야겠다는 생각이 들면, 가장 먼저 주석이 필요 없는 코드로 리팩터링해본다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/89649360&quot;&gt;리팩터링 2판&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 29 Jul 2022 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/refactoring/chapter-3/</link>
        <guid isPermaLink="true">https://origogi.github.io/refactoring/chapter-3/</guid>
        
        
        <category>refactoring</category>
        
      </item>
    
      <item>
        <title>[리팩토링] Chapter 2. 리팩터링 원칙</title>
        <description>&lt;h2 id=&quot;사전적-의미&quot;&gt;사전적 의미&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;리팩터링 : [명사] 소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이래하고 수정하기 쉽도록 내부 구조를 변경하는 기법&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;리팩터링 : [동사] 소프트웨어의 겉보기 동작은 그대로 유지한 채, 여러자기 리팩터링 기법을 적용해서 소프트웨어를 재구성한다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;리팩터링-정의&quot;&gt;리팩터링 정의&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;커드를 정리하는 작업을 모조리 리팩터링 이라고 표현하는데 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;특정한 방식&lt;/code&gt; 에 따라 코드를 정리하는 것만이 리팩터링이다.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;“누군가가 리팩터링하다가 코드가 깨져서 며칠이나 고생했다.” 라고 한다면, 십중팔구 리팩터링한 것이 아니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;리팩터링 과정에서 발견된 버그는 리팩터링 후에도 그대로 남아 있어야 한다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;리팩터링은 성능 최적화와 비슷하며 리팩터링의 목적은 코드를 이해하고 수정하기 쉽게 만드는 것이다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;두-개의-모자&quot;&gt;두 개의 모자&lt;/h2&gt;

&lt;p&gt;기능을 추가할 때는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;기능 추가&lt;/code&gt; 모자를 쓴 다음 기존 코드를 절대 건드리지 않고 새 기능을 추가하기만 한다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;리팩터링&lt;/code&gt; 모자를 쓴 다음 기능 추가는 절대로 하지 않고 오로지 코드 재구성에만 전념한다.&lt;/p&gt;

&lt;h2 id=&quot;리팩터링하는-이유&quot;&gt;리팩터링하는 이유&lt;/h2&gt;

&lt;h3 id=&quot;리팩터링하면-소프트웨어-설계가-좋아진다&quot;&gt;리팩터링하면 소프트웨어 설계가 좋아진다.&lt;/h3&gt;

&lt;p&gt;리팩터링하지 않으면 소프트웨어의 내부 설계가 썩기 쉽다. 시간이 지나면 코드 구조가 무너지기 시작하면 악효과가 누적된다.
같은 일을 하더라고 설계가 나쁘면 코드가 길어지기 십상이다. 그래서 중복 코드 제거는 설계 개선 작업의 중요한 축을 차지한다.&lt;/p&gt;

&lt;h3 id=&quot;리팩터링을-하면-소프트웨어를-이해하기-쉬워진다&quot;&gt;리팩터링을 하면 소프트웨어를 이해하기 쉬워진다&lt;/h3&gt;

&lt;p&gt;프로그래밍은 여러 면에서 마치 컴퓨터와 대화하는 것과 같다. 그래서 컴퓨토에게 시키려는 일과 이를 표현한 코드의 차이를 최대한 줄여야한다. &lt;br /&gt;
&lt;br /&gt;
리팩터링은 코드를 잘 읽히게 도와준다. 그리고 코드의 목적이 더 잘 드러나게 내의도를 더 명확하게 전달하도록 개선할 수 있다.&lt;/p&gt;

&lt;h3 id=&quot;리팩터링하면-버그를-쉽게-찾을-수-있다&quot;&gt;리팩터링하면 버그를 쉽게 찾을 수 있다.&lt;/h3&gt;

&lt;p&gt;코드를 이해하기 쉽다는 말은 버그를 찾기 쉽다는 말이다.&lt;/p&gt;

&lt;h3 id=&quot;리팩터링을-하면-프로그래밍-속도를-높일수-있다&quot;&gt;리팩터링을 하면 프로그래밍 속도를 높일수 있다&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/refactoring/figure-1.png&quot; alt=&quot;figure-1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;내부 설계가 잘 된 소프트웨어는 새로운 기능을 추가할 지점과 어떻게 고칠지를 쉽게 찾을 수 있다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;모듈화&lt;/code&gt; 가 잘 되어 있으면 전체 코드베이스 중 작은 일부만 이해하면 된다. 코드가 명확하면 버그를 만들 가능성도 줄고 디버깅하기가 훨씬 쉽다. 설령 프로그램의 요구사항이 바뀌더라도 설계를 지속해서 개선할 수 있다.&lt;/p&gt;

&lt;h3 id=&quot;언제-리팩터링해야-할까&quot;&gt;언제 리팩터링해야 할까?&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;3의 법칙&lt;/strong&gt; &lt;br /&gt;1. 처음에는 그냥한다. &lt;br /&gt;2. 비슷한 일을 두 번째로 하게되면 일단 계속 진행한다. &lt;br /&gt;3. 비슷한 일을 세 번째하게 되면 리팩터링한다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;준비를-위한-리책터링--기능을-쉽게-추가하게-만들기&quot;&gt;준비를 위한 리책터링 : 기능을 쉽게 추가하게 만들기&lt;/h3&gt;

&lt;p&gt;리팩터링하기 가장 좋은 시점은 코드베이스에 기능을 새로 추가하기 직전이다.&lt;/p&gt;

&lt;h3 id=&quot;이해를-위한-리팩터링--코드를-이해하기-쉽게-만들기&quot;&gt;이해를 위한 리팩터링 : 코드를 이해하기 쉽게 만들기&lt;/h3&gt;

&lt;p&gt;코드를 수정하려면 먼저 그 코드가 하는 일을 파악해야 한다. 리팩터링을 하면 머리로 이해한 것을 코드에 옮겨 담을 수 있다.&lt;br /&gt;
&lt;br /&gt;
랄프 존슨은 이런 초기 단계의 리팩터링을 밖을 잘 내다보기 위한 창문 닦기에 비유한다. 코드를 분석할 때 리팩터링을 해보면 깊은 수준까지 이해하게 된다.&lt;/p&gt;

&lt;h3 id=&quot;쓰레기-줍기-리팩터링&quot;&gt;쓰레기 줍기 리팩터링&lt;/h3&gt;

&lt;p&gt;간단히 수정할 수 있는 것은 즉시 고치고, 시간이 좀 걸리는 일은 짧은 메모만 남긴 다음, 하던 일을 끝내고 처리한다.&lt;br /&gt;
캠핑 규칙이 제안하듯, 항상 처음 봤을 때 보다 깔끔하게 정리하고 떠나자&lt;/p&gt;

&lt;h3 id=&quot;계획된-리팩터링과-수시로-하는-리팩터링&quot;&gt;계획된 리팩터링과 수시로 하는 리팩터링&lt;/h3&gt;

&lt;p&gt;리팩터링 일정을 따로 잡아두지 않고, 기능을 추가하거나 버그를 잡는 동안 리팩터링도 함께한다.&lt;/p&gt;

&lt;h3 id=&quot;오래-걸리는-리팩터링&quot;&gt;오래 걸리는 리팩터링&lt;/h3&gt;

&lt;p&gt;팀 전체가 리팩터링에 매달리기 보다는 주어진 문제를 몇 주에 걸쳐 &lt;strong&gt;조금씩&lt;/strong&gt; 해결해가는 편이 효과적일 때가 많다.&lt;/p&gt;

&lt;h3 id=&quot;코드-리뷰에-리팩터링-활용하기&quot;&gt;코드 리뷰에 리팩터링 활용하기&lt;/h3&gt;

&lt;p&gt;코드 리뷰는 개발팀 전체에 지식을 전파하는데 좋다. 리팩터링은 코드 리뷰의 결과를 더 구체적으로 도출하는데 도움이 된다.&lt;/p&gt;

&lt;h3 id=&quot;관리자에게는-뭐라고-말해야-할까&quot;&gt;관리자에게는 뭐라고 말해야 할까?&lt;/h3&gt;

&lt;p&gt;“리팩터링은 누적된 오류를 잡는 일이거나, 혹은 가치 있는 기능을 만들어내지 못하는 작업” 이라고 오해하여 리팩터링이 금기어가 돼버린 조직도 있다. 리팩터링을 위한 일정을 몇 주씩 잡는 개발팀을 보면 오해는 더욱 커진다.&lt;br /&gt;
하지만 리팩터링을 하면 소프트웨어를 빠르게 만드는 데 아주 효과적이다. 프로개발자에게 주어진 임무는 새로운 기능을 빠르게 구현하는 것이고, 가장 빠른 방법은 리팩터링이다. 그래서 리팩터링 부터 한다.&lt;/p&gt;

&lt;h3 id=&quot;리팩터링하지-말아야-할-때&quot;&gt;리팩터링하지 말아야 할 때&lt;/h3&gt;

&lt;p&gt;지저분한 코드를 발견해도 굳이 수정할 필요가 없다면 리팩터링을 하지 않는다. 리팩터링하는 것보다 처음부터 새로 작성하는 게 쉬울 때도 리팩터링하지 않는다.&lt;/p&gt;

&lt;h2 id=&quot;리팩터링-아키텍처-야그니yagni&quot;&gt;리팩터링, 아키텍처, 야그니(YAGNI)&lt;/h2&gt;

&lt;p&gt;소프트웨어 설계와 아키텍쳐는 코드로 작성된 뒤로는 변경할수 없고 부주의로 부패할 밀만 남았다고 여기곤 했다.&lt;br /&gt;
&lt;br /&gt;
리팩터링은 이런 관점을 크기 바꿔놓았다. 리팩터링을 통해 수년 동안 운영되던 소프트웨어라도 아키텍쳐를 대폭 변경할 수 있었다. &lt;br /&gt;
리팩터링이 아키텍처에 미치는 실질적 효과는 요구사항 변화에 자연스럽게 대응하도록 코드 베이스를 잘 설계해준다에 있다.&lt;br /&gt;
&lt;br /&gt;
코딩 전에 아키텍쳐를 확정지으려 할 때 대표적인 문제는 소프트웨어의 요구사항을 사전에 모두 파악해야한다는 것이다. 하지만 막상 해보면 실현할 수 없는 목표일 때가 많다.&lt;br /&gt;
한 가지 방법은 유연성 메커니즘을 적용하는 것이다. 예를 들어 함수를 작성할 때 다양항 예상 시나리오를 대응하기 위한 매개변수들을 추가한다. 하지만 모든 사항을 고려하려다 보면 유연성 메커니즘이 오히려 변화에 대응하는 능력을 떨어뜨릴 때가 많다.&lt;br /&gt;
&lt;br /&gt;
리팩터링을 활용하면 다르게 접근할 수 있다. 그저 현재까지 파악한 요구사항만을 해결하는 소프트웨어를 구축한다. 단 이 요구를 멋지게 해결하도록 설계한다. 진행하면서 사용자의 요구사항을 더 잘 이해하게 되면 그에 맞게 리팩터링해서 바꾼다.&lt;br /&gt;
이런 식으로 설계하는 방식을 &lt;strong&gt;간결한 설계, 점진적 설계, YAGNI&lt;/strong&gt; 등으로 부른다.&lt;/p&gt;

&lt;h2 id=&quot;리팩터링과-소트트웨어-개발-프로세스&quot;&gt;리팩터링과 소트트웨어 개발 프로세스&lt;/h2&gt;

&lt;p&gt;리팩터링이 퍼지기 시작한 것은 익스트림 프로그래밍(XP) 이 도입때문이다.&lt;br /&gt;
&lt;br /&gt;
XP의 특징은 지속적인 통합, 자기 테스트, 리팩터링 등의 개성이 강하면서 상호 의존하는 기법들을 하나로 묶은 프로세스라는 점이다.&lt;br /&gt;
&lt;br /&gt;
자가 테스트와 리팩터링을 묶어서 &lt;strong&gt;테스트 주도 개발(TDD)&lt;/strong&gt; 이라 한다.&lt;/p&gt;

&lt;h2 id=&quot;리팩터링과-성능&quot;&gt;리팩터링과 성능&lt;/h2&gt;

&lt;p&gt;리팩터링하면 프로그래 성능이 느려질까봐 걱정하는 사람이 많다.&lt;br /&gt;
성능에 대한 흥미로운 사실은, 대부분 프로그램은 전체 코드 중 극히 일부에서 대부분의 시간을 소비한다는 것이다.&lt;br /&gt;
&lt;br /&gt;
프로그램을 잘 리팩터링해두면 이런 식의 최적화에 두 가지 면에서 도움이 된다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;성능 튜닝에 투입할 시간을 벌수 있다.&lt;/li&gt;
  &lt;li&gt;리팩터링이 잘 되어 있는 프로그램은 성능을 더 세밀하게 분석할 수 있다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;리팩터링은 성능 좋은 소프트웨어를 만드는 데 기여한다.&lt;/p&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/89649360&quot;&gt;리팩터링 2판&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 29 Jul 2022 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/refactoring/chapter-2/</link>
        <guid isPermaLink="true">https://origogi.github.io/refactoring/chapter-2/</guid>
        
        
        <category>refactoring</category>
        
      </item>
    
      <item>
        <title>[CSS] Flexbox 정리</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://media.vlpt.us/images/chayezo/post/2d640f76-1ba2-477f-8725-784e11a073ee/cssImage.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;예제 코드를 통해서 CSS의 속성 중 Flexbox 의 동작 방식에 대해 정리한 포스트입니다.&lt;/p&gt;

&lt;h2 id=&quot;1-flexbox란&quot;&gt;1. Flexbox란?&lt;/h2&gt;

&lt;p&gt;아래와 같이 Container 에 여러개의 item 을 배치를 개발자가 쉽게 컨트롤할수 있도록 도와준다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149147412-cdbbf8c4-3784-4868-ad87-e60969b77bab.png&quot; alt=&quot;CSS Flexbox 완전 정리  포트폴리오 만드는 날까지! _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 2-47 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;예를 들어 각 아이템의 height 를 동일하게 하거나 각 아이템의 간격을 동일하게 하는 작업을 좀 더 편하게 수행할수 있다.&lt;/p&gt;

&lt;p&gt;flexbox 는 크게 두 가지 타입에 대해 적용하게 되는 데&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149148924-896b8ef8-a080-44f3-a0f5-6ffbeecff0a3.png&quot; alt=&quot;CSS Flexbox 완전 정리  포트폴리오 만드는 날까지! _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 4-22 screenshot&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;container : item 을 담고 있는 박스&lt;/li&gt;
  &lt;li&gt;item : container 에 포함되어 있는 요소들&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;container&quot;&gt;container&lt;/h3&gt;

&lt;p&gt;container에 지정되어 있는 속성은 아래와 같다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149149134-b47f8d8b-0efa-4f8d-ba44-0fd0593e3ffe.png&quot; alt=&quot;CSS Flexbox 완전 정리  포트폴리오 만드는 날까지! _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 4-27 screenshot&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;item&quot;&gt;item&lt;/h3&gt;

&lt;p&gt;item에 지정되어 있는 속성은 아래와 같다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149149626-04c2031e-a932-47d5-a56d-3ffcf9d89f7c.png&quot; alt=&quot;CSS Flexbox 완전 정리  포트폴리오 만드는 날까지! _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 4-46 screenshot&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;axis&quot;&gt;axis&lt;/h3&gt;

&lt;p&gt;flexbox 는 수평 축, 수직 축을 가지고 있고 둘중 하나를 main axis으로 선택하면 남은 하나는 cross axis 가 된다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149156154-765f320b-bf67-41fa-b44f-c8c3ce7a3f72.png&quot; width=&quot;300&quot; /&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149156163-f9fb705c-c430-4b14-af66-77479f1a9b9e.png&quot; width=&quot;300&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;2-container-flexbox-속성-적용&quot;&gt;2. &lt;strong&gt;container&lt;/strong&gt; flexbox 속성 적용&lt;/h2&gt;

&lt;p&gt;아래와 같이 10개의 블록을 선언해보자&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149166004-4008f056-6089-4b77-9436-4605ab440241.png&quot; alt=&quot;CSS Flexbox 완전 정리  포트폴리오 만드는 날까지! _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 8-47 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;위 샘플 코드는 아래와 같다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewport&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width=device-width&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;JS Bin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;container&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item3&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;3&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item4&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;4&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item5&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;5&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item6&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;6&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item7&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;7&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;8&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item9&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;9&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item10&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;10&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;CSS&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;beige&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100vh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#e57373&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f06292&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ba68c8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#9575cd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#7986cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#64b5f6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item7&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#4fc3f7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#4dd0e1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item9&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#4db6ac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#81c784&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;container 를 flexbox 로 적용하기 위해서 display:flex 를 추가하면 아래와 같이 블록이 정렬이 되는 것을 확인할 수 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149166323-988c427d-f88e-4b7b-bbf4-4811ee7d0a35.png&quot; alt=&quot;CSS Flexbox 완전 정리  포트폴리오 만드는 날까지! _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 9-37 screenshot&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;21-flex-direction&quot;&gt;2.1. flex-direction&lt;/h3&gt;

&lt;p&gt;블록의 방향을 변경할 때 사용하며 기본 값은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;row&lt;/code&gt; 이다.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;beige&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100vh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;그 외 값은 아래와 같다.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;row&lt;/th&gt;
      &lt;th&gt;row-reverse&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149167069-8d1767d4-71e2-49d8-bfa7-7560b5bdbd82.png&quot; alt=&quot;image&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149167399-8f5b7a2c-72b5-4d78-a080-27bc45c5a39a.png&quot; alt=&quot;image&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;column&lt;/th&gt;
      &lt;th&gt;column-reverse&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149167582-23d31f3c-5979-46c1-90ed-5d3918cab1fc.png&quot; alt=&quot;image&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149167813-c1da7288-e595-4f2d-9e98-69dfa9be031a.png&quot; alt=&quot;image&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;22-flex-wrap&quot;&gt;2.2. flex-wrap&lt;/h3&gt;

&lt;p&gt;한 줄에 아이템이 너무 많을 경우 아이템을 다른 줄로 줄 바꿈을 할지 아님 아이템 사이즈를 축소시켜서 한 줄에 표시할지를 결정한다.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;...&lt;/span&gt; 
  &lt;span class=&quot;nl&quot;&gt;flex-wrap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nowrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;`wrap`&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;기본 값은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nowrap&lt;/code&gt; 이며 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nowrap&lt;/code&gt; 일 경우 아래와 같이 item 을 감싸는 container 가 작아져도 무조건 한줄로 표시됨을 볼 수있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149457393-0e5e8069-8b50-41db-bee0-9517ac362ebb.gif&quot; alt=&quot;111&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flex-wrap&lt;/code&gt; 값을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wrap&lt;/code&gt; 으로 지정한 경우 아래와 같이 container 가 작아지면 자동으로 줄 바꿈을 하는 것을 볼수 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149457400-d220ba7b-4cdc-4587-88a0-893a72712bac.gif&quot; alt=&quot;2222&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;23-justify-content&quot;&gt;2.3. justify-content&lt;/h3&gt;

&lt;p&gt;중심 축 기준으로 item 을 어떻게 배치할지 결정한다.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;justify-content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;space-around&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;start&quot;&gt;&lt;strong&gt;start&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;디폴트 값으로 중심 축이 수평 방향이면 item 을 왼 쪽에 그리고 수직 방향이면 item 을 위 쪽에 배치를 한다.&lt;/p&gt;

&lt;h4 id=&quot;end&quot;&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;중심 축이 수평 방향이면 아이템을 오른 쪽 에 그리고 수직 방향이면 아이템을 아래 쪽에 배치를 한다.&lt;/p&gt;

&lt;h4 id=&quot;center&quot;&gt;&lt;strong&gt;center&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;item 을 중앙으로 배치한다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149459069-6aa21c6d-3fbc-4e03-8df9-480b56d46e1d.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;space-around&quot;&gt;&lt;strong&gt;space-around&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;중심 축이 수평 방향이면 아이템 좌 우에 동일한 크기의 space 가 추가된다. 그리고 수직 방향이면 위, 아래에 동일한 크기의 space 가 추가된다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149458581-7f4b7bbd-bd6e-4a07-a5c5-44baeaab8ea1.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;space-between&quot;&gt;&lt;strong&gt;space-between&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;item 과 item 사이의 space 의 사이즈는 모두 동일하고 item 과 container 의 space 사이즈는 0가 된다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149458619-cf1c5017-c828-4db1-98c9-7b002a6af41d.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;space-evenly&quot;&gt;&lt;strong&gt;space-evenly&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;item 과 item 사이 그리고 item 과 container 의 space 사이즈는 모두 동일하다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149458680-5c613b5e-6a68-4a26-976a-e0025ef8eb8e.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;24-align-content&quot;&gt;2.4. align-content&lt;/h3&gt;

&lt;p&gt;반대 축의 item 을 어떻게 배치할지 결정&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;center&lt;/th&gt;
      &lt;th&gt;space-around&lt;/th&gt;
      &lt;th&gt;space-between&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149462601-775d7eb7-cdeb-4e18-965d-94f5ff917747.png&quot; alt=&quot;image&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149462880-9d8904e6-b86d-4812-bbcc-c778cbb65309.png&quot; alt=&quot;image&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149462761-d8e81e0f-b008-45e1-982f-70464c90d251.png&quot; alt=&quot;image&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;align-content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;space-around&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-item-flexbox-속성-적용&quot;&gt;3. &lt;strong&gt;item&lt;/strong&gt; flexbox 속성 적용&lt;/h2&gt;

&lt;p&gt;다음은 item 에 대해서 적용되는 여러 flexbox 의 속성을 알아보자   먼저 아래와 같이 container 애 item 이 있다고 가정할 때&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149463943-fb3193fe-e5e9-480a-97f9-c539aabe341b.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewport&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width=device-width&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;JS Bin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;container&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;item item3&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;3&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;CSS&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;beige&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100vh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#e57373&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f06292&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ba68c8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;31-flex-grow&quot;&gt;3.1. flex-grow&lt;/h3&gt;

&lt;p&gt;item 이 container 에 여유 공간이 있으면 사이즈를 차지하려고 할 때 사용되는 값이다.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex-grow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defalt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;item1 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flex-grow&lt;/code&gt; 를 1로 지정하게 되면 아래와 같이 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;item1&lt;/code&gt; 이 남은 공간을 모두 차지하는 것을 볼수 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149464600-e52b8d79-eab1-44b1-a089-4983ecbd7ae2.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;그리고 모든 item 들에 대해서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flex-grow&lt;/code&gt; 를 1로 지정하게 되면 아래와 같이 모든 item 들이 동일한 크기로 남은 공간을 차지하게 된다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149464712-0cbe5ff6-b433-4067-ab09-e9eb623ebf01.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;위 상태에서 item1 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flex-grow&lt;/code&gt; 를 2로 변경하게 되면 item1은 item2, item3 의 두 배의 사이즈를 차지하게 된다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149464944-a1653271-c578-4711-8df5-73894f8ded61.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;31-flex-shrink&quot;&gt;3.1. flex-shrink&lt;/h3&gt;

&lt;p&gt;container 에 여유 공간이 없을 때 item 의 사이즈를 결정하는 속성이다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flex-grow&lt;/code&gt; 의 반대 속성이다.&lt;/p&gt;

&lt;p&gt;아래 화면은 item1의 flex-shrink 값을 2로 하고 나머지는 flex-shrink 를 1로 했을 때 어떻게 동작하는 지 보여준다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149465796-47adecb8-df98-41df-b22f-32257ae22484.gif&quot; alt=&quot;2222&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;32-flex-basis&quot;&gt;3.2. flex-basis&lt;/h3&gt;

&lt;p&gt;container 의 사이즈가 늘어나거나 줄어들때 동일한 비율을 유지할 때 사용되는 속성이다.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nl&quot;&gt;flex-basis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0%&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;각 item 의 속성을 아래와 같이 지정했을 때&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.item1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#e57373&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex-basis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;70%&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f06292&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex-basis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20%&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.item3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ba68c8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex-basis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10%&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;결과 화면이 아래와 같이 출력되는 것을 볼수 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149466535-83f40158-8337-46aa-885a-4e5828d66b57.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;33-align-self&quot;&gt;3.3. align-self&lt;/h3&gt;

&lt;p&gt;하나의 item에 대해 위치를 변경하고 싶을 때 사용한다.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.item&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;align-self&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;item1 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;align-self&lt;/code&gt; 값을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;center&lt;/code&gt; 로 변경하게 item1이 container 중앙에 배치되는 것을 볼수 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149466911-03dd1c93-b014-4af0-96a6-f3c1f8724b0f.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;4-참고-자료&quot;&gt;4. 참고 자료&lt;/h2&gt;

&lt;h3 id=&quot;41-guide-to-flexbox&quot;&gt;4.1. Guide to Flexbox&lt;/h3&gt;

&lt;p&gt;좀 더 자세한 flexbox 정보는 아래 사이트 참고&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149463176-d6b2a1ac-8380-4551-834f-8738912e0142.png&quot; alt=&quot;image&quot; /&gt;
&lt;a href=&quot;https://css-tricks.com/snippets/css/a-guide-to-flexbox/&quot;&gt;A Complete Guide to Flexbox&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;42-flexbox-froggy&quot;&gt;4.2. FLEXBOX FROGGY&lt;/h3&gt;

&lt;p&gt;flexbox 를 학습하기 위한 game 형식 사이트&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149467713-283eea56-dad2-4bd5-a889-3e29d673407d.png&quot; alt=&quot;CSS Flexbox 완전 정리  포트폴리오 만드는 날까지! _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 22-35 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://flexboxfroggy.com/#ko&quot;&gt;FLEXBOX FROGGY&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;5-reference&quot;&gt;5. Reference&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=7neASrWEFEM&quot;&gt;youtube, 드림코딩 by 엘리&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 12 Jan 2022 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/html/CSS-Flexbox/</link>
        <guid isPermaLink="true">https://origogi.github.io/html/CSS-Flexbox/</guid>
        
        <category>CSS</category>
        
        <category>FE</category>
        
        
        <category>HTML</category>
        
      </item>
    
      <item>
        <title>[CSS] 레이아웃 정리(display, position)</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://media.vlpt.us/images/chayezo/post/2d640f76-1ba2-477f-8725-784e11a073ee/cssImage.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;예제 코드를 통해서 CSS의 속성 중 display 와 position 의 동작 방식을 정리한 포스트입니다.&lt;/p&gt;

&lt;h2 id=&quot;display&quot;&gt;display&lt;/h2&gt;

&lt;p&gt;CSS 의 display 속성은 태그를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inline&lt;/code&gt; 또는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;block&lt;/code&gt; 타입으로 변경을 할수 있다.&lt;/p&gt;

&lt;p&gt;예를 들어 아래와 같이 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; 와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;span&lt;/code&gt; 을 정의하였다고 할 때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; 는 기본적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;block&lt;/code&gt; 타입 이고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;span&lt;/code&gt; 은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inline&lt;/code&gt; 타입이다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148952972-fbe0ffbf-7d05-4199-a336-eb38f8bc9dff.png&quot; alt=&quot;CSS 레이아웃 정리 display, position 완성 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 3-11 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;여기서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;span&lt;/code&gt; 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display&lt;/code&gt; 를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;block&lt;/code&gt; 으로 선언하면 div 와 똑같이 한줄에 한 개 씩 표시가 된다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148953498-a089eb76-654a-4587-a8e7-dbe0a68c570a.png&quot; alt=&quot;CSS 레이아웃 정리 display, position 완성 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 3-29 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;그리고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; 를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inline-block&lt;/code&gt; 으로 변경하게 되면 한줄에 여러개의 블록이 표시가 된다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148954154-ab1ab668-5a7c-4c36-8c7d-edd3e305ad18.png&quot; alt=&quot;CSS 레이아웃 정리 display, position 완성 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 3-38 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;추가적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; 를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inline&lt;/code&gt; 으로 변경하게 되면 아무것도 표시가 되지 않는데 그 이유는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inline&lt;/code&gt; 은 내부 컨텐츠를 꾸며주는 것이고 현재 div 에는 컨텐츠가 아무것도 없기 때문이다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148954442-94b49760-a84c-4cee-85f4-26a2a67a8030.png&quot; alt=&quot;CSS 레이아웃 정리 display, position 완성 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 3-45 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;따라서 div 에 컨텐츠를 추가하면 span 처럼 화면에 표시가 되는 것을 볼수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148954680-513abfa0-821b-4546-883d-18edf336bd7c.png&quot; alt=&quot;CSS 레이아웃 정리 display, position 완성 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 3-54 screenshot&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;position&quot;&gt;position&lt;/h2&gt;

&lt;p&gt;position은 태그의 위치를 지정할 때 사용하는 속성이다.&lt;/p&gt;

&lt;h3 id=&quot;static&quot;&gt;static&lt;/h3&gt;

&lt;p&gt;예를 들어 아래와 같이 태그들이 존재할 때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container&lt;/code&gt; 의 위치를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;left&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;top&lt;/code&gt; 를 사용해서 변경을 하려고 한다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148958297-7ef95ff1-959c-411b-821c-2e2e8304f95e.png&quot; alt=&quot;CSS 레이아웃 정리 display, position 완성 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 5-57 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;하지만 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container&lt;/code&gt; 의 위치는 변경이 되지 않는데 그 이유는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;positon&lt;/code&gt; 의 디폴트 값은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static&lt;/code&gt; 이며 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static&lt;/code&gt; 의 의미는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position을 따로 지정하지 않는다&lt;/code&gt; 이다.&lt;/p&gt;

&lt;h3 id=&quot;relative&quot;&gt;relative&lt;/h3&gt;

&lt;p&gt;따라서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position&lt;/code&gt; 을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static&lt;/code&gt; 에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;relative&lt;/code&gt; 로 변경을 하게 되면 노란색 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container&lt;/code&gt; 의 위치가 오른 쪽에서 20px, 아래 쪽으로 20px 이동이 됨을 볼수 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148958989-a97a76c2-c2a0-44aa-aa8d-9cf24f205b8d.png&quot; alt=&quot;CSS 레이아웃 정리 display, position 완성 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 6-41 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;정리하자면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;relative&lt;/code&gt; 는 원래 위치해야하는 포지션 기준으로 태그를 이동한다.&lt;/p&gt;

&lt;h3 id=&quot;absolute&quot;&gt;absolute&lt;/h3&gt;

&lt;p&gt;추가적으로 box 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position&lt;/code&gt; 을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;absolute&lt;/code&gt; 로 변경을 하게되면 아래와 같이 포지션이 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container&lt;/code&gt; 기준으로 오른 쪽에서 20px, 아래 쪽으로 20px 이동이 됨을 볼수 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149138194-6c75c8f2-7d1c-4583-9537-2d2729fa3a15.png&quot; alt=&quot;CSS 레이아웃 정리 display, position 완성 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 7-43 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;정리하자면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;absolute&lt;/code&gt; 는 현재 태그를 담겨있는 상자를 기준으로 태그를 이동한다.&lt;/p&gt;

&lt;h3 id=&quot;fixed&quot;&gt;fixed&lt;/h3&gt;

&lt;p&gt;box 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position&lt;/code&gt; 을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fixed&lt;/code&gt; 로 변경하면 아래와 같이 창을 기준으로 오른 쪽에서 20px, 아래 쪽으로 20px 이동이 됨을 볼수 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149139771-34614609-1ce4-4877-a4fa-a36e0eb03bc1.png&quot; alt=&quot;CSS 레이아웃 정리 display, position 완성 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 8-13 screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fixed&lt;/code&gt; 는 태그를 window 를 기준으로 이동을 시킨다.&lt;/p&gt;

&lt;h3 id=&quot;sticky&quot;&gt;sticky&lt;/h3&gt;

&lt;p&gt;box 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position&lt;/code&gt; 을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sticky&lt;/code&gt; 로 변경하면 아래와 같이 스크롤을 할 때 사라지지 않고 상단에 계속 붙어있는 것을 볼수 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/149142598-74aa1034-ac15-4c2d-b04e-c3bf009bc087.gif&quot; alt=&quot;다운로드 (1)&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=jWh3IbgMUPI&quot;&gt;youtube, 드림코딩 by 엘리&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 10 Jan 2022 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/html/CSS-layout-copy/</link>
        <guid isPermaLink="true">https://origogi.github.io/html/CSS-layout-copy/</guid>
        
        <category>CSS</category>
        
        <category>FE</category>
        
        
        <category>HTML</category>
        
      </item>
    
      <item>
        <title>[CSS] 셀렉터, 기초 이론 정리</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://media.vlpt.us/images/chayezo/post/2d640f76-1ba2-477f-8725-784e11a073ee/cssImage.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;CSS 의 가장 기본 및 기초에 대해 정리한 포스트입니다.&lt;/p&gt;

&lt;h2 id=&quot;1-의미-정의&quot;&gt;1. 의미, 정의&lt;/h2&gt;

&lt;p&gt;CSS 는 Cascading Style Sheet 의 약자이다.&lt;br /&gt;
CSS는 HTML 요소들이 각종 미디어에서 어떻게 보이는가를 정의하는 데 사용되는 스타일 시트 언어이다.&lt;/p&gt;

&lt;h3 id=&quot;cascading&quot;&gt;Cascading&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148683780-16e84b67-2e24-440b-b845-4497bce48c66.png&quot; alt=&quot;11111&quot; /&gt;&lt;/p&gt;

&lt;p&gt;HTML 의 스타일 적용 우선 순위는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Author Style&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User Style&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Browser Style&lt;/code&gt; 순으로 결정이 된다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Author Style&lt;/code&gt; 은 .css 의 문서를 뜻하며 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User Style&lt;/code&gt; 은 유저가 선택한 스타일이다. 예를 들어 다크 모드나 라이드 모드 전환이 있다. 마지막으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Browser Style&lt;/code&gt; 은 웹 브라우저에 내장된 스타일를 뜻한다.&lt;/p&gt;

&lt;p&gt;HTML 의 요소의 디자인/스타일을 결정할 때 위에 언급된 우선 순위로 탐색을 하기 때문에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cascading&lt;/code&gt; 특징을 가진다.&lt;/p&gt;

&lt;p&gt;추가적으로 위 스타일의 연결고리를 끊고 싶을 때에는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!important&lt;/code&gt; 키워드를 사용한다. 하지만 가능다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!important&lt;/code&gt; 사용을 배제를 해야한다.&lt;/p&gt;

&lt;h2 id=&quot;2-selector&quot;&gt;2. Selector&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148685121-7eaf1267-a838-4665-9ebe-f68eaa6db47b.png&quot; alt=&quot;33&quot; /&gt;&lt;/p&gt;

&lt;p&gt;CSS 에서 Selector 란 HTML 에서 어떤 태그를 골라서 스타일을 적용할지 를 결정하는 문법&lt;/p&gt;

&lt;h3 id=&quot;universal&quot;&gt;Universal&lt;/h3&gt;

&lt;p&gt;HTML 내에 모든 태그에 대해서 적용&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 CSS 를 적용하면 모든 태그에 대해 색상이 green 으로 적용이 된다.&lt;/p&gt;

&lt;h3 id=&quot;type&quot;&gt;Type&lt;/h3&gt;

&lt;p&gt;HTML 내에 지정된 같은 타입의 태그들에 대해서 스타일 적용&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 CSS 를 적용하면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;li&lt;/code&gt;  태그에 대해 색상이 blue 로 적용이 된다.&lt;/p&gt;

&lt;h3 id=&quot;id&quot;&gt;ID&lt;/h3&gt;

&lt;p&gt;문서 안에 있는 단 하나의 태그에 스타일을 적용하는 경우에 사용합니다. 선택자에 샤프(#)와 id명(임의의 이름)을 붙여 식별한다.&lt;/p&gt;

&lt;p&gt;html&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;special&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;First&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;css&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;#special&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;pink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드를 적용하면 HTML에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;special&lt;/code&gt; id를 가진 태그를 선택해서 스타일을 적용한다.&lt;/p&gt;

&lt;h3 id=&quot;class&quot;&gt;Class&lt;/h3&gt;

&lt;p&gt;HTML 내에 같은 class 이름을 가진 복수의 태그에 대해 스타일을 적용할  경우에 사용한다. 피리어드(.)와 class명(임의의 이름)을 붙여 구별한다.&lt;/p&gt;

&lt;p&gt;html&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;red&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
 ....
&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;red&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;css&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.red&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드를 적용하면 HTML에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;red&lt;/code&gt; class를 가진 모든 태그에 대해서 스타일을 적용한다.&lt;/p&gt;

&lt;h3 id=&quot;state&quot;&gt;State&lt;/h3&gt;

&lt;p&gt;태그의 특정 상태에 대해서 스타일을 적용할수 있다. 이때 적용하고자 하는 태그 뒤에 콜론(:) 과 상태를 붙여서 정의한다.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:hover&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;red&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드를 적용하면 버튼에 마우스 커서를 올리게 되면 색상이 red 로 변경이 된다.&lt;/p&gt;

&lt;h3 id=&quot;attribute&quot;&gt;Attribute&lt;/h3&gt;

&lt;p&gt;태그가 가진 특정 attribute에 대해서 스타일을 적용할수 있다.&lt;/p&gt;

&lt;p&gt;예를 들어 HTML 문서에 아래와 같이 태그가 정의가 되어있을 때&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;naver.com&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Naver&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;  # 1. 
&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;googlenaver.com&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Google&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;  # 2. 
&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&amp;gt;&lt;/span&gt;empty&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;   # 3. 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;아래와 같이 CSS를 적용하게 되면&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;1번과 2번 태그의 색상이 red로 변경이 된다.&lt;/p&gt;

&lt;p&gt;추가적으로 특정 attribute의 값을 가지고 있는 태그에 대해서 선택이 가능하다.&lt;br /&gt;
예를 들어 아래와 같이 CSS를 적용하게 되면&lt;/p&gt;

&lt;p&gt;1번 태그만 색상이 red로 변경이 된다.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;naver.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;/h2&gt;

&lt;p&gt;아래 간단한 게임을 통해서 seletor에 대해서 학습/복습을 해보자.(이미지를 클릭!)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://flukeout.github.io/&quot;&gt; 
&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148687382-d78fd1bb-b9a4-4f93-8833-8d9a8309469d.png&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=gGebK7lWnCk&quot;&gt;youtube, 드림코딩 by 엘리&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 09 Jan 2022 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/html/css-basic/</link>
        <guid isPermaLink="true">https://origogi.github.io/html/css-basic/</guid>
        
        <category>CSS</category>
        
        <category>FE</category>
        
        
        <category>HTML</category>
        
      </item>
    
      <item>
        <title>[HTML] Sections</title>
        <description>&lt;h2 id=&quot;html-section&quot;&gt;HTML Section&lt;/h2&gt;

&lt;p&gt;HTML 에서 권장하는 기본 레이아웃 구성&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148676504-17785e80-f51b-4a25-a011-da8147420fed.png&quot; alt=&quot;111&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;type&quot;&gt;Type&lt;/h2&gt;

&lt;p&gt;Tag는 아래와 같이 크게 두 종료가 있으며&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt; 는 또 다른 Tag 들을 묶어주는 역할을 하며 사용자에게 보여지지 않는다.&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Item&lt;/code&gt; 은 사용자에게 컨텐츠를 보여주는 역할을 한다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148676608-2fd88a65-0a18-4d88-8210-2a607a00c492.png&quot; alt=&quot;HTML 태그 완성, 웹 포트폴리오 만들기 전에 꼭 보세요 _ 프론트엔드 개발자 입문편_ HTML, CSS, Javascript 6-3 screenshot&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;block-inline&quot;&gt;Block, Inline&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148676909-6db17fbc-8b23-4a8d-8504-a5a003d70419.png&quot; alt=&quot;333&quot; /&gt;&lt;/p&gt;

&lt;p&gt;태그는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Block&lt;/code&gt; 또는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inline&lt;/code&gt; 타입으로 나눠진다. 예를 들어 아래 그림에서 핑크색 태그가 있는 상태에서 노란 색 태그를 배치하고자 할 때&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Block&lt;/code&gt; 타입은 핑크 색 태그 옆에 충분한 공간이 있음에도 불구하고 다음 줄에 배치된 것을 볼수 있는데, 즉 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Block&lt;/code&gt; 타입은 한줄에 하나 만 배치를 한다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inline&lt;/code&gt; 타입은 핑크 색 태그 옆에 여유 공간에 노란 색 블록이 배치가 되는 데, 즉 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inline&lt;/code&gt; 은 공간이 허용이 되면 한 줄에 여러개의 태그를 배치할수 있다.&lt;/p&gt;

&lt;h2 id=&quot;tag--element&quot;&gt;Tag &amp;amp; Element&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148676991-525f0502-4de9-4012-b4eb-ffebf3df4f79.png&quot; alt=&quot;444&quot; /&gt;&lt;/p&gt;

&lt;p&gt;꺽쇠로 감싸 있는 부분을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tag&lt;/code&gt; 라 하며 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Opening Tag&lt;/code&gt; 와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Closing Tag&lt;/code&gt; 사이를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Content&lt;/code&gt; 라고 한다. 그리고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tag&lt;/code&gt; 와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Content&lt;/code&gt; 를 합쳐서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Element&lt;/code&gt; 또는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node&lt;/code&gt; 라고 한다.&lt;/p&gt;

&lt;h2 id=&quot;자주-사용하는-tag-들&quot;&gt;자주 사용하는 Tag 들&lt;/h2&gt;

&lt;h3 id=&quot;a-tag-anchor-tag&quot;&gt;A tag (Anchor Tag)&lt;/h3&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://www.google.com&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_blank&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;Click&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;하이퍼링크를 가능하게 하는 태그, 즉 다른 페이지로 이동이 가능하다.&lt;/p&gt;

&lt;h3 id=&quot;p-tag-paragraph-tag&quot;&gt;P tag (Paragraph Tag)&lt;/h3&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is a sentence. &lt;span class=&quot;nt&quot;&gt;&amp;lt;b&amp;gt;&lt;/span&gt;That&lt;span class=&quot;nt&quot;&gt;&amp;lt;/b&amp;gt;&lt;/span&gt; is.. &lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt; 
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is a sentence. &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&amp;gt;&lt;/span&gt;That&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt; is.. &lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt; 
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is a sentence. &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;That&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt; is.. &lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;문단을 정의하는 태그이며 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;span&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; 태그를 포함 할수 있다.&lt;/p&gt;

&lt;h3 id=&quot;list-tag-ol-vs-ul&quot;&gt;List tag (ol vs ul)&lt;/h3&gt;

&lt;p&gt;ol 태그는 순서가 있는 태그이다.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;nt&quot;&gt;&amp;lt;ol&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;3&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드를 실행하면 브라우져에서 아래와 같이 순서를 나타내는 prefix가 추가가 된다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148677502-675d08ff-7e85-4a7b-8b28-56c38143b24e.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;ul 태그는 순서가 없는 태그이다.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;nt&quot;&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;3&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드를 실행하면 브라우져에서 아래와 같이 기호 prefix가 추가가 된다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/148677589-651ab5ac-e8f1-4bda-95f7-d5ae09e5dec1.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;input-tag&quot;&gt;Input tag&lt;/h3&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;lable&amp;gt;&lt;/span&gt;Name: &lt;span class=&quot;nt&quot;&gt;&amp;lt;/lable&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;input_name&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;사용자로 부터 입력받을수 있는 태그이다. 보통 앞에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;label&lt;/code&gt; 태그를 붙여서 어떤 데이터를 입력받을지를 나타낸다.&lt;/p&gt;

&lt;p&gt;attr의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; 같은 경우 하나의 page에 여러개의 input 태그가 존재할 경우 각 태그들을 구별하기 위해 사용한다.&lt;/p&gt;

&lt;p&gt;attr 의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt; 같은 경우 date, color, number, password, checkbox 등 다양한 타입이 존재한다.&lt;/p&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=OoA70D2TE0A&quot;&gt;Youtube, 드림코딩 by 엘리&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 09 Jan 2022 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/html/HTML-basic/</link>
        <guid isPermaLink="true">https://origogi.github.io/html/HTML-basic/</guid>
        
        <category>HTML</category>
        
        <category>FE</category>
        
        
        <category>HTML</category>
        
      </item>
    
      <item>
        <title>[React] 자주 사용하는 라이브러리</title>
        <description>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;자주 사용하는 React Library 를 정리한 포스트 입니다.&lt;/p&gt;

&lt;h2 id=&quot;1-material-ui-mui&quot;&gt;1. Material UI (MUI)&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://media.vlpt.us/images/sunkim01/post/ab1bc5e7-5c3d-4d61-8b49-6feba35492a0/MeterialUI.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @mui/material
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;icons&quot;&gt;Icons&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @mui/icons-material
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MUI&lt;/code&gt; 사용 시 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Module not found: Can&apos;t resolve &apos;@emotion/react&apos;&lt;/code&gt; 에러가 발생 시 아래 패키지를 추가로 설치&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @emotion/react 
npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @emotion/styled
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 29 Dec 2021 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/react/React-Library/</link>
        <guid isPermaLink="true">https://origogi.github.io/react/React-Library/</guid>
        
        <category>React</category>
        
        
        <category>React</category>
        
      </item>
    
      <item>
        <title>[React] 스니펫 단축키 정리</title>
        <description>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;VS Code extension 스니펫 단축키를 정리한 포스트입니다.&lt;/p&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/145939927-ac72717d-319c-4927-b92c-fc53332cfa0b.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets&quot;&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;import&quot;&gt;Import&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Commnad&lt;/th&gt;
      &lt;th&gt;Method&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;imp&lt;/td&gt;
      &lt;td&gt;import moduleName from ‘module’&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;imr&lt;/td&gt;
      &lt;td&gt;import React from ‘react’&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;impt&lt;/td&gt;
      &lt;td&gt;import PropTypes from ‘prop-types’&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;imrpc&lt;/td&gt;
      &lt;td&gt;import React, { PureComponent } from ‘react’&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;imrpcp&lt;/td&gt;
      &lt;td&gt;import React, { PureComponent } from ‘react’ &amp;amp; import PropTypes from ‘prop-types’&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;component&quot;&gt;Component&lt;/h2&gt;

&lt;h3 id=&quot;rcc&quot;&gt;rcc&lt;/h3&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;React&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FileName&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/div&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;rpcp&quot;&gt;rpcp&lt;/h3&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;React&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PureComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PropTypes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;prop-types&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FileName&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PureComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;propTypes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/div&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 14 Dec 2021 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/react/React-Snippet/</link>
        <guid isPermaLink="true">https://origogi.github.io/react/React-Snippet/</guid>
        
        <category>React</category>
        
        
        <category>React</category>
        
      </item>
    
      <item>
        <title>[React] Debounce, Throttle</title>
        <description>&lt;h2 id=&quot;디바운스와-쓰로틀&quot;&gt;디바운스와 쓰로틀&lt;/h2&gt;

&lt;p&gt;디바운스와 쓰로틀은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;지연 처리&lt;/code&gt; 를 효율적으로 처리하기 위한 알고리즘 혹은 기법입니다. 두 개념은 서버의 데이터를 요청하거나 UI 이벤트를 처리하는 과정에서 작업에서 생기는 부하를 크게 줄여주기에 꼭 알아두는 것이 좋습니다.&lt;/p&gt;

&lt;h3 id=&quot;디바운스&quot;&gt;디바운스&lt;/h3&gt;

&lt;p&gt;디바운스(Debounce) 는 어떤 내용을 입력하다가 특정 시간 동안 대기하고 있으면 마지막에 입력된 내용을 바탕으로 서버 요청을 하는 벙법입니다. 즉 쉽게 말해 짧은 시간(특정 시간) 동안 이벤트가 계속 발생하면 그중 마지막 이벤트를 처리하는 것입니다. 예를 들어 네이버나 구글 검색창에 내용을 입력할 때는 검색창 하단에 아무 내용도 나오지 않다가 입력을 멈추면 검색창 하단에 연관 검색어 목록이 나타납니다. 이것이 디바운스를 적용한 것입니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
   &lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99C4B6335B33302822&quot; /&gt;
&lt;/div&gt;

&lt;h4 id=&quot;code&quot;&gt;Code&lt;/h4&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;inDebounce&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;inDebounce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;clearTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;inDebounce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;inDebounce&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 2초 이후 2 를 출력&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;쓰로틀&quot;&gt;쓰로틀&lt;/h3&gt;

&lt;p&gt;쓰로틀(Throttle) 은 디바운스 개념과 비슷하지만 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;입력되는 동안에도 바로 이전에 요청한 작업을 주기적으로 실행한다는 점&lt;/code&gt; 이 다릅니다. 쉽게 말해 이벤트를 일정 주기마다 한 번씩만 발생하도록 해줍니다. 이벤트가 한 번 실행이 되면 설정한 시간이 자니고 나서야 다음 이벤트를 실행하며 특정 시간 동안 입력된 모든 이벤트들은 마지막 이벤트를 제외한 나머지는 무시하게 됩니다.&lt;/p&gt;

&lt;p&gt;쓰로틀이 적용된 예를 들면 페이스북의 타임라인은 스크롤을 내리는 동안 계속해서 다음 내용이 출력되는 일명 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;무한 스크롤&lt;/code&gt; 기능이 구현되어 있습니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
   &lt;img src=&quot;https://blog.kakaocdn.net/dn/cAAR95/btqB1dhwq7n/YDGBzhgPsxnrKWdKYbMX2K/img.gif&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;만약 사용자가 지연 시간 동안 스크롤을 계속 움직이면 여러번의 서버 요청이 발생합니다. 쓰로틀은 첫 번째 요청이 지연 실행되는 동안에는 중복된 요청을 무시하게 됩니다.&lt;/p&gt;

&lt;h4 id=&quot;code-1&quot;&gt;Code&lt;/h4&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;throttle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastRan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lastRan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;lastRan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lastFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;clearTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lastFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;lastFunc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastRan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;lastRan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;delay&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastRan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;throttle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// a를 바로 출력&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// b를 무시&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 2초 이후 2 를 출력&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 28 Oct 2021 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/react/Debounce-Throttle/</link>
        <guid isPermaLink="true">https://origogi.github.io/react/Debounce-Throttle/</guid>
        
        <category>React</category>
        
        
        <category>React</category>
        
      </item>
    
      <item>
        <title>[Flutter] Amplify 와 Flutter 연동하기 (6) - Google OAuth 인증</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://d2908q01vomqb2.cloudfront.net/7b52009b64fd0a2a49e6d8a939753077792b0554/2021/02/17/Site-Merch_AWS-Amplify-Flutter_SocialMedia_1-1024x536-1.png&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;해당 포스트는 Amplify 의 인증 기능 중 Google 계정으로 OAuth 기능을 어떻게 구현하는지에 대해 정리해보았습니다. 참고로 Facebook 이나 Google OAuth 같은 경우 코드로 구현하기 보다는 설정하는 것이 어렵고 복잡함으로 혹시 따라하시다가 막히는 부분이 있을 시 댓글로 남겨주시기 바랍니다.&lt;/p&gt;

&lt;h2 id=&quot;1-google-앱-생성&quot;&gt;1. Google 앱 생성&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Google Develop console 에 접속합니다.&lt;/li&gt;
  &lt;li&gt;아래 순서대로 프로젝트를 생성합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;471&quot; alt=&quot;iShot2021-07-15 18 24 21&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125764495-9e9a33fa-f229-4da9-8d00-8827a1666021.png&quot; /&gt;
&lt;img width=&quot;848&quot; alt=&quot;iShot2021-07-15 18 24 40&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125764507-44f4b8e5-73f2-4e64-bd99-3c7e3afff54f.png&quot; /&gt;
&lt;img width=&quot;606&quot; alt=&quot;iShot2021-07-15 18 25 02&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125764519-62170aa6-b60d-4b22-8bf2-acb63343b937.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;OAuth 동의화면에 들어가서 만들기를 선택합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;914&quot; alt=&quot;iShot2021-07-15 20 36 09&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125782985-ee0ce476-ffc1-49ff-9e87-7b6cc27b2fb9.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;그 다음 필수 입력란에 앱 정보와 개발자 연락처를 입력하여 설정을 완료합니다.&lt;/li&gt;
  &lt;li&gt;사용자 인증 정보 &amp;gt; +사용자 인증 정보 만들기 &amp;gt; OAuth 클라이언트 ID 를 선택합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;986&quot; alt=&quot;iShot2021-07-15 20 39 35&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125783074-8361673f-f63c-4194-8218-19d767a81a00.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;어플리케이션 타입을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;웹 어플리케이션&lt;/code&gt; 그리고 이름을 입력을 하여 생성합니다. 이 때 OAuth URL 정보를 비워 놓습니다.&lt;/li&gt;
  &lt;li&gt;생성이 완료되면 아래와 같이 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;클라이언트 ID&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;클라이언트 보안 비밀번호&lt;/code&gt; 가 생성이 되며 이 정보를 노트패드에 저장합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;609&quot; alt=&quot;iShot2021-07-15 20 40 37&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125783635-85a98f6d-c269-46ad-b32f-68b8bab7c8ea.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;2-amplfy-auth-설정&quot;&gt;2. Amplfy Auth 설정&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Flutter Project로 돌아가서 Terminal를 실행하고 명령어를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;amplify update auth
// 만약 기존에 auth 서비스를 생성하지 않았다면 amplify add auth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;추가로 아래와 같이 설정을 합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;you want to use the default authentication and security configuration? 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;Default configuration with Social Provider &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Federation&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? How &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;you want &lt;span class=&quot;nb&quot;&gt;users &lt;/span&gt;to be able to sign &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;? 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;Username&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Do you want to configure advanced settings? 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;No, I am &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;.&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? What domain name prefix you want us to create &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;you? 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;default&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Enter your redirect signin URI: 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;myapp://&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Do you want to add another redirect signin URI 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;No&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Enter your redirect signout URI: 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;myapp://&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Do you want to add another redirect signout URI 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;No&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Select the social providers you want to configure &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;your user pool: 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&amp;lt;choose your provider and follow the prompts to input the proper tokens&amp;gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;중간에 Social Provider를 선택을 해야하는데 이때 Google 를 선택합니다. 추가로 Facebook 도 같이 사용한다면 Facebook 도 같이 선택합니다.&lt;br /&gt;
Google 을 선택했을 때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;클라이언트 ID&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;클라이언트 보안 비밀번호&lt;/code&gt; 를 입력해야하며 추가적으로 Facebook 를 
선택했을 경우 추가적으로  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;앱 ID&lt;/code&gt; 와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;시크릿 코드&lt;/code&gt; 를 입력해야 합니다.&lt;br /&gt;
Facebook의 앱 ID, 앱 시크릿 코드 생성 방법은 아래 링크를 참고바랍니다.&lt;br /&gt;
&lt;a href=&quot;https://origogi.github.io/flutter/amplify-5/&quot;&gt;Amplify Facebook OAuth 인증 (5)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;설정이 완료되었다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amplify push&lt;/code&gt; 를 입력하여 Amplify Auth service 를 업데이트 합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-google-앱-업데이트&quot;&gt;3. Google 앱 업데이트&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;구글 콘솔을 다시 엽니다.&lt;/li&gt;
  &lt;li&gt;사용자 인증 정보 에 들어가서 OAuth Client를 수정합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;1052&quot; alt=&quot;iShot2021-07-15 21 15 09&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125786412-7cff144c-fad5-4792-a6c2-0d9f3da9d263.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URI&lt;/code&gt; 와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;승인된 리디렉션 URI&lt;/code&gt; 에 URI 를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;893&quot; alt=&quot;iShot2021-07-15 21 12 54&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125786542-af951df9-c6c7-42e0-8539-fab7f4392b4b.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URI&lt;/code&gt; 에 는 아래 형식의 데이터를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://&amp;lt;your-user-pool-domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;승인된 리디렉션 URI&lt;/code&gt; 에 는 아래 형식의 데이터를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://&amp;lt;your-user-pool-domain&amp;gt;/oauth2/idpresponse
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;저장&lt;/code&gt; 버튼을 눌러 설정을 완료합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;4-flutter-app-설정&quot;&gt;4. Flutter App 설정&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;만약 Facebook 인증 과정에서 이미 설정하였다면 이 과정을 스킵해도 됩니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;안드로이드-설정&quot;&gt;안드로이드 설정&lt;/h3&gt;

&lt;p&gt;android 디렉토리에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnroidMenifest.xml&lt;/code&gt; 에 아래 Activity 정보를 추가합니다.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;activity&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.amazonaws.mobileconnectors.cognitoauth.activities.CustomTabsRedirectActivity&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;intent-filter&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;action&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android.intent.action.VIEW&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class=&quot;nt&quot;&gt;&amp;lt;category&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android.intent.category.DEFAULT&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;category&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android.intent.category.BROWSABLE&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class=&quot;nt&quot;&gt;&amp;lt;data&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:scheme=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;myapp&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;ios-설정&quot;&gt;iOS 설정&lt;/h3&gt;

&lt;p&gt;ios 디렉토리의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Info.plist&lt;/code&gt; 에 아래 정보를 추가합니다.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- YOUR OTHER PLIST ENTRIES HERE --&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ADD AN ENTRY TO CFBundleURLTypes for Cognito Auth --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- IF YOU DO NOT HAVE CFBundleURLTypes, YOU CAN COPY THE WHOLE BLOCK BELOW --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleURLTypes&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleURLSchemes&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;myapp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-flutter-api-구현&quot;&gt;5. Flutter API 구현&lt;/h2&gt;

&lt;p&gt;아래와 같은 화면에 OAuth button을 추가하고&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;

&lt;img width=&quot;30%&quot; alt=&quot;iShot2021-07-15 15 42 01&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125787703-bf884d21-8549-4f99-b95c-507acec85b09.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;만약 사용자가 button을 눌렀을 경우 아래 API를 호출하도록 코드를 추가합니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amplify&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Auth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;signInWithWebUI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;provider:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AuthProvider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;google&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AmplifyException&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;실행화면&quot;&gt;실행화면&lt;/h3&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/125788448-e221592e-e818-417a-aaec-bc43618c83e5.gif&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;총평&quot;&gt;총평&lt;/h2&gt;

&lt;p&gt;이번 시간에는 Amplify와 OAuth provider 중 Google 과 연동하는 방법에 대해 알아 보았습니다.&lt;/p&gt;

&lt;h2 id=&quot;이전-단계&quot;&gt;이전 단계&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://origogi.github.io/flutter/amplify-5/&quot;&gt;Amplify  Facebook OAuth 인증 (5)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.amplify.aws/start/getting-started/installation/q/integration/flutter&quot;&gt;AWS Amplify for Flutter&lt;/a&gt;
-&lt;a href=&quot;https://docs.amplify.aws/lib/auth/social_signin_web_ui/q/platform/flutter#amazon-cognito-user-pool-setup&quot;&gt;Social sign in with web UI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 15 Jul 2021 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/flutter/amplify-6/</link>
        <guid isPermaLink="true">https://origogi.github.io/flutter/amplify-6/</guid>
        
        <category>Flutter</category>
        
        <category>Dart</category>
        
        <category>Amplify</category>
        
        
        <category>Flutter</category>
        
      </item>
    
      <item>
        <title>[Flutter] Amplify 와 Flutter 연동하기 (5) - Facebook OAuth 인증</title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://d2908q01vomqb2.cloudfront.net/7b52009b64fd0a2a49e6d8a939753077792b0554/2021/02/17/Site-Merch_AWS-Amplify-Flutter_SocialMedia_1-1024x536-1.png&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;해당 포스트는 Amplify 의 인증 기능 중 Facebook 계정으로 OAuth 기능을 어떻게 구현하는지에 대해 정리해보았습니다. 참고로 Facebook 이나 Google OAuth 같은 경우 코드로 구현하기 보다는 설정하는 것이 어렵고 복잡함으로 혹시 따라하시다가 막히는 부분이 있을 시 댓글로 남겨주시기 바랍니다.&lt;/p&gt;

&lt;h2 id=&quot;1-facebook-앱-생성&quot;&gt;1. Facebook 앱 생성&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Facebook developer 계정을 생성합니다.&lt;/li&gt;
  &lt;li&gt;로그인 후 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;내 앱&lt;/code&gt; 을 선택합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;1169&quot; alt=&quot;iShot2021-07-15 13 58 52&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125731232-3ddb90bc-fb32-4e5e-bfd0-944e3863efb8.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;앱 만들기&lt;/code&gt; 를 선택하여 앱을 생성합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;838&quot; alt=&quot;iShot2021-07-15 14 01 30&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125731497-5366b284-54e2-47a9-b675-09a3dfafaf0b.png&quot; /&gt;
&lt;img width=&quot;838&quot; alt=&quot;iShot2021-07-15 14 01 57&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125731502-9cd281f4-2783-4c9f-b6df-6090ae590032.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;설정 &amp;gt; 기본 설정에서 해당 App의 앱 ID, 앱 시크릿코드 를 메모장이나 노트패드에 저장합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;1160&quot; alt=&quot;iShot2021-07-15 14 05 07&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125731721-df2b253e-2f81-4f97-a615-a4a819acaed7.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;2-amplfy-auth-설정&quot;&gt;2. Amplfy Auth 설정&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Flutter Project로 돌아가서 Terminal를 실행하고 명령어를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;amplify update auth
// 만약 기존에 auth 서비스를 생성하지 않았다면 amplify add auth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;추가로 아래와 같이 설정을 합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;you want to use the default authentication and security configuration? 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;Default configuration with Social Provider &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Federation&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? How &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;you want &lt;span class=&quot;nb&quot;&gt;users &lt;/span&gt;to be able to sign &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;? 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;Username&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Do you want to configure advanced settings? 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;No, I am &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;.&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? What domain name prefix you want us to create &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;you? 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;default&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Enter your redirect signin URI: 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;myapp://&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Do you want to add another redirect signin URI 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;No&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Enter your redirect signout URI: 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;myapp://&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Do you want to add another redirect signout URI 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;No&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
? Select the social providers you want to configure &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;your user pool: 
    &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&amp;lt;choose your provider and follow the prompts to input the proper tokens&amp;gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;중간에 social Provider를 선택을 해야하는데 이때 Facebook를 선택합니다.&lt;br /&gt;
그리고 앱 ID와 시크릿 코드를 물어보게 되면 미리 메모장에 저장 한 값을 입력합니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;설정이 완료되었다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amplify push&lt;/code&gt; 를 입력하여 Amplify Auth service 를 업데이트 합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-facebook-앱-업데이트&quot;&gt;3. Facebook 앱 업데이트&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;설정 &amp;gt; 기본 설정에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;앱 도메인&lt;/code&gt; 에 Cognito user pool 도메인 주소를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;819&quot; alt=&quot;iShot2021-07-15 15 12 18&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125737836-872a3b70-082a-4e9a-b50b-921d9ca01157.png&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://&amp;lt;your-user-pool-domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Dashboard에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Facebook 로그인&lt;/code&gt; 의 설정을 클릭합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;1310&quot; alt=&quot;iShot2021-07-15 14 11 36&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125732239-a4d9629c-0c05-4eb6-bcf1-e3f3c396b872.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;웹을 선택합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;955&quot; alt=&quot;iShot2021-07-15 14 12 34&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125732676-fc162f4c-40fe-475c-8844-b6dd5d3cf726.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Site Urls 입력창에 아래 형식의 주소를 입력하고 저장을 합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;943&quot; alt=&quot;iShot2021-07-15 14 28 30&quot; src=&quot;https://docs.amplify.aws/images/cognitoHostedUI/facebook5.png&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://&amp;lt;your-user-pool-domain&amp;gt;/oauth2/idpresponse
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Facebook 로그인 &amp;gt; 빠른 설정  에 오게 되면 빨간 경고 창이 뜨고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Get Advanced Access&lt;/code&gt; 를 선택합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;968&quot; alt=&quot;iShot2021-07-15 14 14 07&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125732743-cbb0e7e7-943b-4579-a443-acbb861722d1.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;public profile 를 활성화합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;959&quot; alt=&quot;iShot2021-07-15 14 14 35&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125732848-957c5b2d-15fb-4d5c-8b4c-8eda7a3082cc.png&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Dashboard로 돌아가 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;유효한 OAuth 리디렉션 URI&lt;/code&gt; 에 아래와 같이 URL를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img width=&quot;1168&quot; alt=&quot;iShot2021-07-15 15 07 46&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125737362-7a34bf72-ddc7-4bd5-8a32-77945a9c0fcf.png&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://&amp;lt;your-user-pool-domain&amp;gt;/oauth2/idpresponse
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;4-flutter-app-설정&quot;&gt;4. Flutter App 설정&lt;/h2&gt;

&lt;h3 id=&quot;안드로이드-설정&quot;&gt;안드로이드 설정&lt;/h3&gt;

&lt;p&gt;android 디렉토리에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnroidMenifest.xml&lt;/code&gt; 에 아래 Activity 정보를 추가합니다.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;activity&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.amazonaws.mobileconnectors.cognitoauth.activities.CustomTabsRedirectActivity&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;intent-filter&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;action&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android.intent.action.VIEW&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class=&quot;nt&quot;&gt;&amp;lt;category&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android.intent.category.DEFAULT&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;category&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android.intent.category.BROWSABLE&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class=&quot;nt&quot;&gt;&amp;lt;data&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:scheme=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;myapp&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;ios-설정&quot;&gt;iOS 설정&lt;/h3&gt;

&lt;p&gt;ios 디렉토리의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Info.plist&lt;/code&gt; 에 아래 정보를 추가합니다.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- YOUR OTHER PLIST ENTRIES HERE --&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ADD AN ENTRY TO CFBundleURLTypes for Cognito Auth --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- IF YOU DO NOT HAVE CFBundleURLTypes, YOU CAN COPY THE WHOLE BLOCK BELOW --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleURLTypes&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleURLSchemes&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;myapp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-flutter-설정&quot;&gt;5. Flutter 설정&lt;/h2&gt;

&lt;p&gt;아래와 같은 화면에 OAuth button을 추가하고&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;

&lt;img width=&quot;30%&quot; alt=&quot;iShot2021-07-15 15 42 01&quot; src=&quot;https://user-images.githubusercontent.com/35194820/125743061-f746910b-5ba9-4c3b-b747-ba2a42090d15.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;만약 사용자가 button을 눌렀을 경우 아래 API를 호출하도록 코드를 추가합니다.&lt;/p&gt;

&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amplify&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Auth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;signInWithWebUI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;provider:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AuthProvider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;facebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AmplifyException&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;실행화면&quot;&gt;실행화면&lt;/h3&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/125742864-274092c5-8219-4a0f-aee8-227294171546.gif&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;총평&quot;&gt;총평&lt;/h2&gt;

&lt;p&gt;이번 시간에는 Amplify와 OAuth provider 중 Facebook 과 연동하는 방법에 대해 알아 보았으며 다음 포스트에는 Google과 연동하는 방법에 대해 알아보도록 하겠습니다.&lt;/p&gt;

&lt;h2 id=&quot;이전-단계&quot;&gt;이전 단계&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://origogi.github.io/flutter/amplify-4/&quot;&gt;Amplify Log in/Out 구현 (4)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;다음-단계&quot;&gt;다음 단계&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://origogi.github.io/flutter/amplify-6/&quot;&gt;Amplify Google OAuth 인증 (6)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.amplify.aws/start/getting-started/installation/q/integration/flutter&quot;&gt;AWS Amplify for Flutter&lt;/a&gt;
-&lt;a href=&quot;https://docs.amplify.aws/lib/auth/social_signin_web_ui/q/platform/flutter#amazon-cognito-user-pool-setup&quot;&gt;Social sign in with web UI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 15 Jul 2021 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/flutter/amplify-5/</link>
        <guid isPermaLink="true">https://origogi.github.io/flutter/amplify-5/</guid>
        
        <category>Flutter</category>
        
        <category>Dart</category>
        
        <category>Amplify</category>
        
        
        <category>Flutter</category>
        
      </item>
    
      <item>
        <title>[Android] Dark mode 와 Light mode 에 대해 Status bar 색상 변경 </title>
        <description>&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJL9vl/btqOhu7VvOD/VA8LKZQOeQyXyXKMRTmjH1/img.png&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;보통 앱을 개발하게 되면 App bar 의 색상과 Status bar의 색상을 서로 일치하고자 하는 니즈가 생기게 되는데요 왜냐하면 일반적으로 서로 색상이 서로 일치하게 되면 앱의 UI 가 좀 더 깔끔하게 보여지기 때문입니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/125153938-1cd30b80-e192-11eb-9ce6-a24196753be8.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;따라서 Light mode 와 Dark mode 일 경우 Status bar 를 어떻게 셋팅하는지 에 대해 알아보도록 하겠습니다.&lt;/p&gt;

&lt;h2 id=&quot;light-mode-theme&quot;&gt;Light Mode Theme&lt;/h2&gt;

&lt;p&gt;Status bar 의 색상을 변경하기 위해서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;res/values/theme.xml&lt;/code&gt; 를 수정해야 합니다.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;resources&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:tools=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.android.com/tools&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  ...
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;item&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android:statusBarColor&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;tools:targetApi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;@color/white&lt;span class=&quot;nt&quot;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  ...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/resources&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;그 중 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:statusBarColor&lt;/code&gt; 를 App bar와 일치하는 색상으로 변경하면 됩니다. 하지만 만약 색상을 White로 셋팅하는 경우 statusBar text 색상이 White로 나타내기 때문에 아래와 같이 보이게 됩니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/125154100-4e98a200-e193-11eb-921d-6a795ff01737.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;따라서 위와 같이 Status 정보가 아무것도 보이지 않는 문제가 발생합니다.&lt;/p&gt;

&lt;p&gt;위 문제를 해결하기 위해 아래와 같이 xml 에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:windowLightStatusBar&lt;/code&gt; 라는 새로운 값을 추가 합니다.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;resources&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:tools=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.android.com/tools&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  ...
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;item&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android:statusBarColor&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;tools:targetApi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;@color/white&lt;span class=&quot;nt&quot;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;item&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android:windowLightStatusBar&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;tools:targetApi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  ...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/resources&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:windowLightStatusBar&lt;/code&gt; 값을 true 로 set 하는 경우 status bar text 색이 black 으로 노출이 되어 아래와 같이 정상적으로 Status 정보가 노출이되는 것을 확인할 수 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;dark-mode-theme&quot;&gt;Dark Mode Theme&lt;/h2&gt;

&lt;p&gt;Dark mode 일 경우 App bar의 색상을 Black으로 하고 싶은 경우 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;res/values/theme.xml (night)&lt;/code&gt; 를 수정해야 합니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/125154304-560c7b00-e194-11eb-863b-fb4b30bea95c.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;그리고 theme.xml 의 값을 아래와 같이 수정합니다.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;resources&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:tools=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://schemas.android.com/tools&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  ...
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;item&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android:statusBarColor&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;tools:targetApi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;l&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;@color/black&lt;span class=&quot;nt&quot;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;item&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android:windowLightStatusBar&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;tools:targetApi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;m&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  ...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/resources&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;그리고 설정에서 Dark mode 를 적용하게 되면 아래와 같이 화면이 보이게 됩니다.&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/35194820/125154469-25791100-e195-11eb-93f4-19b6b37ca180.png&quot; /&gt;
&lt;/div&gt;
</description>
        <pubDate>Sat, 10 Jul 2021 00:00:00 +0000</pubDate>
        <link>https://origogi.github.io/android/dark-mode/</link>
        <guid isPermaLink="true">https://origogi.github.io/android/dark-mode/</guid>
        
        <category>Android</category>
        
        
        <category>Android</category>
        
      </item>
    
  </channel>
</rss>