<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://daeyoungee.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://daeyoungee.github.io/" rel="alternate" type="text/html" /><updated>2024-07-10T08:57:10+00:00</updated><id>https://daeyoungee.github.io/feed.xml</id><title type="html">대영 블로그</title><subtitle>대영이 블로그입니다.</subtitle><author><name>DaeYoung</name></author><entry><title type="html">[Kotlin ] Koltin sorteBy(), sortedWith()를 이용한 정렬</title><link href="https://daeyoungee.github.io/kotlin/sort/" rel="alternate" type="text/html" title="[Kotlin ] Koltin sorteBy(), sortedWith()를 이용한 정렬" /><published>2024-07-07T00:00:00+00:00</published><updated>2024-07-10T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/sort</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/sort/"><![CDATA[<p>kotlin의 collection에서 정렬 기능을 제공하는 메서드는 무수히 많다. 이 메서드에 대해 하나씩 알아보자.<br />
ex) <code class="language-plaintext highlighter-rouge">sort()</code>, <code class="language-plaintext highlighter-rouge">sorted()</code>, <code class="language-plaintext highlighter-rouge">sortedBy{}</code>, <code class="language-plaintext highlighter-rouge">sortBy{}</code>, <code class="language-plaintext highlighter-rouge">sortedDescending()</code>, <code class="language-plaintext highlighter-rouge">sortedByDescending()</code>, <code class="language-plaintext highlighter-rouge">sortedWith()</code></p>

<p>기본적으로 <code class="language-plaintext highlighter-rouge">sort()</code> 메서드를 사용하면 오름차순 정렬이된다. 내림차순 정렬을 하고 싶으면 <code class="language-plaintext highlighter-rouge">sortDescending()</code> 메서드를 사용하면된다.</p>

<h3 id="sort-vs-sorted">sort() VS sorted()</h3>

<p>먼저 <code class="language-plaintext highlighter-rouge">sort()</code>와 <code class="language-plaintext highlighter-rouge">sorted()</code>의 차이를 알아보자.</p>

<p>이 둘의 차이는 기존의 배열을 건드리냐 아니냐의 차이가 있다. <code class="language-plaintext highlighter-rouge">sort()</code>는 기존의 배열을 오름차순으로 정렬한 것이다. 기존의 배열을 정렬했기에 반환타입은 Unit이 된다.</p>

<p>아래의 코드를 보면 이해가 갈 것이다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">arr1</span> <span class="p">=</span> <span class="nf">intArrayOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">arr2</span> <span class="p">=</span> <span class="nf">intArrayOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>


    <span class="nf">println</span><span class="p">(</span><span class="s">"arr1: ${arr1.contentToString()}"</span><span class="p">)</span> <span class="c1">// 1, 3, 4, 2, 5</span>

    <span class="n">arr1</span><span class="p">.</span><span class="nf">sort</span><span class="p">()</span>

    <span class="nf">println</span><span class="p">(</span><span class="s">"arr1: ${arr1.contentToString()}"</span><span class="p">)</span> <span class="c1">// 1, 2, 3, 4, 5</span>

    <span class="kd">val</span> <span class="py">arr3</span> <span class="p">=</span> <span class="n">arr2</span><span class="p">.</span><span class="nf">sorted</span><span class="p">()</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr2: ${arr2.contentToString()}"</span><span class="p">)</span> <span class="c1">// 1, 3, 4, 2, 5</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr3: ${arr3}"</span><span class="p">)</span> <span class="c1">// 1, 2, 3, 4, 5</span>
<span class="p">}</span>
</code></pre></div></div>

<p><br /></p>

<p>내림차순으로 정렬하고 싶은 경우 <code class="language-plaintext highlighter-rouge">sortDescending()</code>와 <code class="language-plaintext highlighter-rouge">sortedDescending()</code>를 사용해보자.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">arr1</span> <span class="p">=</span> <span class="nf">intArrayOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">arr2</span> <span class="p">=</span> <span class="nf">intArrayOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>

    <span class="nf">println</span><span class="p">(</span><span class="s">"arr1: ${arr1.contentToString()}"</span><span class="p">)</span> <span class="c1">// 1, 3, 4, 2, 5</span>

    <span class="n">arr1</span><span class="p">.</span><span class="nf">sortDescending</span><span class="p">()</span>

    <span class="nf">println</span><span class="p">(</span><span class="s">"arr1: ${arr1.contentToString()}"</span><span class="p">)</span> <span class="c1">// 5, 4, 3, 2, 1</span>

    <span class="kd">val</span> <span class="py">arr3</span> <span class="p">=</span> <span class="n">arr2</span><span class="p">.</span><span class="nf">sortedDescending</span><span class="p">()</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr2: ${arr2.contentToString()}"</span><span class="p">)</span> <span class="c1">// 1, 3, 4, 2, 5</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr3: ${arr3}"</span><span class="p">)</span> <span class="c1">// 5, 4, 3, 2, 1</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="sortby-vs-sortedby">sortBy{} VS sortedBy{}</h3>

<p><code class="language-plaintext highlighter-rouge">sortBy{}</code>는 기존 배열의 손대지 않고 기존 배열에서 정렬된 새로운 List를 반환한다.<br />
<code class="language-plaintext highlighter-rouge">sortedBy{}</code>는 기존의 배열을 정렬히시키며 Unit을 반환한다.</p>

<p><code class="language-plaintext highlighter-rouge">sortBy{}</code>는 <code class="language-plaintext highlighter-rouge">sort()</code>와 달리 특정 클래스나 컬렉션의 프로퍼티를 기준으로 정렬할 수 있다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">arr1</span> <span class="p">=</span> <span class="nf">arrayOf</span><span class="p">(</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"J"</span><span class="p">,</span> <span class="mi">25</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"K"</span><span class="p">,</span> <span class="mi">20</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"L"</span><span class="p">,</span> <span class="mi">22</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"M"</span><span class="p">,</span> <span class="mi">21</span><span class="p">)</span>
    <span class="p">)</span>
    <span class="kd">val</span> <span class="py">arr2</span> <span class="p">=</span> <span class="nf">arrayOf</span><span class="p">(</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"J"</span><span class="p">,</span> <span class="mi">25</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"K"</span><span class="p">,</span> <span class="mi">20</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"L"</span><span class="p">,</span> <span class="mi">22</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"M"</span><span class="p">,</span> <span class="mi">21</span><span class="p">)</span>
    <span class="p">)</span>


    <span class="nf">println</span><span class="p">(</span><span class="s">"arr1: ${arr1.contentToString()}"</span><span class="p">)</span>
    <span class="c1">// Person(name=J, age=25), Person(name=K, age=20), Person(name=L, age=22), Person(name=M, age=21)</span>

    <span class="n">arr1</span><span class="p">.</span><span class="nf">sortBy</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">age</span> <span class="p">}</span> <span class="c1">// age 프로퍼티를 기준으로 오름차순 정렬</span>

    <span class="nf">println</span><span class="p">(</span><span class="s">"arr1: ${arr1.contentToString()}"</span><span class="p">)</span>
    <span class="c1">// Person(name=K, age=20), Person(name=M, age=21), Person(name=L, age=22), Person(name=J, age=25)</span>

    <span class="kd">val</span> <span class="py">arr3</span> <span class="p">=</span> <span class="n">arr2</span><span class="p">.</span><span class="nf">sortedBy</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">age</span> <span class="p">}</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr2: ${arr2.contentToString()}"</span><span class="p">)</span>
    <span class="c1">// Person(name=J, age=25), Person(name=K, age=20), Person(name=L, age=22), Person(name=M, age=21)</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr3: ${arr3}"</span><span class="p">)</span>
    <span class="c1">// Person(name=K, age=20), Person(name=M, age=21), Person(name=L, age=22), Person(name=J, age=25)</span>
<span class="p">}</span>

</code></pre></div></div>

<p>위에서의 예제는 data class의 프로퍼티를 기준으로 정렬했지만 <strong>Map</strong>이나 <strong>Pair</strong>의 first나 second로도 정렬할 수 있다.<br />
<code class="language-plaintext highlighter-rouge">sortBy{}</code> 또한 마찬가지로 내림차순 정렬을 하고 싶은 경우 <code class="language-plaintext highlighter-rouge">sortByDescending()</code>, <code class="language-plaintext highlighter-rouge">sortedByDescending()</code>를 사용하면 된다.</p>

<h3 id="sortedwith">sortedWith()</h3>

<p>만약 두 개 이상의 프로퍼티로 기준을 정해서 정렬을 하게 되는 경우 <code class="language-plaintext highlighter-rouge">sortedWith()</code>를 사용하게 된다.
<code class="language-plaintext highlighter-rouge">thenBy{}</code>, <code class="language-plaintext highlighter-rouge">thenByDescending{}</code>를 이용해서 오름차순 또는 내림차순 정렬을 한다.<br />
<code class="language-plaintext highlighter-rouge">reversed()</code> 메서드를 통해서 오름차순 정렬을 내림차순 정렬로 사용할 수 있다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">val</span> <span class="py">arr1</span> <span class="p">=</span> <span class="nf">arrayOf</span><span class="p">(</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"J"</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">170</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"K"</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">160</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"L"</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">180</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"M"</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">174</span><span class="p">),</span>
        <span class="nc">Person</span><span class="p">(</span><span class="s">"A"</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">168</span><span class="p">)</span>
    <span class="p">)</span>

    <span class="nf">println</span><span class="p">(</span><span class="s">"arr1: ${arr1.contentToString()}"</span><span class="p">)</span>
    <span class="c1">// Person(name=J, age=25), Person(name=K, age=20), Person(name=L, age=22), Person(name=M, age=21)</span>

    <span class="kd">val</span> <span class="py">arr3</span> <span class="p">=</span> <span class="n">arr1</span><span class="p">.</span><span class="nf">sortedWith</span><span class="p">(</span><span class="nf">compareBy</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">age</span> <span class="p">})</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr3: $arr3"</span><span class="p">)</span>
    <span class="c1">// Person(name=K, age=20), Person(name=M, age=21), Person(name=A, age=21), Person(name=L, age=22), Person(name=J, age=25)</span>

<span class="c1">//  val arr4 = arr1.sortedWith(compareBy({it.age}, {it.name}))</span>
    <span class="kd">val</span> <span class="py">arr4</span> <span class="p">=</span> <span class="n">arr1</span><span class="p">.</span><span class="nf">sortedWith</span><span class="p">(</span><span class="n">compareBy</span><span class="p">&lt;</span><span class="nc">Person</span><span class="p">&gt;{</span><span class="n">it</span><span class="p">.</span><span class="n">age</span><span class="p">}.</span><span class="nf">thenBy</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">name</span> <span class="p">})</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr4: $arr4"</span><span class="p">)</span>
    <span class="c1">// Person(name=K, age=20), Person(name=A, age=21), Person(name=M, age=21), Person(name=L, age=22), Person(name=J, age=25)</span>

    <span class="kd">val</span> <span class="py">arr5</span> <span class="p">=</span> <span class="n">arr1</span><span class="p">.</span><span class="nf">sortedWith</span><span class="p">(</span><span class="n">compareBy</span><span class="p">&lt;</span><span class="nc">Person</span><span class="p">&gt;{</span><span class="n">it</span><span class="p">.</span><span class="n">age</span><span class="p">}.</span><span class="nf">thenByDescending</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">height</span> <span class="p">})</span>
    <span class="kd">val</span> <span class="py">arr6</span> <span class="p">=</span> <span class="n">arr1</span><span class="p">.</span><span class="nf">sortedWith</span><span class="p">(</span><span class="nf">compareBy</span><span class="p">({</span><span class="n">it</span><span class="p">.</span><span class="n">age</span><span class="p">},</span> <span class="p">{</span> <span class="p">-</span><span class="n">it</span><span class="p">.</span><span class="n">height</span> <span class="p">}))</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr5: $arr5"</span><span class="p">)</span>
    <span class="c1">// Person(name=K, age=20, height=160), Person(name=M, age=21, height=174), Person(name=A, age=21, height=168), Person(name=L, age=22, height=180), Person(name=J, age=25, height=170)</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr6: $arr6"</span><span class="p">)</span>
    <span class="c1">// Person(name=K, age=20, height=160), Person(name=M, age=21, height=174), Person(name=A, age=21, height=168), Person(name=L, age=22, height=180), Person(name=J, age=25, height=170)</span>

    <span class="kd">val</span> <span class="py">arr7</span> <span class="p">=</span> <span class="n">arr1</span><span class="p">.</span><span class="nf">sortedWith</span><span class="p">(</span><span class="n">compareBy</span><span class="p">&lt;</span><span class="nc">Person</span><span class="p">&gt;{</span><span class="n">it</span><span class="p">.</span><span class="n">age</span><span class="p">}.</span><span class="nf">reversed</span><span class="p">().</span><span class="nf">thenBy</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">height</span> <span class="p">})</span>
    <span class="nf">println</span><span class="p">(</span><span class="s">"arr7: $arr7"</span><span class="p">)</span>
<span class="p">}</span>


</code></pre></div></div>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="method" /><summary type="html"><![CDATA[Kotlin에서 제공하는 정렬 메서드에 대해 알아보자.]]></summary></entry><entry><title type="html">[Kotlin] Canvas 꺾은선 그래프 그리기(1), drawLine과 drawText</title><link href="https://daeyoungee.github.io/kotlin/Canvas(3)/" rel="alternate" type="text/html" title="[Kotlin] Canvas 꺾은선 그래프 그리기(1), drawLine과 drawText" /><published>2024-06-19T00:00:00+00:00</published><updated>2024-06-19T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/Canvas(3)</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/Canvas(3)/"><![CDATA[<p>Canvas로 꺾은선 그래프를 그리기 전에 먼저 <code class="language-plaintext highlighter-rouge">drawLine()</code> 함수를 알아야 한다.<br />
아래의 사진은 <code class="language-plaintext highlighter-rouge">drawLine()</code>을 이용해 그린 꺾은선 그래프이다.</p>

<p><img width="240" alt="Screenshot 2024-06-19 at 2 45 10 PM" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/c7e1294e-78af-4e3a-93b8-a7a68dbd9e07" /></p>

<h2 id="drawline">drawLine()</h2>

<p><strong><span style="font-size: 18">drawLine()의 기본 매개변수 설명</span></strong></p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Canvas</span><span class="p">(</span><span class="n">modifier</span> <span class="p">=</span> <span class="nc">Modifier</span><span class="p">.</span><span class="nf">fillMaxWidth</span><span class="p">().</span><span class="nf">aspectRatio</span><span class="p">(</span><span class="mf">1f</span><span class="p">))</span> <span class="p">{</span>
    <span class="nf">drawLine</span><span class="p">(</span>
        <span class="n">color</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">(</span><span class="mh">0xFF0C964A</span><span class="p">),</span>
        <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="mf">0f</span><span class="p">,</span> <span class="mf">0f</span><span class="p">),</span>
        <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">size</span><span class="p">.</span><span class="n">width</span><span class="p">/</span><span class="mi">2</span><span class="p">,</span> <span class="mf">0f</span> <span class="p">),</span>
        <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">20f</span>
    <span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img width="240" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/530463f9-aa19-41a6-8343-6707ba63236a" /></p>

<p>start 매개변수는 선의 시작점,<br />
end 매개변수는 선의 끝점이다.<br />
strokeWidth는 선의 두께를 결정한다.</p>

<p><strong><span style="font-size: 18">선끝 모양 변경</span></strong></p>

<p>cap 매개변수를 이용해 둥근 선을 만들 수 있다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Canvas</span><span class="p">(</span><span class="n">modifier</span> <span class="p">=</span> <span class="nc">Modifier</span><span class="p">.</span><span class="nf">fillMaxWidth</span><span class="p">().</span><span class="nf">aspectRatio</span><span class="p">(</span><span class="mf">1f</span><span class="p">))</span> <span class="p">{</span>
    <span class="nf">drawLine</span><span class="p">(</span>
        <span class="n">color</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">(</span><span class="mh">0xFF0C964A</span><span class="p">),</span>
        <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="mf">0f</span><span class="p">,</span> <span class="mf">0f</span><span class="p">),</span>
        <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">size</span><span class="p">.</span><span class="n">width</span><span class="p">/</span><span class="mi">2</span><span class="p">,</span> <span class="mf">0f</span> <span class="p">),</span>
        <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">20f</span><span class="p">,</span>
        <span class="n">cap</span> <span class="p">=</span> <span class="nc">StrokeCap</span><span class="p">.</span><span class="nc">Round</span>
    <span class="p">)</span>
<span class="p">}</span>

</code></pre></div></div>

<p><img width="240" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/a4e1263d-aa86-4cf4-8dd3-5e70a4e5128c" /></p>

<p><strong>&lt;div style="font-size: 18"&gt;점선&lt;/div&gt;</strong>
pathEffect를 통해 점선으로 화면에 나타낼 수 있다. <code class="language-plaintext highlighter-rouge">dashPathEffect()</code>의 첫번 째 매개변수의 크기를 조절하면 점선의 간격을 조절할 수 있다. 아래의 사진을 보면 이해가 빠를것 이다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Canvas</span><span class="p">(</span> <span class="n">modifier</span> <span class="p">=</span> <span class="nc">Modifier</span><span class="p">.</span><span class="nf">fillMaxWidth</span><span class="p">().</span><span class="nf">aspectRatio</span><span class="p">(</span><span class="mf">1f</span><span class="p">)</span>
<span class="p">)</span> <span class="p">{</span>
    <span class="nf">drawLine</span><span class="p">(</span>
        <span class="n">color</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">(</span><span class="mh">0xFF0C964A</span><span class="p">),</span>
        <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="mf">0f</span><span class="p">,</span> <span class="mf">0f</span><span class="p">),</span>
        <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">size</span><span class="p">.</span><span class="n">width</span> <span class="p">/</span> <span class="mi">2</span><span class="p">,</span> <span class="mf">0f</span><span class="p">),</span>
        <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">20f</span><span class="p">,</span>
        <span class="n">cap</span> <span class="p">=</span> <span class="nc">StrokeCap</span><span class="p">.</span><span class="nc">Round</span><span class="p">,</span>
        <span class="n">pathEffect</span> <span class="p">=</span> <span class="nc">PathEffect</span><span class="p">.</span><span class="nf">dashPathEffect</span><span class="p">(</span><span class="nf">floatArrayOf</span><span class="p">(</span><span class="mf">10f</span><span class="p">,</span> <span class="mf">40f</span><span class="p">),</span> <span class="mf">0f</span><span class="p">),</span>
<span class="c1">//      pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 20f), 0f)</span>
    <span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>
    <p>40f인 경우<br />
<img width="240" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/220d3990-bbce-4189-94dc-e7caeed22170" /></p>
  </li>
  <li>
    <p>20f인 경우<br />
<img width="240" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/95f64d79-ceae-4670-a304-fda00456feb8" /></p>
  </li>
</ul>

<h2 id="drawtext">drawText()</h2>

<p>Canvas내에서 text를 ui에 나타내고 싶을 때 사용하는 함수이다.<br />
drawText()를 사용하기 위해서는 <code class="language-plaintext highlighter-rouge">TextMeasurer</code>, <code class="language-plaintext highlighter-rouge">TextLayoutResult</code> 객체가 필요하다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">textMeasurer</span> <span class="p">=</span> <span class="nf">rememberTextMeasurer</span><span class="p">()</span>
  <span class="kd">val</span> <span class="py">text</span> <span class="p">=</span> <span class="s">"Hello"</span>
  <span class="kd">val</span> <span class="py">textLayoutResult</span> <span class="p">=</span> <span class="nf">remember</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">textMeasurer</span><span class="p">.</span><span class="nf">measure</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
  <span class="p">}</span>
  <span class="nc">Canvas</span><span class="p">(</span>
      <span class="n">modifier</span> <span class="p">=</span> <span class="nc">Modifier</span>
          <span class="p">.</span><span class="nf">fillMaxWidth</span><span class="p">()</span>
          <span class="p">.</span><span class="nf">aspectRatio</span><span class="p">(</span><span class="mf">1f</span><span class="p">)</span>
  <span class="p">)</span> <span class="p">{</span>
      <span class="nf">drawLine</span><span class="p">(</span> <span class="o">..</span> <span class="p">)</span>
      <span class="nf">drawText</span><span class="p">(</span>
          <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">textLayoutResult</span><span class="p">,</span>
          <span class="n">color</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">Black</span><span class="p">,</span>
          <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">size</span><span class="p">.</span><span class="n">width</span> <span class="p">/</span> <span class="mi">2</span><span class="p">,</span> <span class="mf">0f</span><span class="p">)</span>
      <span class="p">)</span>
  <span class="p">}</span>
</code></pre></div></div>

<p><img width="480" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/a45908b1-1be6-465c-938a-f968a411384d" /></p>

<p>drawText는 좌상단의 빨간점을 기준으로 Offset를 결정한다. 빨간점은 이해하기 쉽도록 임의로 표시했다.</p>

<p>만약 drawText의 위치를 가운데 정렬하거나 우측 끝에 맞추고 싶다면 다음과 같이 코드를 작성하면된다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">drawText</span><span class="p">(</span>
    <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">textLayoutResult</span><span class="p">,</span>
    <span class="n">color</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">Black</span><span class="p">,</span>
    <span class="c1">// 가운데 정렬</span>
    <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">size</span><span class="p">.</span><span class="n">width</span><span class="p">/</span><span class="mi">2</span> <span class="p">-</span> <span class="n">textLayoutResult</span><span class="p">.</span><span class="n">size</span><span class="p">.</span><span class="n">width</span><span class="p">/</span><span class="mi">2</span> <span class="p">,</span> <span class="mf">0f</span><span class="p">)</span>
    <span class="c1">// 우측 정렬</span>
    <span class="c1">// topLeft = Offset(size.width/2 - textLayoutResult.size.width , 0f)</span>
<span class="p">)</span>
</code></pre></div></div>

<ul>
  <li>
    <p>가운데 정렬<br />
<img width="240" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/5af81743-c720-49fb-959a-859e11b9d6f3" /></p>
  </li>
  <li>
    <p>우측 정렬<br />
<img width="240" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/b6edcf50-4226-40a4-974f-6e93aeea8762" /></p>
  </li>
</ul>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="Compose" /><category term="Canvas" /><summary type="html"><![CDATA[Compose에서 Canvas를 이용해 꺾은선 그래프를 그리기 위해 drawLine과 drawText을 알아보자]]></summary></entry><entry><title type="html">[Kotlin] Canvas 꺾은선 그래프 그리기(2)</title><link href="https://daeyoungee.github.io/kotlin/Canvas(4)/" rel="alternate" type="text/html" title="[Kotlin] Canvas 꺾은선 그래프 그리기(2)" /><published>2024-06-19T00:00:00+00:00</published><updated>2024-06-19T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/Canvas(4)</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/Canvas(4)/"><![CDATA[<p>이번 포스팅은 앞서 설명했던 <code class="language-plaintext highlighter-rouge">drawLine()</code>, <code class="language-plaintext highlighter-rouge">drawText()</code>를 이용해 꺾은선그래프를 그리는 과정을 담은 part이다.<br />
<code class="language-plaintext highlighter-rouge">drawLine()</code>, <code class="language-plaintext highlighter-rouge">drawText()</code>에 미숙하거나 잘 모르겠다면 <a href="https://daeyoungee.github.io/kotlin/Canvas(3)/">여기</a>를 클릭해서 학습하고 오자.</p>

<p><img width="480" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/fa49704f-4081-4346-a0f4-949ac7133e50" /></p>

<p>위의 그래프를 얻을 수 있다.</p>

<p>DrawScope의 확장함수를 별도로 만들어서 가독성을 높였다. 별도로 만들어진 확장함수를 차례대로 살펴보겠다.
<strong>(baseLine -&gt; baseLineText -&gt; dataLine -&gt; 전체코드)</strong></p>

<h3 id="baseline">baseLine()</h3>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nc">DrawScope</span><span class="p">.</span><span class="nf">baseLine</span><span class="p">(</span><span class="n">color</span><span class="p">:</span> <span class="nc">Color</span><span class="p">,</span> <span class="n">startX</span><span class="p">:</span> <span class="nc">Float</span><span class="p">,</span> <span class="n">endX</span><span class="p">:</span> <span class="nc">Float</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">drawLine</span><span class="p">(</span>
        <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
        <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">startX</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
        <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">endX</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
        <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">2f</span>
    <span class="p">)</span>
    <span class="nf">drawLine</span><span class="p">(</span>
        <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
        <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">startX</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
        <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">startX</span><span class="p">,</span> <span class="mf">0f</span><span class="p">),</span>
        <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">2f</span>
    <span class="p">)</span>
    <span class="nf">drawLine</span><span class="p">(</span>
        <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
        <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">endX</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
        <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">endX</span><span class="p">,</span> <span class="mf">0f</span><span class="p">),</span>
        <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">2f</span>
    <span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img width="360" alt="Screenshot 2024-06-20 at 3 19 13 AM" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/31657760-e140-48bb-91a7-bc5ccbc413d1" /></p>

<p><br /></p>

<p>보라색으로 표시한부분이 baseLine이다. 그래프의 수치를 확인할 수 있는 뼈대라고 생각하면 된다.</p>

<p>color는 baseLine의 색상,<br />
startX는 RowBaseLine의 시작 x좌표,<br />
endX는 RowBaseLine의 끝 x좌표를 의미한다.<br />
❗️높이는 canvas의 높이와 동일하게 고정하였기 때문에 별도로 설정하지 않는다.</p>

<h3 id="baselinetext">baseLineText()</h3>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nc">DrawScope</span><span class="p">.</span><span class="nf">rowBaseLineText</span><span class="p">(</span><span class="n">count</span><span class="p">:</span> <span class="nc">Int</span><span class="p">,</span> <span class="n">color</span><span class="p">:</span> <span class="nc">Color</span><span class="p">,</span> <span class="n">list</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">TextLayoutResult</span><span class="p">&gt;,</span> <span class="n">rowSize</span><span class="p">:</span> <span class="nc">Float</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// row 점선 데이터</span>
    <span class="nf">repeat</span><span class="p">(</span><span class="n">count</span> <span class="p">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">it</span> <span class="p">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">drawText</span><span class="p">(</span>
                <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">],</span>
                <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">rowSize</span> <span class="p">*</span> <span class="p">(</span><span class="n">it</span> <span class="p">/</span> <span class="n">count</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">()),</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span>
            <span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">it</span> <span class="p">==</span> <span class="n">count</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">drawText</span><span class="p">(</span>
                <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">],</span>
                <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">*</span> <span class="p">(</span><span class="n">it</span> <span class="p">/</span> <span class="n">count</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">].</span><span class="n">size</span><span class="p">.</span><span class="n">width</span><span class="p">,</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span>
                <span class="p">),</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span>
            <span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nf">drawText</span><span class="p">(</span>
                <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">],</span>
                <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">*</span> <span class="p">(</span><span class="n">it</span> <span class="p">/</span> <span class="n">count</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">].</span><span class="n">size</span><span class="p">.</span><span class="n">width</span> <span class="p">/</span> <span class="mi">2</span><span class="p">,</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span>
                <span class="p">),</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span>
            <span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="k">fun</span> <span class="nc">DrawScope</span><span class="p">.</span><span class="nf">columnBaseLineText</span><span class="p">(</span><span class="n">count</span><span class="p">:</span> <span class="nc">Int</span><span class="p">,</span> <span class="n">color</span><span class="p">:</span> <span class="nc">Color</span><span class="p">,</span> <span class="n">list</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">TextLayoutResult</span><span class="p">&gt;,</span> <span class="n">endPadding</span><span class="p">:</span> <span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// column 점선 데이터</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..</span><span class="n">count</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">drawText</span><span class="p">(</span>
            <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span><span class="p">],</span>
            <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                <span class="n">size</span><span class="p">.</span><span class="n">width</span> <span class="p">-</span> <span class="n">endPadding</span><span class="p">,</span>
                <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">i</span> <span class="p">/</span> <span class="n">count</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span>
            <span class="p">),</span>
            <span class="n">color</span> <span class="p">=</span> <span class="n">color</span>
        <span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img width="360" alt="Screenshot 2024-06-20 at 3 30 56 AM" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/ee23e87f-83c3-4a45-a3a6-877b6b41d936" /></p>

<p><br /></p>

<p>보라색 점선의 영역으로 표시된 부분이 baseLineText()이다. row영역과 column영역으로 구분하였다.
row의 text는 <code class="language-plaintext highlighter-rouge">rowBaseLineText()</code>로 column의 text는 <code class="language-plaintext highlighter-rouge">columnBaseLineText</code>으로 작성한다.</p>

<p>count는 text의 개수,<br />
color는 text의 색상,<br />
list는 text의 정보가 들어있는 List,<br />
endPadding은 보통 text의 크기를 나타내며, 전체 길이에서 text의 크기만큼 좌측으로 이동한 크기를 의미한다. (0으로 설정할 경우 화면에 보이지 않는다.)</p>

<h3 id="dataline">dataLine()</h3>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nc">DrawScope</span><span class="p">.</span><span class="nf">dataLine</span><span class="p">(</span><span class="n">color</span><span class="p">:</span> <span class="nc">Color</span><span class="p">,</span> <span class="n">rowSize</span><span class="p">:</span> <span class="nc">Float</span><span class="p">,</span> <span class="n">rowCount</span><span class="p">:</span> <span class="nc">Int</span><span class="p">,</span> <span class="n">list</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;,</span> <span class="n">lastColumnValue</span><span class="p">:</span> <span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// 데이터의 그래프</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="k">in</span> <span class="mi">0</span> <span class="n">until</span> <span class="n">list</span><span class="p">.</span><span class="n">size</span> <span class="p">-</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="p">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">drawLine</span><span class="p">(</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
                <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="mf">0f</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">/</span> <span class="n">lastColumnValue</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())),</span>
                <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">/</span> <span class="p">(</span><span class="n">rowCount</span> <span class="p">-</span> <span class="mi">1</span><span class="p">),</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">/</span> <span class="n">lastColumnValue</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span>
                <span class="p">),</span>
                <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">5f</span>
            <span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nf">drawLine</span><span class="p">(</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
                <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">*</span> <span class="p">(</span><span class="n">i</span> <span class="p">/</span> <span class="p">(</span><span class="n">rowCount</span> <span class="p">-</span> <span class="mi">1</span><span class="p">).</span><span class="nf">toFloat</span><span class="p">()),</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">/</span> <span class="n">lastColumnValue</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span>
                <span class="p">),</span>
                <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">*</span> <span class="p">((</span><span class="n">i</span> <span class="p">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">/</span> <span class="p">(</span><span class="n">rowCount</span> <span class="p">-</span> <span class="mi">1</span><span class="p">).</span><span class="nf">toFloat</span><span class="p">()),</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span> <span class="p">+</span> <span class="mi">1</span><span class="p">]</span> <span class="p">/</span> <span class="n">lastColumnValue</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span>
                <span class="p">),</span>
                <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">5f</span>
            <span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img width="360" alt="Screenshot 2024-06-20 at 3 41 33 AM" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/f4751d58-4237-4aa3-9328-7063e654c6bd" /></p>

<p><br /></p>

<p><code class="language-plaintext highlighter-rouge">dataLine()</code>는 살제 데이터를 의미하는 꺾은선 그래프이다. 보라색 점선영역으로 표시되었다.</p>

<p>color는 꺾은선 그래프의 색상,<br />
rowSize는 rowBaseLine의 width(꺾은선 그래프의 각 선마다 x축 값을 정하는데 필요하다.),<br />
rowCount는 실제 데이터의 수,
list는 실제 데이터의 리스트,<br />
lastColumnValue는 list의 max 값(꺾은선 그래프의 각 선마다 y축 값을 정하는데 필요하다.)</p>

<h2 id="전체-코드">전체 코드</h2>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Composable</span>
<span class="k">fun</span> <span class="nf">LineGraph</span><span class="p">(</span><span class="n">rowData</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;,</span> <span class="n">list</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;,</span> <span class="n">isShowBaseLineText</span><span class="p">:</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="k">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">rowDottedLineCount</span> <span class="p">=</span> <span class="mi">4</span>                          <span class="c1">// rowBaseLine 개수</span>
    <span class="kd">val</span> <span class="py">columnDottedLineCount</span> <span class="p">=</span> <span class="mi">3</span>                       <span class="c1">// columnBaseLine 개수</span>

    <span class="kd">val</span> <span class="py">dataLineCount</span> <span class="p">=</span> <span class="n">list</span><span class="p">.</span><span class="n">size</span>                  <span class="c1">// 꺾은선 그래프의 개수(실제 데이틔 수)</span>
    <span class="kd">val</span> <span class="py">lastRowValue</span> <span class="p">=</span> <span class="n">rowData</span><span class="p">.</span><span class="nf">last</span><span class="p">()</span>              <span class="c1">// rowBaseLine 마지막 값</span>
    <span class="kd">val</span> <span class="py">maxColumnValue</span> <span class="p">=</span> <span class="n">list</span><span class="p">.</span><span class="nf">max</span><span class="p">()</span>                <span class="c1">// columnBaseLine 마지막 값(max 값)</span>

    <span class="c1">// ------&gt; BaseLine Text 설정</span>
    <span class="kd">val</span> <span class="py">textMeasurer</span> <span class="p">=</span> <span class="nf">rememberTextMeasurer</span><span class="p">()</span>

    <span class="kd">val</span> <span class="py">style</span> <span class="p">=</span> <span class="nc">TextStyle</span><span class="p">(</span>
        <span class="n">fontSize</span> <span class="p">=</span> <span class="mi">15</span><span class="p">.</span><span class="n">sp</span><span class="p">,</span>
        <span class="n">color</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">White</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="c1">// &lt;---------</span>

    <span class="kd">val</span> <span class="py">rowBaseLineTextList</span> <span class="p">=</span> <span class="n">mutableListOf</span><span class="p">&lt;</span><span class="nc">TextLayoutResult</span><span class="p">&gt;()</span>
    <span class="kd">val</span> <span class="py">columnBaseLineTextList</span> <span class="p">=</span> <span class="n">mutableListOf</span><span class="p">&lt;</span><span class="nc">TextLayoutResult</span><span class="p">&gt;()</span>



    <span class="k">if</span> <span class="p">(</span><span class="n">isShowBaseLineText</span><span class="p">)</span> <span class="p">{</span>

        <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">rowDottedLineCount</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">val</span> <span class="py">result</span> <span class="p">=</span> <span class="p">(</span><span class="n">lastRowValue</span> <span class="p">*</span> <span class="p">(</span><span class="n">i</span> <span class="p">/</span> <span class="n">rowDottedLineCount</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">()))</span>
            <span class="kd">val</span> <span class="py">text</span> <span class="p">=</span> <span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="p">*</span> <span class="mi">10</span> <span class="p">%</span> <span class="mi">10</span> <span class="p">==</span> <span class="mf">0f</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">result</span><span class="p">.</span><span class="nf">toInt</span><span class="p">().</span><span class="nf">toString</span><span class="p">()</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">result</span><span class="p">.</span><span class="nf">toString</span><span class="p">()</span>
            <span class="p">}</span>
            <span class="n">rowBaseLineTextList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nf">remember</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="p">{</span> <span class="n">textMeasurer</span><span class="p">.</span><span class="nf">measure</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">style</span><span class="p">)</span> <span class="p">})</span>
        <span class="p">}</span>


        <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">columnDottedLineCount</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">val</span> <span class="py">text</span> <span class="p">=</span> <span class="p">(</span><span class="n">maxColumnValue</span> <span class="p">*</span> <span class="p">(</span><span class="n">i</span> <span class="p">/</span> <span class="n">columnDottedLineCount</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())).</span><span class="nf">toInt</span><span class="p">().</span><span class="nf">toString</span><span class="p">()</span>
            <span class="n">columnBaseLineTextList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nf">remember</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="p">{</span> <span class="n">textMeasurer</span><span class="p">.</span><span class="nf">measure</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">style</span><span class="p">)</span> <span class="p">})</span>
        <span class="p">}</span>
    <span class="p">}</span>



    <span class="kd">val</span> <span class="py">baseLineColor</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">Black</span>
    <span class="kd">val</span> <span class="py">dataLineColor</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">(</span><span class="mh">0xFF0C964A</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">textColor</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">Black</span>

    <span class="nc">Canvas</span><span class="p">(</span>
        <span class="n">modifier</span> <span class="p">=</span> <span class="nc">Modifier</span>
            <span class="p">.</span><span class="nf">fillMaxWidth</span><span class="p">()</span>
            <span class="p">.</span><span class="nf">aspectRatio</span><span class="p">(</span><span class="mf">1.2f</span><span class="p">)</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">lastRowPadding</span> <span class="p">=</span> <span class="k">if</span> <span class="p">(</span><span class="n">columnBaseLineTextList</span><span class="p">.</span><span class="nf">isEmpty</span><span class="p">())</span> <span class="mi">0</span> <span class="k">else</span> <span class="n">columnBaseLineTextList</span><span class="p">.</span><span class="nf">maxOf</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">size</span><span class="p">.</span><span class="n">width</span> <span class="p">}</span>
        <span class="kd">val</span> <span class="py">rowSize</span> <span class="p">=</span> <span class="n">size</span><span class="p">.</span><span class="n">width</span> <span class="p">-</span> <span class="p">(</span><span class="n">lastRowPadding</span> <span class="p">+</span> <span class="mf">10f</span><span class="p">)</span>

        <span class="nf">baseLine</span><span class="p">(</span><span class="n">color</span> <span class="p">=</span> <span class="n">baseLineColor</span><span class="p">,</span> <span class="n">startX</span> <span class="p">=</span> <span class="mf">0f</span><span class="p">,</span> <span class="n">endX</span> <span class="p">=</span> <span class="n">rowSize</span><span class="p">)</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">isShowBaseLineText</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// true, false check 할 것</span>
            <span class="nf">columnBaseLineText</span><span class="p">(</span><span class="n">count</span> <span class="p">=</span> <span class="n">columnDottedLineCount</span><span class="p">,</span> <span class="n">color</span> <span class="p">=</span> <span class="n">textColor</span><span class="p">,</span> <span class="n">list</span> <span class="p">=</span> <span class="n">columnBaseLineTextList</span><span class="p">,</span> <span class="n">endPadding</span> <span class="p">=</span> <span class="n">lastRowPadding</span><span class="p">)</span>

            <span class="c1">// true, false check 할 것</span>
            <span class="nf">rowBaseLineText</span><span class="p">(</span><span class="n">count</span> <span class="p">=</span> <span class="n">rowDottedLineCount</span><span class="p">,</span> <span class="n">color</span> <span class="p">=</span> <span class="n">textColor</span><span class="p">,</span> <span class="n">list</span> <span class="p">=</span> <span class="n">rowBaseLineTextList</span><span class="p">,</span> <span class="n">rowSize</span> <span class="p">=</span> <span class="n">rowSize</span><span class="p">)</span>
        <span class="p">}</span>


        <span class="nf">dataLine</span><span class="p">(</span><span class="n">color</span> <span class="p">=</span> <span class="n">dataLineColor</span><span class="p">,</span> <span class="n">rowSize</span> <span class="p">=</span> <span class="n">rowSize</span><span class="p">,</span> <span class="n">rowCount</span> <span class="p">=</span> <span class="n">dataLineCount</span><span class="p">,</span> <span class="n">list</span> <span class="p">=</span> <span class="n">list</span><span class="p">,</span> <span class="n">lastColumnValue</span> <span class="p">=</span> <span class="n">maxColumnValue</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nc">DrawScope</span><span class="p">.</span><span class="nf">baseLine</span><span class="p">(</span><span class="n">color</span><span class="p">:</span> <span class="nc">Color</span><span class="p">,</span> <span class="n">startX</span><span class="p">:</span> <span class="nc">Float</span><span class="p">,</span> <span class="n">endX</span><span class="p">:</span> <span class="nc">Float</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">drawLine</span><span class="p">(</span>
        <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
        <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">startX</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
        <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">endX</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
        <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">2f</span>
    <span class="p">)</span>
    <span class="nf">drawLine</span><span class="p">(</span>
        <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
        <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">startX</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
        <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">startX</span><span class="p">,</span> <span class="mf">0f</span><span class="p">),</span>
        <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">2f</span>
    <span class="p">)</span>
    <span class="nf">drawLine</span><span class="p">(</span>
        <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
        <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">endX</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
        <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">endX</span><span class="p">,</span> <span class="mf">0f</span><span class="p">),</span>
        <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">2f</span>
    <span class="p">)</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nc">DrawScope</span><span class="p">.</span><span class="nf">rowBaseLineText</span><span class="p">(</span><span class="n">count</span><span class="p">:</span> <span class="nc">Int</span><span class="p">,</span> <span class="n">color</span><span class="p">:</span> <span class="nc">Color</span><span class="p">,</span> <span class="n">list</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">TextLayoutResult</span><span class="p">&gt;,</span> <span class="n">rowSize</span><span class="p">:</span> <span class="nc">Float</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// row 점선 데이터</span>
    <span class="nf">repeat</span><span class="p">(</span><span class="n">count</span> <span class="p">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">it</span> <span class="p">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">drawText</span><span class="p">(</span>
                <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">],</span>
                <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">rowSize</span> <span class="p">*</span> <span class="p">(</span><span class="n">it</span> <span class="p">/</span> <span class="n">count</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">()),</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span><span class="p">),</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span>
            <span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">it</span> <span class="p">==</span> <span class="n">count</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">drawText</span><span class="p">(</span>
                <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">],</span>
                <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">*</span> <span class="p">(</span><span class="n">it</span> <span class="p">/</span> <span class="n">count</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">].</span><span class="n">size</span><span class="p">.</span><span class="n">width</span><span class="p">,</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span>
                <span class="p">),</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span>
            <span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nf">drawText</span><span class="p">(</span>
                <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">],</span>
                <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">*</span> <span class="p">(</span><span class="n">it</span> <span class="p">/</span> <span class="n">count</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">it</span><span class="p">].</span><span class="n">size</span><span class="p">.</span><span class="n">width</span> <span class="p">/</span> <span class="mi">2</span><span class="p">,</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span>
                <span class="p">),</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span>
            <span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nc">DrawScope</span><span class="p">.</span><span class="nf">columnBaseLineText</span><span class="p">(</span><span class="n">count</span><span class="p">:</span> <span class="nc">Int</span><span class="p">,</span> <span class="n">color</span><span class="p">:</span> <span class="nc">Color</span><span class="p">,</span> <span class="n">list</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">TextLayoutResult</span><span class="p">&gt;,</span> <span class="n">endPadding</span><span class="p">:</span> <span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// column 점선 데이터</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..</span><span class="n">count</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">drawText</span><span class="p">(</span>
            <span class="n">textLayoutResult</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span><span class="p">],</span>
            <span class="n">topLeft</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                <span class="n">size</span><span class="p">.</span><span class="n">width</span> <span class="p">-</span> <span class="n">endPadding</span><span class="p">,</span>
                <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">i</span> <span class="p">/</span> <span class="n">count</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span>
            <span class="p">),</span>
            <span class="n">color</span> <span class="p">=</span> <span class="n">color</span>
        <span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nc">DrawScope</span><span class="p">.</span><span class="nf">dataLine</span><span class="p">(</span><span class="n">color</span><span class="p">:</span> <span class="nc">Color</span><span class="p">,</span> <span class="n">rowSize</span><span class="p">:</span> <span class="nc">Float</span><span class="p">,</span> <span class="n">rowCount</span><span class="p">:</span> <span class="nc">Int</span><span class="p">,</span> <span class="n">list</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;,</span> <span class="n">lastColumnValue</span><span class="p">:</span> <span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// 데이터의 그래프</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="k">in</span> <span class="mi">0</span> <span class="n">until</span> <span class="n">list</span><span class="p">.</span><span class="n">size</span> <span class="p">-</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="p">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">drawLine</span><span class="p">(</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
                <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span><span class="mf">0f</span><span class="p">,</span> <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">/</span> <span class="n">lastColumnValue</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())),</span>
                <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">/</span> <span class="p">(</span><span class="n">rowCount</span> <span class="p">-</span> <span class="mi">1</span><span class="p">),</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">/</span> <span class="n">lastColumnValue</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span>
                <span class="p">),</span>
                <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">5f</span>
            <span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nf">drawLine</span><span class="p">(</span>
                <span class="n">color</span> <span class="p">=</span> <span class="n">color</span><span class="p">,</span>
                <span class="n">start</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">*</span> <span class="p">(</span><span class="n">i</span> <span class="p">/</span> <span class="p">(</span><span class="n">rowCount</span> <span class="p">-</span> <span class="mi">1</span><span class="p">).</span><span class="nf">toFloat</span><span class="p">()),</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">/</span> <span class="n">lastColumnValue</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span>
                <span class="p">),</span>
                <span class="n">end</span> <span class="p">=</span> <span class="nc">Offset</span><span class="p">(</span>
                    <span class="n">rowSize</span> <span class="p">*</span> <span class="p">((</span><span class="n">i</span> <span class="p">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">/</span> <span class="p">(</span><span class="n">rowCount</span> <span class="p">-</span> <span class="mi">1</span><span class="p">).</span><span class="nf">toFloat</span><span class="p">()),</span>
                    <span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="p">*</span> <span class="p">(</span><span class="mi">1</span> <span class="p">-</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span> <span class="p">+</span> <span class="mi">1</span><span class="p">]</span> <span class="p">/</span> <span class="n">lastColumnValue</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">())</span>
                <span class="p">),</span>
                <span class="n">strokeWidth</span> <span class="p">=</span> <span class="mf">5f</span>
            <span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<ul>
  <li>
    <p>실제 적용한 코드<br />
<img width="435" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/dce6eb41-2f99-4929-9266-3984cf850cf7" /></p>

    <p>isShowBaseLineText는 BaseLineText를 UI에 나타낼지에 대한 유무를 결정한다.</p>
  </li>
</ul>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="Compose" /><category term="Canvas" /><summary type="html"><![CDATA[Compose에서 Canvas를 이용해 꺾은선 그래프를 그리는 과정을 살펴보자.]]></summary></entry><entry><title type="html">[Kotlin] Compose Recompostion 최적화</title><link href="https://daeyoungee.github.io/kotlin/ComposeRecompostion/" rel="alternate" type="text/html" title="[Kotlin] Compose Recompostion 최적화" /><published>2024-06-11T00:00:00+00:00</published><updated>2024-06-11T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/ComposeRecompostion</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/ComposeRecompostion/"><![CDATA[<p>프로젝트가 커지면서 화면을 구성하고 변경될 때 버벅거리는 현상을 경험해 본적 있을 것이다. UX의 불편함을 해소하기 위해 recompostion 최적화에 대해 많이 찾아봤고 공부해본 내용을 기록하고자 한다.<br />
<strong>compostion</strong>은 처음 화면을 그리는(화면 구성) 작업, <strong>recompostion</strong>은 화면 구성이 이 후 재구성하는(화면을 다시 그림) 작업이다.<br />
compose에서는 앱의 성능을 향상시키기위해 불필요한 recompostion을 최소화해야 한다.</p>

<h2 id="recompostion">recompostion</h2>

<p><strong>recomposition</strong>은 Composable 내부의 원소가 변경이 되었을 때 Compose가 다시 계산되어 UI가 화면에 다시 그려지는 것이다. Compose가 화면에 그려지기까지는 3단계를 거치게 된다.</p>

<blockquote>
  <ol>
    <li><strong>Composition</strong>: Composable tree를 생성하고 어떠한 UI들을 화면에 표시할 것인지 결정하는 과정입니다. - <font style="color:#cccccc;">**what to show**</font></li>
    <li><strong>Layout</strong> : UI를 배치할 위치를 결정합니다. 이 과정은 측정과 배치라는 두 단계로 구성되며 레아이웃 요소는 Composable tree에 있는 각 노드의 레이아웃 요소 및 모든 하위 요소를 2D 좌표로 측정하고 배치합니다. - <font style="color:#cccccc;">**Where to show**</font></li>
    <li><strong>Draw</strong> : UI를 화면에 렌더링하는 과정입니다. - <font style="color:#cccccc;">**Draw to the screen**</font></li>
  </ol>
</blockquote>

<p><br /></p>

<p>컴포저블의 내부 데이터가 변경되어 recomposition을 하는 상황일 때, <strong>컴포지션 단계</strong>에서 기존의 컴포저블과 비교했을 때 변화가 없다면 <strong>컴포지션 단계</strong>를 건너뛴다. 이는 성능 향상으로 이어진다.</p>

<h3 id="불필요한-recomposition">불필요한 recomposition</h3>

<p>compose에서는 불필요한 recomposition울 줄이는 것이 compose의 성능 개선에 많은 도움을 준다.<br />
불필요한 recomposition이란 값은 그대로이고 UI가 변하지 않는 것이라 한다.</p>

<h2 id="compose의-stability">Compose의 Stability</h2>

<p>Compose는 <strong>stable</strong>, <strong>unstable</strong> 타입 2가지로 구분된다.<br />
stable: Compose가 immutable하거나 recomposition이 일어났을 때 내부의 어떤 값이 변했는지 파악할 수 있는 경우,<br />
unstable: recomposition이 일어났을 때 내부의 어떤 값이 변했는지 파악할 수 없는 경우</p>

<p>타입이 stable이냐, unstable이냐에 따라 recomposition을 일으키는 결정적인 요인이 된다.<br />
만약 Compose 컴포넌트가 stable하고 내부의 값이 변경되지 않는다면 해당 컴포넌트는 recomposition이 일어나지 않는다. 그러나 Compose 컴포넌트가 unstable하고 내부의 값이 변경되지 않았어도 부모 컴포넌트가 recomposition 될 때 자식 컴포넌트도 recomposition된다.</p>

<blockquote>
  <p><strong>즉, 값이 변하지 않아도 unstable하면 recomposition이 발생하기 때문에 이는 성능 저하로 이루어질 수 있다.</strong></p>
</blockquote>

<h2 id="compose에서-구현">Compose에서 구현</h2>

<p>Compose 컴파일러는 코드를 읽으면서 각 컴포넌트에 태그를 표시한다. 해당 태그를 보고 recomposition을 수행할지 여부를 정한다.</p>

<p><br /></p>

<p>다음은 Composable 함수에 표시하는 태그이다. 둘 중 하나만 표시할 수도 있고 아무것도 표시하지 않는 경우도 있다.</p>

<ul>
  <li>Skippable: 해당 태그가 있는 컴포넌트는 Compose가 recomposition을 일으키지 않고 스킵한다. 해당 컴포넌트의 모든 인자 값들이 이전의 값과 새롭게 바뀐 값이 동일한 경우에 해당 태그가 달린다.</li>
  <li>Restartable: 해당 컴포넌트를 기준으로 스코프를 만들어 recomposition이 일어난다. 즉 state가 변할 수 있고 recomposition의 시작점이다.</li>
</ul>

<p>Compose의 Parameter 구분</p>

<ul>
  <li>
    <p>Immutable<br />
Compose에서는 모든 프로퍼티들이 immutable이고 메서드들이 참조 투명하면 immutable 태그를 붙입니다. 모든 primitive 타입(String, Int, Float 등)은 immutable로 표시된다. val 형태인 data class도 있다.</p>
  </li>
  <li>Stable<br />
생성 후 프로퍼티가 변경될 수 있는 유형으로, 즉 값이 바뀔 수는 있지만 런타임 중 프로퍼티가 변경된다면 Compose가 변경 사항을 추적할 수 있는 컴포넌트, 대표적으로 <code class="language-plaintext highlighter-rouge">State&lt;T&gt;</code>가 있다.</li>
  <li>Unstable
Immutable과 Stable에 속하지 않는 유형으로 recomposition을 유발한다.</li>
</ul>

<h2 id="불필요한-recomposition-줄이기">불필요한 Recomposition 줄이기</h2>

<ol>
  <li>
    <p>immutable collection 사용
Compose는 List, Set, Map과 같은 collection을 값이 바뀔 수 있다고 생각하여 unstable한 객체라고 판판한다. 즉, 우리가 흔히 사용하는 data class 안에 List 타입의 프로퍼티가 있다면 val로 선언되어 해당 data class는 unstable한 객체가 된다.</p>

    <p>nstable한 Collection 객체들을 stable하게 사용하고 싶다고 compose에게 알려주기 위해서는 @immutable이나 @Stable 어노테이션을 붙이면 된다. 단 개발자가 임의로 @Immutable, @Stable 붙여도 컴파일러가 unstable하다고 판단한다면 recomposition이 일어날 수 있다.<br />
이 뿐 아니라 List, Set, Map과 같은 Collection을 ImmutableList, ImmutableSet, ImmutableMap 과 같은 immutable collection로 사용해서 compose에게 stable하다고 알려줄 수 있다.</p>

    <p>immutable collection을 사용하기 위해서는 dependency를 추가해야 한다. -&gt; 변경되는 버전이나 최신 사항(<a href="https://github.com/Kotlin/kotlinx.collections.immutable">깃허브 힝크</a>) 참조하면 된다.</p>

    <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">implementation</span> <span class="p">(</span><span class="s">"org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.6"</span><span class="p">)</span>
</code></pre></div>    </div>

    <p>사용방법은 아래와 같다.</p>

    <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">examples</span><span class="p">:</span> <span class="nc">ImmutableList</span><span class="p">&lt;</span><span class="nc">Example</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">persistentListOf</span><span class="p">()</span>
</code></pre></div>    </div>

    <p><code class="language-plaintext highlighter-rouge">persistentListOf()</code>는 empty한 ImmutableList를 반환한다.</p>
  </li>
  <li>
    <p>Painter 클래스 사용 X<br />
이미지를 그리는 Painter 클래스는 unstable하다. 이미지의 painter 인자에 Painter 클래스를 활용하면 이미지가 변하지 않는 상황에서도 불필요한 recompostion이 일어날 수 있다. Painter 클래스 대신 painterResource를 이용해 stable한 Int 값인 resourceId를 넘겨주면 불필요한 recomposition을 막을 수 있다.</p>

    <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Image</span><span class="p">(</span>
 <span class="n">painter</span> <span class="p">=</span> <span class="nf">painterResource</span><span class="p">(</span><span class="n">id</span> <span class="p">=</span> <span class="nc">R</span><span class="p">.</span><span class="n">drawable</span><span class="p">.</span><span class="n">example_img</span><span class="p">),</span>
 <span class="n">contentDescription</span> <span class="p">=</span> <span class="s">"Example Image"</span>
<span class="p">)</span>
</code></pre></div>    </div>

    <p>만약, svg 파일을 사용하는 경우 ImageVector 클래스를 이용할 수 있다. ImageVector class는 내부적으로 @Immutable 어노테이션을 가지고 있기 때문에 stable하다.</p>
  </li>
  <li>
    <p>반복문(for, forEach), Lazy Composable에 key값 사용<br />
 아래와 같은 for문이 있을 때 원소가 추가되거나 삭제될 경우 recompostion이 어떻게 발생할까?</p>

    <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Composable</span>
<span class="k">fun</span> <span class="nf">MoviesScreen</span><span class="p">(</span><span class="n">movies</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">Movie</span><span class="p">&gt;)</span> <span class="p">{</span>
   <span class="nc">Column</span> <span class="p">{</span>
       <span class="k">for</span> <span class="p">(</span><span class="n">movie</span> <span class="k">in</span> <span class="n">movies</span><span class="p">)</span> <span class="p">{</span>
           <span class="nc">MovieOverview</span><span class="p">(</span><span class="n">movie</span><span class="p">)</span>
       <span class="p">}</span>
   <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>

    <p>정답은 원소가 추가되는 위치에 따라 다르다. 원소가 마지막에 추가되면 기존에 있는 원소들의 위치가 그대로이기 때문에 MovieOverview 인스턴스를 재활용할 수 있어 recomposition이 일어나지 않는다.</p>

    <p>반면 원소가 맨 앞에 추가되거나 정렬이 바뀌거나 중간에 원소가 추가되어 위치가 바뀌게 되는 원소들은 내부의 movie 값이 동일해도 MovieOverview가 recomposition이 일어나게 된다. <strong>“따라서 내부의 값이 동일하면 Skippable하게 만들어야하고 이는 key를 이용해야 한다.”</strong></p>

    <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Composable</span>
 <span class="k">fun</span> <span class="nf">MoviesScreen</span><span class="p">(</span><span class="n">movies</span><span class="p">:</span> <span class="nc">List</span><span class="p">&lt;</span><span class="nc">Movie</span><span class="p">&gt;)</span> <span class="p">{</span>
   <span class="nc">Column</span> <span class="p">{</span>
       <span class="k">for</span> <span class="p">(</span><span class="n">movie</span> <span class="k">in</span> <span class="n">movies</span><span class="p">)</span> <span class="p">{</span>
           <span class="nf">key</span><span class="p">(</span><span class="n">movie</span><span class="p">.</span><span class="n">id</span><span class="p">)</span> <span class="p">{</span>
               <span class="nc">MovieOverview</span><span class="p">(</span><span class="n">movie</span><span class="p">)</span>
           <span class="p">}</span>
       <span class="p">}</span>
   <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>

    <p>위의 코드와 같이 moive를 식별할 수 있는 id를 key로 주어기게 된다면 각각의 MovieOverview가 movie의 id와 매핑되어 composition 트리내에 인스턴스를 재활용하고 재정렬해서 불필요한 recompositon을 줄일 수 있다.</p>
  </li>
  <li>
    <p>외부 모듈에 위치한 class는 unstable로 처리된다.<br />
 Compose compiler 동작하지 안는 외부 모듈에 위치한 class들은 전부 unstable로 처리된다. 특히 <code class="language-plaintext highlighter-rouge">LocalDateTime</code>을 사용하는 경우 그렇다.</p>

    <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">data class</span> <span class="nc">FoodInfo</span><span class="p">(</span><span class="kd">val</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="kd">val</span> <span class="py">timestamp</span><span class="p">:</span> <span class="nc">LocalDateTime</span><span class="p">)</span>

<span class="c1">// Compose compiler report</span>
<span class="n">unstable</span> <span class="kd">class</span> <span class="nc">FoodInfo</span> <span class="p">{</span>
    <span class="n">stable</span> <span class="kd">val</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span>
    <span class="n">unstable</span> <span class="kd">val</span> <span class="py">timestamp</span><span class="p">:</span> <span class="nc">LocalDateTime</span>
    <span class="p">&lt;</span><span class="n">runtime</span> <span class="n">stability</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">Unstable</span>
<span class="p">}</span>
</code></pre></div>    </div>

    <p>LocalDateTime을 사용하면서 FoodInfo class 자체가 unstable로 변경되었다. 이를 해결하기 위해 @Immutable 또는 @Stable annotation을 이용하여 강제로 stability를 줄수 있지만. 이러한 방법은 지양되는 방법이다. 데아터가 변경되어 recomposition이 일어날 시 skip될 수 있기 때문이다.</p>

    <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 변경전</span>
<span class="kd">data class</span> <span class="nc">FoodInfo</span><span class="p">(</span><span class="kd">val</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="kd">val</span> <span class="py">timestamp</span><span class="p">:</span> <span class="nc">LocalDateTime</span><span class="p">)</span>

<span class="c1">// 변경후</span>
<span class="kd">data class</span> <span class="nc">FoodUiInfo</span><span class="p">(</span><span class="kd">val</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="kd">val</span> <span class="py">timestamp</span><span class="p">:</span> <span class="nc">Long</span><span class="p">)</span>

<span class="c1">// mapper의 구성</span>
<span class="k">fun</span> <span class="nc">FoodInfo</span><span class="p">.</span><span class="nf">toImmutable</span><span class="p">():</span> <span class="nc">FoodUiInfo</span> <span class="p">=</span>
<span class="nc">FoodUiInfo</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="nc">Timestamp</span><span class="p">.</span><span class="nf">valueOf</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nf">getTime</span><span class="p">())</span>

</code></pre></div>    </div>
  </li>
</ol>

<p>* 참조 투명한 함수: 함수나 메서드가 주어진 입력에 대해 항상 동일한 출력을 생성, 부작용(side effects)가 없는 성질을 의미</p>

<ul>
  <li>참조 투명한 함수</li>
</ul>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nc">Int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nc">Int</span><span class="p">):</span> <span class="nc">Int</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">a</span> <span class="p">+</span> <span class="n">b</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>참조 투명하지 않은 함수</li>
</ul>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="py">total</span> <span class="p">=</span> <span class="mi">0</span>
<span class="k">fun</span> <span class="nf">addWithSideEffect</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nc">Int</span><span class="p">):</span> <span class="nc">Int</span> <span class="p">{</span>
    <span class="n">total</span> <span class="p">+=</span> <span class="n">a</span>
    <span class="k">return</span> <span class="n">total</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="reference">Reference</h2>

<p><a href="https://developer.android.com/develop/ui/compose/phases?hl=ko">그림으로 이해하는 컴포지션 3단계</a><br />
<a href="https://wannabe-master.tistory.com/m/8">recomposetion 1</a><br />
<a href="https://medium.com/androiddevelopers/jetpack-compose-debugging-recomposition-bfcf4a6f8d37">recomposetion 2</a><br />
<a href="https://tourspace.tistory.com/536">recomposetion 3</a></p>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="Compose" /><category term="recompostion" /><summary type="html"><![CDATA[Compose에서 recompostion 최적화 방법에 대해 알아보자]]></summary></entry><entry><title type="html">[Kotlin] Activity 간 데이터 전달</title><link href="https://daeyoungee.github.io/kotlin/ActivityDataTransfer/" rel="alternate" type="text/html" title="[Kotlin] Activity 간 데이터 전달" /><published>2024-06-10T00:00:00+00:00</published><updated>2024-06-10T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/ActivityDataTransfer</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/ActivityDataTransfer/"><![CDATA[<!-- 영상 첨부,  -->

<p>A activity에서 B activity로 화면 전환 시 데이터 전달을 하거나(단방향)<br />
A activity -&gt; B activity -&gt; A activity로 다시 돌아오면서 B activity에서 연산처리된 결과 데이터를 A activity에서 필요로 할 때 데이터를 전달하는 방법을 알아보자.(양방향)</p>

<p>필자는 맵(B activity)에서 특정 위치를 검색하고 해당 LatLng(위도, 경도)를 A activity로 전달이 필요한 경우에 사용 했다.</p>

<h2 id="activity-간-단방향-통신">Activity 간 단방향 통신</h2>

<p>단방향 통신을 너무 간단하다. MainActivity에서 PostedActivity로 이동하는 메서드를 살펴보자.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">fun</span> <span class="nf">startPostedInfoActivity</span><span class="p">(</span><span class="n">postId</span><span class="p">:</span> <span class="nc">Long</span><span class="p">,</span> <span class="n">board</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">intent</span> <span class="p">=</span> <span class="nc">Intent</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nc">PostedInfoActivity</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nf">putExtra</span><span class="p">(</span><span class="s">"postId"</span><span class="p">,</span> <span class="n">postId</span><span class="p">)</span>
        <span class="k">this</span><span class="p">.</span><span class="nf">putExtra</span><span class="p">(</span><span class="s">"board"</span><span class="p">,</span> <span class="n">board</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="nf">startActivity</span><span class="p">(</span><span class="n">intent</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Intent.putExtra()</code>를 사용하여 key 값과 value 값을 매개변수에 넣어주면 된다.</p>

<h2 id="activity-간-양방향-통신">Activity 간 양방향 통신</h2>

<p>맵을 사용하는 MainAcvitiy에서 SearchActivity로 이동 후 검색어의 위도와 경도를 가지고 다시 MainAcvitiy로 돌아오는 코드이다.<br />
<code class="language-plaintext highlighter-rouge">registerForActivityResult()</code> 를 사용해서 activity의 화면 이동과 데이터 전달을 할 수 있다.</p>

<ul>
  <li>
    <p>MainActivity.kt</p>

    <pre><code class="language-koltin">@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    val activityForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -&gt;
            if (result.resultCode == RESULT_OK) {
                Log.d(TAG, "SearchActivity의 반환 값: ${result.data?.getDoubleArrayExtra("latlng").contentToString()}")
                result.data?.getDoubleArrayExtra("latlng").let {
                    recordViewModel.searchResultPlace = LatLng(it?.getOrNull(0)!!, it.getOrNull(1)!!)
                }
            }
        }

    override fun onCreate(savedInstanceState: Bundle?) { ... }

    private fun startSearchActivity() {
        val intent = Intent(this, SearchActivity::class.java)
        activityForResult.launch((intent))
    }
}
</code></pre>
  </li>
</ul>

<p>SearchActivity 종료 시 <code class="language-plaintext highlighter-rouge">setResult()</code> 메서드를 통해 intent를 등록하고 intent의 상태값을 반환한다.<br />
상태 값은 RESULT_OK와 RESULT_CANCELED로 안드로이드에 이미 상수로 정의 되어 있다.<br />
finish() 메서드가 종료되면 SearchActivity가 종료되면서 MainAcvitiy에 데이터가 전달된다.</p>

<ul>
  <li>
    <p>SearchActivity.kt</p>

    <pre><code class="language-koltin">@AndroidEntryPoint
class SearchActivity : ComponentActivity() {

      override fun onCreate(savedInstanceState: Bundle?) {...}

      private fun returnMainActivity(place: DoubleArray) {
          val intent = intent.apply {
              putExtra("latlng", place)
          }
          setResult(RESULT_OK, intent)
          finish()
      }
}
</code></pre>
  </li>
</ul>

<h2 id="reference">Reference</h2>

<p>https://pangseyoung.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%A1%ED%8B%B0%EB%B9%84%ED%8B%B0%EA%B0%84-%ED%99%94%EB%A9%B4-%EC%A0%84%ED%99%98-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC-Intent-Kotlin</p>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="Activity" /><summary type="html"><![CDATA[Activity 전환 시 데이터 전달 방법을 알아보자.(단방향, 양방향)]]></summary></entry><entry><title type="html">[자료구조, Kotlin] List, Array, Map</title><link href="https://daeyoungee.github.io/kotlin/List,Array,Map/" rel="alternate" type="text/html" title="[자료구조, Kotlin] List, Array, Map" /><published>2024-06-03T00:00:00+00:00</published><updated>2024-06-03T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/List,Array,Map</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/List,Array,Map/"><![CDATA[<p>이번 포스팅 또한 마찬가지로 코딩테스트를 하면서 언제가 한 번 공부해야지 하며 미뤄뒀던 주제다. 모든지 기본에 충실하는 말을 코테를 할 때마다 느낀다.</p>

<h2 id="array">Array</h2>

<p>Array(배열)은 초기화할 때 크기를 지정해야한다. -&gt; 메모리가 고정되어 있다.(정적 할당)<br />
배열은 메모리 공간에 연속적으로 저장되어 있는 자료구조이다. 이러한 특징때문에 인덱스를 통한 접근이 용이하다.<br />
<code class="language-plaintext highlighter-rouge">arrayOf()</code> or <code class="language-plaintext highlighter-rouge">intArrayOf()</code> or <code class="language-plaintext highlighter-rouge">Array(){}</code>로 배열을 초기화 한다.</p>

<h3 id="시간복잡도">시간복잡도</h3>

<ul>
  <li>
    <p>access(접근)<br />
시간복잡도: O(1)<br />
인덱스 x에 곧바로 접근할 수 있다.</p>
  </li>
  <li>
    <p>find(탐색)<br />
시간 복잡도: O(n)<br />
0번째 인덱스부터 마지막 인덱스까지 접근하면서 원하는 데이터를 찾는다.<br />
 배열안에 찾으려는 데이터가 앞에 있다면 빠른 시간안에 찾을 수 있지만, 배열 안에 찾는 데이터가 없거나 또는 찾으려는 데이터가 마지막에 있는 경우 n개의 데이터를 살펴봐야 한다.</p>
  </li>
  <li>
    <p>Insertion/Deletion(삽입/삭제)<br />
시간 복잡도: O(n)<br />
특정 인덱스에 데이터를 삽입, 삭제하기 위해 기존의 데이터를 옮기는 작업이 필요하다.<br />
최악의 경우 -&gt; <strong>0번째 인덱스에 데이터를 삽입하기 위해서는</strong> 현재 배열의 0번째 인덱스부터 마지막 인덱스까지의 데이터들을 모두 한 인덱스 뒤로 옮겨야한다.<br />
하지만 <strong>배열의 마지막에 데이터를 삽입하거나 삭제</strong>하는 경우에는 O(1)의 시간 복잡도를 가진다.</p>
  </li>
</ul>

<h2 id="list">List</h2>

<p>Kotlin에서 리스트는 연결리스트(Linked List)라고 생각하면 된다.<br />
필요할 때마다 노드를 생성하여 연결하면 되기 때문에 메모리를 효율적으로 사용할 수 있다.(동적 할당)</p>

<ul>
  <li>
    <p>access(접근)<br />
시간복잡도: O(n)<br />
인덱스 x에 있는 노드에 접근하려면 Head에서 다음 노드로 x번 가면 된다.</p>
  </li>
  <li>
    <p>find(탐색)<br />
시간 복잡도: O(n)<br />
배열을 탐색할 때와 같은 방법으로 구한다.
0번째 인덱스부터 마지막 인덱스까지 접근하면서 원하는 데이터를 찾는다.<br />
가장 앞의 노드부터 다음 노드순서로 원하는 데이터를 찾는다. -&gt; <strong>선형탐색</strong><br />
최악의 경우 -&gt; 링크드 리스트안에 찾는 데이터가 없거나 또는 찾으려는 데이터가 마지막 노드에 있는 경우 n개의 노드를 다 봐야한다.</p>
  </li>
  <li>
    <p>Insertion/Deletion(삽입/삭제)<br />
시간 복잡도: O(1)<br />
삽입, 삭제할 노드의 주변 노드들의 Link만 수정하면 된다. 따라서 삽입, 삭제가 실행되는 시간은 특정 값에 비례하지 않고 항상 일정하다.<br />
⚠️ 현실적으로 삽입, 삭제를 위해서는 노드를 탐색하는 과정이 필요하다. 최악의 경우 시간복잡도 O(n)이 걸린다. 그러나 연결리스트의 맨 앞 or 맨 뒤의 노드에 삽입, 삭제를 하는 경우 이미 Head를 알고 있기 떄문에 시간복잡도 O(1)을 가진다.</p>
  </li>
</ul>

<h2 id="map">Map</h2>

<p>Map은 &lt;key,value&gt;로 객체를 관리하는 컬렉션이다. 특징으로는 순서가 없이 저장되지만 <strong>삽입/삭제/조회</strong>가 빠르다.</p>

<ul>
  <li>
    <p>access(접근)<br />
시간 복잡도: O(1)</p>
  </li>
  <li>
    <p>find(탐색)<br />
시간 복잡도: O(1)<br />
key로 데이터를 검색했을 때 시간복잡도가 O(1)이지만, value로 데이터를 검색했을 경우 시간복잡도가 O(n)이다.</p>
  </li>
  <li>
    <p>Insertion/Deletion(삽입/삭제)<br />
시간 복잡도: O(1)</p>
  </li>
</ul>

<h2 id="reference">Reference</h2>

<p><a href="https://hyeinisfree.tistory.com/64">Array와 List의 차이(시간복잡도 포함)</a><br />
<a href="https://bb-library.tistory.com/144">List, ArrayList, Set, Map, Stack, Queue 자료구조 시간복잡도</a><br />
<a href="https://velog.io/@suev72/Kotlin-Map%EC%9D%98-%EC%A2%85%EB%A5%98TreeMap-LinkedHashMap-HashMap">TreeMap, LinkedHashMap, HashMap</a></p>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="List" /><category term="Array" /><category term="Map" /><summary type="html"><![CDATA[List, Array, Map의 차이와, 순회, 삽입, 삭제의 시간복잡도에 대해 알아보자.]]></summary></entry><entry><title type="html">[자료구조] String, StringBuffer, StrinfBuilder</title><link href="https://daeyoungee.github.io/kotlin/String,StringBuffer,Stringbuilder/" rel="alternate" type="text/html" title="[자료구조] String, StringBuffer, StrinfBuilder" /><published>2024-05-31T00:00:00+00:00</published><updated>2024-05-31T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/String,StringBuffer,Stringbuilder</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/String,StringBuffer,Stringbuilder/"><![CDATA[<p>요즘 프로그래머스 코딩테스트를 하고있다. 같은 문제이지만 나는 String을 사용했고 다른 사람의 StringBuilder를 사용하는 것이 많이 보였다. 차이점이 궁금했고 어떤 타입을 써야 더 좋은 코드가 될지 궁금해서 찾아보게 되었다.</p>

<h2 id="string">String</h2>

<p>String과 (StringBuilder, StringBuffer)와의 차이점은 immutable(불변)하고 mutable(변함)에 있다.</p>

<table>
  <thead>
    <tr>
      <th>클래스</th>
      <th>특징</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>String</td>
      <td>immutable</td>
    </tr>
    <tr>
      <td>StringBuilder</td>
      <td>mutable</td>
    </tr>
    <tr>
      <td>StringBuffer</td>
      <td>mutable</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<p>String이 불변하다고 했는데 기존의 문자열에 + 기호를 이용해서 새로운 문자열을 만들 수 있지 않나 생각이 들 것 이다. 필자 또한 그러한 방식으로 해왔다.</p>

<p>String이 불변한 이유를 이제부터 설명하겠다.</p>

<p>String 객체는 한번 생성하면 할당된 메모리의 공간이 변하지 않는다. 기존에 생성한 String 문자열에 +, concat()를 사용해서 추가로 문자열을 붙여도 기존 문자열에 새로운 문자열을 붙이는 것이 아니라</p>

<p><strong>새로운 String 객체가 만들어지고</strong> 새롭게 만들어진 문자열을 저장하게 된다. (String 객체는 Heap 메모리 영역에 생성되는데 한번 생생된 객체의 내용을 변화시킬 수 없다, 기존 객체가 제거되면 Java 가비지 컬렉션이 회수한다.)</p>

<p>이러한 immutable한 객체인 String은 문자열 연산이 많은 경우 성능이 좋지 않다.</p>

<h2 id="stringbuffer-stringbuilder">StringBuffer, StringBuilder</h2>

<p>StringBuffer/ StringBuilder은 String과 다르게 동작한다. 문자열 연산으로 기존 객체의 메모리가 부족한 경우 기존 버퍼의 크기를 늘리며 유연하게 동작한다.<br />
StringBuffer와 StringBuilder의 메서드는 모두 동일하다. 그렇다면 차이점은 무엇일까?</p>

<p><strong>StringBuffer</strong>는 각 메서드별로 Synchronized Keyword가 존재하여, 멀티스레드 환경에서도 동기화를 지원.<br />
<strong>StringBuilder</strong>는 동기화를 지원하지 않는다.</p>

<blockquote>
  <p>단일스레드 환경이라면 StringBuilder를 사용하는 것이 좋습니다. 단일 스레드환경에서 StringBuffer를 사용한다고 문제가 되는 것은 아니지만, 동기화 관련 처리로 인해 StringBuilder에 비해 성능이 좋지 않습니다.</p>
</blockquote>

<h3 id="정리">정리</h3>

<p>String은 짧은 문자열을 더할 경우 사용합니다.<br />
StringBuffer는 문자열 연산이 많고 멀티 스레드 환경에서 사용
StringBuilder는 문자열 연산이 많고 단일 스레드 환경에서 사용</p>

<h2 id="reference">Reference</h2>

<p><a href="https://12bme.tistory.com/42">String, StringBuffer, StringBuilder의 차이</a></p>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="String" /><category term="StringBuilder" /><category term="StringBuffer" /><summary type="html"><![CDATA[String, StringBuffer, StringBuilder의 차이를 알아보자]]></summary></entry><entry><title type="html">[Kotlin Compose] CollectAsState와 CollectAsStateWithLifecycle</title><link href="https://daeyoungee.github.io/kotlin/CollectAsStateWithLifecycle/" rel="alternate" type="text/html" title="[Kotlin Compose] CollectAsState와 CollectAsStateWithLifecycle" /><published>2024-04-23T00:00:00+00:00</published><updated>2024-04-23T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/CollectAsStateWithLifecycle</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/CollectAsStateWithLifecycle/"><![CDATA[<p>Jetpack Compose로 Android 앱을 빌드하는 경우 collectAsStateWithLifecycleAPI를 사용하여 UI에서 수명 주기 인식 방식으로 흐름을 수집해야 한다.<br />
<code class="language-plaintext highlighter-rouge">collectAsStateWithLifecycle</code>를 사용하면 앱이 백그라운드에 있어 사용하지 않을 때 불필요한 리소스 낭비를 방지할 수 있다.</p>

<h2 id="collectasstate">collectAsState</h2>

<p>Ui 에서 mutableStateFlow.value만 사용해서는 값이 변경되었을 때 리컴포지션이 일어나지 않는다. <code class="language-plaintext highlighter-rouge">collectAsState</code>을 통해서 flow를 state로 변환해주고 State.value를 Ui 에서 사용하면 값이 변경되었을 때 리컴포지션이 일어난다.</p>

<p><code class="language-plaintext highlighter-rouge">collectAsState</code> 는 컴포지션의 수명 주기를 따릅니다. 컴포저블이 컴포지션에 들어갈 때 흐름 수집을 시작하고 컴포지션을 떠날 때 수집을 중지합니다.<br />
Android 앱이 백그라운드에 있는 동안 Compose가 재구성을 중단하더라도 <strong>collectAsState 컬렉션을 활성 상태로 유지</strong>합니다. 이로 인해 나머지 계층 구조에서 리소스를 확보할 수 없습니다.</p>

<p><code class="language-plaintext highlighter-rouge">collectAsState</code> 는 다른 플랫폼용으로 개발할 때 사용, <code class="language-plaintext highlighter-rouge">collectAsStateWithLifecycle</code> 은 Android 앱을 개발할 때 사용한다.</p>

<h2 id="collectasstatewithlifecycle">CollectAsStateWithLifecycle</h2>

<p><code class="language-plaintext highlighter-rouge">collectAsStateWithLifecycle</code> 흐름에서 값을 수집하고 State 수명 주기 인식 방식으로 Compose로 최신 값을 나타내는 Composable 함수이다. 새로운 flow 방출이 발생할 때마다 이 State객체의 값이 업데이트된다. 이로 인해 State.value를 사용한 컴포저블 함수는 리컴포지션된다.</p>

<p><br /></p>

<p><img src="https://github.com/DaeYoungee/Compose_study/assets/121485300/ada831ab-d658-499e-bef9-23ba7e1c9304" alt="image" /></p>

<p><code class="language-plaintext highlighter-rouge">collectAsStateWithLifecycle</code>는 minActiveState 매개변수를 통해 flow의 값을 수집하는 생명 주기를 정할 수 있다. <code class="language-plaintext highlighter-rouge">Lifecycle.State.STARTED</code>는 디폴트 값으로 Activity가 시작되어 <strong>onStart</strong>가 호출된 직후부터 <strong>onPause</strong>가 호출되기 직전까지 flow의 수집을 가능하게 한다는 뜻이다.</p>

<p><br /></p>

<p><img width="403" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/bd7c7d84-18d7-4c9e-b5fa-024aa905220b" /></p>

<h2 id="종속-항목-추가">종속 항목 추가</h2>

<p><code class="language-plaintext highlighter-rouge">CollectAsStateWithLifecycle</code>를 사용하기 위해서는 module 수준의 gradle파일에 종속 항목을 추가해줘야 한다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">dependencies</span> <span class="p">{</span>
    <span class="n">implementation</span> <span class="s">"androidx.lifecycle:lifecycle-runtime-compose:2.6.0"</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="reference">Reference</h2>

<p><a href="https://medium.com/androiddevelopers/consuming-flows-safely-in-jetpack-compose-cde014d0d5a3">medium</a></p>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="Compose" /><summary type="html"><![CDATA[Compose에서 CollectAsState의 사용과 CollectAsStateWithLifecycle 필요성에 대해 알아보자.]]></summary></entry><entry><title type="html">[Kotlin] get a picture from a gallery</title><link href="https://daeyoungee.github.io/kotlin/Gallery/" rel="alternate" type="text/html" title="[Kotlin] get a picture from a gallery" /><published>2024-04-20T00:00:00+00:00</published><updated>2024-04-20T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/Gallery</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/Gallery/"><![CDATA[<p>갤러리에서 사진을 가져오려면 먼저 <strong>Content Provider</strong>와 <strong>Content Provider</strong>를 알아야 한다.</p>

<p><span style="font-size:12pt; font-weight:bold;">- 콘텐츠 프로바이더 (Content Provider)</span><br />
다른 앱의 데이터를 제공받아 사용하려면 <strong>Content Provider</strong>를 구현해야한다. 다른 앱 또는 안드로이드 OS에 이미 구현되어 이쓴 ContentProvider로부터 데이터를 제공받아 사용한다.</p>

<p><span style="font-size:12pt; font-weight:bold;">- 콘텐츠 리졸버 (Content Resolver)</span><br />
<strong>Content Provider</strong>로부터 데이터를 가져오는 도구가 <strong>Content Resolver</strong>이다. 안드로이드에 있는 연락처 갤러리, 음악 같은 기본 데이터를 이용하는 용도로 많이 사용된다.</p>

<h2 id="갤러리에서-사진-가져오기">갤러리에서 사진 가져오기</h2>

<p>기존의 안드로이드에서 갤러리에서 이미지를 가져오려면 <code class="language-plaintext highlighter-rouge">registerForActivityResult</code>를 이용했던 기억이 있다.<br />
Compose에서는 <code class="language-plaintext highlighter-rouge">rememberLauncherForActivityResult</code>를 이용해서 갤러리로부터 사진을 가져온다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 갤러리에서 사진 가져오기</span>
<span class="kd">val</span> <span class="py">takePhotoFromAlbumLauncher</span> <span class="p">=</span> <span class="nf">rememberLauncherForActivityResult</span><span class="p">(</span><span class="nc">ActivityResultContracts</span><span class="p">.</span><span class="nc">StartActivityForResult</span><span class="p">())</span> <span class="p">{</span> <span class="n">result</span> <span class="p">-&gt;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="n">resultCode</span> <span class="p">==</span> <span class="nc">Activity</span><span class="p">.</span><span class="nc">RESULT_OK</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">result</span><span class="p">.</span><span class="n">data</span><span class="o">?.</span><span class="n">data</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">uri</span> <span class="p">-&gt;</span>
                <span class="nf">getImageUrl</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="n">resultCode</span> <span class="p">!=</span> <span class="nc">Activity</span><span class="p">.</span><span class="nc">RESULT_CANCELED</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// ..</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>갤러리에서 사진을 가져오는 건 <code class="language-plaintext highlighter-rouge">GetContent()</code>로도 가능하지만, 이는 MIME 타입을 하나밖에 지정하지 못한다. 이미지 중에서 jpeg, png, bmp, webp 를 가져와야 한다면 <code class="language-plaintext highlighter-rouge">StartActivityForResult()</code>로 하고 인텐트를 따로 지정해줘야 한다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">takePhotoFromAlbumIntent</span> <span class="p">=</span>
    <span class="nc">Intent</span><span class="p">(</span><span class="nc">Intent</span><span class="p">.</span><span class="nc">ACTION_GET_CONTENT</span><span class="p">,</span> <span class="nc">MediaStore</span><span class="p">.</span><span class="nc">Images</span><span class="p">.</span><span class="nc">Media</span><span class="p">.</span><span class="nc">EXTERNAL_CONTENT_URI</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">type</span> <span class="p">=</span> <span class="s">"image/*"</span>
        <span class="n">action</span> <span class="p">=</span> <span class="nc">Intent</span><span class="p">.</span><span class="nc">ACTION_GET_CONTENT</span>
        <span class="nf">putExtra</span><span class="p">(</span>
            <span class="nc">Intent</span><span class="p">.</span><span class="nc">EXTRA_MIME_TYPES</span><span class="p">,</span>
            <span class="nf">arrayOf</span><span class="p">(</span><span class="s">"image/jpeg"</span><span class="p">,</span> <span class="s">"image/png"</span><span class="p">,</span> <span class="s">"image/bmp"</span><span class="p">,</span> <span class="s">"image/webp"</span><span class="p">)</span>
        <span class="p">)</span>
        <span class="nf">putExtra</span><span class="p">(</span><span class="nc">Intent</span><span class="p">.</span><span class="nc">EXTRA_ALLOW_MULTIPLE</span><span class="p">,</span> <span class="k">false</span><span class="p">)</span>
    <span class="p">}</span>
</code></pre></div></div>

<h2 id="multipart로-캐스팅">Multipart로 캐스팅</h2>

<p>갤러리로부터 이미지를 가져와서 백엔드와 api 통신을 할 경우 이미지를 <strong>Multipart</strong>로 캐스팅해줘야 한다.
갤러리로부터 이미지를 가져오는 경우 Uri 타입으로 이미지가 반환되고 Uri를 <strong>Multipart</strong>로 캐스팅한다.</p>

<p><br /></p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">contentResolver</span><span class="p">.</span><span class="nf">query</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="k">null</span><span class="p">)</span>  <span class="c1">// cursor 반환</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">contentResolver.query(uri, ...)</code>는 해당 uri에 있는 데이터에 순차적으로 접근하기 위한 코드이다. 여기서는 <strong>Intent</strong>에서 설정했던 <strong>MediaStore.Images.Media.EXTERNAL_CONTENT_URI</strong>에 있는 이미지에 접근하게 된다.</p>

<p><br /></p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">displayName</span> <span class="p">=</span> <span class="n">it</span><span class="p">.</span><span class="nf">getString</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="nf">getColumnIndex</span><span class="p">(</span><span class="nc">OpenableColumns</span><span class="p">.</span><span class="nc">DISPLAY_NAME</span><span class="p">))</span>
</code></pre></div></div>

<p><strong>OpenableColumns.DISPLAY_NAME</strong> 은 파일이나 다른 컨텐츠 프로바이더에서 열 수 있는 컨텐츠의 Column 이름 중 하나이다. 이것을 사용하면 파일의 이름을 쉽게 얻을 수 있다.</p>

<p><br /></p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">writeTo</span><span class="p">(</span><span class="n">sink</span><span class="p">:</span> <span class="nc">BufferedSink</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">sink</span><span class="p">.</span><span class="nf">writeAll</span><span class="p">(</span><span class="n">contentResolver</span><span class="p">.</span><span class="nf">openInputStream</span><span class="p">(</span><span class="k">this</span><span class="nd">@asMultipart</span><span class="p">)</span><span class="o">?.</span><span class="nf">source</span><span class="p">()</span><span class="o">!!</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>이 함수의 목적은 <strong>RequestBody</strong>의 내용을 <strong>sink</strong>에 쓰는 것입니다. 이때 <strong>sink</strong>는 HTTP 요청의 바디를 보내기 위한 스트림입니다.</p>

<p><br /></p>

<ul>
  <li>
    <p>MultipartBody로 바꾸는 전체 코드</p>

    <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nc">Uri</span><span class="p">.</span><span class="nf">asMultipart</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">contentResolver</span><span class="p">:</span> <span class="nc">ContentResolver</span><span class="p">):</span> <span class="nc">MultipartBody</span><span class="p">.</span><span class="nc">Part</span><span class="p">?</span> <span class="p">{</span>
  <span class="k">return</span> <span class="n">contentResolver</span><span class="p">.</span><span class="nf">query</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="k">null</span><span class="p">)</span><span class="o">?.</span><span class="nf">use</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="nf">moveToNext</span><span class="p">())</span> <span class="p">{</span>
          <span class="kd">val</span> <span class="py">displayName</span> <span class="p">=</span> <span class="n">it</span><span class="p">.</span><span class="nf">getString</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="nf">getColumnIndex</span><span class="p">(</span><span class="nc">OpenableColumns</span><span class="p">.</span><span class="nc">DISPLAY_NAME</span><span class="p">));</span>
          <span class="nc">Log</span><span class="p">.</span><span class="nf">d</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"displayName: $displayName"</span><span class="p">)</span>
          <span class="kd">val</span> <span class="py">requestBody</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">RequestBody</span><span class="p">()</span> <span class="p">{</span>
              <span class="k">override</span> <span class="k">fun</span> <span class="nf">contentType</span><span class="p">():</span> <span class="nc">MediaType</span><span class="p">?</span> <span class="p">{</span>
                  <span class="k">return</span> <span class="n">contentResolver</span><span class="p">.</span><span class="nf">getType</span><span class="p">(</span><span class="k">this</span><span class="nd">@asMultipart</span><span class="p">)</span><span class="o">?.</span><span class="nf">toMediaType</span><span class="p">()</span>
              <span class="p">}</span>

              <span class="k">override</span> <span class="k">fun</span> <span class="nf">writeTo</span><span class="p">(</span><span class="n">sink</span><span class="p">:</span> <span class="nc">BufferedSink</span><span class="p">)</span> <span class="p">{</span>
                  <span class="n">sink</span><span class="p">.</span><span class="nf">writeAll</span><span class="p">(</span><span class="n">contentResolver</span><span class="p">.</span><span class="nf">openInputStream</span><span class="p">(</span><span class="k">this</span><span class="nd">@asMultipart</span><span class="p">)</span><span class="o">?.</span><span class="nf">source</span><span class="p">()</span><span class="o">!!</span><span class="p">)</span>
              <span class="p">}</span>
          <span class="p">}</span>
          <span class="nc">MultipartBody</span><span class="p">.</span><span class="nc">Part</span><span class="p">.</span><span class="nf">createFormData</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">displayName</span><span class="p">,</span> <span class="n">requestBody</span><span class="p">)</span>
      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
          <span class="k">null</span>
      <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="api-통신">API 통신</h2>

<p><code class="language-plaintext highlighter-rouge">Uri.asMultipart()</code>로 부터 반환된 MultipartBody를 통해 백엔드와 api 통신을 한다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">getImageUrl</span><span class="p">(</span><span class="n">uri</span><span class="p">:</span> <span class="nc">Uri</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="nc">Context</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">uri</span><span class="p">.</span><span class="nf">asMultipart</span><span class="p">(</span><span class="s">"file"</span><span class="p">,</span> <span class="n">context</span><span class="p">.</span><span class="n">contentResolver</span><span class="p">)</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">multipartBody</span> <span class="p">-&gt;</span>
        <span class="nf">postImageUseCase</span><span class="p">(</span><span class="n">multipartBody</span><span class="p">)</span>
    <span class="p">}</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">multipart</span> <span class="p">-&gt;</span>
        <span class="k">when</span> <span class="p">(</span><span class="kd">val</span> <span class="py">apiState</span> <span class="p">=</span> <span class="n">multipart</span><span class="p">.</span><span class="nf">first</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">is</span> <span class="nc">ApiState</span><span class="p">.</span><span class="nc">Success</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;</span> <span class="p">-&gt;</span> <span class="p">{</span>
                <span class="nc">Log</span><span class="p">.</span><span class="nf">d</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"getImageUrl success: ${apiState.value as ImageUrl}"</span><span class="p">)</span>
                <span class="n">userInfo</span> <span class="p">=</span> <span class="n">userInfo</span><span class="p">.</span><span class="nf">copy</span><span class="p">(</span><span class="n">imageUrl</span> <span class="p">=</span> <span class="p">(</span><span class="n">apiState</span><span class="p">.</span><span class="n">value</span><span class="p">).</span><span class="n">imageUrl</span><span class="p">)</span>
            <span class="p">}</span>

            <span class="k">is</span> <span class="nc">ApiState</span><span class="p">.</span><span class="nc">Error</span> <span class="p">-&gt;</span> <span class="p">{</span>
                <span class="nc">Log</span><span class="p">.</span><span class="nf">d</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"getImageUrl Error: ${apiState.errMsg}"</span><span class="p">)</span>
            <span class="p">}</span>

            <span class="nc">ApiState</span><span class="p">.</span><span class="nc">Loading</span> <span class="p">-&gt;</span> <span class="nc">TODO</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="reference">Reference</h2>

<p><a href="https://velog.io/@taeyang/Kotlin-%EC%BD%98%ED%85%90%ED%8A%B8-%EB%A6%AC%EC%A1%B8%EB%B2%84">Content Resolver</a><br />
<a href="https://medium.com/sungbinland/jetpack-compose-%EA%B0%A4%EB%9F%AC%EB%A6%AC-%EC%B9%B4%EB%A9%94%EB%9D%BC-%EC%97%90%EC%84%9C-%EC%82%AC%EC%A7%84-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0-cf517eaca8bd">Compose, 갤러리에서 사진 가져오기</a><br />
<a href="https://developer.android.com/training/data-storage/shared/photopicker?hl=ko">안드로이드 공식 홈페이지 사진 선택 도구</a><br />
<a href="https://thdev.tech/kotlin/2020/11/03/kotlin_effective_09/">kotlin에서 use 사용</a></p>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="Gallery" /><summary type="html"><![CDATA[Kotlin Gallery에서 사진을 가져오고 Multipart로 파싱해보자.]]></summary></entry><entry><title type="html">[Kotlin]Flow Intermediary(중간연산자)</title><link href="https://daeyoungee.github.io/kotlin/FlowIntermediary/" rel="alternate" type="text/html" title="[Kotlin]Flow Intermediary(중간연산자)" /><published>2024-04-17T00:00:00+00:00</published><updated>2024-04-17T00:00:00+00:00</updated><id>https://daeyoungee.github.io/kotlin/FlowIntermediary</id><content type="html" xml:base="https://daeyoungee.github.io/kotlin/FlowIntermediary/"><![CDATA[<p>flow는 <strong>순차적으로 값을 배출</strong>하는 <strong>비동기 데이터 스트림</strong>이다. 플로우에서는 값이 순차적으로 흐르는데 플로우 생성과 최종 연산 사이 중간연산이 가능하다. 이러한 연산들을 <strong>플로우 처리</strong>(flow processing)이라고 한다.</p>

<h3 id="map">map</h3>

<p>가장 먼저 배울 중요한 함수는 flow의 값을 입맛에 맞게 변형하는 map 함수이다.<br />
<code class="language-plaintext highlighter-rouge">map()</code>은 데이터를 순차적으로 변형하고 새로운 flow를 반환한다.</p>

<p><img width="312" alt="image" src="https://github.com/DaeYoungee/Compose_study/assets/121485300/5a91e00e-e937-4e58-9390-dff3a49cbbd8" /></p>

<p><br /></p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="nf">flowOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>                   <span class="c1">// [1, 2, 3]</span>
        <span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span> <span class="p">*</span> <span class="n">it</span> <span class="p">}</span>              <span class="c1">// [1, 4, 9]</span>
        <span class="p">.</span><span class="nf">collect</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>        <span class="c1">// 149</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="filter">filter</h3>

<p>filter 또한 중요한 함수이며, 원래 플로우에서 <strong>주어진 조건에 맞는 값들만 가진 플로우</strong>로 반환한다.<br />
<strong>filter는 관심 없는 원소를 제거할 때 주로 사용된다.</strong></p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="p">).</span><span class="nf">asFlow</span><span class="p">()</span>                     <span class="c1">// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]</span>
        <span class="p">.</span><span class="nf">filter</span> <span class="p">{</span> <span class="n">it</span> <span class="p">&lt;=</span> <span class="mi">5</span> <span class="p">}</span>              <span class="c1">// [1, 2, 3, 4, 5]</span>
        <span class="p">.</span><span class="nf">filter</span> <span class="p">{</span> <span class="n">it</span> <span class="p">%</span> <span class="mi">2</span> <span class="p">==</span> <span class="mi">0</span> <span class="p">}</span>          <span class="c1">// [2, 4]</span>
        <span class="p">.</span><span class="nf">collect</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>           <span class="c1">// 24</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="take">take</h3>

<p><strong>특정 수의 원소만 통과</strong>시키기 위해 take를 사용한다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="p">(</span><span class="sc">'A'</span><span class="o">..</span><span class="sc">'Z'</span><span class="p">).</span><span class="nf">asFlow</span><span class="p">()</span>                 <span class="c1">// [A, B, C, .. , Z]</span>
        <span class="p">.</span><span class="nf">take</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>                        <span class="c1">// [A, B, C, D, E]</span>
        <span class="p">.</span><span class="nf">collect</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>          <span class="c1">// ABCDE</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="drop">drop</h3>

<p><strong>특정 수의 원소를 무시</strong>시키기 위해 drop을 사용한다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="p">(</span><span class="sc">'A'</span><span class="o">..</span><span class="sc">'Z'</span><span class="p">).</span><span class="nf">asFlow</span><span class="p">()</span>                <span class="c1">// [A, B, C, .. , Z]</span>
        <span class="p">.</span><span class="nf">drop</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>                      <span class="c1">// [U, V, W, X, Y, Z]</span>
        <span class="p">.</span><span class="nf">collect</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>         <span class="c1">// UVWXYZ</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="flow-결합">flow 결합</h2>

<p>여러개의 flow를 합칠 때 3가지의 메서드가 있다. <code class="language-plaintext highlighter-rouge">merge</code>, <code class="language-plaintext highlighter-rouge">zip</code>, <code class="language-plaintext highlighter-rouge">combine</code></p>

<h3 id="merge">merge</h3>

<p><strong>두개의 flow를 하나로 합칠 때</strong> merge를 사용한다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">ints</span><span class="p">:</span> <span class="nc">Flow</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">flowOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">doubles</span><span class="p">:</span> <span class="nc">Flow</span><span class="p">&lt;</span><span class="nc">Double</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">flowOf</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">,</span> <span class="mf">0.3</span><span class="p">)</span>

    <span class="kd">val</span> <span class="py">together</span><span class="p">:</span> <span class="nc">Flow</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">merge</span><span class="p">(</span><span class="n">ints</span><span class="p">,</span> <span class="n">doubles</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">together</span><span class="p">.</span><span class="nf">toList</span><span class="p">())</span>

    <span class="c1">// [1, 0.1, 0.2, 0.3, 2, 3]</span>
    <span class="c1">// 또는 [1, 0.1, 2, 3, 2, 0.2, 0.3]</span>
    <span class="c1">// 또는 [1, 2, 3, 0.1, 0.2, 0.3]</span>
    <span class="c1">// 또는 [0.1, 1, 0.2, 0.3, 2, 3]</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">merge()</code>를 사용하면 한 플로우의 원소가 다른 플로우를 기다리지 않는다는 것이 중요하다. 예를 들어 첫 번째 플로우의 원소 생성이 지연된다고 해서 두 번째 플로우의 원소 생성이 중단되지 않는다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">ints</span><span class="p">:</span> <span class="nc">Flow</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">flowOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">).</span><span class="nf">onEach</span> <span class="p">{</span> <span class="nf">delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span> <span class="p">}</span>
    <span class="kd">val</span> <span class="py">doubles</span><span class="p">:</span> <span class="nc">Flow</span><span class="p">&lt;</span><span class="nc">Double</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">flowOf</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">,</span> <span class="mf">0.3</span><span class="p">)</span>

    <span class="kd">val</span> <span class="py">together</span><span class="p">:</span> <span class="nc">Flow</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">merge</span><span class="p">(</span><span class="n">ints</span><span class="p">,</span> <span class="n">doubles</span><span class="p">)</span>
    <span class="n">together</span><span class="p">.</span><span class="nf">collect</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>

    <span class="c1">// 0.1</span>
    <span class="c1">// 0.2</span>
    <span class="c1">// 0.3</span>
    <span class="c1">// (1초 후)</span>
    <span class="c1">// 1</span>
    <span class="c1">// (1초 후)</span>
    <span class="c1">// 2</span>
    <span class="c1">// (1초 후)</span>
    <span class="c1">// 3</span>
<span class="p">}</span>
</code></pre></div></div>

<p>실제 사용에서는 <code class="language-plaintext highlighter-rouge">merge()</code>는 여러 개의 이벤트들을 똑같은 방법으로 처리할 떄 사용한다.</p>

<h3 id="zip"> zip</h3>

<p>…</p>

<h2 id="flatmap">FlatMap</h2>

<p>컬렉션에서 잘 알려진 도 다른 함수는 flatMap이다. 컬렉션의 경우 flatMap은 map과 비슷하지만 변환 함수가 평탄화된 컬렉션을 반환한다는 점이 다르다.<br />
<code class="language-plaintext highlighter-rouge">map()</code>은 flow내의 원소를 내 입맛대로 변형하여 flow로 반환한다면,<br />
<code class="language-plaintext highlighter-rouge">flatMap()</code>은 flow와 다른 flow를 결합하여 새로운 flow를 반환한다.</p>

<h3 id="flatmapconcat">FlatMapConcat</h3>

<p>flatMapConcat은 평탄화 관련 중간 연산자에 있어 가장 기본이 되는 메서드이다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="nf">flowOf</span><span class="p">(</span><span class="s">"A"</span><span class="p">,</span><span class="s">"B"</span><span class="p">,</span><span class="s">"C"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">flatMapConcat</span> <span class="p">{</span> <span class="nf">productNewFlow</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">collect</span> <span class="p">{</span> <span class="nf">println</span><span class="p">(</span><span class="s">"flatMapConcatTest: $it"</span><span class="p">)</span> <span class="p">}</span>

    <span class="cm">/* 결과값
    1 + A
    2 + A
    3 + A
    1 + B
    2 + B
    3 + B
    1 + C
    2 + C
    3 + C
    */</span>
<span class="p">}</span>
<span class="k">fun</span> <span class="nf">productNewFlow</span><span class="p">(</span><span class="n">element</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">=</span>
    <span class="nf">flowOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">onEach</span> <span class="p">{</span> <span class="nf">delay</span><span class="p">(</span><span class="mi">1000L</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="s">"$it + $element"</span> <span class="p">}</span>
</code></pre></div></div>

<p><br /></p>

<p>flatMapConcat은 하위 데이터 스트림의 생산부 처리를 모두 끝내고 상위 데이터 스트림의 값을 차례대로 방출한다.</p>

<p>상위 데이터 스트림의 첫 번째 원소인 A를 방출 -&gt; 하위 데이터 스트림 연산 -&gt; 상위 데이터 스트림의 두 번째 원소인 B를 방출-&gt; 하위 데이터 스트림 연산</p>

<p>이 와 같은 순서로 이뤄진다.</p>

<blockquote>
  <p><span style="font-size:13pt; color:rgb(235, 64, 52)">즉, FlatMapConcat은 동기적으로 스트림을 결합한다.</span></p>
</blockquote>

<h3 id="flatmapmerge">flatMapMerge</h3>

<p>flatMapConcat은 하위 데이터 스트림의 생산부 처리와 상관없이 상위 데이터 스트림의 값을 차례대로 방출한다. 즉, 하위 데이터 스트림의 생산부 처리가 끝나지 않아도 상위 데이터 스트림의 값은 계속해서 방출된다는 뜻이다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="nf">flowOf</span><span class="p">(</span><span class="s">"A"</span><span class="p">,</span> <span class="s">"B"</span><span class="p">,</span> <span class="s">"C"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">flatMapMerge</span> <span class="p">{</span> <span class="nf">productNewFlow</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">collect</span> <span class="p">{</span> <span class="nf">println</span><span class="p">(</span><span class="s">"flatMapMergeTest: $it"</span><span class="p">)</span> <span class="p">}</span>

    <span class="cm">/*결과값
    1 + A
    1 + B
    1 + C
    2 + A
    2 + B
    2 + C
    3 + A
    3 + C
    3 + B
    */</span>

    <span class="c1">// or</span>
    <span class="cm">/*
    1 + C
    1 + A
    1 + B
    2 + B
    2 + A
    2 + C
    3 + A
    3 + C
    3 + B
     */</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">productNewFlow</span><span class="p">(</span><span class="n">element</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">=</span>
    <span class="nf">flowOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">onEach</span> <span class="p">{</span> <span class="nf">delay</span><span class="p">(</span><span class="mi">1000L</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="s">"$it + $element"</span> <span class="p">}</span>
</code></pre></div></div>

<p><br /></p>

<p>상위 데이터 스트림의 원소인 A, B, C를 동시에 방출 -&gt; 하위 데이터 스트림 연산(1초 delay) -&gt; 하위 데이터 스트림 연산((1 + A), (1 + B), (1 + C)를 동시에 방출) -&gt; 하위 데이터 스트림 연산(1초 delay) -&gt; 하위 데이터 스트림 연산((2 + A), (2 + B), (2 + C)를 동시에 방출)</p>

<p>이 와 같은 순서로 이뤄진다.</p>

<blockquote>
  <p><span style="font-size:13pt; color:rgb(235, 64, 52)">즉, flatMapMerge는 비동기적으로 스트림을 결합한다.</span></p>
</blockquote>

<h3 id="flatmaplatest">flatMapLatest</h3>

<p>flatMapLatest은 동기적인 스트림을 지원한다.<br />
flatMapLatest 함수는 새로운 플로우가 나타나면 이전에 처리하던 플로우를 잊어버린다.새로운 값이 나올 떄마다 이전 플로우 처리는 사라져 버린다. <strong>flatMapLatest</strong>는 <strong>flatMapConcat</strong>, <strong>flatMapMerge</strong> 와는쓰임새가 다르다.</p>

<blockquote>
  <p><span style="font-size:12pt; font-weight:bold;">[flatMapConcat과 flatMapMerge의 공통점]</span><br />
상위 스트림의 데이터 생산 속도 &gt; 하위 스트림의 데이터 생산 속도</p>
</blockquote>

<blockquote>
  <p><span style="font-size:12pt; font-weight:bold;"> flatMapLatest</span><br />
상위 스트림의 데이터 생산 속도 &lt; 하위 스트림의 데이터 생산 속도</p>
</blockquote>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="nf">flowOf</span><span class="p">(</span><span class="s">"A"</span><span class="p">,</span><span class="s">"B"</span><span class="p">,</span><span class="s">"C"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">onEach</span> <span class="p">{</span> <span class="nf">delay</span><span class="p">(</span><span class="mi">1200L</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">flatMapLatest</span> <span class="p">{</span> <span class="nf">productNewFlow</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">collect</span> <span class="p">{</span> <span class="nf">println</span><span class="p">(</span><span class="s">"flatMapLatestTest: $it"</span><span class="p">)</span> <span class="p">}</span>

    <span class="cm">/*결과값
    1 + A
    1 + B
    1 + C
    2 + C
    3 + C
    */</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">productNewFlow</span><span class="p">(</span><span class="n">element</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">=</span>
    <span class="nf">flowOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">onEach</span> <span class="p">{</span> <span class="nf">delay</span><span class="p">(</span><span class="mi">1000L</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="s">"$it + $element"</span> <span class="p">}</span>
</code></pre></div></div>

<p><br /></p>

<p>상위 데이터 스트림 연산(1.2초 delay)<br />
-&gt; 상위 데이터 스트림의 첫 번째 원소인 A를 방출<br />
-&gt; 하위 데이터 스트림 연산(1초 delay)와 동시에 상위 데이터 스트림 연산(1.2초 delay)<br />
-&gt; 하위 데이터 스트림 연산(1 + A를방출)<br />
-&gt; 하위 데이터 스트림 연산(1초 delay), 상위 데이터 스트림 연산(1.2초 delay)마치고 두 번째 원소인 B 방출<br />
-&gt; 하위 데이터 스트림 연산(0.8초 delay 멈춤)<br />
-&gt; 하위 데이터 스트림 연산(1초 delay)와 동시에 상위 데이터 스트림 연산(1.2초 delay)<br />
-&gt; 하위 데이터 스트림 연산(1 + B를방출)<br />
-&gt; 하위 데이터 스트림 연산(1초 delay), 상위 데이터 스트림 연산(1.2초 delay)마치고 두 번째 원소인 C 방출
-&gt; …</p>

<p>이 와 같은 순서로 이뤄진다.</p>

<p>위 코드를 보면 알다시피, 상위 스트림의 데이터 생산속도가 더 길다. 상위 스트림의 데이터 생산 속도는 1.2초마다 생산이 되며, 하위 스트림의 데이터 생산 속도는 1초이다. flatMapLatest는 Latest라는 이름이 포함된 것 처럼 최신의 값만 방출하는 것을 알 수 있다.</p>

<blockquote>
  <p><span style="font-size:12pt; font-weight:bold;">flatMapLatest는 하위 스트림 데이터 처리 여부를 상관하지 않는다.</span><br />
오로지 상위 스트림의 최신 데이터가 방출되면 기존에 진행되던 하위 스트림의 데이터 처리는 cancel시키고 상위 스트림에 맞는 데이터 처리를 진행한다.</p>
</blockquote>

<h2 id="flow-중복-제거-함수">flow 중복 제거 함수</h2>

<p>반복되는 원소가 동일하다고 판단되면 제거하면 <strong>distinctUntilChanged</strong> 함수도 아주 유용하다. 이 함수는 바로 이전의 원소와 동일한 원소만 제거한다.<br />
<strong>distinctUntilChangedBy</strong>는 두 원소가 동일한지 판단하기 위해 비교할 키(key)를 인자로 받는다.<br />
<strong>distinctUntilChanged</strong>는 람다 표현식을 받아 두 원소가 비교되는 방법을 정의한다.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">data class</span> <span class="nc">User</span><span class="p">(</span>
    <span class="kd">val</span> <span class="py">id</span><span class="p">:</span> <span class="nc">Int</span><span class="p">,</span>
    <span class="kd">val</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span>
<span class="p">)</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">toString</span><span class="p">():</span> <span class="nc">String</span> <span class="p">=</span> <span class="s">"[$id] $name"</span>
<span class="p">}</span>
<span class="k">suspend</span> <span class="k">fun</span> <span class="nf">main</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="nf">coroutineScope</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">users</span> <span class="p">=</span> <span class="nf">flowOf</span><span class="p">(</span>
        <span class="nc">User</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Alex"</span><span class="p">),</span>
        <span class="nc">User</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Bob"</span><span class="p">),</span>
        <span class="nc">User</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"Bob"</span><span class="p">),</span>
        <span class="nc">User</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"Celine"</span><span class="p">),</span>
    <span class="p">)</span>
    <span class="nf">println</span><span class="p">(</span><span class="n">users</span><span class="p">.</span><span class="nf">distinctUntilChangedBy</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">id</span> <span class="p">}.</span><span class="nf">toList</span><span class="p">())</span>
    <span class="nf">println</span><span class="p">(</span><span class="n">users</span><span class="p">.</span><span class="nf">distinctUntilChangedBy</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">name</span> <span class="p">}.</span><span class="nf">toList</span><span class="p">())</span>
    <span class="nf">println</span><span class="p">(</span><span class="n">users</span><span class="p">.</span><span class="nf">distinctUntilChanged</span> <span class="p">{</span> <span class="n">prev</span><span class="p">,</span> <span class="n">next</span> <span class="p">-&gt;</span> <span class="n">prev</span><span class="p">.</span><span class="n">id</span> <span class="p">==</span> <span class="n">next</span><span class="p">.</span><span class="n">id</span> <span class="p">||</span> <span class="n">prev</span><span class="p">.</span><span class="n">name</span> <span class="p">==</span> <span class="n">next</span><span class="p">.</span><span class="n">name</span> <span class="p">}.</span><span class="nf">toList</span><span class="p">())</span>

    <span class="cm">/*결과값
    [[1] Alex, [2] Bob]
    [[1] Alex, [1] Bob, [2] Celine]
    [[1] Alex, [2] Bob]
    */</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="reference">Reference</h2>

<p><a href="https://velog.io/@squart300kg/flatMapConcat-flatMapMerge-flatMapLatest%EC%B0%A8%EC%9D%B4">flatMapConcat, flatMapMerge, flatMapLatest</a></p>]]></content><author><name>DaeYoung</name></author><category term="Kotlin" /><category term="Kotlin" /><category term="Flow" /><category term="Intermediary" /><summary type="html"><![CDATA[Kotlin Flow에서 제공하는 중간연산자에 대해 알아보자.]]></summary></entry></feed>