<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hh_0705.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 27 Sep 2022 14:09:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hh_0705.log</title>
            <url>https://images.velog.io/images/hh_0705/profile/55e0fb3b-69ae-4e13-a8fc-bd6aa8629a4f/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hh_0705.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hh_0705" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[모의고사]]></title>
            <link>https://velog.io/@hh_0705/%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC</link>
            <guid>https://velog.io/@hh_0705/%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC</guid>
            <pubDate>Tue, 27 Sep 2022 14:09:22 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.</p>
<p>1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ...
3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, ...</p>
<p>1번 문제부터 마지막 문제까지의 정답이 순서대로 들은 배열 answers가 주어졌을 때, 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 return 하도록 solution 함수를 작성해주세요.</p>
<h4 id="제한-조건">제한 조건</h4>
<p>시험은 최대 10,000 문제로 구성되어있습니다.
문제의 정답은 1, 2, 3, 4, 5중 하나입니다.
가장 높은 점수를 받은 사람이 여럿일 경우, return하는 값을 오름차순 정렬해주세요.</p>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th>answers</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[1,2,3,4,5]</td>
<td>[1]</td>
</tr>
<tr>
<td>[1,3,2,4,2]</td>
<td>[1,2,3]</td>
</tr>
</tbody></table>
<hr>
<h3 id="풀이">풀이</h3>
<h4 id="내-풀이">내 풀이</h4>
<ol>
<li>찍는 방식이 반복되는 구간을 찾는다.</li>
<li>index=id-1, value=점수인 배열에 점수를 저장.</li>
<li>최고점을 찾고 최고점과 같은 점수 index+1을 배열에 담아 리턴.</li>
</ol>
<pre><code class="language-java">class Solution {
    public int[] solution(int[] answers) {
        int[] first = new int[]{1,2,3,4,5};
        int[] second = new int[]{2, 1, 2, 3, 2, 4, 2, 5};
        int[] third = new int[]{3, 3, 1, 1, 2, 2, 4, 4, 5, 5};

        int[] scores = new int[]{0,0,0};

        for (int i = 0; i &lt; answers.length; i++) {
            if (first[i % first.length] == answers[i]) {
                scores[0]++;
            }
            if (second[i % second.length] == answers[i]) {
                scores[1]++;
            }
            if (third[i % third.length] == answers[i]) {
                scores[2]++;
            }
        }

        int highest = Math.max(Math.max(scores[0], scores[1]), scores[2]);
        List&lt;Integer&gt; answer = new ArrayList&lt;&gt;();

        for (int i = 0; i &lt; scores.length; i++) {
            if (scores[i] == highest) {
                answer.add(i+1);
            }
        }

        return answer.stream().mapToInt(Integer::intValue).toArray();
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[최소직사각형]]></title>
            <link>https://velog.io/@hh_0705/%EC%B5%9C%EC%86%8C%EC%A7%81%EC%82%AC%EA%B0%81%ED%98%95</link>
            <guid>https://velog.io/@hh_0705/%EC%B5%9C%EC%86%8C%EC%A7%81%EC%82%AC%EA%B0%81%ED%98%95</guid>
            <pubDate>Tue, 27 Sep 2022 13:30:41 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>명함 지갑을 만드는 회사에서 지갑의 크기를 정하려고 합니다. 다양한 모양과 크기의 명함들을 모두 수납할 수 있으면서, 작아서 들고 다니기 편한 지갑을 만들어야 합니다. 이러한 요건을 만족하는 지갑을 만들기 위해 디자인팀은 모든 명함의 가로 길이와 세로 길이를 조사했습니다.</p>
<p>아래 표는 4가지 명함의 가로 길이와 세로 길이를 나타냅니다.</p>
<table>
<thead>
<tr>
<th>명함 번호</th>
<th>가로 길이</th>
<th>세로 길이</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>60</td>
<td>50</td>
</tr>
<tr>
<td>2</td>
<td>30</td>
<td>70</td>
</tr>
<tr>
<td>3</td>
<td>60</td>
<td>30</td>
</tr>
<tr>
<td>4</td>
<td>80</td>
<td>40</td>
</tr>
<tr>
<td>가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p>모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.</p>
<h4 id="제한사항">제한사항</h4>
<ul>
<li>sizes의 길이는 1 이상 10,000 이하입니다.</li>
<li>sizes의 원소는 [w, h] 형식입니다.</li>
<li>w는 명함의 가로 길이를 나타냅니다.</li>
<li>h는 명함의 세로 길이를 나타냅니다.</li>
<li>w와 h는 1 이상 1,000 이하인 자연수입니다.</li>
</ul>
<hr>
<h3 id="풀이">풀이</h3>
<h4 id="내-풀이">내 풀이</h4>
<ol>
<li>명함의 긴 변 중 가장 긴 변을 구한다.</li>
<li>명함의 짧은 변 중 가장 긴 변을 구한다.</li>
<li>둘을 곱해서 리턴.</li>
</ol>
<pre><code class="language-java">class Solution {
    public int solution(int[][] sizes) {
        int longSide = 0;
        int shortSide = 0;
        for (int[] size : sizes) {
            longSide = Math.max(longSide, Math.max(size[0], size[1]));
            shortSide = Math.max(shortSide, Math.min(size[0], size[1]));
        }
        return longSide * shortSide;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[올바른 괄호]]></title>
            <link>https://velog.io/@hh_0705/%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</link>
            <guid>https://velog.io/@hh_0705/%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</guid>
            <pubDate>Sun, 25 Sep 2022 13:45:02 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>괄호가 바르게 짝지어졌다는 것은 &#39;(&#39; 문자로 열렸으면 반드시 짝지어서 &#39;)&#39; 문자로 닫혀야 한다는 뜻입니다. 예를 들어</p>
<ul>
<li>&quot;()()&quot; 또는 &quot;(())()&quot; 는 올바른 괄호입니다.</li>
<li>&quot;)()(&quot; 또는 &quot;(()(&quot; 는 올바르지 않은 괄호입니다.</li>
</ul>
<p>&#39;(&#39; 또는 &#39;)&#39; 로만 이루어진 문자열 s가 주어졌을 때, 문자열 s가 올바른 괄호이면 true를 return 하고, 올바르지 않은 괄호이면 false를 return 하는 solution 함수를 완성해 주세요.</p>
<h4 id="제한사항">제한사항</h4>
<ul>
<li>문자열 s의 길이 : 100,000 이하의 자연수</li>
<li>문자열 s는 &#39;(&#39; 또는 &#39;)&#39; 로만 이루어져 있습니다.</li>
</ul>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th>s</th>
<th>answer</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;()()&quot;</td>
<td>true</td>
</tr>
<tr>
<td>&quot;(())()&quot;</td>
<td>true</td>
</tr>
<tr>
<td>&quot;)()(&quot;</td>
<td>false</td>
</tr>
<tr>
<td>&quot;(()(&quot;</td>
<td>false</td>
</tr>
</tbody></table>
<h4 id="입출력-예-설명">입출력 예 설명</h4>
<p>입출력 예 #1,2,3,4
문제의 예시와 같습니다.</p>
<hr>
<h4 id="나의-풀이">나의 풀이</h4>
<p>시도한 방법</p>
<ol>
<li>스택을 사용 -&gt; <span style="color: red">효율성 테스트 실패</span></li>
<li>int 사용</li>
</ol>
<p>풀이법
실패하는 경우를 먼저 생각하고 이외의 경우 성공으로 취급했다.
실패하는 경우 </p>
<ol>
<li><code>(</code> 개수가 많다.</li>
<li><code>)</code> 개수가 많다.</li>
<li><code>(</code>, <code>)</code> 개수는 맞지만 순서가 맞지 않는다.</li>
</ol>
<p>풀이 내용</p>
<ol>
<li><code>(</code> -&gt; stack/int에 값을 넣는다.</li>
<li><code>)</code> -&gt; stack/int에서 값을 뺀다. </li>
<li><ol>
<li>stack이 비었거나 int가 0인 경우: 앞서 연달아 들어왔던 <code>(</code> 개수보다 <code>)</code> 개수가 많다. -&gt; 실패하는 경우 2., 3. 해결</li>
</ol>
</li>
<li><ol start="2">
<li>String 끝까지 검증한 후 stack/int를 확인한다 -&gt; <code>(</code> 개수가 많은 경우 stack이 비어있지 않음/ int &gt; 0</li>
</ol>
</li>
</ol>
<p>구현</p>
<ol>
<li><p>Stack</p>
<pre><code class="language-java">class Solution {
 public boolean solution(String s) {
      Stack&lt;String&gt; stack = new Stack&lt;&gt;();

     for (int i = 0; i &lt; s.length(); i++) {
         String input = Character.toString(s.charAt(i));
         if (input.equals(&quot;(&quot;)) {
             stack.push(&quot;)&quot;);
         } else if (input.equals(&quot;)&quot;)) {
             if (stack.empty()) {
                 return false;
             }
             stack.pop();
         }
     }
     return stack.empty();
 }
}</code></pre>
<p>정확성 테스트는 성공. 효율성 테스트에서 실패.
클래스 -&gt; primitive type 사용으로 시간 단축.</p>
</li>
<li><p>int</p>
<pre><code class="language-java">class Solution {
 public boolean solution(String s) {
     int count = 0;

     for (int i = 0; i &lt; s.length(); i++) {
         if (s.charAt(i) == &#39;(&#39;) {
             count++;
         } else {
             if (count == 0) {
                 return false;
             }
             count--;
         }
     }

     return count == 0;
 }
}</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[가장 큰 수]]></title>
            <link>https://velog.io/@hh_0705/%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%88%98</link>
            <guid>https://velog.io/@hh_0705/%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%88%98</guid>
            <pubDate>Sun, 25 Sep 2022 11:19:38 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요.</p>
<p>예를 들어, 주어진 정수가 [6, 10, 2]라면 [6102, 6210, 1062, 1026, 2610, 2106]를 만들 수 있고, 이중 가장 큰 수는 6210입니다.</p>
<p>0 또는 양의 정수가 담긴 배열 numbers가 매개변수로 주어질 때, 순서를 재배치하여 만들 수 있는 가장 큰 수를 문자열로 바꾸어 return 하도록 solution 함수를 작성해주세요.</p>
<h4 id="제한-사항">제한 사항</h4>
<p>numbers의 길이는 1 이상 100,000 이하입니다.
numbers의 원소는 0 이상 1,000 이하입니다.
정답이 너무 클 수 있으니 문자열로 바꾸어 return 합니다.</p>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th>numbers</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[6, 10, 2]</td>
<td>&quot;6210&quot;</td>
</tr>
<tr>
<td>[3, 30, 34, 5, 9]</td>
<td>&quot;9534330&quot;</td>
</tr>
</tbody></table>
<hr>
<h3 id="풀이">풀이</h3>
<h4 id="내-풀이">내 풀이</h4>
<ol>
<li>나열했을 때 가장 크도록 정렬.</li>
</ol>
<p>처음에는 단순히</p>
<pre><code>Arrays.sort(array, Comparator.reverseOrder()); //array는 String[]</code></pre><p>로 정렬했는데 30이 3보다 앞으로 와서 실패.</p>
<p>Math.pow로 자리수 더하기 등등 이런저런 뻘짓을 하다가 깨달았다. 문제 조건 그대로 순서를 바꿔보면 되는 거구나..
3과 30이면 330과 303 비교하는 식.</p>
<pre><code class="language-java">import java.util.*;

class Solution {
    public String solution(int[] numbers) {

        List&lt;String&gt; strings = new ArrayList&lt;&gt;();
        Boolean isAllZero = true; // 11번 케이스 [0,0,0,0] 통과

        for (int number : numbers) {
            if (number != 0) {
                isAllZero = false;
            }
            strings.add(Integer.toString(number));
        }

        if(isAllZero) {
            return &quot;0&quot;;
        }

        strings.sort((o1, o2) -&gt; {
            String s1 = o1 + o2;
            String s2 = o2 + o1;
            return Integer.parseInt(s2) - Integer.parseInt(s1);
        });

        String answer = &quot;&quot;;

        for (String string : strings) {
            answer += string;
        }

        return answer;
    }
}</code></pre>
<h4 id="다른-사람-풀이">다른 사람 풀이</h4>
<p>프로그래머스에서 참고용으로 가져왔다.
주석은 내가 이해하려고 공부하면서 단 것.</p>
<pre><code class="language-java">class Solution {
    public String solution(int[] numbers) {
        String answer = &quot;&quot;;

        List&lt;Integer&gt; list = new ArrayList&lt;&gt;();
        for(int i = 0; i &lt; numbers.length; i++) {
            list.add(numbers[i]);
        }
        Collections.sort(list, (a, b) -&gt; {
            String as = String.valueOf(a), bs = String.valueOf(b);
            return -Integer.compare(Integer.parseInt(as + bs), Integer.parseInt(bs + as));
        });
        StringBuilder sb = new StringBuilder();
        for(Integer i : list) {
            sb.append(i);
        }
        answer = sb.toString();

        if(answer.charAt(0) == &#39;0&#39;) {
            return &quot;0&quot;;
        }else {
            return answer;
        }
    }
}</code></pre>
<pre><code class="language-java">class Solution {
    public String solution(int[] numbers) {
        String[] nums = new String[numbers.length];

        for (int i=0; i&lt;nums.length; i++) 
            nums[i] = numbers[i] + &quot;&quot;;

        Arrays.sort(nums, new Comparator&lt;String&gt;() {
            public int compare(String o1, String o2) {
                return (o2 + o1).compareTo(o1 + o2);
            }
        });

        String ans = &quot;&quot;;
        for (int i=0; i&lt;numbers.length; i++)
            ans += nums[i];

        return ans.charAt(0) == &#39;0&#39; ? &quot;0&quot; : ans;
    }
}</code></pre>
<p>StringBuilder가 +=보다 빠르다고 한다.
compare 부분도 참고해서 간결하게 고칠 수 있을 것 같다. </p>
<h4 id="코드-개선">코드 개선</h4>
<pre><code class="language-java">class Solution {
    public String solution(int[] numbers) {

        List&lt;String&gt; strings = new ArrayList&lt;&gt;();

        for (int number : numbers) {
            strings.add(Integer.toString(number));
        }

        strings.sort((o1, o2) -&gt; (o2 + o1).compareTo(o1 + o2));

        StringBuilder answer = new StringBuilder();

        for (String string : strings) {
            answer.append(string);
        }

        return answer.charAt(0) == &#39;0&#39;? &quot;0&quot; : answer.toString();
    }
}</code></pre>
<p>람다 축약. StringBuilder 사용.
값이 0인지 체크하는 부분도 다른분들 코드 참고해서 <code>isAllZero</code>에서 <code>answer.charAt(0) == &#39;0&#39;</code>으로 변경. </p>
<p>전부 0인 경우에는 <code>isAllZero</code> 체크로 sort하지 않고 빨리 리턴하고 싶었는데 <code>answer.charAt(0) == &#39;0&#39;</code> 체크가 훨씬 빠르다. 
값이 전부 0일 때 성능 향상 vs 그렇지 않을 때 성능 향상 (전부 0일 때도 같은 성능) 중에서 택하자면 후자를 선택하는 게 맞는 것 같다. 값이 전부 0인 케이스보다 그렇지 않은 케이스가 훨씬 많을테니까. </p>
<p><em><code>answer.charAt(0) == &#39;0&#39;</code> 에서 따옴표 주의할 것. <code>&#39;0&#39;</code>을 <code>&quot;0&quot;</code>로 쓰는 실수가 있었다.</em> </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[K번째수]]></title>
            <link>https://velog.io/@hh_0705/K%EB%B2%88%EC%A7%B8%EC%88%98</link>
            <guid>https://velog.io/@hh_0705/K%EB%B2%88%EC%A7%B8%EC%88%98</guid>
            <pubDate>Sun, 25 Sep 2022 09:00:31 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<h4 id="문제-설명">문제 설명</h4>
<p>배열 array의 i번째 숫자부터 j번째 숫자까지 자르고 정렬했을 때, k번째에 있는 수를 구하려 합니다.</p>
<p>예를 들어 array가 [1, 5, 2, 6, 3, 7, 4], i = 2, j = 5, k = 3이라면</p>
<ul>
<li>array의 2번째부터 5번째까지 자르면 [5, 2, 6, 3]입니다.</li>
<li>1에서 나온 배열을 정렬하면 [2, 3, 5, 6]입니다.</li>
<li>2에서 나온 배열의 3번째 숫자는 5입니다.</li>
</ul>
<p>배열 array, [i, j, k]를 원소로 가진 2차원 배열 commands가 매개변수로 주어질 때, commands의 모든 원소에 대해 앞서 설명한 연산을 적용했을 때 나온 결과를 배열에 담아 return 하도록 solution 함수를 작성해주세요.</p>
<h4 id="제한사항">제한사항</h4>
<ul>
<li>array의 길이는 1 이상 100 이하입니다.</li>
<li>array의 각 원소는 1 이상 100 이하입니다.</li>
<li>commands의 길이는 1 이상 50 이하입니다.</li>
<li>commands의 각 원소는 길이가 3입니다.</li>
</ul>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th>array</th>
<th>commands</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[1, 5, 2, 6, 3, 7, 4]</td>
<td>[[2, 5, 3], [4, 4, 1], [1, 7, 3]]</td>
<td>[5, 6, 3]</td>
</tr>
</tbody></table>
<h4 id="입출력-예-설명">입출력 예 설명</h4>
<p>[1, 5, 2, 6, 3, 7, 4]를 2번째부터 5번째까지 자른 후 정렬합니다. [2, 3, 5, 6]의 세 번째 숫자는 5입니다.
[1, 5, 2, 6, 3, 7, 4]를 4번째부터 4번째까지 자른 후 정렬합니다. [6]의 첫 번째 숫자는 6입니다.
[1, 5, 2, 6, 3, 7, 4]를 1번째부터 7번째까지 자릅니다. [1, 2, 3, 4, 5, 6, 7]의 세 번째 숫자는 3입니다.</p>
<hr>
<h3 id="풀이">풀이</h3>
<h4 id="내-풀이">내 풀이</h4>
<ol>
<li>i 번째부터 j 번째까지 값을 가진 리스트를 만든다. 인덱스로는 i - 1 부터 j - 1 까지.</li>
<li><ol>
<li>리스트를 정렬.</li>
</ol>
</li>
<li>2에서 나온 배열 중 k 번째 (인덱스로 k - 1) 값을 정답 리스트에 넣는다. </li>
<li>정답 리스트를 배열로 바꿈</li>
</ol>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Solution {
    public int[] solution(int[] array, int[][] commands) {
        List&lt;Integer&gt; answer = new ArrayList&lt;&gt;();
        for (int[] command : commands) {
            List&lt;Integer&gt; subList = new ArrayList&lt;&gt;();
            for (int i = command[0] - 1; i &lt; command[1]; i++) {
                subList.add(array[i]);
            }
            subList.sort(Comparator.comparingInt(a -&gt; a));
            answer.add(subList.get(command[2] - 1));
        }

        return answer.stream().mapToInt(i -&gt; i).toArray();
    }
}</code></pre>
<h4 id="다른-사람-풀이">다른 사람 풀이</h4>
<p>프로그래머스에서 참고용으로 다른 사람 풀이를 가져왔다.
주석은 공부용으로 내가 적은 것.</p>
<pre><code class="language-java">import java.util.Arrays;
class Solution {
    public int[] solution(int[] array, int[][] commands) {
        int[] answer = new int[commands.length];

        for(int i=0; i&lt;commands.length; i++){
        //array의 commands[i][0]-1 ~ commands[i][1](포함 안함) 까지 범위를 복사해서 새 배열 리턴
            int[] temp = Arrays.copyOfRange(array, commands[i][0]-1, commands[i][1]);
            //정렬
            Arrays.sort(temp);
            answer[i] = temp[commands[i][2]-1];
        }

        return answer;
    }
}</code></pre>
<p>위 방법을 사용하면 List를 생성 -&gt; List에 값 추가 -&gt; List를 배열로 변환하는 과정이 필요 없다.</p>
<p>Arrays.copyOfRange(원본Array, 시작, 끝) // 끝 포함 안함
Arrays.copyOf(원본Array, 끝) // 시작 값 아님. 끝 포함 안함.
Arrays.sort(array) </p>
<p>를 배웠다. </p>
<h4 id="풀이-개선">풀이 개선</h4>
<pre><code class="language-java">import java.util.Arrays;

public class Solution {
    public int[] solution(int[] array, int[][] commands) {
        int[] answer = new int[commands.length];
        for (int i = 0; i &lt; commands.length; i++) {
            int[] command = commands[i];
            int[] subArray = Arrays.copyOfRange(array, command[0] - 1, command[1]);
            Arrays.sort(subArray);
            answer[i] = subArray[command[2] - 1];
        }
        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin Examples > Kotlin JS]]></title>
            <link>https://velog.io/@hh_0705/Kotlin-Examples-Kotlin-JS</link>
            <guid>https://velog.io/@hh_0705/Kotlin-Examples-Kotlin-JS</guid>
            <pubDate>Mon, 04 Apr 2022 15:09:37 GMT</pubDate>
            <description><![CDATA[<p><em>2022-03-16 작성완료</em></p>
<h2 id="dynamic">dynamic</h2>
<hr>
<p>dynamic은 코틀린/js의 특별한 타입이다. 이것은 기본적으로 코틀린의 타입 확인자(체커)를 끈다. 이것은 타입이 없거나 루즈하게 타입이 설정되어 있는 자바스크립트와 같은 환경과 상호운용하기 위해 필요하다.</p>
<pre><code class="language-kotlin">val a: dynamic = &quot;abc&quot;                                               // 1
val b: String = a                                                    // 2

fun firstChar(s: String) = s[0]

println(&quot;${firstChar(a)} == ${firstChar(b)}&quot;)                        // 3

println(&quot;${a.charCodeAt(0, &quot;dummy argument&quot;)} == ${b[0].toInt()}&quot;)   // 4

println(a.charAt(1).repeat(3))                                       // 5

fun plus(v: dynamic) = v + 2

println(&quot;2 + 2 = ${plus(2)}&quot;)                                        // 6
println(&quot;&#39;2&#39; + 2 = ${plus(&quot;2&quot;)}&quot;)</code></pre>
<ol>
<li>어떤 값이든 <code>dynamic</code> 변수 값으로 할당될 수 있다.</li>
<li>dynamic 값은 어떤 것에든 할당될 수 있다.</li>
<li>dynamic 변수는 어떤 함수에든 아규먼트로써 전달될 수 있다.</li>
<li>어떤 프로퍼티든, 또는 어떤 아규먼트를 가진 함수이든 <code>dynamic</code> 변수에서 호출될 수 있다.</li>
<li><code>dynamic</code> 함수에서 호출된 함수는 언제나 다이나믹 값을 반환한다. 그러므로 호출 체인이 가능하다. </li>
<li>연산자, 할당, 그리고 인덱스를 통한 접근(<code>[..]</code>)은 &quot;있는 그대로(as is)&quot; 변환되므로 조심해야 한다. </li>
</ol>
<br>

<h2 id="js-function">JS function</h2>
<hr>
<p><code>js(&quot;...&quot;)</code> 함수를 사용해 코틀린 코드에 자바스크립트 코드를 인라인할 수 있다. 매우 조심스럽게 사용해야 한다.</p>
<pre><code class="language-kotlin">js(&quot;alert(\&quot;alert from Kotlin!\&quot;)&quot;) // 1</code></pre>
<ol>
<li>코틀린 함수에서 자바스크립트 alert를 보낸다.</li>
</ol>
<pre><code class="language-kotlin">val json = js(&quot;{}&quot;)               // 1
json.name = &quot;Jane&quot;                // 2
json.hobby = &quot;movies&quot;

println(JSON.stringify(json))     // 3</code></pre>
<ol>
<li>자바스크립트 객체 리터럴을 생성한다. <code>js(...)</code> 함수의 리턴 타입은 <code>dynamic</code>이다.</li>
<li><code>dynamic</code> 타입 기능을 이용해 프로퍼티를 추가한다.</li>
<li>JSON을 자바스크립트 API로 보낸다. </li>
</ol>
<br>

<h2 id="external-declarations">External declarations</h2>
<hr>
<p>external 키워드는 기존 자바스크립트 API를 type-safe한 방법으로 선언할 수 있도록 한다.</p>
<pre><code class="language-kotlin">external fun alert(msg: String)   // 1

fun main() {
  alert(&quot;Hi!&quot;)                    // 2
}</code></pre>
<ol>
<li>단일 <code>String</code> 아규먼트를 받는 기존 자바스크립트 함수 <code>alert</code>를 선언한다.</li>
<li>일반 코틀린처럼 <code>alert</code>를 사용한다.</li>
</ol>
<p>기존 자바스크립트 API 서술에 대해 더 많이 배우기 위해서는 <a href="https://kotlinlang.org/docs/reference/js-interop.html?&amp;_ga=2.77731284.1975964084.1647331891-1497297377.1644238287#external-modifier">문서</a>를 참조해 주기 바란다.</p>
<br>

<h2 id="canvas-hello-kotlin">Canvas (Hello Kotlin)</h2>
<hr>
<p>아래 예시는 HTML5 캔버스를 코틀린에서 사용하는 방법을 보여준다.</p>
<p>여기 두 이상한 생물들이 코틀린 로고를 보고 있다. 이 생물들과 코틀린 로고를 드래그 앤 드롭할 수 있다. 더블클릭으로 생물들을 추가할 수 있다. 그들이 당신을 볼 테니까 조심하자! (<a href="https://play.kotlinlang.org/byExample/09_Kotlin_JS/05_Canvas">예시 참고</a>)</p>
<pre><code class="language-kotlin">CanvasState(canvas).apply {
    addShape(Kotlin)
    addShape(Creature(size * 0.25, this))
    addShape(Creature(size * 0.75, this))
}</code></pre>
<br>

<h2 id="html-builder">Html Builder</h2>
<hr>
<p>코틀린은 빌더를 사용하여 선언적 스타일로 구조화된 데이터를 설명하는 옵션을 제공한다.</p>
<p>아래는 type-safe Groovy-style builder의 예시이다. 이 에시에서, HTML 페이지를 코틀린 내에서 describe할 수 있다. </p>
<pre><code class="language-kotlin">val result = html {                                            // 1
    head {                                                     // 2
        title { +&quot;HTML encoding with Kotlin&quot; }
    }
    body {                                                     // 2
        h1 { +&quot;HTML encoding with Kotlin&quot; }
        p {
            +&quot;this format can be used as an&quot;                   // 3
            +&quot;alternative markup to HTML&quot;                      // 3
        }

        // an element with attributes and text content
        a(href = &quot;http://kotlinlang.org&quot;) { +&quot;Kotlin&quot; }

        // mixed content
        p {
            +&quot;This is some&quot;
            b { +&quot;mixed&quot; }
            +&quot;text. For more see the&quot;
            a(href = &quot;http://kotlinlang.org&quot;) {
                +&quot;Kotlin&quot;
            }
            +&quot;project&quot;
        }
        p {
            +&quot;some text&quot;
            ul {
                for (i in 1..5)
                li { +&quot;${i}*2 = ${i*2}&quot; }
            }
        }
    }
}</code></pre>
<ol>
<li><p><code>html</code>은 사실 아규먼트로 람다 표현식을 받는 함수 호출이다. <code>html</code> 함수는 그 자체가 함수인 하나의 파라미터를 받는다. 함수의 타입은 <code>HTML.() -&gt; Unit</code>으로, 리시버가 있는 함수 타입이다. 이것은 <code>HTML</code> 타입 인스턴스(리시버)를 함수에 전달해야 하며, 이 인스턴스의 멤버들을 함수 내부에서 호출할 수 있음을 뜻한다. </p>
</li>
<li><p><code>head</code>와 <code>body</code>는 <code>HTML</code> 클래스의 멤버 함수이다.</p>
</li>
<li><p><code>unaryPlus()</code> 연산을 호출함으로써 태그에 텍스트를 추가할 수 있다. 예: <code>+&quot;HTML encoding with Kotlin&quot;</code>.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin Examples > Productivity Boosters]]></title>
            <link>https://velog.io/@hh_0705/Kotlin-Examples-Productivity-Boosters</link>
            <guid>https://velog.io/@hh_0705/Kotlin-Examples-Productivity-Boosters</guid>
            <pubDate>Mon, 04 Apr 2022 15:08:12 GMT</pubDate>
            <description><![CDATA[<p><em>2022-03-16 작성완료</em></p>
<h2 id="named-arguments">Named Arguments</h2>
<hr>
<p>대부분의 다른 프로그래밍 언어들과 같이(java, c++ 등), 코틀린은 정의된 순서에 따라 메서드와 생성자에 아규먼트를 전달할 수 있다. 코틀린은 또한 더 명확한 호출을 허용하고 아규먼트 순서에 따른 실수를 피할 수 있도록 <a href="https://kotlinlang.org/docs/reference/functions.html?&amp;_ga=2.83410646.1975964084.1647331891-1497297377.1644238287#named-arguments">named arguments</a>를 지원한다. 그러한(아규먼트 순서로 인한) 실수는 컴파일러에 의해 탐지되지 않으므로 발견하기 힘들다. 예를 들어 두 연속된 아규먼트가 같은 타입일 때 그렇다. </p>
<pre><code class="language-kotlin">fun format(userName: String, domain: String) = &quot;$userName@$domain&quot;

fun main() {
    println(format(&quot;mario&quot;, &quot;example.com&quot;))                         // 1
    println(format(&quot;domain.com&quot;, &quot;username&quot;))                       // 2
    println(format(userName = &quot;foo&quot;, domain = &quot;bar.com&quot;))           // 3
    println(format(domain = &quot;frog.com&quot;, userName = &quot;pepe&quot;))         // 4
}</code></pre>
<ol>
<li>아규먼트 값으로 함수를 호출한다.</li>
<li>순서가 바뀐 아규먼트로 함수를 호출한다. syntax 에러는 없지만, 결과 <em>domain.com@username</em> 은 부정확하다.</li>
<li>함수를 지명 인자(named arguments)로 호출한다.</li>
<li>지명 인자(named arguments)를 사용해 호출할 경우 원하는 어떤 순서로든 아규먼트들을 지정할 수 있다. </li>
</ol>
<br>

<h2 id="string-templates">String Templates</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/basic-types.html?&amp;_ga=2.147495671.1975964084.1647331891-1497297377.1644238287#string-templates">String templates</a>는 변수 참조 및 표현식을 문자열에 포함시킬 수 있도록 한다. 스트링의 값이 요구될 때 (예: <code>println</code>) 모든 참조와 표현식들은 실제 값으로 대체된다. </p>
<pre><code class="language-kotlin">val greeting = &quot;Kotliner&quot;

println(&quot;Hello $greeting&quot;)                  // 1 
println(&quot;Hello ${greeting.toUpperCase()}&quot;)  // 2</code></pre>
<ol>
<li>변수 참조를 가진 문자열을 프린트한다. 문자열에서 참조는 <code>$</code>로 시작한다.</li>
<li>표현식을 가진 문자열을 프린트한다. 문자열은 <code>$</code>로 시작하고 중괄호로 에워싸인다.</li>
</ol>
<br>

<h2 id="destructuring-declarations">Destructuring Declarations</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/multi-declarations.html?&amp;_ga=2.112964932.1975964084.1647331891-1497297377.1644238287#destructuring-declarations">Destructuring declaration</a> 구문은 매우 편리할 수 있다. 특히 멤버에 접근하기 위한 인스턴스가 필요할 때 편리하다. 이것은 지정된 이름 없이 인스턴스를 정의할 수 있게 해 주며, 그로 인해 코드 몇 줄을 줄일 수 있다. </p>
<pre><code class="language-kotlin">val (x, y, z) = arrayOf(5, 10, 15)                              // 1

val map = mapOf(&quot;Alice&quot; to 21, &quot;Bob&quot; to 25)
for ((name, age) in map) {                                      // 2
    println(&quot;$name is $age years old&quot;)          
}

val (min, max) = findMinMax(listOf(100, 90, 50, 98, 76, 83))    // 3</code></pre>
<ol>
<li><code>Array</code> 구조 분해. 왼편 변수의 수는 오른편 아규먼트의 수와 같아야 한다. </li>
<li>맵 또한 구조 분해 될 수 있다. <code>name</code>과 <code>age</code> 변수는 맵의 키와 값에 매핑되었다.</li>
<li>빌트인 <code>Pair</code>와 <code>Triples</code> 타입도 구조 분해를 지원한다. 심지어 함수의 리턴값일 경우에도 구조 분해 가능하다. </li>
</ol>
<pre><code class="language-kotlin">data class User(val username: String, val email: String)    // 1

fun getUser() = User(&quot;Mary&quot;, &quot;mary@somewhere.com&quot;)

fun main() {
    val user = getUser()
    val (username, email) = user                            // 2
    println(username == user.component1())                  // 3

    val (_, emailAddress) = getUser()                       // 4

}</code></pre>
<ol>
<li>데이터 클래스를 정의한다.</li>
<li>인스턴스를 구조분해한다. 선언된 값은 인스턴스 필드로 매핑된다.</li>
<li>데이터 클래스는 자동으로 <code>component1()</code>과 <code>component2()</code>메서드를 정의한다. 이 메서드들은 구조분해 중에 호출될 것이다.</li>
<li>사용하지 않을 값이 있다면 언더스코어(<code>_</code>)를 사용한다. 이것은 사용하지 않는 변수를 나타내는 컴파일러 힌트를 피하게 한다.</li>
</ol>
<pre><code class="language-kotlin">class Pair&lt;K, V&gt;(val first: K, val second: V) {  // 1
    operator fun component1(): K {              
        return first
    }

    operator fun component2(): V {              
        return second
    }
}

fun main() {
    val (num, name) = Pair(1, &quot;one&quot;)             // 2

    println(&quot;num = $num, name = $name&quot;)
}</code></pre>
<ol>
<li><code>component1()</code>과 <code>component2()</code> 메서드를 가진 커스텀 <code>Pair</code> 클래스를 정의한다.</li>
<li>이 클래스의 인스턴스를 빌트인 <code>Pair</code>와 동일한 방법으로 구조분해한다.</li>
</ol>
<br>

<h2 id="smart-casts">Smart Casts</h2>
<hr>
<p>코틀린 컴파일러는 대부분의 경우 타입 캐스트를 자동으로 수행할 수 있을 정도로 똑똑하다. 타입 캐스트가 가능한 경우는 다음을 포함한다:</p>
<ol>
<li>nullable 타입에서 대응하는  non-nullable 타입으로의 캐스트</li>
<li>supertype에서 subtype으로의 캐스트.</li>
</ol>
<pre><code class="language-kotlin">import java.time.LocalDate
import java.time.chrono.ChronoLocalDate

fun main() {
    val date: ChronoLocalDate? = LocalDate.now()    // 1

    if (date != null) {
        println(date.isLeapYear)                    // 2
    }

    if (date != null &amp;&amp; date.isLeapYear) {          // 3
        println(&quot;It&#39;s a leap year!&quot;)
    }

    if (date == null || !date.isLeapYear) {         // 4
        println(&quot;There&#39;s no Feb 29 this year...&quot;)
    }

    if (date is LocalDate) {
        val month = date.monthValue                 // 5
        println(month)
    }
}</code></pre>
<ol>
<li>nullable 변수를 선언한다.</li>
<li>non-nullable로 스마트캐스팅해서 <code>isLeapYear</code>에 직접 접근할 수 있게 한다.</li>
<li>조건 내부에서 스마트캐스팅이 일어난다 (자바와 마찬가지로 코틀린이 단축평가(short-circuiting)을 사용하고 있기 때문에 가능하다). (<em>data !=null을 먼저 계산했기 때문에 가능하다는 뜻으로 추측</em>)</li>
<li>조건 내부에서 스마트캐스팅이 일어난다 (마찬가지로 단축평가 덕에 가능하다).</li>
<li>하위 타입 <code>LocalDate</code>로 스마트캐스트된다.</li>
</ol>
<p>이 방법으로, 분명한 캐스팅을 수동으로 하지 않고도 대부분의 경우 원하는 변수를 자동으로 사용할 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin Examples > Delegation]]></title>
            <link>https://velog.io/@hh_0705/Delegation</link>
            <guid>https://velog.io/@hh_0705/Delegation</guid>
            <pubDate>Wed, 16 Mar 2022 00:44:50 GMT</pubDate>
            <description><![CDATA[<p><em>2022-03-15 작성완료</em></p>
<h2 id="delegation-pattern">Delegation Pattern</h2>
<hr>
<p>코틀린은 네이티브 레벨 <a href="https://kotlinlang.org/docs/reference/delegation.html?_ga=2.83483990.1975964084.1647331891-1497297377.1644238287">Delegation Pattern</a>의 쉬운 구현을 지원한다. 여기에는 보일러플레이트 코드가 사용되지 않는다.</p>
<pre><code class="language-kotlin">interface SoundBehavior {                                                          // 1
    fun makeSound()
}

class ScreamBehavior(val n:String): SoundBehavior {                                // 2
    override fun makeSound() = println(&quot;${n.toUpperCase()} !!!&quot;)
}

class RockAndRollBehavior(val n:String): SoundBehavior {                           // 2
    override fun makeSound() = println(&quot;I&#39;m The King of Rock &#39;N&#39; Roll: $n&quot;)
}

// Tom Araya is the &quot;singer&quot; of Slayer
class TomAraya(n:String): SoundBehavior by ScreamBehavior(n)                       // 3

// You should know ;)
class ElvisPresley(n:String): SoundBehavior by RockAndRollBehavior(n)              // 3

fun main() {
    val tomAraya = TomAraya(&quot;Thrash Metal&quot;)
    tomAraya.makeSound()                                                           // 4
    val elvisPresley = ElvisPresley(&quot;Dancin&#39; to the Jailhouse Rock.&quot;)
    elvisPresley.makeSound()
}</code></pre>
<ol>
<li>메서드 하나를 가진 <code>SoundBehavior</code> 인터페이스를 정의한다.</li>
<li><code>ScreamBehavior</code> 클래스와 <code>RockAndRollBehavior</code> 클래스는 해당 인터페이스를 구현하고 메서드에 대한 자신만의 구현을 가진다.</li>
<li><code>TomAraya</code> 클래스와 <code>ElvisPresley</code>클래스도 해당 인터페이스를 구현하지만 메서드를 구현하지는 않는다. 대신에, 그것들은 메서드 호출을 책임이 있는 구현에게(responsible implementation) 위임한다. delegate object는 <code>by</code> 키워드 뒤에 정의된다. 보다시피, 보일러플레이트 코드가 필요하지 않다. </li>
<li><code>makeSound()</code>가 <code>TomAraya</code>타입의 <code>tomAraya</code>에서, 또는 <code>ElvisPresley</code> 타입의 <code>elvisPresley</code>에서 호출될 때, 그 호출은 상응하는 delegate object로 위임된다.</li>
</ol>
<br>

<h2 id="delegated-properties">Delegated Properties</h2>
<hr>
<p>코틀린은 <a href="http://kotlinlang.org/docs/reference/delegated-properties.html?_ga=2.113357255.1975964084.1647331891-1497297377.1644238287">프로퍼티 위임</a> 메커니즘을 제공한다. 이는 프로퍼티 <code>set</code>과 <code>get</code> 메서드 호출을 특정 객체에 위임하는 것을 허용한다. 이 경우 delegate object는 반드시 <code>getValue</code> 메서드를 갖고 있어야 한다. 가변 프로퍼티의 경우 <code>setValue</code>또한 필요하다.</p>
<pre><code class="language-kotlin">import kotlin.reflect.KProperty

class Example {
    var p: String by Delegate()                                               // 1

    override fun toString() = &quot;Example Class&quot;
}

class Delegate() {
    operator fun getValue(thisRef: Any?, prop: KProperty&lt;*&gt;): String {        // 2     
        return &quot;$thisRef, thank you for delegating &#39;${prop.name}&#39; to me!&quot;
    }

    operator fun setValue(thisRef: Any?, prop: KProperty&lt;*&gt;, value: String) { // 2
        println(&quot;$value has been assigned to ${prop.name} in $thisRef&quot;)
    }
}

fun main() {
    val e = Example()
    println(e.p)
    e.p = &quot;NEW&quot;
}</code></pre>
<ol>
<li><code>String</code> 타입의 <code>p</code> 프로퍼티를 클래스 <code>Delegate</code>의 인스턴스에 위임한다. delegate object는 <code>by</code> 키워드 뒤에서 정의된다.</li>
<li>메서드 위임. 이 메서드들의 시그니처는 언제나 예제에서 보여주는 것과 같다. 구현은 필요한 어떤 단계이든 포함할 수 있을 것이다. 불변 프로퍼티에는 <code>getValue</code>만 필요하다. </li>
</ol>
<br>

<h3 id="standard-delegates">Standard Delegates</h3>
<hr>
<p>코틀린 표준 라이브러리는 유용한 위임들을 포함하고 있다. 예: <code>lazy</code>, <code>observable</code> 등. 이것들을 그대로 사용할 수 있다. 예를 들어 <code>lazy</code>는 lazy initialization에 사용된다. </p>
<pre><code class="language-kotlin">class LazySample {
    init {
      println(&quot;created!&quot;)            // 1
    }

    val lazyStr: String by lazy {
        println(&quot;computed!&quot;)          // 2
        &quot;my lazy&quot;
    }
}

fun main() {
    val sample = LazySample()         // 1
    println(&quot;lazyStr = ${sample.lazyStr}&quot;)  // 2
    println(&quot; = ${sample.lazyStr}&quot;)  // 3
}</code></pre>
<ol>
<li>프로퍼티 <code>lazy</code>는 객체 생성 시 초기화되지 않는다. </li>
<li>첫 <code>get()</code> 호출은 <code>lazy()</code>에 아규먼트로 전달된 람다 표현식을 실행시키고 그 결과를 저장한다.</li>
<li>이후 <code>get()</code> 호출은 저장된 값을 반환한다. </li>
</ol>
<p>스레드 세이프티를 원한다면 대신 <code>blockingLazy()</code>를 사용해아 한다: 이것은 값이 한 스레드에서만 계산되며 모든 스레드가 같은 값을 바라본다는 것을 보장한다.</p>
<br>

<h2 id="storing-properties-in-a-map">Storing Properties in a Map</h2>
<hr>
<p>프로퍼티 위임은 맵에 프로퍼티를 저장하기 위해 사용될 수도 있다. 이것은 JSON 파싱 또는 다른 동적 작업에 유용하다. </p>
<pre><code class="language-kotlin">class User(val map: Map&lt;String, Any?&gt;) {
    val name: String by map                // 1
    val age: Int     by map                // 1
}

fun main() {
    val user = User(mapOf(
            &quot;name&quot; to &quot;John Doe&quot;,
            &quot;age&quot;  to 25
    ))

    println(&quot;name = ${user.name}, age = ${user.age}&quot;)
}</code></pre>
<ol>
<li>대리자(Delegates)는 프로퍼티 이름인 문자열 키를 사용해 맵으로부터 값을 가져온다.</li>
</ol>
<p>변경 가능한 속성을 맵에 위임할 수도 있다. 이 경우 맵은 프로퍼티 할당 시 수정될 것이다. 읽기 전용 <code>Map</code> 대신 <code>MutableMap</code>를 사용해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin Examples > Functional]]></title>
            <link>https://velog.io/@hh_0705/Functional</link>
            <guid>https://velog.io/@hh_0705/Functional</guid>
            <pubDate>Fri, 11 Mar 2022 07:53:24 GMT</pubDate>
            <description><![CDATA[<p><em>2022-03-10 작성완료</em></p>
<h2 id="higher-order-functions">Higher-Order Functions</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/lambdas.html?_ga=2.45494116.1142960634.1646637005-521320736.1644819103">Higher-Order Function</a>은 다른 함수를 파라미터로 가지고/가지거나 함수를 반환하는 함수이다.</p>
<br>

<h3 id="함수를-파라미터로-가짐">함수를 파라미터로 가짐</h3>
<hr>
<pre><code class="language-kotlin">fun calculate(x: Int, y: Int, operation: (Int, Int) -&gt; Int): Int {  // 1
    return operation(x, y)                                          // 2
}

fun sum(x: Int, y: Int) = x + y                                     // 3

fun main() {
    val sumResult = calculate(4, 5, ::sum)                          // 4
    val mulResult = calculate(4, 5) { a, b -&gt; a * b }               // 5
    println(&quot;sumResult $sumResult, mulResult $mulResult&quot;)
}</code></pre>
<ol>
<li>고차 함수를 선언한다. 이것은 두 정수 파라미터 <code>x</code> 와 <code>y</code>를 가진다. 그리고 다른 함수 <code>operation</code>을 파라미터로 가진다. <code>operation</code> 파라미터와 리턴 타입 또한 선언에서 정의된다.</li>
<li>고차 함수는 제공된 아규먼트로 호출된 <code>operation</code>의 결과를 리턴한다.</li>
<li><code>operation</code> signature와 일치하는 함수를 선언한다.</li>
<li>두 정수 값과 함수 아규먼트 <code>::sum</code>을 전달해 고차 함수를 호출한다. <code>::</code>는 코틀린에서 함수를 이름으로 참조하는 표기법이다.</li>
<li>람다를 함수 아규먼트로써 전달해 고차함수를 호출한다. 더 깔끔해보이지 않는지?</li>
</ol>
<br>

<h3 id="함수를-반환함">함수를 반환함</h3>
<hr>
<pre><code class="language-kotlin">fun operation(): (Int) -&gt; Int {                                     // 1
    return ::square
}

fun square(x: Int) = x * x                                          // 2

fun main() {
    val func = operation()                                          // 3
    println(func(2))                                                // 4
}</code></pre>
<ol>
<li>함수를 반환하는 고차함수를 선언한다. <code>(Int) -&gt; Int</code>는 <code>square</code> 함수의 파라미터와 반환값을 나타낸다.</li>
<li>시그니처와 일치하는 함수를 선언한다.</li>
<li>결과를 변수에 할당하기 위해 <code>operation</code>을 호출한다. 이 <code>func</code>는 <code>operation</code>에 의해 반환된 <code>square</code>가 된다. </li>
<li><code>func</code>를 호출한다. <code>square</code> 함수가 실제로 실행된다. </li>
</ol>
<br>

<h2 id="lambda-functions">Lambda Functions</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/lambdas.html?_ga=2.41273186.1142960634.1646637005-521320736.1644819103">Lambda Functions</a>(&quot;람다&quot;)는 애드혹 함수를 만드는 간단한 방법이다. 타입 추론과 암묵적 <code>it</code> 변수 덕에, 람다는 많은 경우 아주 간결하게 표시될 수 있다. </p>
<pre><code class="language-kotlin">// All examples create a function object that performs upper-casing.
// So it&#39;s a function from String to String

val upperCase1: (String) -&gt; String = { str: String -&gt; str.uppercase() } // 1

val upperCase2: (String) -&gt; String = { str -&gt; str.uppercase() }         // 2

val upperCase3 = { str: String -&gt; str.uppercase() }                     // 3

// val upperCase4 = { str -&gt; str.uppercase() }                          // 4

val upperCase5: (String) -&gt; String = { it.uppercase() }                 // 5

val upperCase6: (String) -&gt; String = String::uppercase                  // 6

println(upperCase1(&quot;hello&quot;))
println(upperCase2(&quot;hello&quot;))
println(upperCase3(&quot;hello&quot;))
println(upperCase5(&quot;hello&quot;))
println(upperCase6(&quot;hello&quot;))</code></pre>
<ol>
<li>모든 곳에 명시적 타입이 있는 람다. 람다는 중괄호 안에 있는 부분이고, <code>(String) -&gt; String</code> 타입(함수 타입) 변수에 할당된다.</li>
<li>람다 내부 타입 추론: 람다 파라미터의 타입이 그 람다가 할당된 타입으로부터 추론된다.</li>
<li>람다 외부 타입 추론: 변수의 타입이 람다 파라미터와 리턴값으로부터 추론된다.</li>
<li>이 둘을 함께 할 수는 없다. 컴파일러는 타입을 추론할 수 없다.</li>
<li>단일 파라미터를 가진 람다는 명시적으로 파라미터를 언급할 필요가 없다. 대신 암묵적인 <code>it</code> 변수를 사용할 수 있다. 이것은 <code>it</code> 타입이 추론될 수 있을 때(자주 있는 일이다) 특히 유용하다.</li>
<li>람다가 단일 함수 호출로 구성될 경우 함수 포인터 <code>::</code>를 쓸 수 있다.</li>
</ol>
<br>

<h2 id="extension-functions-and-properties">Extension Functions and Properties</h2>
<hr>
<p>코틀린은 어떤 클래스에든 <a href="https://kotlinlang.org/docs/reference/extensions.html?_ga=2.157793466.1890978350.1646886976-521320736.1644819103">확장(extensions)</a> 메커니즘으로 새 멤버를 추가할 수 있다. 즉, 확장에는 두 가지 유형이 있다: 확장 함수(extension functions)와 확장 프로퍼티(extension properties)이다. 그것들은 일반적인 함수와 프로퍼티처럼 보이지만 한 가지 중요한 차이점이 있다: 확장하는 타입을 지정해야 한다.</p>
<pre><code class="language-kotlin">data class Item(val name: String, val price: Float)                                         // 1  

data class Order(val items: Collection&lt;Item&gt;)  

fun Order.maxPricedItemValue(): Float = this.items.maxByOrNull { it.price }?.price ?: 0F    // 2  
fun Order.maxPricedItemName() = this.items.maxByOrNull { it.price }?.name ?: &quot;NO_PRODUCTS&quot;

//프로퍼티 commaDelimitedItemNames
val Order.commaDelimitedItemNames: String
//getter 작성                                                   
    get() = items.map { it.name }.joinToString()                                            // 3

fun main() {

    val order = Order(listOf(Item(&quot;Bread&quot;, 25.0F), Item(&quot;Wine&quot;, 29.0F), Item(&quot;Water&quot;, 12.0F)))

    println(&quot;Max priced item name: ${order.maxPricedItemName()}&quot;)                           // 4
    println(&quot;Max priced item value: ${order.maxPricedItemValue()}&quot;)
    println(&quot;Items: ${order.commaDelimitedItemNames}&quot;)                                      // 5

}</code></pre>
<ol>
<li><code>Item</code>과 <code>Order</code>의 단순한 모델을 정의한다. <code>Order</code>는 <code>Item</code> 객체 컬렉션을 가질 수 있다.</li>
<li><code>Order</code> 타입에 확장 함수를 추가한다.</li>
<li><code>Order</code> 타입에 확장 함수를 추가한다.</li>
<li><code>Order</code> 인스턴스에서 확장 함수를 직접 호출한다.</li>
<li><code>Order</code> 인스턴스에서 확장 프로퍼티에 접근한다.</li>
</ol>
<p><code>null</code> 참조에서도 확장을 실행할 수 있다. 확장 함수 내에서 객체가 <code>null</code>인지 체크하고 그 결과를 코드에서 사용할 수 있다:</p>
<pre><code class="language-kotlin">fun &lt;T&gt; T?.nullSafeToString() = this?.toString() ?: &quot;Null&quot;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin Examples > Special Classes]]></title>
            <link>https://velog.io/@hh_0705/Special-Classes</link>
            <guid>https://velog.io/@hh_0705/Special-Classes</guid>
            <pubDate>Fri, 11 Mar 2022 07:43:55 GMT</pubDate>
            <description><![CDATA[<p><em>2022-03-08 작성완료</em></p>
<h2 id="data-classes">Data Classes</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/data-classes.html?_ga=2.69676752.1142960634.1646637005-521320736.1644819103">Data Classes</a>는 값을 저장하는 클래스를 생성하기 쉽게 만든다. 이런 클래스들은 자동으로 복사, 문자열 표현 획득, collections에서의 인스턴스 사용을 위한 메서드를 제공한다. 이 메서드들을 클래스 선언 내에서 구현해 오버라이딩 할 수 있다. </p>
<pre><code class="language-kotlin">data class User(val name: String, val id: Int) {           // 1
    override fun equals(other: Any?) =
        other is User &amp;&amp; other.id == this.id               // 2
}
fun main() {
    val user = User(&quot;Alex&quot;, 1)
    println(user)                                          // 3

    val secondUser = User(&quot;Alex&quot;, 1)
    val thirdUser = User(&quot;Max&quot;, 2)

    println(&quot;user == secondUser: ${user == secondUser}&quot;)   // 4
    println(&quot;user == thirdUser: ${user == thirdUser}&quot;)

    // hashCode() function
    println(user.hashCode())                               // 5
    println(secondUser.hashCode())
    println(thirdUser.hashCode())

    // copy() function
    println(user.copy())                                   // 6
    println(user === user.copy())                          // 7
    println(user.copy(&quot;Max&quot;))                              // 8
    println(user.copy(id = 3))                             // 9

    println(&quot;name = ${user.component1()}&quot;)                 // 10
    println(&quot;id = ${user.component2()}&quot;)
}</code></pre>
<ol>
<li><code>data</code> 수정자로 data class를 정의한다.</li>
<li>유저들이 같은 <code>id</code>를 가질 경우 동일하다고 선언함으로써 <code>equals</code> 메서드를 오버라이딩한다.</li>
<li><code>toString</code> 메서드는 자동으로 생성되고, 이것이 <code>println</code> 아웃풋을 보기 좋게 한다.</li>
<li>커스텀 <code>equals</code>는 아이디가 같다면 두 인스턴스가 동일하다고 취급한다.</li>
<li>정확히 일치하는 속성들을 가진 데이터 클래스 인스턴스는 동일한 <code>hashCode</code>를 가진다.</li>
<li>자동 생성된 <code>copy</code> 함수는 새 인스턴스를 만들기 쉽게 한다.</li>
<li><code>copy</code>는 새 인스턴스를 생성한다. 그러므로 객체와 그 카피는 별개의 참조를 가진다.</li>
<li>카피 시 특정 프로퍼티를 변경할 수 있다. <code>copy</code>는 클래스 생성자와 같은 순서로 아규먼트를 받아들인다.</li>
<li><code>copy</code>를 명명된 아규먼트와 함께 사용해 프로퍼티 순서와 상관 없이 값을 바꿀 수 있다.</li>
<li>자동 생성된 <code>componentN</code> 함수는 선언 순서대로 값을 얻을 수 있게 해 준다. </li>
</ol>
<br>

<h2 id="enum-classes">Enum Classes</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/enum-classes.html?_ga=2.240042947.1142960634.1646637005-521320736.1644819103">Enum classes</a>는 고유 값들의 유한 집합을 표현하는 타입을 모델링하는데에 사용된다(예: 방향, 상태, 모드).</p>
<pre><code class="language-kotlin">enum class State {
    IDLE, RUNNING, FINISHED                           // 1
}

fun main() {
    val state = State.RUNNING                         // 2
    val message = when (state) {                      // 3
        State.IDLE -&gt; &quot;It&#39;s idle&quot;
        State.RUNNING -&gt; &quot;It&#39;s running&quot;
        State.FINISHED -&gt; &quot;It&#39;s finished&quot;
    }
    println(message)
}</code></pre>
<ol>
<li>세 열거형 상수로 간단한 열거형 클래스를 정의한다. 상수의 수는 언제나 유한하고 모든 값은 고유하다(distinct).</li>
<li>클래스 이름을 통해 열거형 상수에 접근한다.</li>
<li>열거형을 사용할 때, <code>when</code> 표현식이 철저해서 <code>else</code>-case 가 필요하지 않은지를 컴파일러가 추론할 수 있다.</li>
</ol>
<br>

<p>열거형은 다른 클래스들과 마찬가지로 프로퍼티와 메서드들을 가질 수 있다. 이들은 세미콜론을 통해 열거형 상수와 분리된다.</p>
<pre><code class="language-kotlin">enum class Color(val rgb: Int) {                      // 1
    RED(0xFF0000),                                    // 2
    GREEN(0x00FF00),
    BLUE(0x0000FF),
    YELLOW(0xFFFF00);

    fun containsRed() = (this.rgb and 0xFF0000 != 0)  // 3
}

fun main() {
    val red = Color.RED
    println(red)                                      // 4
    println(red.containsRed())                        // 5
    println(Color.BLUE.containsRed())                 // 6
    println(Color.YELLOW.containsRed())               // 7
}</code></pre>
<ol>
<li>프로퍼티와 메서드를 가진 열거형 클래스를 정의한다.</li>
<li>각 열거형 상수는 반드시 생성자 파라미터로 아규먼트를 넘겨주어야 한다.</li>
<li>열거형 클래스 멤버들은 세미콜론으로 상수 정의와 구별된다.</li>
<li>기본 <code>toString</code>이 상수의 이름을 반환한다. 여기서는 <code>RED</code>이다. </li>
<li>열거형 상수의 메서드를 호출한다.</li>
<li>열거형 클래스 이름을 통해 메서드를 호출한다.</li>
<li><code>RED</code>와 <code>YELLOW</code>의 RGB 값이 첫 비트들(FF)를 공유하므로 <code>true</code>를 출력한다.</li>
</ol>
<br>

<h2 id="sealed-classes">Sealed Classes</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/sealed-classes.html?_ga=2.10456053.1142960634.1646637005-521320736.1644819103">Sealed Classes</a>는 상속을 제한할 수 있도록 한다. 클래스를 sealed로 선언하면 
 sealed class가 선언된 패키지의 서브패키지에서만 서브클래싱 할 수 있다. sealed class가 선언된 패키지 외부에서는 서브클래싱 할 수 없다.</p>
<pre><code class="language-kotlin">sealed class Mammal(val name: String)                                                   // 1

class Cat(val catName: String) : Mammal(catName)                                        // 2
class Human(val humanName: String, val job: String) : Mammal(humanName)

fun greetMammal(mammal: Mammal): String {
    when (mammal) {                                                                     // 3
        is Human -&gt; return &quot;Hello ${mammal.name}; You&#39;re working as a ${mammal.job}&quot;    // 4
        is Cat -&gt; return &quot;Hello ${mammal.name}&quot;                                         // 5     
    }                                                                                   // 6
}

fun main() {
    println(greetMammal(Cat(&quot;Snowy&quot;)))
}</code></pre>
<ol>
<li>sealed class를 선언한다. </li>
<li>하위 클래스를 선언한다. 모든 하위클래스가 같은 패키지에 위치해야 함을 주의한다.</li>
<li>sealed class의 인스턴스를 <code>when</code> 표현식에서 아규먼트로 사용한다.</li>
<li>스마트캐스트가 수행되어 <code>Mammal</code>이 <code>Human</code>으로 캐스팅된다.</li>
<li>스마트캐스트가 수행되어 <code>Mammal</code>이 <code>Cat</code>으로 캐스팅된다.</li>
<li>sealed class의 모든 가능한 하위 클래스가 커버되었으므로 <code>else</code> 케이스가 필요하지 않다. non-sealed superclass를 가지면 <code>else</code>가 필수적이다.</li>
</ol>
<br>

<h2 id="object-keyword">Object Keyword</h2>
<hr>
<p>코틀린의 클래스와 오브젝트는 대부분의 객체지향 언어와 동일한 방식으로 동작한다: 클래스는 청사진이고, 객체는 클래스의 인스턴스이다. 대개 클래스를 정의하고 클래스로부터 여러 인스턴스를 생성한다:</p>
<pre><code class="language-kotlin">import java.util.Random

class LuckDispatcher {                    //1 
    fun getNumber() {                     //2 
        var objRandom = Random()
        println(objRandom.nextInt(90))
    }
}

fun main() {
    val d1 = LuckDispatcher()             //3
    val d2 = LuckDispatcher()

    d1.getNumber()                        //4 
    d2.getNumber()
}</code></pre>
<ol>
<li>블루스크린을 정의한다.</li>
<li>메서드를 정의한다.</li>
<li>인스턴스를 생성한다.</li>
<li>인스턴스의 메서드를 호출한다.</li>
</ol>
<p>코틀린에는 <a href="https://kotlinlang.org/docs/reference/object-declarations.html?_ga=2.82233174.1142960634.1646637005-521320736.1644819103">object keyword</a>가 있다. 이것은 단일 구현으로 데이터 타입을 얻기 위해 사용한다. </p>
<p>당신이 자바 유저이고 &quot;single&quot;이 무엇을 의미하는지 이해한다면 싱글톤 패턴을 생각할 수 있을 것이다. 이는 두 스레드가 인스턴스를 생성하려고 시도해도 오직 하나의 인스턴스만이 생성된다는 것을 보장한다.</p>
<p>이를 코틀린에서 사용하기 위해서는 <code>object</code>를 선언하면 된다. 여기에는 클래스도 없고, 생성자도 없고, 오직  lazy instance만 있다. 오브젝트에 접근할 때 한 번만 생성되므로 이것은 lazy 하다. 접근하지 않으면 생성되지조차 않는다.</p>
<br>

<h3 id="object-표현식"><code>object</code> 표현식</h3>
<hr>
<p>여기 <code>object</code> 표현식의 기본적이고 전형적인 사용법이 있다. 간단한 오브젝트 프로퍼티 스트럭처를 만드는 것이다. 이것은 클래스 선언에서 할 필요가 없다. 단일 오브젝트를 생성하고, 그 멤버들을 선언하고, 한 함수 내부에서 그것에 접근하면 된다. 이와 같은 오브젝트는 종종 자바에서 익명 함수 인스턴스로 생성된다.</p>
<pre><code class="language-kotlin">fun rentPrice(standardDays: Int, festivityDays: Int, specialDays: Int): Unit {  //1

    val dayRates = object {                                                     //2
        var standard: Int = 30 * standardDays
        var festivity: Int = 50 * festivityDays
        var special: Int = 100 * specialDays
    }

    val total = dayRates.standard + dayRates.festivity + dayRates.special       //3

    print(&quot;Total price: $$total&quot;)                                               //4

}

fun main() {
    rentPrice(10, 2, 1)                                                         //5
}</code></pre>
<ol>
<li>파라미터로 함수를 생성한다.</li>
<li>결과값 계산에 사용하기 위해 오브젝트를 생성한다.</li>
<li>오브젝트의 프로퍼티에 접근한다.</li>
<li>결과를 출력한다.</li>
<li>함수를 호출한다. 여기서 오브젝트가 실제로 생성된다.</li>
</ol>
<br>

<h3 id="object-선언"><code>object</code> 선언</h3>
<hr>
<p><code>object</code> 선언 또한 사용할 수 있다. 이것은 표현식이 아니고, 변수 할당에 사용될 수 없다. 멤버에 직접 접근하기 위해 이것을 사용해야 한다. </p>
<pre><code class="language-kotlin">object DoAuth {                                                 //1 
    fun takeParams(username: String, password: String) {        //2 
        println(&quot;input Auth parameters = $username:$password&quot;)
    }
}

fun main(){
    DoAuth.takeParams(&quot;foo&quot;, &quot;qwerty&quot;)                          //3
}</code></pre>
<ol>
<li>오브젝트 선언을 생성한다.</li>
<li>오브젝트 메서드를 정의한다.</li>
<li>메서드를 호출한다. 여기서 오브젝트가 실제로 생성된다.</li>
</ol>
<br>

<h3 id="companion-objects">Companion Objects</h3>
<hr>
<p>클래스 내부에서의 오브젝트 선언은 또 다른 유용한 케이스를 정의한다: 동반자 객체이다. 문법적으로 이것은 자바의 static 메서드와 유사하다: qualifier로써 클래스 이름을 사용해 오브젝트 멤버를 호출할 수 있다. 코틀린에서 동반자 객체를 사용할 계획이 있다면 패키지 수준 함수 사용을 고려해 보자.</p>
<pre><code class="language-kotlin">class BigBen {                                  //1 
    companion object Bonger {                   //2
        fun getBongs(nTimes: Int) {             //3
            for (i in 1 .. nTimes) {
                print(&quot;BONG &quot;)
            }
        }
    }
}

fun main() {
    BigBen.getBongs(12)                         //4
}</code></pre>
<ol>
<li>클래스를 정의한다.</li>
<li>동반자를 정의한다. 이름은 생략될 수 있다.</li>
<li>동반자 객체 메서드를 정의한다.</li>
<li>클래스명으로 동반자 객체 메서드를 호출한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin Examples > Collections]]></title>
            <link>https://velog.io/@hh_0705/Collections</link>
            <guid>https://velog.io/@hh_0705/Collections</guid>
            <pubDate>Fri, 11 Mar 2022 07:32:59 GMT</pubDate>
            <description><![CDATA[<p><em>2022-03-10 작성완료</em></p>
<h2 id="list">List</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/collections.html?_ga=2.123697035.1890978350.1646886976-521320736.1644819103">list</a>는 아이템의 순서 있는 컬렉션이다(ordered collection). 코틀린에서 리스트는 가변(mutable)이거나 (<code>MutableList</code>) 읽기 전용(<code>List</code>)일 수 있다. 표준 라이브러리 함수 <code>listOf()</code>를 읽기 전용 리스트 생성에 사용하고 <code>mutableListOf()</code>를 가변 리스트 생성에 사용한다. 원하지 않는 수정을 피하기 위해 가변 리스트를 <code>List</code>로 캐스팅해 읽기 전용 뷰를 만들 수 있다.</p>
<pre><code class="language-kotlin">val systemUsers: MutableList&lt;Int&gt; = mutableListOf(1, 2, 3)        // 1
val sudoers: List&lt;Int&gt; = systemUsers                              // 2

fun addSystemUser(newUser: Int) {                                 // 3
    systemUsers.add(newUser)                      
}

fun getSysSudoers(): List&lt;Int&gt; {                                  // 4
    return sudoers
}

fun main() {
    addSystemUser(4)                                              // 5 
    println(&quot;Tot sudoers: ${getSysSudoers().size}&quot;)               // 6
    getSysSudoers().forEach {                                     // 7
        i -&gt; println(&quot;Some useful info on user $i&quot;)
    }
    // getSysSudoers().add(5) &lt;- Error!                           // 8
}</code></pre>
<ol>
<li><code>MutableList</code> 를 생성한다.</li>
<li>리스트의 읽기 전용 뷰를 만든다.</li>
<li>새 아이템을 <code>MutableList</code>에 더한다.</li>
<li>불변 <code>List</code>를 리턴하는 함수</li>
<li><code>MutableList</code>를 업데이트한다. 모든 관련된 읽기 전용 뷰 또한 업데이트된다. 동일한 객체를 가리키고 있기 때문이다.</li>
<li>읽기 전용 리스트의 사이즈를 인출한다.</li>
<li>리스트를 순회하고 그 요소를 프린트한다.</li>
<li>읽기 전용 뷰에 대한 쓰기 시도는 컴파일 에러를 만든다.</li>
</ol>
<br>

<h2 id="set">Set</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/collections.html?_ga=2.152934329.1890978350.1646886976-521320736.1644819103">set</a>은 순서가 없는 컬렉션이고 중복(duplicates)를 지원하지 않는다. set을 생성하기 위해서는 <code>setOf()</code>와 <code>mutableSetOf()</code>함수를 사용한다. 가변 셋의 읽기 전용 뷰는 그것을 <code>Set</code>으로 캐스팅해서 얻을 수 있다.</p>
<pre><code class="language-kotlin">val openIssues: MutableSet&lt;String&gt; = mutableSetOf(&quot;uniqueDescr1&quot;, &quot;uniqueDescr2&quot;, &quot;uniqueDescr3&quot;) // 1

fun addIssue(uniqueDesc: String): Boolean {                                                       
    return openIssues.add(uniqueDesc)                                                             // 2
}

fun getStatusLog(isAdded: Boolean): String {                                                       
    return if (isAdded) &quot;registered correctly.&quot; else &quot;marked as duplicate and rejected.&quot;          // 3
}

fun main() {
    val aNewIssue: String = &quot;uniqueDescr4&quot;
    val anIssueAlreadyIn: String = &quot;uniqueDescr2&quot; 

    println(&quot;Issue $aNewIssue ${getStatusLog(addIssue(aNewIssue))}&quot;)                              // 4
    println(&quot;Issue $anIssueAlreadyIn ${getStatusLog(addIssue(anIssueAlreadyIn))}&quot;)                // 5 
}</code></pre>
<ol>
<li>주어진 요소로 <code>set</code>을 생성한다.</li>
<li>요소가 실제로 추가되었는지를 보여주는 불리언 값을 리턴한다.</li>
<li>함수 입력 파라미터에 따라 문자열을 리턴한다.</li>
<li>성공 메시지를 프린트한다: 새 요소가 <code>Set</code>에 추가되었다.</li>
<li>실패 메시지를 프린트한다: 요소가 이미 존재하는 요소와 중복되므로 추가되지 못했다. </li>
</ol>
<br>

<h2 id="map">Map</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/collections.html?_ga=2.123638795.1890978350.1646886976-521320736.1644819103">map</a>은 키/값 쌍의 컬렉션이다. 각 키는 유니크하고 상응하는 값을 인출하기 위해 사용된다. 맵 생성에는 <code>mapOf()</code>와 <code>mutableMapOf()</code> 함수를 사용한다. <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to.html?_ga=2.78402708.1890978350.1646886976-521320736.1644819103">to</a> infix function은 초기화를 덜 noisy하게 만든다. 가변 맵의 읽기 전용 뷰는 <code>Map</code>으로 캐스팅해 얻을 수 있다.</p>
<pre><code class="language-kotlin">const val POINTS_X_PASS: Int = 15
val EZPassAccounts: MutableMap&lt;Int, Int&gt; = mutableMapOf(1 to 100, 2 to 100, 3 to 100)   // 1
val EZPassReport: Map&lt;Int, Int&gt; = EZPassAccounts                                        // 2

fun updatePointsCredit(accountId: Int) {
    if (EZPassAccounts.containsKey(accountId)) {                                        // 3
        println(&quot;Updating $accountId...&quot;)                                               
        EZPassAccounts[accountId] = EZPassAccounts.getValue(accountId) + POINTS_X_PASS  // 4
    } else {
        println(&quot;Error: Trying to update a non-existing account (id: $accountId)&quot;)
    } 
}

fun accountsReport() {
    println(&quot;EZ-Pass report:&quot;)
    EZPassReport.forEach {                                                              // 5
        k, v -&gt; println(&quot;ID $k: credit $v&quot;)
    }
}

fun main() {
    accountsReport()                                                                    // 6
    updatePointsCredit(1)                                                               // 7
    updatePointsCredit(1)                                                               
    updatePointsCredit(5)                                                               // 8 
    accountsReport()                                                                    // 9
}</code></pre>
<ol>
<li>가변 <code>Map</code>을 생성한다.</li>
<li><code>Map</code>의 읽기 전용 뷰를 생성한다. </li>
<li><code>Map</code>의 키가 존재하는지를 체크한다.</li>
<li>해당 값을 읽고 상수만큼 증가시킨다.</li>
<li>불변 <code>Map</code>을 순회하고 키/값 페어를 프린트한다.</li>
<li>계좌 포인트 잔액을 업데이트 전에 읽는다.</li>
<li>존재하는 계좌를 두번 업데이트한다. </li>
<li>존재하지 않는 계좌를 업데이트 시도한다: 에러 메시지를 출력한다.</li>
<li>업데이트 이후에 계좌 포인트 잔고를 읽는다.</li>
</ol>
<br>

<h2 id="filter">filter</h2>
<hr>
<p><code>filter</code> 함수는 컬렉션을 필터링한다. 필터 술부를 람다 파라미터로 가진다. 술부는 각 요소에 대해 적용된다. 결과 배열에는 술부를 <code>true</code>로 만드는 요소가 반환된다.</p>
<pre><code class="language-kotlin">val numbers = listOf(1, -2, 3, -4, 5, -6)      // 1

val positives = numbers.filter { x -&gt; x &gt; 0 }  // 2

val negatives = numbers.filter { it &lt; 0 }      // 3  </code></pre>
<ol>
<li>숫자 컬렉션을 정의한다.</li>
<li>양수를 얻는다.</li>
<li>음수를 얻기 위해 더 짧은 <code>it</code> 표기법을 사용한다.</li>
</ol>
<br>

<h2 id="map-1">map</h2>
<hr>
<p><code>map</code> 표현식 함수는 컬렉션의 모든 요소에 변환을 적용한다. 이는 변환함수를 람다 파라미터로 가진다.</p>
<pre><code class="language-kotlin">val numbers = listOf(1, -2, 3, -4, 5, -6)     // 1

val doubled = numbers.map { x -&gt; x * 2 }      // 2

val tripled = numbers.map { it * 3 }          // 3</code></pre>
<ol>
<li>숫자 컬렉션을 정의한다.</li>
<li>숫자에 2를 곱한다. </li>
<li>숫자에 3을 곱히기 위해 더 짧은 <code>it</code> 표현식을 사용한다.</li>
</ol>
<br>

<h2 id="any-all-none">any, all, none</h2>
<hr>
<p>이 세 가지는 주어진 술부와 일치하는 컬렉션 요소가 있는지 체크한다.</p>
<br>

<h3 id="함수-any">함수 <code>any</code></h3>
<hr>
<p><code>any</code>는 하나 이상의 요소가 주어진 술부와 일치할 경우 <code>true</code>를 반환한다.</p>
<pre><code class="language-kotlin">val numbers = listOf(1, -2, 3, -4, 5, -6)            // 1

val anyNegative = numbers.any { it &lt; 0 }             // 2

val anyGT6 = numbers.any { it &gt; 6 }                  // 3 </code></pre>
<ol>
<li>숫자 컬렉션을 정의한다.</li>
<li>음수인 요소가 있는지 체크힌다.</li>
<li>6보다 큰 요소가 있는지 체크한다.</li>
</ol>
<br>

<h3 id="함수-all">함수 <code>all</code></h3>
<hr>
<p><code>all</code>은 컬렉션의 모든 요소가 주어진 술부와 일치할 경우 <code>true</code>를 반환한다.</p>
<pre><code class="language-kotlin">val numbers = listOf(1, -2, 3, -4, 5, -6)            // 1

val allEven = numbers.all { it % 2 == 0 }            // 2

val allLess6 = numbers.all { it &lt; 6 }                // 3</code></pre>
<ol>
<li>숫자 컬렉션을 정의한다.</li>
<li>모든 숫자가 짝수인지 체크한다.</li>
<li>모든 숫자가 6보다 작은지 체크한다.</li>
</ol>
<br>

<h3 id="함수-none">함수 <code>none</code></h3>
<hr>
<p><code>none</code>은 컬렉션의 어떤 요소도 주어진 술부와 일치하지 않을 때 <code>true</code>를 반환한다. </p>
<pre><code class="language-kotlin">val numbers = listOf(1, -2, 3, -4, 5, -6)            // 1

val allEven = numbers.none { it % 2 == 1 }           // 2
val allLess6 = numbers.none { it &gt; 6 }                // 3</code></pre>
<ol>
<li>숫자 컬렉션을 정의한다.</li>
<li>홀수가 없는지 (모든 숫자가 짝수인지) 확인한다.</li>
<li>6보다 큰 숫자가 없는지 확인한다.</li>
</ol>
<br>

<h2 id="find-findlast">find, findLast</h2>
<hr>
<p><code>find</code>와 <code>findLast</code> 함수는 주어진 술부와 일치하는 첫 번째 또는 마지막 컬렉션 요소를 반환한다. 일치하는 요소가 없다면 <code>null</code>을 반환한다.</p>
<pre><code class="language-kotlin">val words = listOf(&quot;Lets&quot;, &quot;find&quot;, &quot;something&quot;, &quot;in&quot;, &quot;collection&quot;, &quot;somehow&quot;)  // 1

val first = words.find { it.startsWith(&quot;some&quot;) }                                // 2

val last = words.findLast { it.startsWith(&quot;some&quot;) }                             // 3

val nothing = words.find { it.contains(&quot;nothing&quot;) }                             // 4</code></pre>
<ol>
<li>단어 컬렉션을 정의한다.</li>
<li>&quot;some&quot;으로 시작하는 첫 번째 단어를 찾는다.</li>
<li>&quot;some&quot;으로 시작하는 마지막 단어를 찾는다.</li>
<li>&quot;nothing&quot;을 포함하는 첫 번째 단어를 찾는다. </li>
</ol>
<br>

<h2 id="first-last">first, last</h2>
<hr>
<br>

<h3 id="first-last-1"><code>first</code>, <code>last</code></h3>
<hr>
<p>이 함수들은 컬렉션의 처음과 마지막 요소를 반환한다. 술부와 함께 이를 사용할 수도 있다; 이 경우에는 주어진 술부와 일치하는 처음 또는 마지막 요소가 반환된다.</p>
<p>만약 컬렉션이 비었거나 해당 술부와 일치하는 요소를 가지고 있지 않다면 <code>NoSuchElementException</code>을 반환한다. </p>
<pre><code class="language-kotlin">val numbers = listOf(1, -2, 3, -4, 5, -6)            // 1

val first = numbers.first()                          // 2
val last = numbers.last()                            // 3

val firstEven = numbers.first { it % 2 == 0 }        // 4
val lastOdd = numbers.last { it % 2 != 0}            // 5</code></pre>
<ol>
<li>숫자 컬렉션을 정의한다.</li>
<li>첫 요소를 가져온다.</li>
<li>마지막 요소를 가져온다</li>
<li>첫 짝수 요소를 가져온다.</li>
<li>첫 음수 요소를 가져온다.</li>
</ol>
<br>

<h3 id="firstornull-lastornull"><code>firstOrNull</code>, <code>lastOrNull</code></h3>
<hr>
<p>이 함수들은 한 가지 차이점을 빼면 위와 거의 동일하게 동작한다: 일치하는 요소가 없을 경우 <code>null</code>을 반환한다.</p>
<pre><code class="language-kotlin">val words = listOf(&quot;foo&quot;, &quot;bar&quot;, &quot;baz&quot;, &quot;faz&quot;)         // 1
val empty = emptyList&lt;String&gt;()                        // 2

val first = empty.firstOrNull()                        // 3
val last = empty.lastOrNull()                          // 4

val firstF = words.firstOrNull { it.startsWith(&#39;f&#39;) }  // 5
val firstZ = words.firstOrNull { it.startsWith(&#39;z&#39;) }  // 6
val lastF = words.lastOrNull { it.endsWith(&#39;f&#39;) }      // 7
val lastZ = words.lastOrNull { it.endsWith(&#39;z&#39;) }      // 8</code></pre>
<ol>
<li>단어 컬렉션을 정의한다.</li>
<li>빈 컬렉션을 정의한다.</li>
<li>빈 컬렉션에서 첫 요소를 가져온다. 이것은 <code>null</code>이 되어야 한다.</li>
<li>빈 컬렉션에서 마지막 요소를 가져온다. 이것 또한 <code>null</code>이 될 것이다.</li>
<li>&#39;f&#39;로 시작되는 첫 단어를 가져온다.</li>
<li>&#39;z&#39;로 시작되는 첫 단어를 가져온다.</li>
<li>&#39;f&#39;로 시작되는 마지막 단어를 가져온다.</li>
<li>&#39;z&#39;로 시작되는 마지막 단어를 가져온다.</li>
</ol>
<br>

<h2 id="count">count</h2>
<hr>
<p><code>count</code> 함수는 컬렉션 요소의 총 수 또는 주어진 술부와 일치하는 요소의 수를 반환한다.</p>
<pre><code class="language-kotlin">val numbers = listOf(1, -2, 3, -4, 5, -6)            // 1

val totalCount = numbers.count()                     // 2
val evenCount = numbers.count { it % 2 == 0 }        // 3</code></pre>
<ol>
<li>숫자 컬렉션을 정의한다.</li>
<li>요소의 전체 수를 카운트한다.</li>
<li>짝수 요소의 수를 카운트한다.</li>
</ol>
<br>

<h2 id="associateby-groupby">associateBy, groupBy</h2>
<hr>
<p>함수 <code>associateBy</code>와 <code>groupBy</code>는 지정된 키로 인덱싱된 컬렉션의 요소들로부터 맵을 빌드한다. 키는 <code>keySelector</code> 파라미터에 정의된다. 또한 선택적인 <code>valueSelector</code>를 지정해 맵 요소의 <code>value</code>에 무엇이 저장될지를 지정할 수 있다. </p>
<p><code>associateBy</code>와 <code>groupBy</code>의 차이점은 동일한 키를 가지고 객체를 처리하는 방법이다:</p>
<ul>
<li><code>associateBy</code>는 마지막 적합 요소 (last suitable element)를 값으로 사용한다.</li>
<li><code>groupBy</code>는 모든 적합한 요소들의 리스트를 빌드하고 그것을 값에 집어넣는다.</li>
</ul>
<p>반환된 맵은 원 컬렉션의 entry iteration order를 유지한다. </p>
<pre><code class="language-kotlin">data class Person(val name: String, val city: String, val phone: String) // 1

val people = listOf(                                                     // 2
    Person(&quot;John&quot;, &quot;Boston&quot;, &quot;+1-888-123456&quot;),
    Person(&quot;Sarah&quot;, &quot;Munich&quot;, &quot;+49-777-789123&quot;),
    Person(&quot;Svyatoslav&quot;, &quot;Saint-Petersburg&quot;, &quot;+7-999-456789&quot;),
    Person(&quot;Vasilisa&quot;, &quot;Saint-Petersburg&quot;, &quot;+7-999-123456&quot;))

val phoneBook = people.associateBy { it.phone }                          // 3
val cityBook = people.associateBy(Person::phone, Person::city)           // 4
val peopleCities = people.groupBy(Person::city, Person::name)            // 5
val lastPersonCity = people.associateBy(Person::city, Person::name)      // 6</code></pre>
<ol>
<li>사람을 묘사하는 데이터 클래스를 정의한다.</li>
<li>사람 컬렉션을 정의한다.</li>
<li>전화번호가 그 소유주의 정보로 이어지는 맵을 만든다. <code>it.phone</code>이 <code>keySelector</code>이고, <code>valueSelector</code>는 정의되지 않았으므로 <code>Person</code>객체가 맵의 값이 된다. </li>
<li>전화번호로부터 그 소유주가 사는 도시로 이어지는 맵을 만든다. <code>Person::city</code>는 <code>valueSelector</code>이므로 맵의 값에는 도시만이 담기게 된다.</li>
<li>도시와 그 도시에 사는 사람들을 가진 맵을 만든다. 맵의 값은 각 도시 거주민 이름 리스트다.</li>
<li>도시와 그곳에 사는 마지막 사람을 가진 맵을 만든다. 맵의 값은 각 도시에 사는 마지막 사람의 이름이다.</li>
</ol>
<br>

<h2 id="partition">partition</h2>
<hr>
<p><code>partition</code> 함수는 원본 컬렉션을 주어진 술부를 사용해 다음과 같은 리스트 쌍으로 분리한다: </p>
<ol>
<li>술부가 <code>true</code>인 요소들</li>
<li>술부가 <code>false</code>인 요소들</li>
</ol>
<pre><code class="language-kotlin">val numbers = listOf(1, -2, 3, -4, 5, -6)                // 1

val evenOdd = numbers.partition { it % 2 == 0 }           // 2
val (positives, negatives) = numbers.partition { it &gt; 0 } // 3</code></pre>
<ol>
<li>숫자 컬렉션을 정의한다.</li>
<li><code>numbers</code>를 짝수와 홀수 리스트 <code>Pair</code>로 나눈다.</li>
<li><code>numbers</code>를 양수와 음수 리스트로 나눈다. Pair의 멤버들을 얻기 위해 Pair 구조 분해(Pair destructuring)가 이루어졌다.</li>
</ol>
<br>

<h2 id="flatmap">flatMap</h2>
<hr>
<p><code>flatMap</code>은 컬렉션의 각 요소를 iterable 객체로 변환하다. 그리고 변환 결과의 단일 리스트를 만든다. 변환은 유저가 정의한다.</p>
<pre><code class="language-kotlin">val fruitsBag = listOf(&quot;apple&quot;,&quot;orange&quot;,&quot;banana&quot;,&quot;grapes&quot;)  // 1
val clothesBag = listOf(&quot;shirts&quot;,&quot;pants&quot;,&quot;jeans&quot;)           // 2
val cart = listOf(fruitsBag, clothesBag)                    // 3
val mapBag = cart.map { it }                                // 4
val flatMapBag = cart.flatMap { it }                        // 5</code></pre>
<ol>
<li>과일 이름 문자열 컬렉션을 정의한다.</li>
<li>의류명 문자열 컬렉션을 정의한다.</li>
<li><code>fruitsBag</code>과 <code>clothesBag</code>을 <code>cart</code> 리스트에 넣는다.</li>
<li><code>cart</code> 요소들의 <code>map</code>을 만든다. 이것은 두 리스트의 리스트(두 리스트를 요소로 가지는)가 된다.</li>
<li><code>cart</code> 요소들의 <code>flatMap</code>을 만든다. 이것은 두 리스트에서 온 요소들로 구성된 단일 리스트가 된다. </li>
</ol>
<br>

<h2 id="minornull-maxornull">minOrNull, maxOrNull</h2>
<hr>
<p><code>minOrNull</code>과 <code>maxOrNull</code> 함수는 컬렉션의 최소/최대 요소를 반환한다. 컬렉션이 비어 있다면 <code>null</code>을 반환한다.</p>
<pre><code class="language-kotlin">val numbers = listOf(1, 2, 3)
val empty = emptyList&lt;Int&gt;()
val only = listOf(3)

println(&quot;Numbers: $numbers, min = ${numbers.minOrNull()} max = ${numbers.maxOrNull()}&quot;) // 1
println(&quot;Empty: $empty, min = ${empty.minOrNull()}, max = ${empty.maxOrNull()}&quot;)        // 2
println(&quot;Only: $only, min = ${only.minOrNull()}, max = ${only.maxOrNull()}&quot;)            // 3</code></pre>
<ol>
<li>비어 있지 않은 컬렉션에서 함수는 최소/최대 요소를 반환한다.</li>
<li>비어 있는 컬렉션에서 두 함수는 모두 <code>null</code>을 반환한다.</li>
<li>요소가 하나 밖에 없는 컬렉션에서 두 함수는 같은 값을 반환한다.</li>
</ol>
<br>

<h2 id="sorted">sorted</h2>
<hr>
<p><code>sorted</code>는 자연 정렬 순서(natural sort order) (오름차순)으로 정렬된 컬렉션 요소 리스트를 반환한다.</p>
<p><code>sortedBy</code>는 지정된 선택자 함수(selector function)에 의해 반환된 값의 자연 정렬 순서에 따라 요소를 정렬한다.</p>
<pre><code class="language-kotlin">val shuffled = listOf(5, 4, 2, 1, 3, -10)                   // 1
val natural = shuffled.sorted()                             // 2
val inverted = shuffled.sortedBy { -it }                    // 3
val descending = shuffled.sortedDescending()                // 4
val descendingBy = shuffled.sortedByDescending { abs(it)  } // 5</code></pre>
<ol>
<li>뒤섞인 숫자 컬렉션을 정의한다.</li>
<li>자연 순서로 정렬한다.</li>
<li>선택자 함수로 <code>-it</code>을 사용해 뒤집어진 자연 순서(inverted natural order)로 정렬한다.</li>
<li><code>sortedDescending</code>을 사용해 뒤집어진 자연 순서로 정렬한다.</li>
<li>선택자 함수로 <code>abs(it)</code>을 사용해 아이템 절대값의 뒤집어진 자연 순서로 정렬한다. </li>
</ol>
<br>

<h2 id="map-element-access">Map Element Access</h2>
<hr>
<p>맵에 적용하면 <code>[]</code> 연산자는 주어진 키에 대응하는 값을 반환한다. 맵에 해당 키가 없다면 <code>null</code>을 반환한다.</p>
<p><code>getValue</code> 함수는 주어진 키로 대응하는 값을 반환한다. 키를 찾을 수 없다면 예외를 던진다. <code>withDefault</code>로 생성된 맵에는 <code>getValue</code>는 예외 대신 기본 값을 반환한다.</p>
<pre><code class="language-kotlin">val map = mapOf(&quot;key&quot; to 42)

val value1 = map[&quot;key&quot;]                                     // 1
val value2 = map[&quot;key2&quot;]                                    // 2

val value3: Int = map.getValue(&quot;key&quot;)                       // 1

val mapWithDefault = map.withDefault { k -&gt; k.length }
val value4 = mapWithDefault.getValue(&quot;key2&quot;)                // 3

try {
    map.getValue(&quot;anotherKey&quot;)                              // 4
} catch (e: NoSuchElementException) {
    println(&quot;Message: $e&quot;)
}</code></pre>
<ol>
<li>&quot;key&quot;에 대응되는 값이 42이므로 42를 반환한다.</li>
<li>&quot;key2&quot;가 맵에 없으므로 <code>null</code>을 반환한다.</li>
<li>&quot;key2&quot;가 부재하므로 기본 값을 반환한다. 이 키에게 기본 값은 4이다.</li>
<li>&quot;anotherKey&quot;가 맵에 없으므로 <code>NoSuchElementException</code>을 반환한다.</li>
</ol>
<br>

<h2 id="zip">zip</h2>
<hr>
<p><code>zip</code> 함수는 주어진 두 컬렉션을 새 컬렉션으로 병합한다(Merge). 기본적으로 결과 컬렉션은 동일한 인덱스로 소스 컬렉션 요소들의 <code>Pair</code>를 가진다. 하지만 결과 컬렉션 요소 구조를 정의할 수 있다.</p>
<pre><code class="language-kotlin">val A = listOf(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)                  // 1
val B = listOf(1, 2, 3, 4)                     // 1

val resultPairs = A zip B                      // 2
val resultReduce = A.zip(B) { a, b -&gt; &quot;$a$b&quot; } // 3</code></pre>
<ol>
<li>두 컬렉션을 정의한다.</li>
<li>pair 리스트로 둘을 병합한다. infix notation이 사용되었다.</li>
<li>주어진 변환에 따라 문자열 값 리스트로 병합된다.</li>
</ol>
<br>

<h2 id="getorelse">getOrElse</h2>
<hr>
<p><code>getOrElse</code>는 컬렉션 요소에 대한 안전한 접근을 제공한다. 이것은 인덱스와, 인덱스가 out of bound일 경우 기본 값을 제공하는 함수를 가지고 있다.</p>
<pre><code class="language-kotlin">val list = listOf(0, 10, 20)
println(list.getOrElse(1) { 42 })    // 1
println(list.getOrElse(10) { 42 })   // 2</code></pre>
<ol>
<li>인덱스 <code>1</code>에 있는 요소를 프린트한다.</li>
<li>인덱스 <code>10</code>은 out of bounds이므로 <code>42</code>를 프린트한다.</li>
</ol>
<p><code>getOrElse</code>는 주어진 키로 값을 얻기 위해 맵에도 사용할 수 있다.</p>
<pre><code class="language-kotlin">val map = mutableMapOf&lt;String, Int?&gt;()
println(map.getOrElse(&quot;x&quot;) { 1 })       // 1

map[&quot;x&quot;] = 3
println(map.getOrElse(&quot;x&quot;) { 1 })       // 2

map[&quot;x&quot;] = null
println(map.getOrElse(&quot;x&quot;) { 1 })       // 3</code></pre>
<ol>
<li>키 &quot;x&quot;가 없으므로 기본 값을 출력한다.</li>
<li>키 &quot;x&quot;에 대한 값 3을 출력한다.</li>
<li>키 &quot;x&quot;에 대한 값이 정의되지 않았으므로 기본 값을 출력한다. </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin Examples > Control Flow]]></title>
            <link>https://velog.io/@hh_0705/controlFlow</link>
            <guid>https://velog.io/@hh_0705/controlFlow</guid>
            <pubDate>Wed, 02 Mar 2022 07:23:03 GMT</pubDate>
            <description><![CDATA[<p><em>작성완료: 2022-03-02</em></p>
<h2 id="when">When</h2>
<hr>
<p>널리 사용되는 <code>switch</code> 구문 대신, 코틀린은 더 유연하고 클리어한 <code>when</code> 구조를 지원한다. 이것은 구문 또는 표현식으로 사용될 수 있다.</p>
<pre><code class="language-kotlin">fun main() {
    cases(&quot;Hello&quot;)
    cases(1)
    cases(0L)
    cases(MyClass())
    cases(&quot;hello&quot;)
}

fun cases(obj: Any) {                                
    when (obj) {                                     // 1   
        1 -&gt; println(&quot;One&quot;)                          // 2
        &quot;Hello&quot; -&gt; println(&quot;Greeting&quot;)               // 3
        is Long -&gt; println(&quot;Long&quot;)                   // 4
        !is String -&gt; println(&quot;Not a string&quot;)        // 5
        else -&gt; println(&quot;Unknown&quot;)                   // 6
    }   
}

class MyClass</code></pre>
<ol>
<li><code>when</code>문이다.</li>
<li><code>obj</code>가 <code>1</code>과 같은지 확인한다.</li>
<li><code>obj</code>가 <code>Hello</code>와 같은지 확인한다.</li>
<li>타입 체크를 수행한다.</li>
<li>역 타입 체크를 수행한다.</li>
<li>디폴트 문 (생략 가능).</li>
</ol>
<p>하나가 만족될 때 까지 모든 브랜치 컨디션이 순차적으로 체크된다는 것에 주의하자. 즉 오직 가장 처음 들어맞는 브랜치만 실행된다.</p>
<br>

<h2 id="when-expression">When expression</h2>
<hr>
<pre><code class="language-kotlin">fun main() {
    println(whenAssign(&quot;Hello&quot;))
    println(whenAssign(3.4))
    println(whenAssign(1))
    println(whenAssign(MyClass()))
}

fun whenAssign(obj: Any): Any {
    val result = when (obj) {                   // 1
        1 -&gt; &quot;one&quot;                              // 2
        &quot;Hello&quot; -&gt; 1                            // 3
        is Long -&gt; false                        // 4
        else -&gt; 42                              // 5
    }
    return result
}

class MyClass</code></pre>
<ol>
<li><code>when</code>문이다.</li>
<li><code>obj</code>가 <code>1</code>과 같을 경우 값을 <code>one</code>으로 지정한다.</li>
<li><code>obj</code>가 <code>Hello</code>와 같을 경우 값을 <code>1</code>로 지정한다.</li>
<li><code>obj</code>가 <code>Long</code> 인스턴스일 경우 값을 <code>false</code>로 지정한다. </li>
<li>선행 조건이 아무것도 충족되지 않을 경우 <code>42</code>로 값을 지정한다. <code>when</code> 문과 달리, 디폴트 브랜치는 일반적으로 <code>when</code> 표현식에서는 필수적이다. 컴파일러가 다른 브랜치들이 가능한 모든 케이스를 커버한다고 확인할 수 있는 경우만 예외이다.</li>
</ol>
<br>

<h2 id="loops">Loops</h2>
<hr>
<p>코틀린은 일반적으로 사용되는 모든 루프를 지원한다: <code>for</code>, <code>while</code>, <code>do-while</code></p>
<br>

<h3 id="for"><code>for</code></h3>
<hr>
<p>코틀린의 <code>for</code>은 대부분의 언어와 같은 방식으로 동작한다. </p>
<pre><code class="language-kotlin">val cakes = listOf(&quot;carrot&quot;, &quot;cheese&quot;, &quot;chocolate&quot;)

for (cake in cakes) {                               // 1
    println(&quot;Yummy, it&#39;s a $cake cake!&quot;)
}</code></pre>
<ol>
<li>리스트의 각 케이크를 루프한다.</li>
</ol>
<br>

<h3 id="while-and-do-while"><code>while</code> and <code>do-while</code></h3>
<hr>
<p><code>while</code>과 <code>do-while</code> 구조는 대부분의 언어와 비슷하게 동작한다.</p>
<pre><code class="language-kotlin">fun eatACake() = println(&quot;Eat a Cake&quot;)
fun bakeACake() = println(&quot;Bake a Cake&quot;)

fun main(args: Array&lt;String&gt;) {
    var cakesEaten = 0
    var cakesBaked = 0

    while (cakesEaten &lt; 5) {                    // 1
        eatACake()
        cakesEaten ++
    }

    do {                                        // 2
        bakeACake()
        cakesBaked++
    } while (cakesBaked &lt; cakesEaten)

}</code></pre>
<ol>
<li>조건이 true인 동안 블록을 실행한다.</li>
<li>첫 블록을 실행하고 컨디션을 체크한다.</li>
</ol>
<br>

<h3 id="iterators">Iterators</h3>
<hr>
<p><code>iterator</code> 연산자를 클래스 내부에 구현함으로써 이터레이터를 정의할 수 있다.</p>
<pre><code class="language-kotlin">class Animal(val name: String)

class Zoo(val animals: List&lt;Animal&gt;) {

    operator fun iterator(): Iterator&lt;Animal&gt; {             // 1
        return animals.iterator()                           // 2
    }
}

fun main() {

    val zoo = Zoo(listOf(Animal(&quot;zebra&quot;), Animal(&quot;lion&quot;)))

    for (animal in zoo) {                                   // 3
        println(&quot;Watch out, it&#39;s a ${animal.name}&quot;)
    }

}</code></pre>
<ol>
<li>클래스에 iterator를 정의한다. 반드시 <code>iterator</code>로 명명되어야 하고, <code>operator</code> 제한자/수정자를 가져야 한다. </li>
<li>아래 메서드 요구사항을 충족하는 iterator를 리턴한다.<ul>
<li><code>next()</code> : <code>Animal</code></li>
<li><code>hasNext()</code> : <code>Boolean</code></li>
</ul>
</li>
<li>zoo의 animals를 사용자 정의 iterator를 사용해 루프한다.</li>
</ol>
<p>이터레이터는 확장 함수로써, 또는 타입에 선언할 수 있다. </p>
<br>

<h2 id="ranges">Ranges</h2>
<hr>
<p>코틀린에서 range를 정의하기 위해 사용하는 도구 모음이다. 짧게 살펴보자.</p>
<pre><code class="language-kotlin">for(i in 0..3) {             // 1
    print(i)
}
print(&quot; &quot;)

for(i in 0 until 3) {        // 2
    print(i)
}
print(&quot; &quot;)

for(i in 2..8 step 2) {      // 3
    print(i)
}
print(&quot; &quot;)

for (i in 3 downTo 0) {      // 4
    print(i)
}
print(&quot; &quot;)</code></pre>
<ol>
<li>0에서 3까지(3 포함)인 범위를 순회한다. 다른 언어(C/C++/Java)의 <code>for(i=0; i&lt;=3; ++i)</code>와 같다.</li>
<li>0에서 3까지(3 제외)인 범위를 순회한다. 다른 언어(C/C++/Java)의 <code>for(i=0; i&lt;3; ++i)</code>나 python의 루프와 같다.</li>
<li>연속적인 요소들을 지정한 증가 step으로 순회한다. </li>
<li>역순으로 범위를 순회한다.</li>
</ol>
<p>char range 또한 지원된다:</p>
<pre><code class="language-kotlin">for (c in &#39;a&#39;..&#39;d&#39;) {        // 1
    print(c)
}
print(&quot; &quot;)

for (c in &#39;z&#39; downTo &#39;s&#39; step 2) { // 2
    print(c)
}
print(&quot; &quot;)</code></pre>
<ol>
<li>알파벳 순서로 char range를 순회한다.</li>
<li>char range는 <code>step</code>과 <code>downTo</code> 또한 지원한다.</li>
</ol>
<p>범위는 <code>if</code>문에서도 유용하다.</p>
<pre><code class="language-kotlin">val x = 2
if (x in 1..5) {            // 1
    print(&quot;x is in range from 1 to 5&quot;)
}
println()

if (x !in 6..10) {          // 2
    print(&quot;x is not in range from 6 to 10&quot;)
}</code></pre>
<ol>
<li>값이 범위 안에 있는지를 확인한다.</li>
<li><code>!in</code>은 <code>in</code>의 반대이다.</li>
</ol>
<br>

<h2 id="equality-checks">Equality Checks</h2>
<hr>
<p>코틀린은 구조적 비교(structural comparison)에 <code>==</code>를 사용햐고 참조적 비교(referential comparison)에 <code>===</code>를 사용한다.</p>
<p>더 정확하게는, <code>a == b</code>는 <code>if (a == null) b == null else a.equals(b)</code>로 컴파일된다.</p>
<pre><code class="language-kotlin">val authors = setOf(&quot;Shakespeare&quot;, &quot;Hemingway&quot;, &quot;Twain&quot;)
val writers = setOf(&quot;Twain&quot;, &quot;Shakespeare&quot;, &quot;Hemingway&quot;)

println(authors == writers)   // 1
println(authors === writers)  // 2</code></pre>
<ol>
<li><code>authors.equals(writers)</code>를 호출하고 set은 원소 순서를 무시하므로 <code>true</code>를 반환한다.</li>
<li><code>authors</code>와 <code>writers</code>가 별개의 참조이므로 <code>false</code>를 반환한다.</li>
</ol>
<br>

<h2 id="conditional-expression">Conditional Expression</h2>
<hr>
<p>코틀린에는 삼항연산자 <code>condition ? then : else</code>가 없다. 그 대신 <code>if</code>가 표현문으로 사용될 수 있다:</p>
<pre><code class="language-kotlin">fun max(a: Int, b: Int) = if (a &gt; b) a else b         // 1

println(max(99, -42))</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin Examples > Introduction]]></title>
            <link>https://velog.io/@hh_0705/Introduction</link>
            <guid>https://velog.io/@hh_0705/Introduction</guid>
            <pubDate>Fri, 25 Feb 2022 09:02:57 GMT</pubDate>
            <description><![CDATA[<p><em>작성완료: 2022-02-25</em></p>
<h2 id="hello-world">Hello World</h2>
<hr>
<pre><code class="language-kotlin">package org.kotlinlang.play         // 1

fun main() {                        // 2
    println(&quot;Hello, World!&quot;)        // 3
}</code></pre>
<ol>
<li>코틀린 코드는 일반적으로 패키지에서 선언된다. 패키지 지정은 선택적이다. 만약 패키지를 소스 파일에서 지정하지 않으면 내용은 디폴트 패키지로 가게 된다.</li>
<li>코틀린 어플리케이션의 엔트리 포인트는 <code>main</code> 함수이다. 코틀린 1.3부터 <code>main</code>을 파라미터 없이 선언할 수 있다. 리턴 타입은 지정되지 않았다. 이는 이 함수가 아무것도 리턴하지 않는다는 것을 뜻한다. </li>
<li><code>println</code>은 스탠더드 아웃풋에 문장을 쓴다. 이것은 암묵적으로 import되어 있다. 세미콜론 또한 선택적이다. </li>
</ol>
<p>1.3 이전의 코틀린에서는 <code>main</code> 함수는 반드시 <code>Array&lt;String&gt;</code> 함수를 가져야 했다.</p>
<pre><code class="language-kotlin">fun main(args: Array&lt;String&gt;) {
    println(&quot;Hello, World!&quot;)
}</code></pre>
<br>

<h2 id="functions">Functions</h2>
<hr>
<h3 id="기본-파라미터-값들과-명명된-아규먼트">기본 파라미터 값들과 명명된 아규먼트</h3>
<hr>
<pre><code class="language-kotlin">fun printMessage(message: String): Unit {                               // 1
    println(message)
}

fun printMessageWithPrefix(message: String, prefix: String = &quot;Info&quot;) {  // 2
    println(&quot;[$prefix] $message&quot;)
}

fun sum(x: Int, y: Int): Int {                                          // 3
    return x + y
}

fun multiply(x: Int, y: Int) = x * y                                    // 4

fun main() {
    printMessage(&quot;Hello&quot;)                                               // 5                    
    printMessageWithPrefix(&quot;Hello&quot;, &quot;Log&quot;)                              // 6
    printMessageWithPrefix(&quot;Hello&quot;)                                     // 7
    printMessageWithPrefix(prefix = &quot;Log&quot;, message = &quot;Hello&quot;)           // 8
    println(sum(1, 2))                                                  // 9
    println(multiply(2, 4))                                             // 10
}</code></pre>
<ol>
<li><code>String</code> 파라미터를 가지고 <code>Unit</code>을 리턴하는 단순한 함수 (즉, 값을 리턴하지 않는다).</li>
<li><code>info</code>를 기본 값으로 가진 두번째 선택적 파라미터가 있는 함수. 리턴 타입은 생략되었고, 이는 타입이 실제로는 <code>Unit</code>임을 뜻한다.</li>
<li><code>Int</code>를 리턴하는 함수</li>
<li>추론된 <code>Int</code>를 반환하는 단일 표현식 함수</li>
<li>Hello 아규먼트로 첫 번째 함수 호출</li>
<li>두 파라미터 모두에 값을 전달해서 함수를 호출</li>
<li>두 번째 파라미터를 생략하고 같은 함수를 호출. 기본 값이 사용되었다.</li>
<li>명명된 아규먼트를 사용해 같은 함수를 호출. 아규먼트 순서가 바뀌었다.</li>
<li>sum 함수 호출 결과를 프린트</li>
<li>multiply 함수 호출 결과를 프린트.</li>
</ol>
<h3 id="infix-functions">Infix functions</h3>
<hr>
<p>단일 파라미터를 가진 멤버 변수와 표현식은 infix 함수로 바뀔 수 있다.</p>
<pre><code class="language-kotlin">fun main() {

  infix fun Int.times(str: String) = str.repeat(this)        // 1
  println(2 times &quot;Bye &quot;)                                    // 2

  val pair = &quot;Ferrari&quot; to &quot;Katrina&quot;                          // 3
  println(pair)

  infix fun String.onto(other: String) = Pair(this, other)   // 4
  val myPair = &quot;McLaren&quot; onto &quot;Lucas&quot;
  println(myPair)

  val sophia = Person(&quot;Sophia&quot;)
  val claudia = Person(&quot;Claudia&quot;)
  sophia likes claudia                                       // 5
}

class Person(val name: String) {
  val likedPeople = mutableListOf&lt;Person&gt;()
  infix fun likes(other: Person) { likedPeople.add(other) }  // 6
}</code></pre>
<ol>
<li><code>Int</code>에 infix 표현식 함수를 정의한다.</li>
<li>infix 함수를 호출한다.</li>
<li>스탠더드 라이브러리에서 <code>to</code> infix 함수를 호출해 <code>Pair</code>를 생성한다.</li>
<li><code>onto</code>라고 불리는 당신만의 <code>to</code> 구현.</li>
<li>Infix 표기법은 멤버 함수(메서드)로도 동작한다.</li>
<li>포함하는 클래스가 첫 번째 파라미터가 된다.</li>
</ol>
<p>예시는 지역변수(다른 함수 내에 중첩된 함수)를 사용한다는 점을 주의하자.</p>
<h3 id="operatior-functions">Operatior Functions</h3>
<hr>
<p>특정 함수들은 operator 심볼로 호출함으로써 연산자로 &#39;업그레이드&#39; 될 수 있다.</p>
<pre><code class="language-kotlin">operator fun Int.times(str: String) = str.repeat(this)       // 1
println(2 * &quot;Bye &quot;)                                          // 2

operator fun String.get(range: IntRange) = substring(range)  // 3
val str = &quot;Always forgive your enemies; nothing annoys them so much.&quot;
println(str[0..14])   

// operator fun Int.times(str: String) = &quot;- $str&quot; 
// println(2 * &quot;Bye&quot;) 결과는 - Bye</code></pre>
<ol>
<li><code>operator</code> modifier(수식자/제한자))를 사용해 위 infix 함수를 한 단계 더 발전시킨다.</li>
<li><code>times</code>에 대한 연산자 심볼은 <code>*</code>이므로 <code>2 * &quot;Bye&quot;</code>를 사용해 호출할 수 있다. </li>
<li>연산자 함수는 문자열 범위에 대한 접근을 쉽게 해 준다. </li>
<li><code>get()</code> 연산자는 <a href="https://kotlinlang.org/docs/operator-overloading.html#indexed">대괄호 엑세스 구문</a>으로 활성화된다.</li>
</ol>
<h3 id="vararg-파라미터가-있는-함수"><code>vararg</code> 파라미터가 있는 함수</h3>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/functions.html?&amp;_ga=2.187638063.666056629.1645681929-521320736.1644819103#variable-number-of-arguments-varargs">Varargs</a>는 아규먼트들을 콤마로 나눔으로써 어떤 수의 아규먼트든 전달할 수 있게 해 준다.</p>
<pre><code class="language-kotlin">fun printAll(vararg messages: String) {                            // 1
    for (m in messages) println(m)
}
printAll(&quot;Hello&quot;, &quot;Hallo&quot;, &quot;Salut&quot;, &quot;Hola&quot;, &quot;你好&quot;)                 // 2

fun printAllWithPrefix(vararg messages: String, prefix: String) {  // 3
    for (m in messages) println(prefix + m)
}
printAllWithPrefix(
    &quot;Hello&quot;, &quot;Hallo&quot;, &quot;Salut&quot;, &quot;Hola&quot;, &quot;你好&quot;,
    prefix = &quot;Greeting: &quot;                                          // 4
)

fun log(vararg entries: String) {
    printAll(*entries)                                             // 5
    //   printAll(entries) //Type mismatch : inferred type is Array&lt;out String&gt; but String was expected
}</code></pre>
<ol>
<li><code>vargar</code> modifier는 파라미터를 vararg로 바꾼다.</li>
<li>이것은 <code>printAll</code>을 어떤 수의 문자열 아규먼트를 사용해서든 호출할 수 있도록 한다.</li>
<li>명명된 파라미터(named parameters) 덕분에 vararg 뒤에 동일한 종류의 다른 파라미터를 더할 수도 있다. 자바에서는 이 값을 전달할 방법이 없기 때문에 허용되지 않는다.</li>
<li>명명된 파라미터를 사용해 vararg와 별개로 <code>prefix</code>에 값을 설정할 수 있다.</li>
<li>런타임에 vararg는 단지 배열이다. 이것을 vararg 파라미터로 전달하려면 특별한 전개 구문(spread operator) <code>*</code>를 사용한다. 이를 통해 <code>entries</code> (<code>Array&lt; String &gt;</code>) 대신 <code>*entries</code> (<code>String</code> vararg)를 전달할 수 있다.</li>
</ol>
<br>

<h2 id="variables">Variables</h2>
<hr>
<p>코틀린은 강력한 타입 추론을 가지고 있다. 변수의 타입을 명시적으로 선언할 수 있지만, 일반적으로는 컴파일러가 추론을 통해 그 작업을 하도록 두게 될 것이다. 
(불변성이) 권장되기는 하지만 코틀린은 불변성(immutability)를 강제하지 않는다. 기본적으로 <code>var</code> 보다 <code>val</code>을 사용하도록 하자.</p>
<pre><code class="language-kotlin">var a: String = &quot;initial&quot;  // 1
println(a)
val b: Int = 1             // 2
val c = 3                  // 3</code></pre>
<ol>
<li>변경가능한 변수를 선언하고 초기화한다.</li>
<li>불변 함수를 선언하고 초기화한다.</li>
<li>타입 지정 없이 불변 함수를 선언하고 초기화한다. 컴파일러가 타입 <code>Int</code>를 추론한다.</li>
</ol>
<pre><code class="language-kotlin">var e: Int  // 1
println(e)  // variable &#39;e&#39; must be initialized // 2</code></pre>
<ol>
<li>초기화 없이 변수를 선언한다.</li>
<li>변수 사용은 다음과 같은 컴파일러 에러를 가져온다: 변수 &#39;e&#39;는 반드시 초기화되어야 한다.</li>
</ol>
<p>언제 변수를 초기화할지 자유롭게 선택할 수 있다. 하지만 처음 읽히기 전에는 반드시 초기화되어야 한다.</p>
<pre><code class="language-kotlin">val d: Int  // 1

if (someCondition()) {
    d = 1   // 2
} else {
    d = 2   // 2
}

println(d) // 3</code></pre>
<ol>
<li>초기화 없이 변수를 선언한다.</li>
<li>조건에 따라 다른 값으로 변수를 초기화한다.</li>
<li>초기화되었기 때문에 변수를 읽을 수 있다.</li>
</ol>
<br>

<h2 id="null-safety">Null safety</h2>
<hr>
<p><code>NullPointerException</code> 세계를 지우기 위한 노력으로 코틀린의 변수 타입은 <code>null</code>할당을 허용하지 않는다. 변수가 null이 될 필요가 있다면 <code>?</code>를 타입 마지막에 붙여 nullable을 선언해야 한다. </p>
<pre><code class="language-kotlin">var neverNull: String = &quot;This can&#39;t be null&quot;            // 1

// Null can not be a value of a non-null type String
neverNull = null                                        // 2

var nullable: String? = &quot;You can keep a null here&quot;      // 3

nullable = null                                         // 4

var inferredNonNull = &quot;The compiler assumes non-null&quot;   // 5

// Null can not be a value of a non-null type String
inferredNonNull = null                                  // 6

fun strLength(notNull: String): Int {                   // 7
    return notNull.length
}

strLength(neverNull)                                    // 8

// Type mismatch: inferred type is Nothing? but String was expected
strLength(nullable)                                     // 9</code></pre>
<ol>
<li>non-null String 변수를 선언한다.</li>
<li><code>null</code>을 non-nullable 변수에 할당하려고 시도하면 컴파일 오류가 생성된다.</li>
<li>nullable String variable을 선언한다.</li>
<li><code>null</code>을 nullable variable에 설정할 수 있다.</li>
<li>타입 추론 시, 값으로 초기화된 변수에 대해 컴파일러는 non-null을 가정한다.</li>
<li>추론된 타입에 대해 <code>null</code>을 할당하려고 하면 컴파일 오류가 발생한다.</li>
<li>non-null string parameter를 가진 함수를 선언한다.</li>
<li>String (non-nullable) argument로 함수를 호출할 수 있다.</li>
<li>String? (nullable) argument로 함수를 호출하려고 하면 컴파일 오류가 발생한다.</li>
</ol>
<br>

<h2 id="working-with-nulls">Working with Nulls</h2>
<hr>
<p>가끔씩 코틀린 프로그램은 null 값으로 작업해야 할 경우가 있다. 예를 들어 외부 자바 코드와 상호작용할 때나, 존재하지 않는 상태(truly absent state)를 표현해야 할 때 그렇다. 코틀린은 이런 사왕을 우아하게 다루기 위한 null tracking을 제공한다. </p>
<pre><code class="language-kotlin">fun describeString(maybeString: String?): String {              // 1
    if (maybeString != null &amp;&amp; maybeString.length &gt; 0) {        // 2
        return &quot;String of length ${maybeString.length}&quot;
    } else {
        return &quot;Empty or null string&quot;                           // 3
    }
}</code></pre>
<ol>
<li>nullable string 을 가지고 그에 대한 묘사를 반환하는 함수</li>
<li>주어진 문자열이 null이 아니고 비어있지 않을 경우 길이에 대한 정보를 반환한다.</li>
<li>그렇지 않은 경우 호출자에게 해당 문자열이 null이거나 비어 있다고 말한다. </li>
</ol>
<br>

<h2 id="classes">Classes</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/classes.html?&amp;_ga=2.44985635.666056629.1645681929-521320736.1644819103#classes">클래스 선언</a>은 클래스 이름, 클래스 헤더(타입 파라미터, 기본 생성자 등을 지정한다), 그리고 중괄호로 둘러싸인 클래스 바디로 구성된다. 해더와 바디는 선택적이다. 만약 클래스에 바디가 없다면 중괄호는 생략될 수 있다.</p>
<pre><code class="language-kotlin">class Customer                                  // 1

class Contact(val id: Int, var email: String)   // 2

fun main() {

    val customer = Customer()                   // 3

    val contact = Contact(1, &quot;mary@gmail.com&quot;)  // 4

    println(contact.id)                         // 5
    contact.email = &quot;jane@gmail.com&quot;            // 6
}</code></pre>
<ol>
<li><code>Customer</code>라는 클래스를 프로퍼티나 유저가 정의한 생성자 없이 선언한다. 코틀린에 의해 파라미터 없는 기본 생성자가 자동으로 생성된다.</li>
<li>두 프로퍼티(불변 변수 <code>id</code>와 가변 변수 <code>email</code>)와 두 파라미터 <code>id</code>, <code>email</code>을 사용하는 생성자로 클래스를 선언한다. </li>
<li>기본 생성자로 <code>Customer</code> 클래스의 인스턴스를 생성한다. 코틀린에는 <code>new</code> 키워드가 없다.</li>
<li>두 아규먼트를 가진 생성자를 사용해 <code>Contact</code> 클래스의 인스턴스를 생성한다.</li>
<li>프로퍼티 <code>id</code>에 엑세스한다.</li>
<li>프로퍼티 <code>email</code>의 값을 업데이트한다.</li>
</ol>
<br>

<h2 id="generics">Generics</h2>
<hr>
<p><a href="https://kotlinlang.org/docs/reference/generics.html?_ga=2.154037567.666056629.1645681929-521320736.1644819103">제네릭</a>은 모던 랭귀지에서 표준이 된 일반성 메커니즘(genericity mechanism)이다. 제네릭 클래스와 함수들은 특정한 제너릭 타입과 독립적인 common logic을 캡슐화함으로써 코드 재사용성을 높인다. 한 예로 <code>List&lt;T&gt;</code> 내부 로직은 <code>T</code>가 무엇인지에 독립적이다.</p>
<h3 id="generic-classes">Generic classes</h3>
<hr>
<p>코틀린에서 제네릭을 사용하는 첫 번째 방법은 제네릭 클래스를 생성하는 것이다.</p>
<pre><code class="language-kotlin">class MutableStack&lt;E&gt;(vararg items: E) {              // 1

  private val elements = items.toMutableList()

  fun push(element: E) = elements.add(element)        // 2

  fun peek(): E = elements.last()                     // 3

  fun pop(): E = elements.removeAt(elements.size - 1)

  fun isEmpty() = elements.isEmpty()

  fun size() = elements.size

  override fun toString() = &quot;MutableStack(${elements.joinToString()})&quot;
}</code></pre>
<ol>
<li>제네릭 클래스 <code>MutableStack&lt;E&gt;</code>를 정의한다. <code>E</code>는 제네릭 타입 파라미터라고 불린다. 사용 현장에서는 특정한 타입으로 할당된다. 예: <code>MutableStack&lt;Int&gt;</code>를 선언함으로써 <code>Int</code>할당.</li>
<li>제네릭 클래스 내부에서, <code>E</code>는 다른 타입들과 마찬가지로 파라미터로 사용될 수 있다.</li>
<li>리턴 타입으로도 <code>E</code>를 사용할 수 있다.</li>
</ol>
<p>구현에는 단일 표현식으로 정의될 수 있는 코틀린의 약식 구문을 많이 사용한다.</p>
<h3 id="generic-functions">Generic functions</h3>
<hr>
<p>로직이 특정 타입과 독립적인 <a href="https://kotlinlang.org/docs/reference/generics.html?_ga=2.45171235.666056629.1645681929-521320736.1644819103#generic-functions">함수</a>를 제네릭으로 생성해 사용할 수 있다. 예를 들어 가변 스택을 생성하는 유틸리티 함수를 작성할 수 있다:</p>
<pre><code class="language-kotlin">fun &lt;E&gt; mutableStackOf(vararg elements: E) = MutableStack(*elements)

fun main() {
  val stack = mutableStackOf(0.62, 3.14, 2.7)
  println(stack)
}</code></pre>
<p>컴파일러는 <code>mutableStackOf</code>의 파라미터들에서 제네릭 타입을 추론할 수 있다. 그러므로 <code>mutableStackOf&lt;Double&gt;(...)</code>라고 작성할 필요는 없다.</p>
<br>

<h2 id="inheritance">Inheritance</h2>
<hr>
<p>코틀린은 전통적인 객체 지향 상속 메커니즘을 완전히 지원한다.</p>
<pre><code class="language-kotlin">open class Dog {                // 1
    open fun sayHello() {       // 2
        println(&quot;wow wow!&quot;)
    }
}

class Yorkshire : Dog() {       // 3
    override fun sayHello() {   // 4
        println(&quot;wif wif!&quot;)
    }
}

fun main() {
    val dog: Dog = Yorkshire()
    dog.sayHello()
}</code></pre>
<ol>
<li>코틀린 클래스는 기본적으로 <code>final</code>이다. 클래스 상속을 허용하고 싶을 경우 <code>open</code> 제한자로 클래스를 표시한다.</li>
<li>코틀린 메서드 또한 기본적으로 <code>final</code>이다. 클래스와 마찬가지로, <code>open</code> 제한자를 사용하면 오버라이딩을 허용한다.</li>
<li>클래스는 이름 뒤에 <code>: SuperclassName()</code>을 지정하면 superclass를 상속한다. 빈 괄호 <code>()</code>는 superclass default constructor의 호출을 가리킨다.</li>
<li>오버라이딩 메서드 또는 속성은 <code>override</code> 제한자를 필요로 한다.</li>
</ol>
<br>

<h3 id="inheritance-with-parameterized-constructor">Inheritance with Parameterized Constructor</h3>
<hr>
<pre><code class="language-kotlin">open class Tiger(val origin: String) {
    fun sayHello() {
        println(&quot;A tiger from $origin says: grrhhh!&quot;)
    }
}

class SiberianTiger : Tiger(&quot;Siberia&quot;)                  // 1

fun main() {
    val tiger: Tiger = SiberianTiger()
    tiger.sayHello()
}</code></pre>
<ol>
<li>서브클래스 생성 시 슈퍼클래스의 파라미터가 있는 생성자를 사용하고 싶을 경우 서브클래스 선언에 생성자 아규먼트를 제공한다.</li>
</ol>
<br>

<h3 id="passing-constructor-arguments-to-superclass">Passing Constructor Arguments to Superclass</h3>
<hr>
<pre><code class="language-kotlin">open class Lion(val name: String, val origin: String) {
    fun sayHello() {
        println(&quot;$name, the lion from $origin says: graoh!&quot;)
    }
}

class Asiatic(name: String) : Lion(name = name, origin = &quot;India&quot;) // 1

fun main() {
    val lion: Lion = Asiatic(&quot;Rufo&quot;)                              // 2
    lion.sayHello()
}</code></pre>
<ol>
<li><code>Asiatic</code> 선언에서 <code>name</code>은 <code>val</code>도 <code>var</code>도 아니다. 이것은 생성자 아규먼트로, 그 값은 슈퍼클래스 <code>Lion</code>의 <code>name</code> 프로퍼티로 전달된다.</li>
<li>name <code>Rufo</code>로 <code>Asiatic</code> 인스턴스를 생성한다. 콜은 <code>Rufo</code>와 <code>India</code> 아규먼트를 가진 <code>Lion</code> 생성자를 호출한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin v1.6.10 : Idioms]]></title>
            <link>https://velog.io/@hh_0705/Idioms</link>
            <guid>https://velog.io/@hh_0705/Idioms</guid>
            <pubDate>Thu, 24 Feb 2022 08:18:55 GMT</pubDate>
            <description><![CDATA[<h1 id="idioms"><a href="https://kotlinlang.org/docs/idioms.html">Idioms</a></h1>
<p><em>번역문서 작성일 기준 Last modified: 30 November 2021</em>
<em>번역완료일 2022-02-24</em></p>
<p>코틀린에서 랜덤하고 자주 사용되는 idioms(관용구)의 모음. 좋아하는 idiom이 있다면 pull request로 contribute해 보자.</p>
<br>

<h2 id="create-dtos-pojospocos">Create DTOs (POJOs/POCOs)</h2>
<hr>
<pre><code class="language-kotlin">data class Customer (val name: String, val email: String)</code></pre>
<p>아래 기능을 만족하는 <code>Customer</code> 클래스를 제공한다:</p>
<ul>
<li>모든 프로퍼티에 대한 getter(그리고 <code>var</code>의 경우 setter)</li>
<li>equals()</li>
<li>hashCode()</li>
<li>toString()</li>
<li>copy()</li>
<li>모든 프로퍼티를 위한 component1(), component2(), ... (<a href="https://kotlinlang.org/docs/data-classes.html">Data Classes</a>를 보자)</li>
</ul>
<br>

<h2 id="default-values-for-function-parameters">Default values for function parameters</h2>
<hr>
<pre><code class="language-kotlin">fun foo(a: Int = 0, b: String = &quot;&quot;) {...}</code></pre>
<br>

<h2 id="filter-a-list">Filter a list</h2>
<pre><code class="language-kotlin">val positives = list.filter {x -&gt; x &gt; 0}</code></pre>
<p>또는 아래와 같이 더 짧게:</p>
<pre><code class="language-kotlin">val positivies = list.filter { it &gt; 0 }</code></pre>
<p><a href="https://kotlinlang.org/docs/java-to-kotlin-idioms-strings.html#create-a-string-from-collection-items">자바와 코틀린 필터링 간 차이</a>를 배우자.</p>
<br>

<h2 id="collection에서-element-존재-여부-확인">collection에서 element 존재 여부 확인</h2>
<hr>
<pre><code class="language-kotlin">if (&quot;john@example.com&quot; in emailsList) { ... }
if (&quot;jane@example.com&quot; in emailsList) { ... }</code></pre>
<br>

<h2 id="string-interpolation">String interpolation</h2>
<hr>
<pre><code class="language-kotlin">println(&quot;name $name&quot;)</code></pre>
<p><a href="https://kotlinlang.org/docs/java-to-kotlin-idioms-strings.html#concatenate-strings">자바와 코틀린 간 문자열 연결 차이점</a>을 알아보자.</p>
<br>

<h2 id="인스턴스-체크">인스턴스 체크</h2>
<hr>
<pre><code class="language-kotlin">when (x) {
    is Foo -&gt; ...
    is Bar -&gt; ...
    else -&gt; ...
}</code></pre>
<br>

<h2 id="읽기-전용-리스트">읽기 전용 리스트</h2>
<hr>
<pre><code class="language-kotlin">val list = listOf(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)</code></pre>
<br>

<h2 id="읽기-전용-맵">읽기 전용 맵</h2>
<hr>
<pre><code class="language-kotlin">val map = mapOf(&quot;a&quot; to 1, &quot;b&quot; to 2, &quot;c&quot; to 3)</code></pre>
<br>

<h2 id="맵-앤트리-접근">맵 앤트리 접근</h2>
<hr>
<pre><code class="language-kotlin">println(map[&quot;key&quot;])
map[&quot;key&quot;] = value</code></pre>
<br>

<h2 id="맵-또는-쌍-리스트-탐색-traverse-a-map-or-a-list-of-pairs">맵 또는 쌍 리스트 탐색 (Traverse a map or a list of pairs)</h2>
<hr>
<pre><code class="language-kotlin">for ((k, v) in map) {
    println(&quot;$k -&gt; $v&quot;)
}</code></pre>
<p><code>k</code> 와 <code>v</code>는 <code>name</code>과 <code>age</code>같은 어떤 편리한 이름이든지 될 수 있다.</p>
<br>

<h2 id="iterate-over-a-range">Iterate over a range</h2>
<hr>
<pre><code class="language-kotlin">for (i in 1..100) {...} // closed range: 100 포함

for (i in 1 until 100) {...} // half-open range: 100을 포함하지 않음

for (i in 2..10 step 2) {...}

for (i in 10 downTo 1) {...}

(1..10).forEach {...}</code></pre>
<br>

<h2 id="lazy-property">Lazy property</h2>
<hr>
<pre><code class="language-kotlin">val p : String by lazy {
    // string을 계산
}</code></pre>
<br>

<h2 id="확장-함수-extension-functions">확장 함수 (Extension functions)</h2>
<hr>
<pre><code class="language-kotlin">fun String.spaceToCamelCase() {...}

&quot;Convert this to camelcase&quot;.spaceToCamelCase()</code></pre>
<br>

<h2 id="싱글톤-생성">싱글톤 생성</h2>
<hr>
<pre><code class="language-kotlin">object Resource {
    val name = &quot;name&quot;
}</code></pre>
<br>

<h2 id="추상-클래스-인스턴스화">추상 클래스 인스턴스화</h2>
<hr>
<pre><code class="language-kotlin">abstract class MyAbstractClass {
    abstract fun doSomething()
    abstract fun sleep()
}

fun main() {
    val myObject = object : MyAbstractClass () {
        override fun doSomething() {
            // ...
        }

        override fun sleep() { 
            // ...
        }
    }
    myObject.doSomething()
}</code></pre>
<br>

<h2 id="if-not-null-shorthand">If-not-null shorthand</h2>
<hr>
<pre><code class="language-kotlin">val files = File(&quot;Test&quot;).listFiles()

println(files?.size ?: &quot;empty&quot;) //파일이 null이라면 empty를 프린트</code></pre>
<br>

<h2 id="null일-경우-명령문-실행">null일 경우 명령문 실행</h2>
<hr>
<pre><code class="language-kotlin">val values = ...
val email = values[&quot;email&quot;] ?: throw IllegalStateException(&quot;email is missing&quot;)</code></pre>
<br>

<h2 id="비어있을-수-있는-컬렉션에서-첫-아이템-얻기">비어있을 수 있는 컬렉션에서 첫 아이템 얻기</h2>
<hr>
<pre><code class="language-kotlin">val emails = ... // 비어있을 수 있음
val mainEmail = emails.firstOrNull() ?: &quot;&quot;</code></pre>
<br>

<p><a href="https://kotlinlang.org/docs/java-to-kotlin-collections-guide.html#get-the-first-and-the-last-items-of-a-possibly-empty-collection">자바와 코틀린 간 첫 아이템 획득 방식 차이점</a>을 공부하자.</p>
<br>

<h2 id="null이-아닐-경우-실행">null이 아닐 경우 실행</h2>
<hr>
<pre><code class="language-kotlin">val value = ...
value?.let {
    ... // null이 아니라면 이 블록을 실행한다.
}</code></pre>
<br>

<h2 id="when-문-리턴">when 문 리턴</h2>
<hr>
<pre><code class="language-kotlin">fun transform(color: String): Int {
    return when (color) {
        &quot;Red&quot; -&gt; 0
        &quot;Green&quot; -&gt; 1
        &quot;Blue&quot; -&gt; 2
        else -&gt; throw IllegalArgumentException(&quot;Invalid color param value&quot;)
    }
}</code></pre>
<br>

<h2 id="try-catch-표현식">try-catch 표현식</h2>
<hr>
<pre><code class="language-kotlin">fun test() {
    val result = try {
        count ()
    } catch (e: AritmeticException) {
        throw IllegalStateException(e)
    }

    // 결과를 가지고 작업
}</code></pre>
<br>

<h2 id="if-표현식">if 표현식</h2>
<hr>
<pre><code class="language-kotlin">val y = if (x == 1) {
    &quot;one&quot;
} else if (x == 2) {
    &quot;two&quot;
} else {
    &quot;other&quot;
}</code></pre>
<br>

<h2 id="unit을-리턴하는-메서드의-빌더-스타일-사용법">Unit을 리턴하는 메서드의 빌더 스타일 사용법</h2>
<hr>
<pre><code class="language-kotlin"> fun arrayOfMinusOnes(size: Int): IntArray {
     return IntArray(size).apply {fill(-1)}
 }</code></pre>
<br>

<h2 id="single-expression-functions">Single-expression functions</h2>
<hr>
<pre><code class="language-kotlin">fun theAnswer() = 42</code></pre>
<p>이것은 다음과 동일하다</p>
<pre><code class="language-kotlin">fun theAnswer(): Int {
    return 42
}</code></pre>
<p>이는 다른 idiom들과 효과적으로 결합되면서 더 짧은 코드를 이끌어 낼 수 있다. 예를 들어 <code>when</code> 표현식과 함께 사용할 수 있다:</p>
<pre><code class="language-kotlin">fun transform(color: String): Int = when (color) {
    &quot;Red&quot; -&gt; 0 
    &quot;Green&quot; -&gt; 1
    &quot;Blue&quot; -&gt; 2
    else -&gt; throw IllegalArgumentException(&quot;Invalid color param value&quot;)
}</code></pre>
<br>

<h2 id="오브젝트-인스턴스에서-여러-메서드-호출-with">오브젝트 인스턴스에서 여러 메서드 호출 (with)</h2>
<hr>
<pre><code class="language-kotlin">class Turtle {
    fun penDown()
    fun penUp()
    fun turn(degrees: Double)
    fun forward(pixels: Double)
}

val myTurtle = Turtle()
with(myTurtle) {
    // 100 픽셀 스퀘어를 그린다.
    penDown()
    for (i in 1..4) {
        forward(100.0)
        turn(90.0)
    }
    penUp()
}</code></pre>
<br>

<h2 id="오브젝트-프로퍼티-구성-apply">오브젝트 프로퍼티 구성 (apply)</h2>
<hr>
<pre><code class="language-kotlin">val myRectangle = Rectangle().apply {
    length = 4
    breadth = 5
    color = 0xFAFAFA
}</code></pre>
<p>이것은 오브젝트 생성자에 존재하지 않는 프로퍼티를 설정할 때 유용하다.</p>
<br>

<h2 id="java-7의-try-with-resources">java 7의 try-with-resources</h2>
<hr>
<pre><code class="language-kotlin">val stream = Files.newInputStream(Paths.get(&quot;/some/file.txt&quot;))
stream.buffered().reader().use { reader -&gt;
    println(reader.readText())
}</code></pre>
<br>

<h2 id="제네릭-타입-정보를-요구하는-제네릭-함수">제네릭 타입 정보를 요구하는 제네릭 함수</h2>
<hr>
<pre><code class="language-kotlin">/* public final class Gson {
    ...
    public &lt;T&gt; T fromJson(JsonElement json, Class&lt;T&gt; classOfT) throws JsonSyntaxException { }
    ...
} */

inline fun &lt;reified T: Any&gt; Gson.fromJson(json: JsonElement): T = this.fromJson(json, T::class.java)</code></pre>
<br>

<h2 id="nullable-boolean">Nullable Boolean</h2>
<hr>
<pre><code class="language-kotlin">val b: Boolean? = ...
if (b == true) {
    ...
} else {
    // false 또는 null
}</code></pre>
<br>

<h2 id="두-변수-스왑하기">두 변수 스왑하기</h2>
<hr>
<pre><code class="language-kotlin">var a = 1
var b = 2
a = b.also { b = a }</code></pre>
<br>

<h2 id="코드를-미완성으로-만들기-todo">코드를 미완성으로 만들기 (TODO)</h2>
<hr>
<p>코틀린 스탠더드 라이브러리는 언제나 <code>NotImplementedError</code>를 던지는 <code>TODO()</code> 함수를 가지고 있다. 이 리턴 타입은 <code>Nothing</code>이므로 예상하는 타입과 상관 없이 사용할 수 있다. reason parameter를 받는 오버로딩도 있다.</p>
<pre><code class="language-kotlin">fun calcTaxes(): BigDecimal = TODO(&quot;Waiting for feedback from accounting&quot;)</code></pre>
<br>

<h2 id="다음은">다음은?</h2>
<ul>
<li>관용적인(idiomatic) 코틀린 스타일을 사용해 <a href="https://kotlinlang.org/docs/advent-of-code.html">Advent of Code puzzles in idiomatic Kotlin</a>을 풀어 보자.</li>
<li><a href="https://kotlinlang.org/docs/java-to-kotlin-idioms-strings.html">자바와 코틀린에서 스트링으로 수행하는 일반적인 작업</a> 방법을 공부하자.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin v1.6.10 : Basic syntax]]></title>
            <link>https://velog.io/@hh_0705/Basic-syntax</link>
            <guid>https://velog.io/@hh_0705/Basic-syntax</guid>
            <pubDate>Thu, 17 Feb 2022 08:59:29 GMT</pubDate>
            <description><![CDATA[<h1 id="basic-syntax"><a href="https://kotlinlang.org/docs/basic-syntax.html">Basic syntax</a></h1>
<p><em>번역문서 작성일 기준 Last modified: 13 September 2021</em>
<em>작성완료일 20220217</em></p>
<br>

<p>이것은 예제를 포함한 기본 문법 요소이다. 각 섹션의 마지막에서 관련된 토픽을 자세히 기술하는 링크를 찾을 수 있다.</p>
<p>JetBrains Academy의 무료 <a href="https://hyperskill.org/tracks/18?_gl=1*1o1zuvv*_ga*NTIxMzIwNzM2LjE2NDQ4MTkxMDM.*_ga_J6T75801PF*MTY0NDgxOTEwMi4xLjEuMTY0NDgxOTY3NS4w&amp;_ga=2.8343858.1856821921.1644819104-521320736.1644819103">Kotlin Basics track</a>에서 코틀린의 핵심적인 내용을 배울 수 있다. </p>
<br>

<h2 id="package-definition-and-imports">Package definition and imports</h2>
<hr>
<p>패키지 지정은 반드시 소스 파일의 가장 위에 위치해야 한다.</p>
<pre><code class="language-kotlin">package my.demo

import kotlin.text.*

//...</code></pre>
<p>디렉토리와 패키지를 반드시 일치시킬 필요는 없다. 소스 파일은 파일 시스템에 임의로 배치될 수 있다. </p>
<p><a href="https://kotlinlang.org/docs/packages.html">Packages</a>를 보자.</p>
<br>

<h2 id="program-entry-point">Program entry point</h2>
<hr>
<p>Kotlin application의 entry point는 <code>main</code> function이다.</p>
<pre><code class="language-kotlin">fun main(){
    println(&quot;Hello world&quot;)
}</code></pre>
<p><code>main</code> 의 다른 형식은 다수의 <code>String</code> 아규먼트를 허용한다.</p>
<pre><code class="language-kotlin">fun main(args: Array&lt;String&gt;) {
    println(args.contentToString())
}</code></pre>
<br>

<h2 id="print-to-the-standard-output">Print to the standard output</h2>
<hr>
<p><code>print</code>는 아규먼트를 standard output로 프린트한다.</p>
<pre><code class="language-kotlin">print(&quot;Hello &quot;)
print(&quot;world!&quot;)

//Hello world!</code></pre>
<p><code>println</code>은 아규먼트를 프린트하고 줄바꿈을 추가한다. 따라서 다른 것을 출력하면 그것은 다음 줄에 나타난다.</p>
<pre><code class="language-kotlin">println(&quot;Hello world!&quot;)
println(42)

// Hello world!
// 42</code></pre>
<br>

<h2 id="functions">Functions</h2>
<hr>
<p>다음 함수는 두 <code>Int</code> 파라미터와 <code>Int</code> 리턴 타입을 가진다. </p>
<pre><code class="language-kotlin">fun sum(a: Int, b: Int): Int {
    return a + b
}</code></pre>
<p>function body는 표현식이 될 수 있으며, 이 함수의 리턴 타입은 추론된다.</p>
<pre><code class="language-kotlin">fun sun(a: Int, b: Int) = a + b</code></pre>
<p>어떤 의미있는 값도 반환하지 않는 함수</p>
<pre><code class="language-kotlin">fun printSum(a: Int, b: Int): Unit {
    println(&quot;sum of $a and $b is ${a + b}&quot;)
}</code></pre>
<p><code>Unit</code> 리턴 타입은 생략 가능하다.</p>
<pre><code class="language-kotlin">fun printSum(a: Int, b: Int) {
    println(&quot;sum of $a and $b is ${a + b}&quot;)
}</code></pre>
<p><a href="https://kotlinlang.org/docs/functions.html">Functions</a>를 보자.</p>
<br>

<h2 id="variables">Variables</h2>
<hr>
<p>읽기 전용 로컬 변수는 키워드 <code>val</code>을 사용해 정의된다. 그것들에는 오직 한 번만 값(value)가 할당될 수 있다.</p>
<pre><code class="language-kotlin">val a: Int = 1 // 즉각적인 할당
val b = 2 // Int 타입이 추론된다
val c: Int // 초기화가 없다면 타입이 반드시 필요하다
c = 3 // 지연 할당</code></pre>
<p>재할당 가능한 변수는 <code>var</code> 키워드를 사용한다.</p>
<pre><code class="language-kotlin">var x = 5 // Int 타입이 추론된다
x += 1</code></pre>
<p>top level에서 변수를 선언할 수 있다.</p>
<pre><code class="language-kotlin">val PI = 3.14
var x = 0

fun increment(x) {
    x += 1
}</code></pre>
<p><a href="https://kotlinlang.org/docs/properties.html">Properties</a>를 함께 보자.</p>
<br>

<h2 id="creating-classes-and-instances">Creating classes and instances</h2>
<hr>
<p>클래스를 정의하기 위해 <code>class</code> 키워드를 사용한다.</p>
<pre><code class="language-kotlin">class Shape</code></pre>
<p>클래스의 프로퍼티는 선언이나 바디에 리스트될 수 있다.</p>
<pre><code class="language-kotlin">class Rectangle(var height: Double, var length: Double) {
    var perimeter = (height + length) * 2 
}</code></pre>
<p>클래스 선언에서 나열된 파라미터를 가진 기본 생성자는 자동으로 접근가능(available)하다.</p>
<pre><code class="language-kotlin">val rectangle = Rectangle(5.0, 2.0)
println(&quot;the perimeter is ${rectangle.perimeter}&quot;)</code></pre>
<p>클래스 간 상속은 콜론(:)으로 선언된다. 클래스는 기본적으로 final이다. 이것을 상속가능하게 하려면 <code>open</code>으로 만들어야 한다. </p>
<pre><code class="language-kotlin">open class Shape

class Rectangle(var height: Double, var length: Double): Shape() {
    var perimeter = (height + length) * 2 
}</code></pre>
<p><a href="https://kotlinlang.org/docs/classes.html">classes</a>와 <a href="https://kotlinlang.org/docs/object-declarations.html">objects and instances</a>를 보자.</p>
<br>

<h2 id="comments">Comments</h2>
<hr>
<p>대부분의 현대 언어와 같이, 코틀린은 한 줄(single-line. 또는 end-of-line) 그리고 여러 줄(multi-line. block) 주석을 지원한다.</p>
<pre><code class="language-kotlin">// end-of-line 주석이다.

/* 이것이
여러 줄
주석이다 */</code></pre>
<p>코틀린에서 블록 주석은 중첩될 수 있다.</p>
<pre><code class="language-kotlin">/* 주석이 여기서 시작되어서
/* 중첩된 주석을 가지고 */
여기서 끝난다. */</code></pre>
<p>Documentation comment syntax에 대한 정보는 <a href="https://kotlinlang.org/docs/kotlin-doc.html">Documenting Kotlin Code</a>를 보자.</p>
<br>

<h2 id="string-templates">String templates</h2>
<hr>
<pre><code class="language-kotlin">var a = 1
// 템플릿 내 단순한 이름
val s1 = &quot;a is $a&quot;

a = 2
// 템플릿 내 임의 표현식(arbitrary expression)
val s2 = &quot;${sl.replace(&quot;is&quot;, &quot;was&quot;)}, but now is $a&quot;</code></pre>
<p>자세한 내용은 <a href="https://kotlinlang.org/docs/basic-types.html#string-templates">String templates</a>를 보자.</p>
<br>

<h2 id="conditional-expressions">Conditional expressions</h2>
<hr>
<pre><code class="language-kotlin">fun maxOf(a: Int, b: Int): Int {
    if (a &gt; b) {
        return a
    } else {
        return b
    }
}</code></pre>
<p>코틀린에서는 <code>if</code> 또한 표현식으로 사용가능하다.</p>
<pre><code class="language-kotlin">fun maxOf(a: Int, b: Int) = if (a &gt; b) a else b</code></pre>
<p><a href="https://kotlinlang.org/docs/control-flow.html#if-expression">if-expressions</a>를 보자.</p>
<br>

<h2 id="for-loop">for loop</h2>
<hr>
<pre><code class="language-kotlin">val items = listOf(&quot;apple&quot;, &quot;banana&quot;, &quot;kiwifruit&quot;)
for (item in items) {
    println(item)
}</code></pre>
<p>또는</p>
<pre><code class="language-kotlin">val items = listOf(&quot;apple&quot;, &quot;banana&quot;, &quot;kiwifruit&quot;)
for (index in items.indices) {
    println(&quot;item at $index is ${items[index]}&quot;)
}</code></pre>
<p><a href="https://kotlinlang.org/docs/control-flow.html#for-loops">for loop</a>를 보자.</p>
<br>

<h2 id="while-loop">while loop</h2>
<hr>
<pre><code class="language-kotlin">val items = listOf(&quot;apple&quot;, &quot;banana&quot;, &quot;kiwifruit&quot;)
var index = 0

while (index &lt; items.size) {
    println(&quot;item at $index is ${items[index]}&quot;)
    index++
}</code></pre>
<p><a href="https://kotlinlang.org/docs/control-flow.html#while-loops">while loop</a>를 보자.</p>
<br>

<h2 id="when-expression">when expression</h2>
<hr>
<pre><code class="language-kotlin">fun describe(obj: Any): String = 
    when (obj) {
        1           -&gt; &quot;one&quot;
        &quot;Hello&quot;     -&gt; &quot;Gretting&quot;
        is Long     -&gt; &quot;Long&quot;
        !is String  -&gt; &quot;Not a String&quot;
        else        -&gt; &quot;Unknown&quot;
    }</code></pre>
<p><a href="https://kotlinlang.org/docs/control-flow.html#when-expression">when expression</a>를 보자.</p>
<br>

<h2 id="ranges">Ranges</h2>
<hr>
<p><code>in</code> 오퍼레이터를 사용해 숫자가 범위(range) 안에 포함되는지를 체크하자.</p>
<pre><code class="language-kotlin">val list = listOf(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)

if (-1 !in 0..list.lastIndex) {
    println(&quot;-1 is out of range&quot;)
}
if (list.size !in list.indices) {
    println(&quot;list size is out of valid list indices range, too&quot;)
}</code></pre>
<p>범위를 순회(Iterate)하자.</p>
<pre><code class="language-kotlin">for (x in 1..5) {
    print(x)
}</code></pre>
<p>또는 진행을 뛰어넘자(over a progression).</p>
<pre><code class="language-kotlin">for (x in 1..10 step 2) {
    print(x)
}
println()
for (x in 9 downTo 0 step 3) {
    print(x)
}</code></pre>
<p><a href="https://kotlinlang.org/docs/ranges.html">Ranges and progressions</a>를 보자.</p>
<br>

<h2 id="collections">Collections</h2>
<hr>
<p>컬렉션을 순회한다.</p>
<pre><code class="language-kotlin">for (item in items) {
    println(item)
}</code></pre>
<p><code>in</code> 오퍼레이터를 사용해 컬렉션이 오브젝트를 포함하고 있는지 확인한다.</p>
<pre><code class="language-kotlin">when {
    &quot;orange&quot; in items -&gt; println(&quot;juicy&quot;)
    &quot;apple&quot; in items -&gt; println(&quot;apple is fine too&quot;)
}</code></pre>
<p>컬렉션에 맵과 필터를 사용하기 위해 람다 표현식을 쓴다. </p>
<pre><code class="language-kotlin">val fruits = listOf(&quot;banana&quot;, &quot;avocado&quot;, &quot;apple&quot;, &quot;kiwifruit&quot;)
fruits
    .filter { it.startsWith(&quot;a&quot;) }
    .sortedBy { it }
    .map { it.uppercase() }
    .forEach { println(it) }</code></pre>
<p><a href="https://kotlinlang.org/docs/collections-overview.html">Collections overview</a>를 보자.</p>
<br>

<h2 id="nullable-values-and-null-checks">Nullable values and null checks</h2>
<hr>
<p><code>null</code> 값이 가능할 경우 참조는 반드시 명시적으로 nullable로 표시되어야 한다. nullable 타입은 마지막에 <code>?</code>를 가진다. </p>
<p>다음은 <code>str</code>가 정수(integer)를 가지고 있지 않다면 null을 반환한다.</p>
<pre><code class="language-kotlin">fun parseInt(str: String): Int? {
    // ...
}</code></pre>
<p>nullable 값을 반환하는 함수를 사용한다.</p>
<pre><code class="language-kotlin">fun printProduct(arg1: String, arg2: String) {
    val x = parseInt(arg1)
    val y = parseInt(arg2)

    // `x * y` 사용은 오류를 만든다. null일 수 있기 때문이다.
    if (x != null &amp;&amp; y != null) {
        // x와 y는 널 체크 이후에 자동으로 non-nullable로 캐스팅된다.
        println(x * y)
    } 
    else {
        println(&quot;&#39;$arg1&#39; or &#39;$arg2&#39; is not a number&quot;)
    }
}</code></pre>
<p>또는 </p>
<pre><code class="language-kotlin">// ...

if (x == null) {
    println(&quot;wrong number format in arg1: &#39;$arg1&#39;&quot;)
    return
}
if (y == null) {
    println(&quot;Wrong number format in arg2: &#39;$arg2&#39;&quot;)
    return
}

// x와 y는 널 체크 후에 자동으로 non-nullable로 캐스트된다.
println(x * y)</code></pre>
<p><a href="https://kotlinlang.org/docs/null-safety.html">Null-safety</a>를 보자.</p>
<br>

<h2 id="type-checks-and-automatic-casts">Type checks and automatic casts</h2>
<hr>
<p><code>is</code> 오퍼레이터는 한 표현식이 특정 타입의 인스턴스인지 체크한다. 만약 불변 지역 변수(immutable local variable) 또는 프로퍼티가 특정 타입으로 체크된다면, 그것들은 명시적으로 캐스트될 필요가 없다.</p>
<pre><code class="language-kotlin">fun getStringLength(obj: Any): Int? {

    if (obj is String) {
        // obj는 이 브랜치에서 자동으로 String으로 캐스트된다.
        return obj.length
    }

    // obj는 타입 체크된 브랜치 밖에서는 여전히 Any 타입이다.
    return null
}</code></pre>
<p>또는 </p>
<pre><code class="language-kotlin">fun getStringLength(obj: Any): Int? {

    if (obj !is String) return null 

    // obj는 이 브랜치에서 자동으로 String으로 캐스트된다.
    return obj.length
}</code></pre>
<p>심지어</p>
<pre><code class="language-kotlin">fun getStringLength(obj: Any): Int? {

    // obj는 &amp;&amp; 오른쪽에서 자동으로 String으로 캐스트된다.
    if (obj is String &amp;&amp; obj.length &gt; 0) {
        return obj.length
    }

    return null 
}</code></pre>
<p><a href="https://kotlinlang.org/docs/classes.html">Classes</a>와 <a href="https://kotlinlang.org/docs/typecasts.html">Type casts</a>를 보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin v1.6.10 : Functions]]></title>
            <link>https://velog.io/@hh_0705/Functions</link>
            <guid>https://velog.io/@hh_0705/Functions</guid>
            <pubDate>Tue, 15 Feb 2022 08:46:29 GMT</pubDate>
            <description><![CDATA[<h1 id="functions"><a href="https://kotlinlang.org/docs/functions.html">Functions</a></h1>
<p><em>번역문서 작성일 기준 Last modified: 10 November 2021</em></p>
<p>코틀린 함수는 <code>fun</code> 키워드로 선언된다.</p>
<pre><code class="language-kotlin">fun double(x: Int): Int {
    return 2 * x
}</code></pre>
<br>

<h2 id="function-usage">Function usage</h2>
<hr>
<p>함수는 표준 접근법(standard approach)을 사용해 호출된다.</p>
<pre><code class="language-kotlin">val result = double(2)</code></pre>
<p>member function 호출은 온점 표기법(dot notation)을 사용한다.</p>
<pre><code class="language-kotlin">Stream().read() // class Stream의 인스턴스를 생성하고 read()를 호출한다.</code></pre>
<br>

<h3 id="parameters">Parameters</h3>
<hr>
<p>함수 파라미터는 파스칼 표기법을 사용해 정의된다 - <em>이름, 타입</em>. 파라미터는 콤마를 사용해 구분되고 각 파라미터는 반드시 명시적으로 타입이 지정되어야 한다. </p>
<pre><code class="language-kotlin">fun powerOf(number: Int, exponent: Int): Int {/*...*/}</code></pre>
<p>함수 파라미터를 선언할 때 <a href="https://kotlinlang.org/docs/coding-conventions.html#trailing-commas">후행 쉼표(trailing comma)</a>를 사용할 수 있다.</p>
<pre><code class="language-kotlin">fun powerOf(
    number: Int,
    exponent: Int, //trailing comma
){/*...*/}</code></pre>
<br>

<h3 id="default-arguments">Default arguments</h3>
<hr>
<p>펑션 파라미터는 디폴트 값을 가질 수 있다. 이것은 해당하는 아규먼트를 생략할 때 사용된다. 이는 오버로드를 감소시킨다.</p>
<pre><code class="language-kotlin">fun read(
    b: ByteArray,
    off: Int = 0,
    len: Int = b.size,
){/*...*/}</code></pre>
<p>default value는 타입 다음에 <code>=</code>를 사용한다.</p>
<p>오버라이딩 메서드는 베이스 메서드와 언제나 같은 디폴트 값을 사용한다. 디폴트 파라미터 값을 가진 메서드를 오버라이딩 할 경우 디폴트 파라미터 값은 반드시 생략되어야 한다. </p>
<pre><code class="language-kotlin">open class A {
    open fun foo(i: Int = 10) {/*...*/}
}

class B : A() {
    override fun foo(i: Int) {/*...*/} // 디폴트 값이 허용되지 않는다.
}</code></pre>
<p>디폴트 값이 디폴트 값이 없는 파라미터에 선행할 경우, named arguments로 함수를 호출한 경우에만 디폴트 값을 사용할 수 있다. </p>
<pre><code class="language-kotlin">fun foo(
    bar: Int = 0,
    baz: Int,
) {/*...*/}

foo(baz = 1) //디폴트 값 bar = 0이 사용될 수 있다.</code></pre>
<p>디폴트 파라미터 이후에 오는 마지막 아규먼트가 <a href="https://kotlinlang.org/docs/lambdas.html#lambda-expression-syntax">람다</a>일 경우, 그것을 named argument로써, 또는 괄호 밖에서 넘겨줄 수 있다.</p>
<pre><code class="language-kotlin">fun foo(
    bar: Int = 0,
    baz: Int = 1,
    qux: () -&gt; Unit,
) {/*...*/}

foo(1) {println(&quot;hello&quot;)} //기본 값 baz = 1을 사용한다.

foo(qux = {println(&quot;hello&quot;)}) // 기본값 bar = 0과 baz = 1을 모두 사용한다.

foo {println(&quot;hello&quot;)} // 기본값 bar = 0과 baz = 1을 모두 사용한다.</code></pre>
<br>

<h3 id="named-arguments">Named arguments</h3>
<hr>
<p>함수 호출 시 하나 이상의 아규먼트를 명명할 수 있다. 이것은 함수가 많은 아규먼트를 가지고 있고 값을 아규먼트와 연관시키기 어려울 때 유용하다. 특히 그 값이 불리언이거나 <code>null</code>일 경우 그렇다. </p>
<p>함수 호출에서 명명된 아규먼트를 사용할 경우 순서를 자유롭게 바꿀 수 있다. 그리고 기본 값을 사용하고 싶다면 그 아규먼트를 완전히 배제하면 된다.</p>
<p>아래 함수 <code>reformat()</code>이 기본값을 가진 아규먼트 4개를 가졌다고 가정하자.</p>
<pre><code class="language-kotlin">fun reformat(
    str: String,
    normalizeCase: Boolean = true,
    upperCaseFirstLetter: Boolean = true,
    divideByCamelHymps: Boolean = false,
    wordSeparator: Char = &#39; &#39;,
){/*...*/}</code></pre>
<p>이 함수를 호출할 때 모든 아규먼트를 명명할 필요는 없다.</p>
<pre><code class="language-kotlin">reformat(
    &quot;String!&quot;
    false,
    upperCaseFirstLetter = false,
    divideByCamelHumps = true,
    &#39;_&#39;
)</code></pre>
<p>디폴트 값을 가진 모든 아규먼트를 스킵할 수도 있다.</p>
<pre><code class="language-kotlin">reformat(&quot;This is a long String!&quot;)</code></pre>
<p>전체를 생략하지 않고 디폴트 값을 가진 특정 아규먼트를 스킵할 수도 있다. 하지만 처음 스킵된 아규먼트 다음부터는 반드시 모든 아규먼트를 명명해야 한다.</p>
<pre><code class="language-kotlin">reformat(&quot;This is a long String!&quot;, upperCaseFirstLetter = false, wordSeperator = &#39;_&#39;)</code></pre>
<p>spread operator를 이용해 가변 개수 아규먼트(variable number of arguments (<code>vararg</code>))를 전달할 수 있다.</p>
<pre><code class="language-kotlin">fun foo(vararg strings: String) {/*...*/}

foo(strings = *arrayOf(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;))</code></pre>
<blockquote>
<p>JVM에서: 자바 함수 호출시에는 명명된 아규먼트를 사용할 수 없다. 자바 바이트코드가 언제나 함수 파라미터 이름을 보존하지는 않기 때문이다.</p>
</blockquote>
<br>

<h3 id="unit-returning-functions">Unit-returning functions</h3>
<hr>
<p>함수가 의미 있는 값을 반환하지 않을 경우 리턴 타입은 <code>Unit</code>이다. <code>Unit</code>은 단일 값 - <code>Unit</code> 만을 가진 타입이다. 이 값은 명시적으로 반환될 필요는 없다.</p>
<pre><code class="language-kotlin">fun printHello(name: String?): Unit {
    if (name != null) 
        println(&quot;Hello $name&quot;)
    else
        println(&quot;Hi There!&quot;)

    // return 또는 return Unit 은 선택사항이다.
}</code></pre>
<p><code>Unit</code> 리턴 선언 또한 선택사항이다. 위 코드는 다음처럼 작성할 수 있다.</p>
<pre><code class="language-kotlin">fun printHello(name: String?) { ... }</code></pre>
<br>

<h3 id="single-expression-functions">Single-expression functions</h3>
<hr>
<p>함수가 단일 표현식을 반환할 경우 중괄호가 생략되고 body가 <code>=</code> 심볼 다음에 지정될 수 있다.</p>
<pre><code class="language-kotlin">fun double(x: Int): Int = x * 2</code></pre>
<p>리턴 타입이 컴파일러에 의해 추론 가능할 경우 리턴 타입 선언 또한 선택적이다.</p>
<pre><code class="language-kotlin">fun double(x: Int) = x * 2</code></pre>
<br>

<h3 id="explicit-return-types">Explicit return types</h3>
<hr>
<p>block body를 가진 함수는 <code>Unit</code>을 반환하지 않는 이상 반드시 리턴 타입을 명시적으로 지정해야 한다. <code>Unit</code>을 반환하는 경우에는 리턴 타입 지정이 선택 사항이다.</p>
<p>코틀린은 block body를 가진 함수의 리턴 타입을 추론하지 않는다. 그러한 함수는 body에서 복잡한 컨트롤 플로우를 가지고 있을 것이며, 리턴 타입은 코드를 읽는 사람에게(그리고 가끔씩은 컴파일러에게도) 불명확할 것이기 때문이다. </p>
<br>

<h3 id="variable-number-of-arguments-varargs">Variable number of arguments (varargs)</h3>
<hr>
<p><code>vararg</code> 수정자(modifier)로 함수 파라미터(일반적으로 마지막 파라미터 하나)를 표시할 수 있다.</p>
<pre><code class="language-kotlin">fun &lt;T&gt; asList(vararg ts: T): List&lt;T&gt; {
    val result = ArrayList&lt;T&gt;()
    for (t in ts) //ts는 array이다
        result.add(t)
    return result    
}</code></pre>
<p>이 경우 가변 개수 아규먼트를 함수에 전달할 수 있다.</p>
<pre><code class="language-kotlin">val list = asList(1, 2, 3)</code></pre>
<p>위와 같이, 함수 내부에서 타입 <code>T</code>의 <code>vararg</code> 파리미터는 <code>T</code> 배열로 보인다. 위 경우에서 <code>ts</code> 변수는 <code>Array&lt;out T&gt;</code> 타입을 가진다. </p>
<p>오직 하나의 변수만 <code>vararg</code>으로 표시될 수 있다. <code>vararg</code> 파라미터가 목록의 마지막 하나가 아닐 경우 후속 파라미터의 값들은 named argument 문법으로 전달될 수 있다. 또는, 만약 파라미터가 함수 타입을 가졌다면 괄호 밖에서 람다를 전달할 수 있다. </p>
<p><code>vararg</code> 함수를 호출할 때 아규먼트를 개별적으로 <code>asList(1, 2, 3)</code>처럼 전달할 수 있다. 이미 배열이 있고 그 내용을 함수에 전달하고 싶을 경우 <em>spread operator</em> (배열의 접두사 <code>*</code>)fmf tkdydgksek. </p>
<pre><code class="language-kotlin">val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)</code></pre>
<p>primitive type 배열을 <code>vararg</code>으로 전달하고 싶을 경우 <code>toTypedArray()</code> 함수를 이용해 레귤러(typed) 배열로 전환해야 한다. </p>
<pre><code class="language-kotlin">val a = intArrayOf(1, 2, 3) // IntArray는 primitive type array 이다
val list = asList(-1 0, *a.toTypedArray(), 4)</code></pre>
<br>

<h3 id="infix-notation">Infix notation</h3>
<hr>
<p><code>infix</code> 키워드로 표시된 함수는 infix notation(고정 표기법) (호출을 위한 온점과 괄호를 생략한다)으로 호출할 수 있다. infix 함수는 다음 조건을 반드시 만족시켜야 한다:</p>
<ul>
<li><p>반드시 member functions이거나 <a href="https://kotlinlang.org/docs/extensions.html">extension functions</a>여야 한다.</p>
</li>
<li><p>반드시 단일 파라미터를 가져야 한다.</p>
</li>
<li><p>파라미터는 반드시 가변 개수 아규먼트를 허용하지 않아야 하고, 디폴트 값을 가지지 않아야 한다.</p>
</li>
</ul>
<pre><code class="language-kotlin">infix fun Int.shl(x: Int): Int { ... }

// infix 표기법으로 함수 호출
1 shl 2

// 이것은 아래와 같다
1.shl(2)</code></pre>
<blockquote>
<p>Infix function 호출은 산술연산자, 타입 캐스트, <code>rangeTo</code> 연산자보다 낮은 우선순위를 가진다.
아래 표현식을 참고하자.</p>
<ul>
<li><code>1 shl 2 + 3</code>은 <code>1 shl (2 + 3)</code> 과 동일하다.</li>
<li><code>0 until n * 2</code>는 `0 until (n * 2) 와 동일하다.</li>
<li><code>xs union ys as Set&lt;*&gt;</code>는 <code>xs union (ys as Set&lt;*&gt;)</code>과 동일하다. </li>
</ul>
<p>반면 infix function call의 우선순위는 불리언 연산자 <code>&amp;&amp;</code>와 <code>||</code>, <code>is</code> - 그리고 <code>in</code> - 체크, 그리고 몇몇 다른 연산자보다 높다. 이들 표현식은 다음과 같다.</p>
<ul>
<li><code>a &amp;&amp; b xor c</code>는 <code>a &amp;&amp; (b xor c)</code>와 같다.</li>
<li><code>a xor b in c</code>는 <code>(a xor b) in c</code>와 같다.</li>
</ul>
</blockquote>
<p>infix function은 언제나 리시버와 파라미터를 모두 지정해야 한다는 점을 유의하자. infix notation을 사용하면서 current receiver로 메서드를 호출할 경우  <code>this</code>를 명시적으로 사용해야 한다. 이는 모호하지 않은 파싱을 보장하기 위해 필요하다.</p>
<pre><code class="language-kotlin">class MyStringCollection {
    infix fun add(s: String) { /*...*/}

    fun build() {
        this add &quot;abs&quot; // Correct
        add(&quot;abs&quot;) // Correct
        add &quot;abs&quot; //Incorrect : 리시버가 반드시 지정되어야 한다. 
    }
}</code></pre>
<br>

<h2 id="function-scope">Function scope</h2>
<hr>
<p>코틀린 함수는 파일의 top level에서 선언될 수 있다. 이는 함수를 가지고 있기 위한 클래스를 생성할 필요가 없다는 뜻이다. 자바, C#, Scala와 같은 언어에서는 클래스를 생성할 필요가 있었다. top level function에 더해서, 코틀린 함수는 member function 및 extension function으로 로컬로 선언될 수 있다.</p>
<br>

<h3 id="local-functions">Local functions</h3>
<hr>
<p>코틀린은 다른 함수 내부에 있는 함수인 로컬 함수를 지원한다.</p>
<pre><code class="language-kotlin">fun dfs(graph: Graph) {
    fun dfs(current: Vertex, visited: MutableSet&lt;Vertex&gt;) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

    dfs(graph.vertices[0], HashSet())
}</code></pre>
<p>로컬 함수는 외부 함수(클로저)의 로컬 변수에 접근할 수 있다. 위 케이스에서는 <code>visited</code>가 로컬 변수가 될 수 있다.</p>
<pre><code class="language-kotlin">fun dfs(graph: Graph) {
    val visitied = HashSet&lt;Vertex&gt;()
    fun dfs(current: Vertex) {
        if (!visitied.add(current)) return
        for (v in current.neighbors)
            dfs(v)
    }

    dfs(graph.vertices[0])
}</code></pre>
<br>

<h3 id="member-functions">Member functions</h3>
<hr>
<p>멤버 함수는 클래스 또는 객체 내부에 정의된 함수이다.</p>
<pre><code class="language-kotlin">class Sample {
    fun foo() { print(&quot;Foo&quot;) }
}</code></pre>
<p>멤버 함수는 dot notation으로 호출할 수 있다.</p>
<pre><code class="language-kotlin">Sample().foo() // 클래스 Sample의 인스턴스를 생성하고 foo를 호출한다</code></pre>
<p>클래스와 오버라이딩 멤버에 대한 더 많은 정보는 <a href="https://kotlinlang.org/docs/classes.html">Classes</a>와 <a href="https://kotlinlang.org/docs/classes.html#inheritance">Inheritance</a>를 참고하자.</p>
<br>

<h2 id="generic-functions">Generic functions</h2>
<hr>
<p>함수는 함수 이름 앞에 꺽쇠 괄호를 사용해 지정된 제네릭 파라미터를 가질 수 있다. </p>
<pre><code class="language-kotlin">fun &lt;T&gt; singletonList(item: T): List&lt;T&gt; {/*...*/}</code></pre>
<p>제너릭 함수에 대한 더 많은 정보는 <a href="https://kotlinlang.org/docs/generics.html">Generics</a>을 참조하자.</p>
<br>

<h2 id="tail-recursive-functions">Tail recursive functions</h2>
<hr>
<p>코틀린은 tail recursion이라고 알려진 함수형 프로그래밍 스타일을 지원한다. 일반적으로 루프를 사용하는 어떤 알고리즘들을 위해, 스택 오버플로우 리스크 없이 (루프 대신) 재귀 함수를 사용할 수 있다. 함수가 <code>tailrec</code> 수정자로 표시되고 형식 컨디션을 만족시킬 경우, 컴파일러는 재귀를 최적화하여 빠르고 효과적인 루프 기반 버전을 남긴다.</p>
<pre><code class="language-kotlin">val eps = 1E - 10 // &quot;good enough&quot;. 10^-15가 된다.

tailrec fun findFixPoint(x: Double = 1.0): Double = 
    if (Math.abs(x - Math.cos(x)) &lt; eps) x else findFixPoint(Math.cos(x))</code></pre>
<p>이 코드는 수학적 상수인 코사인의 <code>fixpoint</code>를 계산한다. 이것은 <code>1.0</code>부터 시작해 변화가 없을 때까지 <code>Math.cos</code>를 호출한다. <code>eps</code> precision에 따라 <code>0.7390851332151611</code>라는 결과를 얻는다. 이 결과는 더 전통적인 아래 코드와 같다.</p>
<pre><code class="language-kotlin">val eps = 1E - 10 // &quot;good enough&quot;. 10^-15가 된다.
private fun findFixPoint(): Double {
    var x = 1.0
    while (true) {
        val y = Math.cos(x)
        if (Math.abs(x - y) &lt; eps&gt;) return x
        x = Math.cos(x)
    }
}</code></pre>
<p>tailrec 수정자를 사용하기 위해서는 함수는 반드시 자신이 수행하는 마지막 연산자로서 자기 자신을 호출해야 한다. 재귀 호출 이후, <code>try/catch/finally</code> 블록 내부, 또는 <code>open function</code>에 더 많은 코드가 있다면 꼬리 재귀를 사용할 수 없다. 현재 재귀 함수는  Kotlin for the JVM과 Kotlin/Native에 의해 지원되고 있다.</p>
<p><strong>더보기:</strong></p>
<ul>
<li><p><a href="https://kotlinlang.org/docs/inline-functions.html">Inline functions</a></p>
</li>
<li><p><a href="https://kotlinlang.org/docs/extensions.html">Extension functions</a></p>
</li>
<li><p><a href="https://kotlinlang.org/docs/lambdas.html">Higher-order functions and lambdas</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Writing Connectors]]></title>
            <link>https://velog.io/@hh_0705/caliper-writing-connectors</link>
            <guid>https://velog.io/@hh_0705/caliper-writing-connectors</guid>
            <pubDate>Sun, 09 Jan 2022 08:06:39 GMT</pubDate>
            <description><![CDATA[<h1 id="writing-connectors"><a href="https://hyperledger.github.io/caliper/v0.4.2/writing-connectors/">Writing Connectors</a></h1>
<h2 id="개요">개요</h2>
<hr>
<p>커넥터는 캘리퍼의 가장 중요한 모듈이다. 이것은 SUT와 다른 캘리퍼 컴포넌트들(예: 매니저와 워커 프로세스, 또는 워커 모듈들) 의 추상 레이어를 제공한다. 커넥터의 역할은 SUT와의 상호작용을 가능한 한 간소화하는 것이다. 이는 특질들을 API뒤에 숨김으로써 가능하다. </p>
<blockquote>
<p><strong>참고</strong></p>
<p>커넥터가 캘리퍼 아키텍처에 어떻게 들어맞는지를 이해하려면 <a href="https://hyperledger.github.io/caliper/v0.4.2/architecture/#multi-platform-support">멀티 플랫폼 서포트</a>, <a href="https://hyperledger.github.io/caliper/v0.4.2/architecture/#the-manager-process">매니저 프로세스</a>, 그리고 <a href="https://hyperledger.github.io/caliper/v0.4.2/architecture/#the-worker-process">워커 프로세스</a>와 관련된 문서 섹션을 확인하자.</p>
</blockquote>
<p>캘리퍼는 <a href="https://hyperledger.github.io/caliper/v0.4.2/getting-started/#supported-blockchain-solutions">사전 정의된/내장된 커넥터들을 포함하지만</a>, 일반적으로 커넥터들은 플러그 가능한 컴포넌트들이다(리소스와 TX 모니터, 워크로드 모듈들과 같다). 그러므로 서드파티 커넥터를 구현하고 사용할 수 있다. 하지만 새 커넥터를 구현하기 전에 이 가이드의 모든 부분을 흡수하기를 추천한다.</p>
<h2 id="품질-커넥터-요구사항">품질 커넥터 요구사항</h2>
<hr>
<p>커넥터의 복잡성은 일반적으로 SUT(그리고 프로그래밍 모델)의 복잡성에 비례한다. 따라서, 커넥터는 캘리퍼의 다른 확장지점에 비해 무거운 컴포넌트이다. </p>
<p>커넥터 구현에는 몇 가지 주의해야 할 점이 있다. 몇몇은 기술적이고, 몇몇은 사용성에 영향을 준다.</p>
<blockquote>
<p><strong>참고</strong></p>
<p>캘리퍼는 테스트할 수 있는 SUT 타입을 제한하지 않는다. 따라서, 다음 포인트들은 복잡한 분산된 시스템을 대상으로 하는 커넥터를 위한 일반적인 가이드라인이다. 이 조언을 따르지 않을 수도 있지만, 그런 결정을 내릴때는 사용자를 놀라게 할 요인을 줄이기 위해 해당 선택을 문서화해야 한다.</p>
</blockquote>
<ol>
<li><p>사전 정의된 인터페이스를 유지하자.</p>
<ul>
<li>캘리퍼 모듈이 커넥터와 상호작용할 수 있도록 주어진 인터페이스들을 반드시 구현해야 한다.</li>
<li>인터페이스 외부에 추가 기능을 노출하는 경우, 워크로드 모듈 개발자의 프로그래밍 모델을 방해하게 된다. 그들이 당신의 추가 API를 별개의 케이스로 통제해야 한다. 이는 크로스 플랫폼 벤치마크 개발을 복잡하게 한다. 하지만 특정한 SUT에 대한 성능 테스트 워크로드 모듈을 구현한다면 이 점을 고려하지 않아도 된다.</li>
<li>당신의 커넥터가 이 가이드의 다른 커넥터와 비슷하게 동작할 경우, 사용자는 당신의 커넥터/SUT에 빠르게 적응하고 사용할 것이다.</li>
</ul>
</li>
<li><p>SUT의 분산 특성을 고려하자.</p>
<ul>
<li>분산 시스템은 서로 다른 규칙을 가지고 있기도 한 여러 노드로 구성된다.</li>
<li>당신은 아마 커넥터를 단순히 단일 SUT 노드의 프록시로 사용하고 싶진 않을 것이다. 커넥터는 로드 밸런싱 또는 SUT-특정적 리퀘스트 실행 정책과 같은 부분들을 지원하는 것이 타당한 만큼 많은 SUT노드를 인식해야 한다.</li>
<li>다른 캘리퍼 모듈들, 특히 워크로드 모듈들로부터 가능한 한 많은 네트워크 토폴로지를 숨겨라. 대부분의 경우, 에뮬레이트된 클라이언트는 기능이 실행되는 한 리퀘스트 수신 측을 신경쓰지 않는다.</li>
<li>워크로드 모듈에 특정 노드들을 반드시 노출시켜야 할 경우, 단순한 (텍스트 기반이 좋다) 핸들을 통해 수행해라. 하지만 노드를 나타내는 구현 특정적인 클래스는 노출하지 않아야 한다.</li>
</ul>
</li>
<li><p>SUT 내부 행위자를 고려해라.</p>
<ul>
<li>인증과 권한 부여는 대부분 원격 시스템의 초석이다. 따라서 디지털 신원 (많은 형태로 구현될 수 있는)을 다루는 기능은 반드시 커넥터에서 최고 수준의 기능이 되어야 한다. </li>
<li>SUT의 노드들과 유사하게, 여기에는 서로 다른 리퀘스트를 시작하는 서로 다른 권한을 가진 많은 행위자와 클라이언트들이 있다. 커넥터는  워크로드 모듈에서 다양한 클라이언트 행위를 할 수 있도록 하기 위해, 다수의 클라이언트를 가장할 수 있어야 한다.</li>
<li>커넥터는 각 리퀘스트에 대해 클라이언트 신원이 쉽게 전환될 수 있도록 해야 한다. 즉, 커넥터는 반드시 워크로드 모듈에 신원들을 노출시켜야 한다. 동일한 조언이 노드에도 적용된다. 단순한 (텍스트 기반이 좋다) 핸들을 사용하되, 신원을 가리키는 구현 특정적 클래스는 노출시키지 않아야 한다.</li>
</ul>
</li>
<li><p>바퀴를 재발명하지 마라.</p>
<ul>
<li>각 시스템은 클라이언트와 커뮤니케이션하기 위한 표준 원격 API를 노출한다. 이 API들은 많은 형태를 가질 수 있다(REST, gRPC 등).</li>
<li>사용된 API 기술에 관계 없이, 아마 그를 위한 성숙한 클라이언트 라이브러리가 있을 것이다. 혹은 더 좋게, 타겟 플랫폼이 고유한 SDK를 가지고 있을 수 있다. </li>
<li>커넥터는 네트워크 수준 통신 및 그와 같은 저수준 디테일을 신경쓰지 않아야 한다. 이러한 작업은 SDK들이나 클라이언트 라이브러리에 위임해야 한다. 이렇게 하면 커넥터가 더 견고해지고, 사용된 라이브러리에 친숙한 추가 기여자를 더 쉽게 구할 수 있다.</li>
</ul>
</li>
<li><p>병목이 되지 않도록 해라.</p>
<ul>
<li>캘리퍼의 목적은 클라이언트의 관점에서 SUT 성능을 테스트하는 것이다.</li>
<li>리퀘스트 실행과 마찬가지로 리퀘스트 조립 및 발송에 시간이 걸린다면, 결과는 (SUT성능을) 대표하지 못할 것이다. 리퀘스트 발송은 커넥터에서 hot path로 간주된다. 그리고 이것은 가능한 한 효과적이어야 한다.</li>
<li>SDK들과 널리 알려진 클라이언트 라이브러리를 사용하는 것은 예외이다. 실제 클라이언트 사이트 어플리케이션이 동일한 작업을 할 가능성이 높으므로, 라이브러리 오버헤드가 리퀘스트 지연(latency)에 통합되어야 한다. 단순히 지연 수를 낮추기 위해 고유한 특정 목적 SDK를 작성함으로써 마이크로 분석을 시행하지 마라.    </li>
<li>hot path의 커넥터 병목은 캘리퍼 워커 프로세스의 리퀘스트 출력 비율에 영향을 미치거나 그것을 제한할 것이다. 만약 SUT에 초당 100건의 리퀘스트를 보내기 위해 10 워커 프로세스를 런치해야 한다면 캘리퍼 사용자들은 행복하지 않을 것이다.</li>
</ul>
</li>
</ol>
<blockquote>
<p><strong>커넥터의 역할은 위 사항을 준수하면서 플랫폼을 모르는 캘리퍼 사이드 API와 고수준 SUT 특정적 클라이언트 라이브러리를 연결하는 것이다.</strong> </p>
</blockquote>
<h2 id="커넥터-구현">커넥터 구현</h2>
<hr>
<p>커넥터 구현 프로세스를 본격적인 node.js 프로젝트로 다루어야 한다. 추천 프로젝트 구조는 <a href="#%EC%BA%98%EB%A6%AC%ED%8D%BC%EC%99%80%EC%9D%98-%ED%86%B5%ED%95%A9">캘리퍼와의 통합</a> 섹션을 참조하자. 프로젝트 구조를 잠시 제쳐두면, 네 가지 구현 관련 작업이 있다:</p>
<ol>
<li>커넥터 인터페이스 구현 (사용가능한 유틸리티 기반 클래스를 선택적으로 사용한다)</li>
<li>커넥터 초기화를 위한 팩토리 메서드 구현</li>
<li>네트워크 구성 파일을 위한 스키마 정의</li>
<li>커넥터를 위한 바인딩 구성 제공</li>
</ol>
<h3 id="커넥터-인터페이스">커넥터 인터페이스</h3>
<hr>
<p>프로젝트 디펜던시로 <code>@hyperledger/caliper-core</code> 패키지 (또는 그 특정 버전)을 추가하면 노출된 <code>ConnectorInterface</code> 클래스로 액세스할 수 있다. 이는 아래 <a href="https://github.com/hyperledger/caliper/blob/main/packages/caliper-core/lib/common/core/connector-interface.js">인터페이스</a>를 선언한다:</p>
<pre><code class="language-javascript">class ConnectorInterface extends EventEmitter {
    getType() {}
    getWorkerIndex() {}
    async init(workerInit) {}
    async installSmartContract() {}
    async prepareWorkerArguments(number) {}
    async getContext(roundIndex, args) {}
    async releaseContext() {}
    async sendRequests(requests) {}
}

module.exports = ConnectorInterface;</code></pre>
<p>이 인터페이스는 다음 하위섹션에서 자세히 다룬다. 지금은 다음 사항만 알아두자:</p>
<ol>
<li>커넥터는 두 가지 다른 환경에서 사용된다: 매니저와 워커 프로세스다. 메서드와 연관된 환경은 인터페이스 레퍼런스 하위섹션에서 다룬다.</li>
<li>커넥터는 반드시 리퀘스트에 대한 특정 이벤트를 노출해야 한다. 그렇지 않으면 캘리퍼 워커에 의해 탐색될 수 없어서 캘리퍼 스케줄링 메커니즘을 파괴하게 된다. </li>
<li><code>sendRequests</code>는 인터페이스에 대한 hot path이다. 이것을 주의깊고 효율적으로 구현하자. </li>
<li>커넥터의 행위 (그리고 실제로 구현하기 위한 메서드들)은 네트워크 구성 스키마의 케이퍼빌리티에 크게 의존한다. 캘리퍼 사이드 네트워크에 더 많은 유연성을 허용할수록, 더 많은 기능을 구현해야 한다. 유연한 커넥터는 벤치마크 시나리오를 설정하기 쉽고, 사용자를 행복하게 만든다.</li>
</ol>
<h4 id="인터페이스-레퍼런스"><strong>인터페이스 레퍼런스</strong></h4>
<hr>
<p><code>getType</code></p>
<ul>
<li>설명: 커넥터 타입에 대한 짧은 이름을 반환한다. 이 이름은 대개 SUT를 나타낸다 (예: <code>fast-ledger</code>). 이 이름은 여러 유형의 SUT를 대상으로 할 수 있는 워크로드 모듈에서 사용될 수 있다.</li>
<li>리턴 타입: 문자열</li>
<li>리턴: 커넥터 이름</li>
</ul>
<p><code>getWorkerIndex</code></p>
<ul>
<li>설명: 커넥터를 인스턴스화하는 영 기반 워커 프로세스 인덱스를 찾는다</li>
<li>리턴 타입: 넘버</li>
<li>리턴: 워커 프로세스 인덱스</li>
</ul>
<p><code>init</code></p>
<ul>
<li>설명: 이 메서드는 매니저와 (선택적으로) 워커 프로세스에 의해 호출된다. 이것은 커넥터 인스턴스와 잠재적으로 SUT의 특정 측면을 초기화한다. 초기화 작업은 커넥터 특정적이지만, 일반적으로 프로세스로 나눌 수 있다:<ul>
<li>매니저 프로세스 인스턴스는 SUT와의 상호작용을 요구하는 초기화 작업들을 한 번만(one-time initialization tasks) 수행한다. 이 작업들은 디지털 신원 생성 또는 다른 SUT 관련 하우스키핑 액션 등을 포함할 수 있다.</li>
<li>워커 프로세스 인스턴스는 대개 로컬 하우스키핑 작업만을 수행한다. 예를 들어 필수 데이터 스트럭처 또는 나중에 필요한 캐시들을 리퀘스트를 보낼 때 생성하는 작업이 있다. 이 단계는 선택적이고, 커넥터 인스턴스 생성 후에 팩토리 메서드를 통해 수행될 수 있다. 만약 워커 프로세스가 이 단계에서 SUT에 접근할 필요가 있다면, 멱등(idempotent) 작업을 통해서(예: 구성 쿼리)이를 수행해야 한다. 이것은 임의의 숫자의 병렬적인 워커 프로세스가 정확히 실행되는 것을 보장한다. </li>
</ul>
</li>
<li>파라미터:<ul>
<li>workerInit (boolean): 메서드가 워커 프로세스에 의해 호출되는지, 아니면 매니저 프로세스에 의해 호출되는지를 나타낸다.</li>
</ul>
</li>
<li>리턴 타입: 프로미스</li>
<li>리턴: 메서드 완료(completion) 시 resolve되는 프로미스</li>
</ul>
<p><code>installSmartContract</code></p>
<ul>
<li>설명: 원격으로 허용되는 경우, SUT에서 컨트랙트 배포를 수행하기 위해 매니저 프로세스에 의해 호출된다.</li>
<li>리턴 타입: 프로미스</li>
<li>리턴: 메서드 완료(completion) 시 resolve되는 프로미스</li>
</ul>
<p><code>prepareWorkerArguments</code></p>
<ul>
<li>설명: 매니저 프로세스에 의해 호출되며, 매니저 프로세스의 커넥터 인스턴스가 워커 프로세스의 커넥터 인스턴스에 데이터를 분산시킬 수 있는지를 확인한다. 이 메서드는, 예를 들어 새롭게 생성한 디지털 신원을 매니저 프로세스 같은 것들을, 나중 사용을 위해 워커 프로세스 인스턴스로 차례차례 반환할 수 있도록, 반환하기 위한 최적의 정소이다. </li>
<li>리턴 타입: <code>Promise&lt;object[]&gt;</code></li>
<li>리턴: 각 워커에 대한 커넥터 특정적 오브젝트들의 프로미스. 메서드 완료(completion) 시 resolve된다.</li>
</ul>
<p><code>getContext</code></p>
<ul>
<li>설명: 이 메서드는 각 라운드 전에 워커 프로세스에 의해 호출되고, 커넥터 특정적 오브젝트를 어셈블하는데에 사용된다. 이 오브젝트는 해당 라운드의 워크로드 모듈에 의해 공유된다. 이 메서드는 또한 다음 라운드에 필요한 리소스(원격 로드에 대한 커넥션 생성 등)을 청구하기에 적합한 장소이다. </li>
<li>파라미터:<ul>
<li>roundIndex (number): 임박한 라운드의 영기반 인덱스</li>
<li>args (object): 매니저 인스턴스의 <code>prepareWorkerArguments</code> 메서드에서 워커 인스턴스에 의해 어셈블된(모아진, 조립된?) 오브젝트.  </li>
</ul>
</li>
<li>리턴 타입: <code>Promise&lt;object&gt;</code></li>
<li>리턴: 메서드 완료(completion) 시 resolve되는 커넥터 특정적 오브젝트의 프로미스</li>
</ul>
<p><code>releaseContext</code> </p>
<ul>
<li>설명: 각 라운드 이후에 워커 프로세스에 의해 호출되는 메서드. <code>getContext</code> 메서드에서 청구되는 리소스들을 제공한다.</li>
<li>리턴 타입: 프로미스</li>
<li>리턴:  메서드 완료(completion) 시 resolve되는 프로미스</li>
</ul>
<p><code>sendRequests</code></p>
<ul>
<li><p>설명: 커넥터의 hot path이다. 라운드의 워크로드 모듈에 의해 워커 프로세스에서 호출된다. 이 메서드는 반드시 하나 이상의 오브젝트를 받아야 한다. 이는 SUT로 반드시 보내져야 하는 리퀘스트(들)과 관련된 것이어야 한다. SUT 타입이 리퀘스트 배치를 지원하지 않는 한 커넥터는 리퀘스트의 실행 순서를 보존할 필요가 없다. 커넥터는 TxStatus 인스턴스를 통해 최소한 모든 리퀘스트의 시작 시간, 종료시간, 마지막 상태(성공 또는 실패)를 수집해야 한다.</p>
</li>
<li><p>파라미터: </p>
<ul>
<li>requests (object|object[]) : 리퀘스트(들)을 위한 하나 이상의 커넥터 특정적 설정 오브젝트</li>
</ul>
</li>
<li><p>리턴 타입: <code>Promise&lt;TxStatus|TxStatus[]&gt;</code></p>
</li>
<li><p>리턴: 메서드 완료(completion) 시 resolve되는, 하나 이상의 리퀘스트 실행 결과.</p>
</li>
</ul>
<br>

<h4 id="노출된-이벤트"><strong>노출된 이벤트</strong></h4>
<hr>
<p>커넥터는 반드시 다음 이벤트들을 정의된 <a href="https://github.com/hyperledger/caliper/blob/main/packages/caliper-core/lib/common/utils/constants.js">상수</a>와 매칭되는 이름으로 노출시켜야 한다. 이 이벤트들 없이는 캘리퍼의 스케줄링 메커니즘이 올바르게 작동하지 않고, 다른 컴토넌트들 (TX 모니터 등)이 의존하게 된다.</p>
<p><code>txsSubmitted</code></p>
<ul>
<li>설명: 하나 이상의 리퀘스트가 SUT에 실행을 위해 제출되었을 때 발생해야 한다. 일반적으로 이 이벤트는 모든 개별 리퀘스크에 대해 발생한다. </li>
<li>파라미터 <ul>
<li>count (number) : 제출된 리퀘스트 수</li>
</ul>
</li>
</ul>
<p><code>txsFinished</code>    </p>
<ul>
<li>설명: 하나 이상의 리퀘스트가 SUT에 의해 완전히 처리되었을 때 일어난다(즉, 커넥터가 결과를 수신한다).</li>
<li>파라미터:<ul>
<li>results (TxStatus|TxStatus[]) : 커넥터에 의해 수집된 하나 이상의 리퀘스트 실행 결과</li>
</ul>
</li>
</ul>
<br>

<h4 id="추가적-베이스-클래스"><strong>추가적 베이스 클래스</strong></h4>
<hr>
<p><code>@hyperledger/caliper-core</code> 패키지는 <code>ConnectorBase</code> 클래스 또한 노출시킨다. 이 클래스는 다음 <code>ConnectorInterface</code> 메서드를 위한 합리적인 기본 구현을 제공한다:</p>
<ul>
<li><code>prepareWorkerArguments</code>: 기본값으로 각 워커에 대해 빈 객체가 반환된다. 즉, 워커 프로세스 인스턴스와 아무것도 공유하지 않는다.</li>
<li><code>sendRequests</code>: 단일 및 복수 리퀘스트가 워크로드 모듈에 의해 제출된 경우를 처리한다. 또한 리퀘스트 전후로 필수 이벤트를 발생시킨다. 이 메서드는 단일 리퀘스트 실행을 <code>_sendSingleRequest</code>에 위임한다 (아래를 보자).</li>
<li><code>constructor</code>: 워커 인덱스와 SUT/커넥터 타입을 파라미터로써 요구하는 생성자를 선언한다.</li>
<li><code>getType</code>: 관련된 생성자 argument에 대한 단순한 getter를 제공한다.</li>
<li><code>getWorkerIndex</code>: 관련된 생성자 argument에 대한 단순한 getter를 제공한다.</li>
</ul>
<p>커넥터를 위해 이 기본 클래스를 사용하기로 결정한 경우, 반드시 <code>_sendSingleRequest</code> 메서드를 구현해야 한다.</p>
<p><code>_sendSingleRequest</code></p>
<ul>
<li>설명: 이 메서드는 단일 리퀘스트 전송 및 처리만 처리하면 된다. </li>
<li>파라미터:<ul>
<li>request (object): 리퀘스트를 위한 커넥터 특정적 설정 객체</li>
</ul>
</li>
<li>리턴 타입: <code>Promise&lt;TxStatus&gt;</code></li>
<li>리턴: 메서드 완료(completion) 시 resolve되는, 리퀘스트 실행 결과 프로미스</li>
</ul>
<h3 id="팩토리-메서드">팩토리 메서드</h3>
<hr>
<p>커넥터 구현의 진입점(entry point)은 팩토리 메서드가 될 것이다. 매니저와 워커 프로세스는 이 노출된 팩토리 메서드를 커넥터 인스턴스화를 위해 호출할 것이다 (변수명명법(casing)에 주의하자).</p>
<p><code>ConnectorFactory</code></p>
<ul>
<li>설명: 커넥터를 인스턴스화하고 선택적으로 그것을 초기화한다. 매니저 프로세스에서 호출되는 경우(워커 인덱스 <code>-1</code>로 표시된다), 매니저는 <code>init</code> 및 <code>installSmartContracts</code> 호출을 처리할 것이다. 초기화는 워커 프로세스에서 선택사항이므로, 필요한 경우 팩토리 메서드가 이것을 처리해야 한다.</li>
<li>파라미터:<ul>
<li>workerIndex (number): 영기반 워커 프로세스 인덱스, 또는 매니저 프로세스의 경우 <code>-1</code></li>
</ul>
</li>
<li>리턴 타입: <code>Promise&lt;ConnectorInterface&gt;</code></li>
<li>리턴: 메서드 완료(completion) 시 resolve되는, <code>ConnectorInterface</code> 인스턴스의 프로미스.</li>
</ul>
<p>다음은 <code>fast-ledger</code> 커넥터를 위해 가능한 팩토리 메서드 구현이다:</p>
<pre><code class="language-javascript">    &#39;use strict&#39;;

    const FastLedgerConnector = require(&#39;./fast-ledger-connector&#39;);

    async function ConnectorFactory(workerIndex) {
        const connector = new FastLedgerConnector(workerIndex, &#39;fast-ledger&#39;);

        // initialize the connector for the worker processes
        if (workerIndex &gt;= 0) {
            await connector.init(true);
        }

        return connector;
    }

    module.exports.ConnectorFactory = ConnectorFactory;</code></pre>
<h3 id="네트워크-구성-파일">네트워크 구성 파일</h3>
<hr>
<p>네트워크 구성 파일에는 커넥터가 SUT와 통신하기 위해 필요한 정보들과, 커넥터 품질 요구 사항을 충족시키기 위해 필요한 정보들이 모두 포함될 수 있다. 구성 파일은 JSON 또는 YAML 파일이 될 수 있다. YAML이 가독성 및 주석 지원 때문에 선호된다.</p>
<p>네트워크 구성 스키마는 반드시 필수적인(mandatory) 최상위 수준(top-level) 필드를 아래 구조로 가져야 한다:</p>
<pre><code class="language-yaml"># mandatory
caliper:
  # mandatory
  blockchain: fast-ledger
  # optional
  commands:
    start: startLedger.sh
    end: stopLedger.sh</code></pre>
<p><code>caliper.blockchain</code> 속성은 캘리퍼에게 테스트를 위해 어떤 커넥터를 불러와야 하는지 말해준다. 이 속성의 값은 캘리퍼를 커넥터와 통합하기위해 사용할 방법에 달려 있다.</p>
<h2 id="바인딩-구성">바인딩 구성</h2>
<hr>
<p>캘리퍼의 바인딩 커맨드를 사용하면 (개발하는 동안 커넥터와 패키징되는 대신) 런타임 중에 설치될 주요 커넥터 디펜던시를 지정할 수 있다. SUT SDK들과 다른 클라이언트 라이브러리(즉, SUT와 상호작용을 가능하게 하는 라이브러리)는 대개 이 카테고리에 속한다. 이 라이브러리의 API가 서로 다른 버전에서 일관성이 있는 경우, 단일 커넥터 구현이 여러 SUT 버전을 대상으로 삼을 수 있다.</p>
<p>이 경우, 사용자는 관련 SUT 버전을 대상으로 하는 특정한 SDK버전을 고를 수 있어야 한다. 이것은 커넥터에 바인딩 구성 파일(JSON 또는 YAML)를 제공함으로써 가능하다.</p>
<h3 id="심플-구성">심플 구성</h3>
<hr>
<p>일반적인 바인딩 구성 스키마는 대개 단순하다:</p>
<pre><code class="language-yaml">sut:
  fast-ledger:
    1.0: 
      packages: [&#39;fast-ledger-sdk@1.0.0&#39;]
    1.4:
      packages: [&#39;fast-ledger-sdk@1.4.5&#39;]
    2.0: &amp;fast-ledger-latest
      packages: [&#39;fast-ledger-sdk@2.0.0&#39;]
    latest: *fast-ledger-latest </code></pre>
<p>위 구성에 대한 몇몇 주의사항:</p>
<ol>
<li><code>SUT</code> 최상위 속성은 캘리퍼가 처리할 구성 섹션을 가리킨다. 어떤 스키마 제약 없이 이 속성 바깥에 임시 YAML 섹션을 작성할 수 있다. 이는 예를 들어 YAML 앵커와 별칭 등을 복잡한 바인딩 사양의 가독성을 높이기 위해 활용할 수 있음을 뜻한다. 예시를 곧 보게 될 것이다.</li>
<li><code>SUT</code> 속성은 커넥터가 바인딩을 지원할 SUT 타입을 식별하는 키들을 가지고 있다. 예시 커넥터에서는 단일 SUT 타입(<code>fast-ledger</code>)를 정의했다.</li>
<li><code>fast-ledger</code> 아래에 커넥터가 지원하는 몇몇 SUT 버전들을 정의할 수 있다. SUT의 시멘틱 버전과 상응하는 키를 사용하는 것을 추천한다. 사용자는 SUT 타입과 SUT 버전을 사용해 바인딩을 지정할 것이다. 예를 들어 <code>--caliper-bind-sut fast-ledger:1.4</code> 커맨드 라인 아규먼트를 캘리퍼에 전달할 수 있다. </li>
<li>모든 SUT 버전은 필요한 <code>packages</code>를 선언해야 한다. 이것은 캘리퍼가 런타임 동안 설치해야 하는 것이다. 다른 SUT 버전들은 일반적으로 설치해야 하는 서로 다른 SDK 버전을 선언한다.</li>
<li>SUT 버전으로 1.4를 선언했지만, 캘리퍼에게 1.4.5 SDK 버전을 설치하라고 요청했다. 사용자가 SDK 버전에 대한 최신 버그 fix를 즐길 수 있도록 항상 사용 가능한 최신 패치 릴리즈에 바인딩하는 것이 좋다.</li>
<li>많은 라이브러리 관리 시스템들(NPM이나 DockerHub과 같은)은 최신 릴리즈를 가리키기 위해 <code>latest</code> 태그를 제공한다. 커넥터에 그와 같은 바인딩 버전을 제공할 경우, 유저는 단순화된 <code>--caliper-bind-sut fast-ledger</code> 표기를 사용해 커넥터를 바인딩할 수 있다. YAML 앵커들과 별칭을 사용해 최신으로 간주되는 바인딩 버전을 쉽게 참조할 수 있다. 이를 통해 구성이 더욱 읽고 유지하기 쉬워진다.</li>
</ol>
<h3 id="심화된-구성">심화된 구성</h3>
<hr>
<p>커넥터가 구현 수준에서 여러 SUT 버전을 지원한다 할지라도, 이는 모든 버전이 동일한 환경에서 지원됨을 뜻하지 않는다. 일반적인 예는 해당 SDK 패키지가 새로운 Node.js 버전에서 자동으로 빌드하지 못하는 예전 SUT 버전들을 지원하는 것이다. 바인딩 구성은 이런 패키지들의 설치를 조정할 수 있는 유연성을 제공한다. </p>
<p><a href="https://github.com/hyperledger/caliper/blob/main/packages/caliper-cli/lib/lib/config.yaml">빌트인 캘리퍼 바인딩 구성</a>에서 가져온 다음 예시를 고려해 보자. 패브릭 커넥터의 1.1 SDK 바인딩은 특정 grpc 패키지 버전에 따라 다르다. Node.js 10부터는 해당 패키지 버전에 사용가능한 사전 빌드된 바이너리가 없다. 이 패키지는 반드시 소스로부터 컴파일되어야 하고, 이는 자동적으로 일어나지 않는다(예: 대체 메커니즘). 또한 사용되는 컴파일러는 기본적으로 엄격(strict)하므로, 여러 컴파일 오류가 발생한다. </p>
<p>이러한 어려움을 우회하기 위하여, 바인딩 구성 스키마는 커맨드 라인 아규먼트 및 환경 변수 (<code>npm install</code>로 선택되는)를 지정함으로써 설치 프로세스를 땜질할 수 있다. <code>settings</code> 속성 아래 설치 로직을 놓을 수 있다. </p>
<pre><code class="language-yaml">sut:
  fast-ledger:
    1.0:
      packages: [&#39;fast-ledger-sdk@1.0.0&#39;, &#39;comm-lib@1.0.0&#39;]
      settings:
      # compiling older comm-lib on newer Node.js version
      - versionRegexp: &#39;^((?!v8\.).)*$&#39;
        env:
          CXXFLAGS: &#39;-Wno-error=class-memaccess&#39;
          CFLAGS: &#39;-Wno-error=class-memaccess&#39;
        args: &#39;--build-from-source&#39;</code></pre>
<p><code>settings</code> 속성은 일반적으로 잠재적으로 적용가능한 설정들의 배열이다. 캘리퍼는 이들을 순서대로 처리하고, 정규 표현식(<code>versionRegexp</code>)이 사용하는 Node.js 버전과 일치하는 첫 설정 객체를 선택할 것이다. 이 예시에서는 더 최신 버전인 Node.js가 사용될 경우(즉, 버전이 v.8.x가 아닐 경우) 설정이 적용된다. 이 케이스에서는 (<code>args</code>에 지정된) 커맨드 라인 아규먼트가 <code>npm install</code>로 전달된다. 또한 <code>env</code> 아래 지정된 환경변수들 (<code>npm install</code>와 실행될 서브커맨드에 의해 선택되는) 을 설정한다.</p>
<p>커넥터는 여러 환경에서 넓은 범위의 SUT/SKD 버전에 대한 서포트를 제공함으로써 이와 같은 심화된 사양을 사용할 수 있다.</p>
<h2 id="커넥터-문서화">커넥터 문서화</h2>
<hr>
<p>커넥터에 대한 적절한 사용자 메뉴얼을 제공하는 것은 퀄리티 있는 구현을 제공하는 것 만큼 중요하다. 그렇지 않으면 유저는 커넥터와의 상호작용에 어려움을 겪게 된다. 예시로 패브릭 커넥터 문서를 섹션별로 살펴볼 것이다. </p>
<h3 id="문서화-개요">문서화 개요</h3>
<hr>
<p>커넥터에 대한 짧은 요약을 제공해야 한다. 이는 다음을 포함해야 한다:</p>
<ul>
<li>지원되는 SUT 타입 및 버전들</li>
<li>커넥터의 케이퍼빌리티(지원되는 SUT 기능들 및 제한사항들)</li>
</ul>
<p>개요는 사용자가 커넥터에서 기대할 수 있는 것이 무엇인지에 대한 기본적인 설명을 제공한다.</p>
<h3 id="디펜던시-설치">디펜던시 설치</h3>
<hr>
<p>커넥터가 여러 SUT 버전을 바인딩 프로세스를 통해 지원할 경우, 특정 버전에 바인딩하는 필수 단계들을 문서화해야 한다. 바인딩 프로세스는 모든 커넥터에 공통적이므로, 짧은 예제로 충분하다.</p>
<p>하지만 모든 SUT 기능들이 모든 바인딩에서 지원되지 않을 수 있다. 바인딩들의 제한사항을 주의해서 문서화하고, 가능하다면 해결방안을 제공한다.</p>
<h3 id="런타임-설정">런타임 설정</h3>
<hr>
<p>네트워크 구성 파일은 SUT 토폴로지 및 관련 아티팩트들을 선언한다. SUT를 모르는(SUT-불가지론적인) 디자인 선택은 커넥터 개발 동안 여전히 일어날 수 있다. 스스로 결정하는 대신, 이와 같은 선택을 캘리퍼의 런타임 구성 메커니즘을 활용해 최종 유저에게 위임해야 한다.</p>
<p>이러한 설정은 일반적으로 커넥터의 운영 모드에 영향을 미친다. 하지만 SUT상호작용의 전반적인 시멘틱은 변화하지 않는다. 커넥터에 대한 모든 사용가능한 런타임 설정을 문서화했는지 확인하자. 또, 이와 같은 설정에 대한 합리적인 기본값을 제공하는 것을 잊지 말자.</p>
<h3 id="리퀘스트-api">리퀘스트 API</h3>
<hr>
<p>커넥터의 주 사용자는 워크로드 모듈 개발자일 것이다. 그들은 주로 <code>sendRequests</code> 메서드를 통해 커넥터와 상호작용할 것이다. 메서드는 사용자가 보내고 싶어하는 리퀘스트와 관련된 단일, 또는 복수의 설정 오브젝트를 허옹한다. 리퀘스트에 어떤 종류의 설정이 사용가능한지 정확히 지정해야 한다. 일반적으로 이는 아래를 포함한다:</p>
<ul>
<li>SUT에서 실행할 작업</li>
<li>작업의 아규먼트</li>
<li>리퀘스트를 제출해야 하는 신원</li>
<li>리퀘스트를 보낼 노드(들)</li>
<li>읽기 전용/쓰기 리퀘스트 간 구분</li>
</ul>
<h3 id="수집된-리퀘스트-데이터">수집된 리퀘스트 데이터</h3>
<hr>
<p>커넥터는 반드시 기본 실행 데이터를 캘리퍼에 보고해 정확한 리포팅을 보장해야 한다. 하지만 접근해야 하는 어떤 클라이언트 사이드 데이터이든 수집할 수 있다. 어떤 데이터가 유저에게 유용하게 될지 모른다. 수집된 데이터들을 문서화해야 한다(시멘틱들과 데이터 타입 모두).</p>
<h3 id="네트워크-구성-파일-1">네트워크 구성 파일</h3>
<hr>
<p>문서에서 아마도 가장 중요한 부분은 커넥터가 처리할 수 있는 네트워크 구성 파일 스키마이다. 네트워크 토폴로지, 참가자 및 필수 아티팩트를 정의하는 직관적인 구조를 제공해라. 서로 다른 설정들에 대한 시멘틱 및 데이터 타입을 문서화해야 한다. 여러 속성(상호 배제, 유효한 값 등) 간에 발생할 수 있는 모든 제약조건을 문서화해야 한다. </p>
<h3 id="예시-네트워크-구성">예시 네트워크 구성</h3>
<hr>
<p>완전히 지정되고 기능하는 네트워크 구성 예시를 제공해야 한다. 어떤 사람들에게는 참조 형식의 문서보다 구체적인 예를 흡수하는 것이 더 쉽다.</p>
<h2 id="캘리퍼와의-통합">캘리퍼와의 통합</h2>
<hr>
<p>커넥터를 구현하고 나면, 그것을 캘리퍼와 통합하기 위한 두 가지 선택이 있다:</p>
<ol>
<li>그것을 벤치마크 프로젝트의 일부인 서드파티, 플러그 가능한 컴포넌트로 사용한다.</li>
<li>공식 캘리퍼 코드베이스에 커넥터를 제공한다. 그래서 그것이 캘리퍼와 항상 함께 설치되도록 한다.</li>
</ol>
<h3 id="서드파티-커넥터">서드파티 커넥터</h3>
<hr>
<p>캘리퍼 코드 베이스의 일부가 되지 않고도 쉽게 커넥터를 동적으로 플러그인 할 수 있다. 그 프로세스는 다음과 같다:</p>
<ol>
<li>프로젝트에 <code>index.js</code> 파일을 생성해 커넥터 팩토리를 노출시킨다. 이 파일은 커넥터에 대한 클린 엔트리 포인트를 제공한다:<pre><code class="language-javascript">&#39;use strict&#39;;
module.exports.ConnectorFactory = require(&#39;./lib/connectorFactory&#39;).ConnectorFactory;</code></pre>
</li>
<li>네트워크 구성 파일에서 <code>caliper.blockchain</code> 속성에 대한 <code>./fast-ledger/index.js</code> 경로를 설정한다. 이 경로는 캘리퍼 워크스페이스 디렉토리에 대한 상대경로가 되거나, 절대경로여야 한다(휴대성 이유로 추천하지 않는다). 캘리퍼는 모듈 및 팩토리 메서드를 경로에서 로드한다.</li>
<li>여러 바인딩을 지원할 경우, 커넥터에 대한 바인딩 구성 파일을 준비한다.</li>
<li>캘리퍼를 실행하면 커넥터 구성이 네트워크 구성 파일을 통해 선택될 것이다.</li>
<li>커스텀 바인딩 구성을 지정할 수 있다. 예를 들어 커스텀 파일을 가리키는 <code>--caliper-bind-file ./fast-ledger-binding.yaml</code> 커맨드라인 아규먼트를 사용할 수 있다. <code>--caliper-bind-sut fast-ledger:1.0</code>로 바인딩을 지정하는 것도 잊지 말아야 한다.</li>
</ol>
<p>대안으로, 커넥터를 퍼블리시한 경우 NPM 패키지 이름으로 <code>caliper.blockchain</code> 속성을 지정할 수 있다. 이 경우, 벤치마크 러닝 이전에 패키지가 캘리퍼 워크스페이스 디렉토리에 설치되었는지 반드시 확인해야 한다. 패키지에 대한 추천하는 네이밍 컨벤션은 <code>caliper-sut</code>이다. 예시에서 <code>caliper.blockchain</code> 속성은 <code>caliper-fast-ledger</code>로 지정된다. </p>
<blockquote>
<p><strong>참고</strong></p>
<p>캘리퍼가 첫 메이저 버전에 도달하기 전 까지, 의존하는 <code>@hyperledger/caliper-core</code> 버전에 기반해 커넥터 패키지 버전을 지정하는 것을 추천한다.</p>
</blockquote>
<h3 id="빌트인-커넥터">빌트인 커넥터</h3>
<hr>
<blockquote>
<p><strong>커넥터를 코드베이스에 기부함으로써, 필요한 경우 커넥터를 유지할 책임을 가진다. 그렇지 않으면 향후 릴리즈에서 deprecated되고 더 이상 사용되지 않을 수 있다.</strong></p>
</blockquote>
<p>커넥터를 더 넓은 유저베이스에 노출시키고 싶다면 코드를 공식 캘리퍼 저장소에 기부할 수 있다. 그러면 커넥터가 빌트인 모듈이 되고, 캘리퍼를 설치한 사람이 즉시 사용할 수 있다.</p>
<blockquote>
<p><strong>참고</strong></p>
<p>통합을 도와줄 Rocket.Chat (#caliper-contributors channel) 프로젝트 관리자에게 주저하지 말고 연락하자.</p>
</blockquote>
<p>통합은 아래 단계를 따른다(예시로 <a href="https://github.com/hyperledger/caliper/tree/main/packages/caliper-ethereum">caliper-ethereum 커넥터</a>를 보자):</p>
<ol>
<li>리포지토리의 packages 디렉토리에 <code>caliper-fast-ledger</code> 디렉토리를 만든다. 이것이 당신의 커넥터 구현을 포함하게 된다.</li>
<li>자체 <code>package.json</code> 파일에 메타데이터를 업데이트한다. 패키지 이름은 <code>@hyperledger/caliper-fast-ledger</code>와 같이 스코프되어야 한다.</li>
<li>커넥터가 바인딩을 제공하는 경우, <code>devDependencies</code> 섹션에 동적 패키지를 나열해야 한다. 그래서 캘리퍼와 함께 자동으로 설치되지 않도록 (유저가 리바인드할 것이므로) 해야 한다. 또한, 커넥터의 바인딩 사양을 빌트인 바인딩 구성 파일에 추가해야 한다.</li>
<li>루트 <code>lerna.json</code> 파일의 <code>packages</code> 섹션에 새 디렉토리 경로를 추가한다. 이는 당신의 패키지가 다른 개발자들을 위해 제대로 부트스트랩 될 것임을 보장한다.</li>
<li>캘리퍼 CLI 디펜던시에 당신의 새 패키지를 (이름으로) 추가한다.</li>
<li><code>caliper-utils.js</code> 모듈의 <code>BuiltinConnectors</code> 변수 아래에 빌트인 커넥터로서 당신의 커넥터를 나열한다:<pre><code class="language-javascript">const BuiltinConnectors = new Map([
[&#39;fast-ledger&#39;, &#39;@hyperledger/caliper-fast-ledger&#39;],
// other connectors...
]);</code></pre>
</li>
<li>커넥터에 대한 통합 테스트를 제공하는 것을 강력히 추천한다.</li>
<li>모든 코드 관련 아티팩트들(대개 .js, .yaml, .md 파일들)이 적절한 라이선스 헤더를 가지고 있는지 확인한다.</li>
<li>완료되었다. 이제 사용자들이 그들의 구성 파일에서 <code>fast-ledger</code>로 커넥터를 참조할 수 있다. 커넥터 패키지는 모든 머지된 PR마다 자동으로 퍼블리시될 것이다.</li>
</ol>
<h2 id="라이선스">라이선스</h2>
<p>The Caliper codebase is released under the Apache 2.0 license. Any documentation developed by the Caliper Project is licensed under the Creative Commons Attribution 4.0 International License. You may obtain a copy of the license, titled CC-BY-4.0, at <a href="http://creativecommons.org/licenses/by/4.0/">http://creativecommons.org/licenses/by/4.0/</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Workload Configuration]]></title>
            <link>https://velog.io/@hh_0705/caliper-workload-configuration</link>
            <guid>https://velog.io/@hh_0705/caliper-workload-configuration</guid>
            <pubDate>Sun, 09 Jan 2022 08:03:59 GMT</pubDate>
            <description><![CDATA[<h1 id="workload-configuration"><a href="https://hyperledger.github.io/caliper/v0.4.2/workload-module/">Workload Configuration</a></h1>
<h2 id="개요">개요</h2>
<hr>
<p>Workload Module은 TX를 구성하고 제출하는 역할을 하기 때문에 캘리퍼 벤치마크의 핵심이다. 따라서 Workload Module은 비즈니스, 벤치마크, 또는 사용자 행위에 관련된 로직을 구현한다. Workload Module을 주어진 순간에 제출할 TX의 종류를 결정하는, 에뮬레이트된 SUT 클라이언트의 두뇌로 생각하자. </p>
<h2 id="workload-module-구현">Workload Module 구현</h2>
<hr>
<p>Workload Module은 특정 API를 export하는 Node.js 모듈이다. 구현에 대한 추가 제한이 없으므로 (추가 임의 컴포넌트를 사용하는) 임의의 로직을 구현할 수 있다.</p>
<h3 id="api">API</h3>
<hr>
<p>Workload Module은 캘리퍼의 다른 플러그형 모듈들과 마찬가지로 factory function들을 통해 로드된다. 따라서 워크로드 모듈 구현은 반드시 <code>createWorkloadModule</code>라고 이름 붙여진 단일 factory function을 export해야 한다:</p>
<pre><code class="language-javascript">/**
 * workload module의 새 인스턴스 생성
 * @return {WorkloadModuleInterface}
 */
function createWorkloadModule() {
    return new MyWorkload();
}

module.exports.createWorkloadModule = createWorkloadModule;</code></pre>
<p>이 factory function은 반드시 <a href="https://github.com/hyperledger/caliper/blob/master/packages/caliper-core/lib/worker/workload/workloadModuleInterface.js"><code>WorkloadModuleInterface</code></a> 클래스를 구현하는 인스턴스를 반환해야 한다. 완전한 구현은 <a href="#%EC%98%88%EC%8B%9C">예시</a>를 참고하자.</p>
<p>이 인터페이스는 아래 세가지 비동기 함수를 포함한다:</p>
<h4 id="initializeworkloadmodule"><strong>initializeWorkloadModule</strong></h4>
<hr>
<p><code>initializeWorkloadModule</code> 함수는 각 라운드 전에 워커 프로세스에 의해 호출되어 모듈에 아래 컨텍스트 인수들(contextual arguments)을 제공한다:</p>
<ol>
<li><code>workerIndex</code> <em>(Number)</em> 워크로드 모듈을 인스턴스화하는 워커의 인덱스이다. 0부터 시작한다(0-based index)</li>
<li><code>totalWorkers</code> <em>(Number)</em> 라운드에 참여하는 총 워커의 수이다.</li>
<li><code>roundIndex</code> <em>(Number)</em> 현재 실행되는 라운드의 인덱스이다. 0부터 시작한다.</li>
<li><code>roundArguments</code> <em>(Object)</em> 사용자가 벤치마크 구성 파일을 통해 라운드에 제공하는 인수들이다.</li>
<li><code>sutAdapter</code> <em>(BlockchainConnector)</em> 기반 SUT의 커넥터이다.</li>
<li><code>sutContext</code> <em>(Object)</em> SUT 커넥터에서 제공하는 커스텀 컨텍스트 오브젝트이다.</li>
</ol>
<p>이 함수는 <a href="./benchmark_configuration.md">벤치마크 구성 파일</a>에서 제공된 워크로드 모듈 인수의 유효성을 검증하기 좋은 위치이다. 또한 나중에 <code>submitTransaction</code> 함수에서 TX 컨텐츠를 빠르게 어셈블링하기 위해 필요한 모든 전처리를 여기서 수행하는 것이 좋다.</p>
<h4 id="submittransaction"><strong>submitTransaction</strong></h4>
<hr>
<p><code>submitTransaction</code>함수는 워크로드 생성의 중추이다. rate controller가 다음 TX를 활성화 할 때마다 워커 프로세스가 이 함수를 호출한다. 따라서 고빈도(high frequency) 스케줄링 설정을 따라가기 위해서는 이 함수 구현을 가능한 한 효율적으로 유지하는 것이 중요하다. </p>
<p>이 함수에는 어떤 파라미터도 필요하지 않다. 하지만 이 함수는 커넥터 API를 통해 TX를 제출해야 하는 책임이 있다.</p>
<h4 id="cleanupworkloadmodule"><strong>cleanupWorkloadModule</strong></h4>
<hr>
<p><code>cleanupWorkloadModule</code>함수는 라운드가 끝날 때 호출된다. 워크로드 구현에 필요한 모든 리소스 정리에 사용될 수 있다.</p>
<h3 id="simple-base-class">Simple base class</h3>
<hr>
<p>인터페이스를 직접 구현할 수도 있지만, 캘리퍼는 필요한 인터페이스를 구현하고 몇 가지 일반적인 하우스키핑 작업을 수행하는 simple utility base class를 제공한다. 따라서 <code>WorkloadModuleBase</code> 클래스를 상속하면 더 간단한 구현이 가능하다.</p>
<p>base class는 아래 유틸리티들을 제공한다:</p>
<ul>
<li><code>initializeWorkloadModule</code> 함수의 파라미터와 일치하는 생성자에서 인스턴스 변수를 생성한다.</li>
<li><code>initializeWorkloadModule</code> 함수에 대한, 받은 인수를 인스턴스 변수에 저장하는 구현을 제공한다.</li>
<li><code>cleanupWorkloadModule</code> 함수에 대한 no-op 구현을 제공한다.</li>
</ul>
<p>base class를 상속할 경우 사용자는 <code>submitTransaction</code> 함수만 구현하면 된다. 추가적으로, 필요하다면 초기화 로직을 extend하거나 override할 수 있다.</p>
<h3 id="예시">예시</h3>
<hr>
<p>아래는 워크로드 모듈 구현(<code>WorkloadModuleBase</code> 구현과 거의 동일하다)의 완성된 (임의 샘플) 예시이다:</p>
<pre><code class="language-javascript">&#39;use strict&#39;;

const { WorkloadModuleInterface } = require(&#39;@hyperledger/caliper-core&#39;);

class MyWorkload extends WorkloadModuleInterface {
    constructor() {
        super();
        this.workerIndex = -1;
        this.totalWorkers = -1;
        this.roundIndex = -1;
        this.roundArguments = undefined;
        this.sutAdapter = undefined;
        this.sutContext = undefined;
    }

    async initializeWorkloadModule(workerIndex, totalWorkers, roundIndex, roundArguments, sutAdapter, sutContext) {
        this.workerIndex = workerIndex;
        this.totalWorkers = totalWorkers;
        this.roundIndex = roundIndex;
        this.roundArguments = roundArguments;
        this.sutAdapter = sutAdapter;
        this.sutContext = sutContext;
    }

    async submitTransaction() {
        let txArgs = {
            // &quot;mycontract&quot;를 위한 TX arguments
        };

        return this.sutAdapter.invokeSmartContract(&#39;mycontract&#39;, &#39;v1&#39;, txArgs, 30);
    }

    async cleanupWorkloadModule() {
        // NOOP
    }
}

function createWorkloadModule() {
    return new MyWorkload();
}

module.exports.createWorkloadModule = createWorkloadModule;</code></pre>
<p>utility base class를 사용한 보다 단순한 구현은 아래와 같다:</p>
<pre><code class="language-javascript">&#39;use strict&#39;;

const { WorkloadModuleBase } = require(&#39;@hyperledger/caliper-core&#39;);

class MyWorkload extends WorkloadModuleBase {
    async submitTransaction() {
        let txArgs = {
            // TX arguments for &quot;mycontract&quot;
        };

        return this.sutAdapter.invokeSmartContract(&#39;mycontract&#39;, &#39;v1&#39;, txArgs, 30);
    }
}

function createWorkloadModule() {
    return new MyWorkload();
}

module.exports.createWorkloadModule = createWorkloadModule;</code></pre>
<h2 id="workload-module-구성">Workload Module 구성</h2>
<hr>
<p>주어진 라운드에 워크로드 모듈을 사용하려면 <a href="./benchmark_configuration.md">벤치마크 구성 파일</a>에서 해당 모듈을 참조하기만 하면 된다.</p>
<ol>
<li><code>test.rounds[i].workload.module</code> 속성을 워크로드 모듈 파일 경로로 설정한다. 이 경로는 절대경로이거나 구현된 워크스페이스 경로에 대한 상대경로일 수 있다. 또한 이 속성을 퍼블리시된 워크로드 모듈의 패키지 이름으로 지정할 수 있다. 하지만 이 경우에는 해당 모듈을 먼저 설치해야 한다.</li>
<li>모듈이 여러 설정들을 지원한다면, <code>test.rounds[i].workload.arguments</code> 속성 오브젝트를 그에 따라 설정한다. 이것이 초기화 시 모듈에 전달될 것이다.</li>
</ol>
<h2 id="팁--트릭">팁 &amp; 트릭</h2>
<hr>
<p>아래 어드바이스는 워크로드 모듈 구현을 향상시키는 데 도움이 될 것이다.</p>
<ol>
<li>(코어 캘리퍼 모듈을 포함해) 모든 Node.js 모듈을 코드에서 사용할 수 있다. 모듈화는 구현을 깔끔하고 관리하고 쉽도록 유지하기 위해 중요하다.</li>
<li>서드 파티 모듈을 사용한다면 그것들이 워크로드 모듈에서 사용가능하도록 만드는 것은 개발자가 직접 작업해야 한다(your responsibility). 일반적으로 캘리퍼 시작 전에 모듈 디렉토리에서 <code>npm install</code>호출이 필요하다.</li>
<li>캘리퍼는 <a href="./logging_control.md">로깅</a>이나 <a href="./runtime_configuration.md">런타임 구성</a>와 같은 몇몇 코어 유틸리티를 편의성을 위해 제공하고 있다. 바퀴를 재발명하지 말고 이것들을 사용하자.</li>
<li><code>submitTransaction</code> function은 워커의 워크로드 생성 루프의 hot path에 있다. 계산 집약적인 작업을 조심스럽게 수행해야 한다. 이것이 TXs의 스케줄링 정확도를 떨어트릴 수 있다. 그 대신 <code>initializeWorkloadModule</code>에서 값비싼 전처리 작업을 수행할 수 있다.</li>
</ol>
<h2 id="라이선스">라이선스</h2>
<p>The Caliper codebase is released under the Apache 2.0 license. Any documentation developed by the Caliper Project is licensed under the Creative Commons Attribution 4.0 International License. You may obtain a copy of the license, titled CC-BY-4.0, at <a href="http://creativecommons.org/licenses/by/4.0/">http://creativecommons.org/licenses/by/4.0/</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Runtime Configuration]]></title>
            <link>https://velog.io/@hh_0705/caliper-runtime-configuration</link>
            <guid>https://velog.io/@hh_0705/caliper-runtime-configuration</guid>
            <pubDate>Sun, 09 Jan 2022 08:02:29 GMT</pubDate>
            <description><![CDATA[<h1 id="runtime-configuration"><a href="https://hyperledger.github.io/caliper/v0.4.2/runtime-config/">Runtime Configuration</a></h1>
<h2 id="개요">개요</h2>
<hr>
<p>캘리퍼는 유연하고 계층적인 구성 메커니즘을 런타임 관련 설정에 제공하기 위해 <a href="https://github.com/indexzero/nconf">nconf</a> 패키지에 의존한다. 계층적 구성이란 런타임 설정이 다양한 소스 및 위치에서 설정되거나 오버라이딩 될 수 있으며, 그들 간에 우선순위가 있다는 뜻이다.</p>
<p>일반적으로, 설정은 특정 값과 관련된 단순한 문자열 키이다. 하지만, 설정을 사용하는 계층에서 모듈의 위치를 따르는 방식으로 키를 구성하는 것이 추천된다. 아래 키 예시를 고려해 보자:</p>
<pre><code>caliper-fabric-timeout-invokeorquery</code></pre><p>키는 설정의 목적을 구분하기 쉽도록 여러 분리된 부분으로 구성되어 있다: 이것은 캘리퍼에서, 패브릭 커넥터에 의해 사용되며, 타임아웃 관련 설정이고, 트랜잭션 호출 또는 쿼리의 타임아웃에 사용하는 타임아웃을 지정한다. 캘리퍼의 모든 설정 키는 동일한 컨벤션을 따른다.</p>
<p>엄지의 법칙에 따라(경험적으로) 생각해보면, 소문자(어쩌면 숫자들)을 사용하고 위계는 대시(<code>-</code>)를 분리 기호로 사용해서 표시되어야 한다.</p>
<p>캘리퍼에 의해 사용되는 모든 설정은 <code>caliper-</code> 문자열 접두사를 가진다. 이 접두사는 캘리퍼 모듈의 내부 설정을 위한 네임스페이스 역할을 한다. 이것은 이름 충돌을 방지하기도 하는데, 이는 구성 메커니즘이 다양한 소스에서, 예를 들어 하부 SDK 모듈이나 워크로드 모듈을 목적으로 한, 가능한 모든 설정을 분석하기 때문이다.</p>
<blockquote>
<p>모든 가능한 런타임 설정은 <a href="#%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%84%A4%EC%A0%95">마지막 섹션</a>을 보자.</p>
</blockquote>
<h2 id="소스-설정">소스 설정</h2>
<hr>
<p>캘리퍼는 런타임 설정이 설정되고 오버라이딩 될 수 있는 아래 소스들 및 위치들을 지원한다. 최상위 우선순위부터 나열한다:</p>
<ol>
<li>메모리</li>
<li>커맨드 라인 매개변수</li>
<li>환경변수</li>
<li>프로젝트 수준 구성 파일</li>
<li>유저 수준 구성 파일</li>
<li>머신 수준 구성 파일</li>
<li>대체 및 기본 구성 파일</li>
</ol>
<p>단순화을 위해, 위 순서를 다음과 같이 생각할 수 있다: 설정이 사용 시점과 가까울수록 설정값의 우선순위가 높다.</p>
<h3 id="인-메모리-설정">인 메모리 설정</h3>
<hr>
<p>어떤 구성요소가(내부 구성요소 혹은 플러그인) (구성 API를 사용해) 런타임으로 설정된다면, 그 값은 다른 소스 및 위치에 있는 어떤 같은 설정보다 우선된다.</p>
<p>단순 구성 API는 <code>@hyperledger/caliper-core</code> 패키지의 <code>ConfigUtil</code> 모듈에 의해 제공된다. 이것은 단순한 <code>get</code>, <code>set</code> 메서드에 의해 export 된다: </p>
<ul>
<li><p>get(key:string, fallbackValue:any) =&gt; any</p>
<p>주어진 키와 관련된 설정값을 리턴한다. 설정이 어떤 소스에서도 정해지지 않았다면 <code>fallbackValue</code>가 반환된다.</p>
</li>
<li><p>set(key:string, value:any)</p>
<p>주어진 키와 관련된 값을 설정한다. 이것은 다른 소스들에 의해 설정된 값을 오버라이딩한다.</p>
</li>
</ul>
<p>예:</p>
<pre><code class="language-javascript">const { ConfigUtil } = require(&#39;@hyperledger/caliper-core&#39;);

// retrieves a setting for your module, if not set, use some default
const shouldBeFast = ConfigUtil.get(&#39;mymodule-performance-shoudbefast&#39;, /*default:*/ true);

if (shouldBeFast) { /* ... */ } else { /* ... */ }</code></pre>
<p>위 코드는 플러그인 모듈이 어떻게 쉽게 캘리퍼 구성 메커니즘에 영향을 미칠 수 있는지를 보여주기도 한다. <code>ymodule-performance-shoudbefast</code> 설정이 구성 API를 통해 쿼리되기 때문에, 다양한 소스로부터 이것을 자동으로 설정하는 것이 가능해진다 (자세한 내용은 다음 섹션을 보자).</p>
<blockquote>
<p>즉, 모듈에 유연한 런타임 설정을 더하려면 필요할 때 구성 API를 통해 해당 쿼리를 (원하는 기본 값과 대체값으로) 설정하기만 하면 된다. </p>
</blockquote>
<h3 id="커맨드라인-설정">커맨드라인 설정</h3>
<hr>
<p>서드 파티 코드(예: 캘리퍼나 워크로드 모듈) 동작에 영향을 미치고 싶을 경우, 일반적으로 소스 코드의 설정을 덮어쓰지 못한다(그리고 하고 싶지 않다). 서트파티 및 사전 패키지된 어플리케이션 동작 수정을 위한 표준적인 방법은 커맨드라인 argument를 통한 설정을 제공하는 것이다. </p>
<p>캘리퍼를 CLI를 통해 시작할 때, 런타임 설정을 아래 방법으로 오버라이딩 할 수 있다:</p>
<pre><code>caliper launch manager \
    --caliper-workspace yourworkspace/ \
    --caliper-benchconfig yourconfig.yaml \
    --caliper-networkconfig yournetwork.yaml \
    --mymodule-performance-shoudbefast=true</code></pre><p>argument들은 소문자로 변환되고 모든 <code>_</code>는 <code>-</code>로 대체된다. 그러므로 위 커맨드라인은 아래와 같이 사용할 수도 있다:</p>
<pre><code>caliper launch manager \
    --caliper-workspace yourworkspace/ \
    --caliper-benchconfig yourconfig.yaml \
    --caliper-networkconfig yournetwork.yaml \
    --MyModule_Performance_ShoudBeFast=true</code></pre><p>두 방법 다 <code>mymodule-performance-shoudbefast</code>키에 <code>boolean</code> 값 <code>true</code>를 할당할 것이다.</p>
<p><code>nconf</code>가 값을 자동으로 일반적인 타입으로 해석하므로, <code>true</code>와 <code>false</code> 값은 <code>boolean</code>으로 해석되고 (get 함수를 통해 반환된)다는 것에 주의하자. 이는 또한 integer와 floating point 숫자값을 가질 수 있다.</p>
<p><code>true</code>와 <code>false</code> 값을 명시적으로 설정하지 않고도 <code>boolean</code> 값을 플래그에 지정할 수 있다.</p>
<ul>
<li>키를 <code>true</code>로 지정한다: <pre><code>caliper launch manager \
    --caliper-workspace yourworkspace/ \
    --caliper-benchconfig yourconfig.yaml \
    --caliper-networkconfig yournetwork.yaml \
    --mymodule-performance-shoudbefast</code></pre></li>
<li><code>no-</code> 접두사를 이용해 <code>false</code>로 지정한다:<pre><code>caliper launch manager \
    --caliper-workspace yourworkspace/ \
    --caliper-benchconfig yourconfig.yaml \
    --caliper-networkconfig yournetwork.yaml \
    --no-mymodule-performance-shoudbefast</code></pre>커맨드라인 arguments들은 다음 섹션들에서 정해진 설정을 덮어쓸 수 있다.</li>
</ul>
<h3 id="환경변수">환경변수</h3>
<hr>
<p>캘리퍼가 스크립트 환경의 일부분인 경우 캘리퍼에 커맨드라인 argument들을 전달해 스크립트를 수정하는 것이 성가실 수 있다. 이런 경우 일반적인 접근 방식은 환경 변수를 사용하는 것이다.</p>
<p>환경변수를 사용한 예시는 다음과 같다:</p>
<pre><code>export MYMODULE_PERFORMANCE_SHOULDBEFAST=true

# calling some script containing the following command
caliper launch manager \
    --caliper-workspace yourworkspace/ \
    --caliper-benchconfig yourconfig.yaml \
    --caliper-networkconfig yournetwork.yaml</code></pre><p>환경변수 설정의 표준 표기법을 주의하자: 대문자가 <code>_</code>로 나뉘어진다. 캘리퍼는 커맨드 라인 argument와 동일한 변환을 수행한다: 변수 이름은 소문자로 변환되고 <code>_</code>는 <code>-</code>로 대체된다. 그러므로 위 설정은 <code>mymodule-performance-shoudbefast</code> 키에 불리언 값 <code>true</code>설정한 것과 같다.</p>
<h3 id="구성파일">구성파일</h3>
<hr>
<p>사용자가 다수의 런타임 설정을 변경하고 싶을 수 있다. 커맨드라인이나 환경변수 사용은 성가시고 읽기 어려울 수 있다. 또한, 어떤 경우에는 구성이 CLI 호출 위치 주변에 내장되는 것 보다 별도의 프로젝트 아티팩트(예: 버전 컨트롤이 가능하도록)가 되는 것이 바람직하다.  </p>
<p>구성 파일 사용은 관리하기 쉬운 방법으로 여러 설정을 오버라이딩하는 일반적인 방법이다. 캘리퍼는 설정 계층에 구성 파일을 삽입할 수 있는 여러 구성 &quot;위치&quot;를 제공한다. 이 위치들 또한 계층 구성 메커니즘의 &quot;가까운 것이 이기는&quot; 법칙을 따른다.</p>
<p>또한, YAML 기반 구성 파일을 사용하면 구성이 자체 문서화되고 자기충족적(self-contained)이 될 수 있도록 주석을 사용할 수 있다.</p>
<p>YAML파일의 키 이름에서는 추가 변환이 수행되지 않는다. 각 객체 계층으로부터 플랫 문자열 키를 가져오기 위해 &#39;-&#39;로 연결하기만 하면 된다.</p>
<p>그러므로 계층적 설정은 다음과 같이 이루어진다:</p>
<pre><code class="language-yaml">mymodule:
  performance:
    shouldbefast: true</code></pre>
<p>이것은 <code>mymodule-performance-shouldbefast</code>키에 <code>true</code>를 지정한 것으로 파싱된다. </p>
<h4 id="프로젝트-수준"><strong>프로젝트 수준</strong></h4>
<hr>
<p>캘리퍼 벤치마크 프로젝트에서 언제나 오버라이딩되는 설정 그룹이 있다면, 그것들을 프로젝트 수준 구성 파일로 지정하는 것이 좋다. 이 파일은 일반적으로 기본 구성 파일 (과 커스텀 사용자 모듈과 관련된 커스텀 설정)에 정의된 하위 설정들로 구성된다.</p>
<p>이 프로젝트 수준 구성 파일은 두 가지 방식으로 계층에 포함될 수 있다: </p>
<ul>
<li><p>워크스페이스 디렉토리의 <code>caliper.yaml</code> 파일에서 설정을 정의한다.</p>
</li>
<li><p>또는 앞서 설명한 우선 순위가 더 높은 위치 중 하나(예: 커맨드라인 argument 또는 환경변수)를 사용하여 <code>caliper-projectconfig</code> 설정 키를 통해 구성 파일 경로를 명시적으로 설정한다. </p>
</li>
<li><p>커맨드 라인 접근방법:</p>
<pre><code>  caliper launch manager \
  --caliper-workspace yourworkspace/ \
  --caliper-benchconfig yourconfig.yaml \
  --caliper-networkconfig yournetwork.yaml \
  --Caliper-ProjectConfig mypath/project1-config.yaml</code></pre></li>
<li><p>환경변수 접근방법: </p>
</li>
</ul>
<pre><code>    export CALIPER_PROJECTCONFIG=mypath/project1-config.yaml
    caliper launch manager \
        --caliper-workspace yourworkspace/ \
        --caliper-benchconfig yourconfig.yaml \
        --caliper-networkconfig yournetwork.yaml</code></pre><p>프로젝트 수준 설정은 아래 설명될 설정들을 오버라이딩 할 수 있다. </p>
<h4 id="사용자-수준"><strong>사용자 수준</strong></h4>
<hr>
<p>여러 캘리퍼 벤치마크 프로젝트에서 동일한 설정을 오버라이딩 하고 있다면 공통 설정을 사용자 수준 구성 파일로 발췌하는 것을 추천한다. 사용자 수준 구성 파일을 계층에 포함시키기 위해서는 그 경로를 위에서 설명한 우선순위가 높은 위치 중 하나를 사용해 <code>caliper-userconfig</code> 설정키로 지정한다.</p>
<ul>
<li>커맨드 라인 접근방법:<pre><code>    caliper launch manager \
    --caliper-workspace yourworkspace/ \
    --caliper-benchconfig yourconfig.yaml \
    --caliper-networkconfig yournetwork.yaml \
    --Caliper-UserConfig ~/.config/my-caliper-config.yaml</code></pre></li>
<li>환경변수 접근방법:<pre><code>    export CALIPER_USERCONFIG=~/.config/my-caliper-config.yaml
    caliper launch manager \
      --caliper-workspace yourworkspace/ \
      --caliper-benchconfig yourconfig.yaml \
      --caliper-networkconfig yournetwork.yaml</code></pre></li>
<li>구성 파일 접근방법 (프로젝트 수준 구성 파일):<pre><code>    caliper:
      userconfig: ~/.config/my-caliper-config.yaml
  # additional settings</code></pre></li>
</ul>
<h4 id="머신-수준"><strong>머신 수준</strong></h4>
<hr>
<p>여러 사용자가 동일한 워크스테이션을 사용하고, 캘리퍼 프로젝트 및 유저 간 공통 설정을 공유하기 원한다면 머신 수준 구성 파일을 계층에 포함시킬 수 있다. 이것은 위에서 설명한 우선순위 높은 위치 중 하나를 사용하여 <code>caliper-machineconfig</code> 설정키를 통해 경로를 지정할 수 있다.</p>
<ul>
<li>커맨드 라인 접근방식:<pre><code>    caliper launch manager \
    --caliper-workspace yourworkspace/ \
    --caliper-benchconfig yourconfig.yaml \
    --caliper-networkconfig yournetwork.yaml \
    --Caliper-MachineConfig /etc/config/caliper.yaml</code></pre></li>
<li>환경변수 접근방식:<pre><code>  export CALIPER_MACHINECONFIG=/etc/config/caliper.yaml
  caliper launch manager \
      --caliper-workspace yourworkspace/ \
      --caliper-benchconfig yourconfig.yaml \
      --caliper-networkconfig yournetwork.yaml</code></pre></li>
<li>구성 파일 접근방식 (프로젝트 또는 유저 수준 구성 파일):<pre><code>  caliper:
      machineconfig: /etc/config/caliper.yaml
  # additional settings</code></pre></li>
</ul>
<h4 id="기본-구성"><strong>기본 구성</strong></h4>
<hr>
<p>기본/대체 구성 파일은 합리적인 대체 값들과 캘리퍼 모듈에 의해 사용되는 각 설정들에 대한 문서를 정의하는 캘리퍼 관련 패키지와 함께 제공된다. 이 구성 파일은 지원되는 설정 위치 중 가장 낮은 우선순위를 가진다.</p>
<h2 id="가능한-설정">가능한 설정</h2>
<hr>
<blockquote>
<p>현재 지원되는 런타임 구성 설정은 자체 문서화된 <a href="https://github.com/hyperledger/caliper/blob/master/packages/caliper-core/lib/common/config/default.yaml">기본 구성 파일</a>을 언제나 참조하자.</p>
</blockquote>
<h3 id="기본설정">기본설정</h3>
<hr>
<table>
<thead>
<tr>
<th>키</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>caliper-benchconfig</td>
<td>테스트 워커(들), 테스트 라운드, 모니터를 기술하는 벤치마크 구성파일로의 경로</td>
</tr>
<tr>
<td>caliper-networkconfig</td>
<td>SUT와 상호작용하기 위해 필요한 정보들을 포함한 네트워크 구성 파일로의 경로</td>
</tr>
<tr>
<td>caliper-machineconfig</td>
<td>머신 수준 구성 파일 경로. 워크스페이스의 상대경로일 수 있다.</td>
</tr>
<tr>
<td>caliper-projectconfig</td>
<td>프로젝트 수준 구성 파일 경로. 워크스페이스의 상대경로일 수 있다.</td>
</tr>
<tr>
<td>caliper-userconfig</td>
<td>사용자 수준 구성 파일 경로. 워크스페이스의 상대경로일 수 있다.</td>
</tr>
<tr>
<td>caliper-workspace</td>
<td>모든 구성 정보를 가지고 있는 워크스페이스 디렉토리</td>
</tr>
<tr>
<td>caliper-progress-reporting-enabled</td>
<td>캘리퍼 매니저 프로세스에서 트랜잭션 완료 진행률 표시를 활성화하는지 지정하는 boolean 값</td>
</tr>
<tr>
<td>caliper-progress-reporting-interval</td>
<td>캘리퍼 진행 업데이트 빈도를 지정하기 위해 사용하는 숫자 값(ms)</td>
</tr>
</tbody></table>
<br>

<h3 id="바인딩-설정">바인딩 설정</h3>
<hr>
<table>
<thead>
<tr>
<th>키</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>caliper-bind-args</td>
<td>바인딩 커맨드(예: npm install)에 전달하는 추가 매개변수</td>
</tr>
<tr>
<td>caliper-bind-cwd</td>
<td>바인딩 커맨드(예: npm install)에 사용할 CWD(현재 워킹디렉토리)</td>
</tr>
<tr>
<td>caliper-bind-file</td>
<td>기본 파일을 오버라이딩할 커스텀 바인딩 구성 파일 경로</td>
</tr>
<tr>
<td>caliper-bind-sut</td>
<td><code>&lt;SUT type&gt;:&lt;SDK version&gt;</code> 형식으로 지정하는 바인딩할 SUT</td>
</tr>
</tbody></table>
<br>

<h3 id="리포팅-설정">리포팅 설정</h3>
<hr>
<table>
<thead>
<tr>
<th>키</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>caliper-report-charting-hue</td>
<td>차트 <a href="https://www.npmjs.com/package/color-scheme">색배합(color scheme)</a>을 구성하기 위한 HUE 값</td>
</tr>
<tr>
<td>caliper-report-charting-scheme</td>
<td>차트 색상 성에 사용되는 <a href="https://www.npmjs.com/package/color-scheme">색배합</a> 메서드</td>
</tr>
<tr>
<td>caliper-report-charting-transparency</td>
<td>차트에 사용되는 투명도 [0..1]</td>
</tr>
<tr>
<td>caliper-report-options</td>
<td><code>fs.writeFile</code>에 전달되는 추가 객체</td>
</tr>
<tr>
<td>caliper-report-path</td>
<td>생성되는 리포트 파일의 절대 또는 워크스페이스부터의 상대 경로</td>
</tr>
<tr>
<td>caliper-report-precision</td>
<td>리포트 수치의 정밀도(유효 숫자)</td>
</tr>
</tbody></table>
<br>

<h3 id="로깅-설정">로깅 설정</h3>
<hr>
<table>
<thead>
<tr>
<th>키</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>caliper-logging-formats-align</td>
<td>동일한 위치에 정렬하기 위해 메시지 앞에 탭 구분 기호를 추가한다.</td>
</tr>
<tr>
<td>caliper-logging-formats-attributeformat-&lt;attribute&gt;</td>
<td>로그 메시지 속성 <code>&lt;attribute&gt;</code>에 포맷팅 문자열을 지정한다.</td>
</tr>
<tr>
<td>caliper-logging-formats-json</td>
<td>로그가 JSON 형식으로 직렬화(be serialized)되어야 함을 가리킨다.</td>
</tr>
<tr>
<td>caliper-logging-formats-label</td>
<td>모든 메시지에 지정된 라벨을 붙인다. 분산된 워커 시나리오에서 유용하다.</td>
</tr>
<tr>
<td>caliper-logging-formats-pad</td>
<td>로그 수준 문자열이 동일한 길이가 되도록 채운다(pad).</td>
</tr>
<tr>
<td>caliper-logging-formats-timestamp</td>
<td>특정한 형식으로 메시지에 타임스탬프를 붙인다.</td>
</tr>
<tr>
<td>caliper-logging-formats-colorize-all</td>
<td>모든 로그 메시지 속성이 채색되어야 함을 가리킨다.</td>
</tr>
<tr>
<td>caliper-logging-formats-colorize-&lt;attribute&gt;</td>
<td>메시지 속성 <code>&lt;attribute&gt;</code> 가 채색되어야 함을 가리킨다.</td>
</tr>
<tr>
<td>caliper-logging-formats-colorize-colors-&lt;level&gt;</td>
<td><code>&lt;level&gt;</code> 수준에서 로그 메시지 색상을 설정한다.</td>
</tr>
<tr>
<td>caliper-logging-targets-&lt;target&gt;-enabled</td>
<td><code>&lt;target&gt;</code>이 활성화되는지 여부를 설정한다.</td>
</tr>
<tr>
<td>caliper-logging-template</td>
<td>placeholders를 통해 메시지 구조를 지정한다.</td>
</tr>
</tbody></table>
<br>

<h3 id="워커-관리-설정">워커 관리 설정</h3>
<hr>
<table>
<thead>
<tr>
<th>키</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>caliper-worker-communication-method</td>
<td>매니저와 워커 간 통신 타입을 나타낸다.</td>
</tr>
<tr>
<td>caliper-worker-communication-address</td>
<td>분산된 워커 관리를 위해 사용되는 MQTT 브로커 주소</td>
</tr>
<tr>
<td>caliper-worker-pollinterval</td>
<td>새 사용가능한 워커 폴링 간격(ms)</td>
</tr>
<tr>
<td>caliper-worker-remote</td>
<td>워커가 분산 모드로 작동하는지를 나타낸다.</td>
</tr>
</tbody></table>
<br>

<h3 id="벤치마크-단계-설정">벤치마크 단계 설정</h3>
<hr>
<table>
<thead>
<tr>
<th>키</th>
<th>설정</th>
</tr>
</thead>
<tbody><tr>
<td>caliper-flow-only-end</td>
<td>네트워크 구성 파일에서 종료 커맨드 스크립트만 수행할지 여부를 나타낸다.</td>
</tr>
<tr>
<td>caliper-flow-only-init</td>
<td>벤치마크 초기화(init) 단계만 수행할지 여부를 나타낸다.</td>
</tr>
<tr>
<td>caliper-flow-only-install</td>
<td>벤치마크에서 스마트 컨트랙트 설치 단계만 수행할지 여부를 나타낸다.</td>
</tr>
<tr>
<td>caliper-flow-only-start</td>
<td>네트워크 구성파일에서 시작 커맨드 스크립트만 수행할지 여부를 나타낸다.</td>
</tr>
<tr>
<td>caliper-flow-only-test</td>
<td>벤치마크의 테스트 단계만 수행할지를 나타낸다.</td>
</tr>
<tr>
<td>caliper-flow-skip-end</td>
<td>네트워크 구성파일 종료 커맨드 스크립트를 건너뛸지 여부를 나타낸다.</td>
</tr>
<tr>
<td>caliper-flow-skip-init</td>
<td>벤치마크의 초기화 단계를 건너뛸지를 나타낸다.</td>
</tr>
<tr>
<td>caliper-flow-skip-install</td>
<td>벤치마크의 스마트 컨트랙트 설치 단계를 건너뛸지를 나타낸다.</td>
</tr>
<tr>
<td>caliper-flow-skip-start</td>
<td>네트워크 구성 파일의 시작 커맨드 스크립트를 건너뛸지를 나타낸다.</td>
</tr>
<tr>
<td>caliper-flow-skip-test</td>
<td>벤치마크의 테스트 단계를 건너뛸지를 나타낸다.</td>
</tr>
</tbody></table>
<br>

<h3 id="인증-설정">인증 설정</h3>
<hr>
<table>
<thead>
<tr>
<th>키</th>
<th>설정</th>
</tr>
</thead>
<tbody><tr>
<td>caliper-auth-prometheus-username</td>
<td>기존 프로메테우스 서버로 인증하는 데 사용하기 위한 기본 인증 사용자이름</td>
</tr>
<tr>
<td>caliper-auth-prometheus-password</td>
<td>기존 프로메테우스 서버로 인증하기 위한 기본 인증 비밀번호</td>
</tr>
<tr>
<td>caliper-auth-prometheuspush-username</td>
<td>기존 프로메테우스 푸쉬 게이트웨이로 인증하는 데 사용하기 위한 기본 인증 사용자이름</td>
</tr>
<tr>
<td>caliper-auth-prometheuspush-password</td>
<td>기존 프로메테우스 푸쉬 게이트웨이로 인증하기 위한 기본 인증 비밀번호</td>
</tr>
</tbody></table>
<br>

<h3 id="패브릭-어댑터-설정">패브릭 어댑터 설정</h3>
<hr>
<table>
<thead>
<tr>
<th>키</th>
<th>설정</th>
</tr>
</thead>
<tbody><tr>
<td>caliper-fabric-countqueryasload</td>
<td>쿼리를 워크로드로 셀지 여부를 나타낸다. 즉, 생성될 리포트가 쿼리를 포함할지를 나타낸다.</td>
</tr>
<tr>
<td>caliper-fabric-gateway-eventstrategy</td>
<td>사용할 이벤트 전략을 설정한다.</td>
</tr>
<tr>
<td>caliper-fabric-gateway-localhost</td>
<td>패브릭 게이트웨이 API내부에서 로컬호스트를 기본값으로 사용할지를 나타낸다.</td>
</tr>
<tr>
<td>caliper-fabric-gateway-querystrategy</td>
<td>사용할 쿼리 전략을 설정한다.</td>
</tr>
<tr>
<td>caliper-fabric-gateway-enabled</td>
<td>패브릭 게이트웨이 기반 SDK API를 사용할지 여부를 가리킨다.</td>
</tr>
<tr>
<td>caliper-fabric-latencythreshold</td>
<td>이벤트 소스의 주어진 백분율을 기반으로 보고된 트랜잭션 커밋 시간을 결정한다.</td>
</tr>
<tr>
<td>caliper-fabric-loadbalancing</td>
<td>자동 로드밸런싱이 적용되는 방법을 결정한다.</td>
</tr>
<tr>
<td>caliper-fabric-overwritegopath</td>
<td>GOPATH 환경변수를 임시로 워크스페이스 디렉토리로 설정할지 여부를 가리킨다.</td>
</tr>
<tr>
<td>caliper-fabric-sleepafter-createchannel</td>
<td>채널 생성 후 휴면 시간(ms)</td>
</tr>
<tr>
<td>caliper-fabric-sleepafter-joinchannel</td>
<td>채널 가입 후 휴면 시간(ms)</td>
</tr>
<tr>
<td>caliper-fabric-sleepafter-instantiatecontract</td>
<td>컨트랙트 인스턴스화 후 휴면 시간(ms)</td>
</tr>
<tr>
<td>caliper-fabric-timeout-contractinstantiate</td>
<td>컨트랙트 인스턴스화의 보증 부분에 대한 타임아웃 시간(ms)</td>
</tr>
<tr>
<td>caliper-fabric-timeout-contractinstantiateevent</td>
<td>컨트랙트 인스턴스화 결과 이벤트 수신에 대한 타임아웃 시간(ms)</td>
</tr>
<tr>
<td>caliper-fabric-timeout-invokeorquery</td>
<td>트랜잭션 호출 또는 쿼리에 사용하는 기본 타임아웃(ms)</td>
</tr>
<tr>
<td>caliper-fabric-verify-proposalresponse</td>
<td>수신한 제안 리스폰스를 검증할지 여부를 가리킨다.</td>
</tr>
<tr>
<td>caliper-fabric-verify-readwritesets</td>
<td>보증자가 반환한 읽기-쓰기 세트가 일치하는지를 검증할지 여부를 나타낸다.</td>
</tr>
</tbody></table>
<br>

<h2 id="라이선스">라이선스</h2>
<p>The Caliper codebase is released under the Apache 2.0 license. Any documentation developed by the Caliper Project is licensed under the Creative Commons Attribution 4.0 International License. You may obtain a copy of the license, titled CC-BY-4.0, at <a href="http://creativecommons.org/licenses/by/4.0/">http://creativecommons.org/licenses/by/4.0/</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Rate Controllers]]></title>
            <link>https://velog.io/@hh_0705/caliper-rate-controllers</link>
            <guid>https://velog.io/@hh_0705/caliper-rate-controllers</guid>
            <pubDate>Sun, 09 Jan 2022 07:57:01 GMT</pubDate>
            <description><![CDATA[<h1 id="rate-controllers"><a href="https://hyperledger.github.io/caliper/v0.4.2/rate-controllers/">Rate Controllers</a></h1>
<p>트랜잭션이 블록체인 시스템에 입력되는 비율은 성능 테스트 내의 키 펙터이다. 지정된 비율로 트랜잭션을 보내거나 특정한 프로필을 따르도록 하는 것이 적절할 수 있다. 캘리퍼는 사용자가 사용자 정의 로딩 메커니즘에서 성능을 테스트할 수 있도록 사용자 정의 레이트 컨트롤러 사양을 허용한다. 유저는 자신만의 레이트 컨트롤러를 지정하거나 아래 디폴트 옵션 중 하나를 선택할 수 있다 : </p>
<ul>
<li>Fixed rate (고정 비율)</li>
<li>Fixed feedback rate (고정 피드백 비율)</li>
<li>Fixed load (고정 부하)</li>
<li>Maximum rate (최대 비율)</li>
<li>Linear rate (선형 비율)</li>
<li>Composite rate (복합 비율)</li>
<li>Zero rate (영비율)</li>
<li>Record rate (기록 비율)</li>
<li>Replay rate (반복 비율)</li>
</ul>
<p>사용자 지정 레이트 컨트롤러 구현은 <a href="#%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EC%B6%94%EA%B0%80">커스텀 컨트롤러 추가</a>를 참조한다.</p>
<h2 id="fixed-rate-고정-비율">Fixed rate (고정 비율)</h2>
<hr>
<p>고정 비율 컨트롤러는 가장 기본적인 컨트롤러로, 컨트롤러가 지정되지 않으면 사용되는 디폴트 옵션이기도 하다. 이는 인풋 트랜잭션을 TPS로 지정된 고정 간격으로 보낸다.</p>
<h3 id="옵션과-사용">옵션과 사용</h3>
<p>고정 비율 컨트롤러는 레이트 컨트롤러 <code>type</code>을 <code>fixed-rate</code> 문자열로 설정함으로써 지정된다. </p>
<p>컨트롤러 옵션은 아래를 포함한다: </p>
<ul>
<li><code>tps</code> : 모든 워커들이 SUT로 트랜잭션을 누적 전송하는 비율</li>
</ul>
<p>10 TPS를 가지는 고정 비율 컨트롤러는 아래 옵션으로 지정된다 : </p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;fixed-rate&quot;,
  &quot;opts&quot;: {
      &quot;tps&quot; : 10
  }
}</code></pre>
<h2 id="fixed-feedback-rate-고정-피드백-비율">Fixed feedback rate (고정 피드백 비율)</h2>
<hr>
<p>고정 피드백 비율 컨트롤러는 고정 비율의 확대 버전으로, 마찬가지로 인풋 트랜잭션을 고정 간격으로 전송한다. 종료되지 않은 트랜잭션이 각 워커의 정의된 종료되지 않은 트랜잭션 시간을 초과하면, 오랜 시간 휴면하여 일시적으로 인풋 트랜잭션 전송을 중지한다.</p>
<h3 id="옵션과-사용-1">옵션과 사용</h3>
<p>고정 피드백 비율 컨트롤러는 레이트 컨트롤러 <code>type</code>을 <code>fixed-feedback-rate</code>로 설정하여 지정가능하다. </p>
<p>컨트롤러 옵션은 아래를 포함한다:</p>
<ul>
<li><code>tps</code> : 모든 워커들이 SUT로 트랜잭션을 누적 전송하는 비율</li>
<li><code>transactionLoad</code> : SUT의 최대 트랜잭션 부하로, 워커들이 트랜잭션 전송을 일시 중지하게 되는 부하값</li>
</ul>
<p>100TPS와 SUT의 최대 트랜잭션 부하 100을 가진 고정 피드백 비율 컨트롤러는 아래 컨트롤러 옵션으로 지정된다 :</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;fixed-feedback-rate&quot;,
  &quot;opts&quot;: {
      &quot;tps&quot; : 100,
      &quot;transactionLoad&quot;: 100
  }
}</code></pre>
<h2 id="fixed-load-고정-부하">Fixed load (고정 부하)</h2>
<hr>
<p>고정 부하 비율 컨트롤러는 타겟 부하(백로그 트랜잭션)에서 테스트를 구동하기 위한 컨트롤러다. 이 컨트롤러는 구동 TPS를 수정하여 시스템 내에서 정의된 트랜잭션 백로그를 유지하는 것을 목표로 한다. 결과는 트랜잭션 부하를 유지하면서 가질 수 있는 가능한 최대 TPS를 보여준다.</p>
<h3 id="옵션과-사용-2">옵션과 사용</h3>
<p>고정 부하 비율 컨트롤러는 레이트 컨트롤러 <code>type</code>을 <code>fixed-load</code>로 설정하여 지정가능하다. </p>
<p>컨트롤러 옵션은 아래를 포함한다:</p>
<ul>
<li><code>startingTps</code> : 모든 워커들에 의해 트랜잭션이 SUT에 누적 전송되는 초기 비율</li>
<li><code>transactionLoad</code> : SUT에서 처리 중인 트랜잭션 수. 유지되어야 하는 값이다.</li>
</ul>
<p>시작 TPS 100에서 5 트랜잭션 부하 유지를 목표로 하는 고정 부하 컨트롤러는 아래 컨트롤러 옵션으로 지정된다 :</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;fixed-load&quot;,
  &quot;opts&quot;: {
    &quot;transactionLoad&quot;: 5,
    &quot;startingTps&quot;: 100
  }
}</code></pre>
<h2 id="maximum-rate-최대-비율">Maximum rate (최대 비율)</h2>
<hr>
<p>최대 비율 컨트롤러는 워커를 SUT 오버로딩 없이 달성가능한 최대 비율까지 구동하기 위한 컨트롤러이다. 이 컨트롤러는 워커의 구동 TPS를 최대화하는 것을 목표로 한다. 이는 구동 TPS를 증가시키고, TPS 감소가 목격되면 다시 뒤로 물러는 방법으로 진행된다. 여기서 TPS 감소는 시스템 오버로드를 가리킨다.</p>
<p>여기서 얻어지는 TPS는 txUpdate 사이클 사이에서 평가된다. 이는 그 시점이 TPS 결과가 사용가능해지는 시점이기 때문이다. 컨트롤러 안정성을 강화시키기 위해 TPS 비율 안정을 보장하는 최소 샘플 간격이 고려되어야 한다.</p>
<p>이 컨트롤러의 동작은 역치에 도달할 때 까지 각 워커의 달성가능한 최대 비율까지 천천히 증가함에 주의해라. 이는 라운드 평균 결과를 왜곡할 수 있는 유의미한 워밍업 단계가 있다는 뜻이다. Prometheus 쿼리나 Grafana 시각화를 사용해 달성가능한 결과를 분석하는 것이 추천된다.</p>
<h3 id="옵션과-사용-3">옵션과 사용</h3>
<p>최대 비율 컨트롤러는 레이트 컨트롤러 <code>type</code>을 <code>maximum-rate</code>로 설정하여 지정가능하다. </p>
<p>컨트롤러 옵션은 아래를 포함한다:</p>
<ul>
<li><code>tps</code> : 시작 TPS</li>
<li><code>step</code> : 각 간격에서 증가하는 TPS. &quot;물러서기&quot; 에서 이 스텝 사이즈는 TPS 증가를 다시 시도하기 전에 자동으로 축소된다.</li>
<li><code>sampleInterval</code> : 달성가능한 TPS 비율 안정을 보장하기 위한 스텝 간 최소 간격</li>
<li><code>includeFailed</code> : 컨트롤러 내 달성가능한 TPS 분석이 실패한 트랜잭션을 포함하는지를 가리키는 불리언 플래그(기본값 <code>true</code>)</li>
</ul>
<p>스타팅 TPS 100, TPS 스텝 사이즈 5, 최소 샘플 간격 20초를 가진 최대 비율 컨트롤러는 아래 컨트롤러 옵션으로 지정된다 :</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;maximum-rate&quot;,
  &quot;opts&quot;: {
    &quot;tps&quot;: 100,
    &quot;step&quot;: 5,
    &quot;sampleInterval&quot;: 20,
    &quot;includeFailed&quot;: true
  }
}</code></pre>
<h2 id="linear-rate-선형-비율">Linear rate (선형 비율)</h2>
<hr>
<p>시스템 성능 한계 탐색은 대개 부하 강도를 증가시키며 여러 번 측정을 실시함으로써 이루어진다. 하지만 이 방법으로 시스템의 티핑 토인트를 찾는 것은 쉽지 않고, 이 방법은 좀 더 시행착오 방식에 가깝다. </p>
<p>선형 비율 컨트롤러는 시작 및 종료 TPS 값 사이에서 점진적으로 (선형적으로) TPS 비율을 변화시킬 수 있다. 이는 증가하는 방식과 감소하는 방식 모두에서 가능하다. 이것은 시스템 성능에 영향을 미치는 워크로드 비율을 더 쉽게 찾을 수 있게 해 준다. </p>
<p>선형 비율 컨트롤러는 기간-기반 및 트랜잭션 수-기반 라운드 둘 다에서 사용할 수 있다. </p>
<h3 id="옵션과-사용-4">옵션과 사용</h3>
<p>선형 비율 컨트롤러는 레이트 컨트롤러 <code>type</code>을 <code>linear-rate</code>로 설정하여 지정가능하다. </p>
<p>컨트롤러 옵션은 아래를 포함한다:</p>
<ul>
<li><code>startingTps</code> : 라운드 시작 시점에 모든 워커가 SUT에 누적 전송하는 트랜잭션 비율</li>
<li><code>finishingTps</code> : 라운드 종료 시점에 모든 워커가 SUT에 누적 전송하는 트랜잭션 비율</li>
</ul>
<p>아래 예시는 벤치마크 라운드 동안 트랜잭션 부하를 25TPS에서 75TPS까지 점진적으로 변화시키는 레이트 컨트롤러를 지정한다:</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;linear-rate&quot;,
  &quot;opts&quot;: {
    &quot;startingTps&quot;: 25,
    &quot;finishingTps&quot;: 75
    }
}</code></pre>
<p><strong>참고</strong> : 고정 비율 컨트롤러와 유사하게, 이 컨트롤러는 사용가능한 워커 사이에서 워크로드(작업 부하)를 나눈다. 따라서 이 구성에서 지정되는 비율은 누적 비율이고, 각 워커에 대한 비율이 아니다. 워커 다섯을 이용해 위 구성을 사용하면 각 워커의 비율은 5TPS에서 시작해 15TPS에서 종료된다. 합산해서 [25~75] TPS 부하를 생성한다. </p>
<h2 id="composite-rate-복합-비율">Composite rate (복합 비율)</h2>
<hr>
<p>캘리퍼의 벤치마크 라운드는 싱글 레이트 컨트롤러와 연관되어 있다. 하지만 싱글 레이트 컨트롤러는 고급(advanced) 워커 동작 모델링에 충분하지 않다. 더욱이, 이러한 동작에 대한 새로운 레이트 컨트롤러 구현은 번거롭고 오류가 발생하기 쉽다. 대부분의 경우 복잡한 워커 동작은 여러개의 간단한 단계로 나눌 수 있다.</p>
<p>따라서, 복합 비율 컨트롤러는 여러 &quot;간단한&quot; 비율 컨트롤러 구성을 한 라운드에서 가능하게 한다. 이는 기존 레이트 컨트롤러 구현의 재사용을 촉진한다. 복합 비율 컨트롤러에서는 지정된 가중치에 따라 자동적으로 주어진 컨트롤 간 전환이 이루어진다. (예시에서 구성 디테일을 살펴 보자).</p>
<h3 id="옵션과-사용-5">옵션과 사용</h3>
<p>복합 비율 컨트롤러는 레이트 컨트롤러 <code>type</code>을 <code>composite-rate</code>로 설정하여 지정가능하다. </p>
<p>컨트롤러 옵션은 아래를 포함한다:</p>
<ul>
<li><p><code>weights</code> : &quot;숫자 같은&quot; 값(숫자 또는 숫자 문자열)의 배열. <code>rateControllers</code> 프로퍼티에서 정의되는 레이트 컨트롤러들과 연관되어 그 가중치를 지정한다. </p>
<p>가중치의 합은 <code>1</code>이 되지 않아도 된다. 이는 가중치가 단위(unit) 길이 벡터로 표준화되기 때문이다. 따라서 주어진 구성에 대해 가장 직관적인 방식으로 가중치를 지정할 수 있다. 즉, 가중치는 지속 시간, 트랜잭션 수, 또는 비율에 대응될 수 있다.</p>
<p>위 예시에서 가중치는 비율(2:1:2)에 대응되었다. 가중치의 정확한 의미는 벤치마크 라운드가 지속기간-기반(duration-based)인지 트랜잭션 수-기반(transaction number-based)인지에 따라 결정된다. 위 컨트롤러 정의가 지속 시간 5분인 라운드에 사용된다면, 첫 2분간은 트랜잭션이 100TPS로 제출되고, 다음 1분간 300 TPS로 제출되었다가 마지막 2분 간은 200TPS로 제출될 것이다.</p>
<p>이 배열에서 가중치 0 또한 허용된다. 하나 이상의 컨트롤러 가중치를 0으로 설정하는 것은 구성 파일에서 실제로 컨트롤러를 제거하지 않고 컨트롤러를 제거/사용 불가로 만드는 편리한 방법이다.</p>
</li>
<li><p><code>rateControllers</code> : 임시 레이트 컨트롤러 사양 배열. 구성 방법은 개별 레이트 컨트롤러 문서를 확인해야 한다. 지정된 레이트 컨트롤러 수는 지정된 가중치 수와 같아야 한다.</p>
</li>
<li><p><code>logChange</code> : 지정된 레이트 컨트롤러 간 전환이 로깅되는지를 가리키는 <code>boolean</code> 값.</p>
</li>
</ul>
<p>아래 예시는 트랜잭션 제출 비율로 다양한 진폭을 가진 구형파 함수를 정의한 것이다. 이것은 서로 다른 TPS 설정을 가진 고정 비율 컨트롤러 간 전환으로 구성되어 설정이 쉽다:</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;composite-rate&quot;,
  &quot;opts&quot;: {
    &quot;weights&quot;: [2, 1, 2],
    &quot;rateControllers&quot;: [
      {
        &quot;type&quot;: &quot;fixed-rate&quot;,
        &quot;opts&quot;: {&quot;tps&quot; : 100}
      },
      {
        &quot;type&quot;: &quot;fixed-rate&quot;,
        &quot;opts&quot;: {&quot;tps&quot; : 300}
      },
      {
        &quot;type&quot;: &quot;fixed-rate&quot;,
        &quot;opts&quot;: {&quot;tps&quot; : 200}
      }
    ],  
    &quot;logChange&quot;: true
  }
}</code></pre>
<p><strong>주의!</strong> 복합 비율 컨트롤러는 지정된 &quot;하위 컨트롤러들&quot;을 거의 투영한다. 이는 해당 컨트롤러들이 &quot;가상화된&quot; 라운드에 놓이기 때문에 가능하다. 이는 컨트롤러들에게 다음 사항에 대해 &quot;거짓말&quot; 한다는 것을 뜻한다: </p>
<ul>
<li>(지속 시간 기반 라운드에서) 라운드 지속 시간</li>
<li>(트랜잭션 수 기반 라운드에서) 제출되는 총 트랜잭션 수</li>
<li>라운드 시작 시간</li>
<li>제출되는 다음 트랜잭션의 인덱스.
가까운 시간 내에 완료된 트랜잭션의 결과는 하위 컨트롤러에 있는 그대로 전파되므로, 새로 활성화된 하위 컨트롤러의 첫 몇 호출에 대해서는 가상화 라운드에 속하지 않은 최근 결과를 수신할 수 있다.</li>
</ul>
<p>기억에 의존하지 않는, 즉 컨트롤 로직이 전체 라운드 프로퍼티나 과거 트랜잭션 결과에 의존하지 않는 컨트롤러에는 가상화가 영향을 미치지 않는다. 하지만, 다른 컨트롤러들은 가상화 접근 방식 때문에 이상한(아마도 일시적인) 동작을 보일 수 있다. 예를 들어, <a href="https://hyperledger.github.io/caliper/v0.4.2/rate-controllers/#pid-rate">PID 컨트롤러</a>의 로직은 트랜잭션 백로그에 의존한다. </p>
<h2 id="zero-rate-영비율">Zero rate (영비율)</h2>
<hr>
<p>이 컨트롤러는 라운드 지속 기간 동안 워크로드 생성을 멈춘다.</p>
<h3 id="옵션과-사용-6">옵션과 사용</h3>
<p>한 라운드에 이 컨트롤러를 단독으로 사용하는 것은 의미가 없다. 하지만 복합 비율 컨트롤러 내부에 블록을 생성하기 위해 사용할 수 있다. <strong>영비율 컨트롤러는 오직 지속기간-기반 라운드에서만 사용될 수 있다!</strong></p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;composite-rate&quot;,
  &quot;opts&quot;: {
    &quot;weights&quot;: [30, 10, 10, 30],
    &quot;rateControllers&quot;: [
      {
        &quot;type&quot;: &quot;fixed-rate&quot;,
        &quot;opts&quot;: {&quot;tps&quot; : 100}
      },
      {
        &quot;type&quot;: &quot;fixed-rate&quot;,
        &quot;opts&quot;: {&quot;tps&quot; : 500}
      },
      {
        &quot;type&quot;: &quot;zero-rate&quot;,
        &quot;opts&quot;: { }
      },
      {
        &quot;type&quot;: &quot;fixed-rate&quot;,
        &quot;opts&quot;: {&quot;tps&quot; : 100}
      }
    ],  
    &quot;logChange&quot;: true
  }
}</code></pre>
<p>위 예시가 지속기간 80초를 가진 라운드 정의에 사용된다고 가정해 보자(직관적인 가중치 지정에 주의하자). 이 케이스는 최초 30초의 일반 워크로드 이후 10초 간 집약적인 워크로드가 따라오고, 이후 10초간의 쿨다운 기간이 있는 식으로 이어진다.</p>
<p>이 컨트롤러는 레이트 컨트롤러 <code>type</code>을 <code>zero-rate</code>로 설정하여 지정가능하고, 추가 구성을 요구하지 않는다.</p>
<h2 id="record-rate-기록-비율">Record rate (기록 비율)</h2>
<hr>
<p>이 레이트 컨트롤러는 다른 (임시) 컨트롤러 주변에서 데코레이터 역할을 한다. 이것의 목적은 각 트랜잭션이 제출되는, 즉 트랜잭션이 &quot;하위 컨트롤러&quot;에 의해 &quot;활성화(enabled)&quot;되는, 시간을 (라운드 시작을 기준으로) 기록하는 것이다. </p>
<p>아래 예시는 하위 고정 비율 컨트롤러가 트랜잭션을 활성화하는 시간을 기록한다(자세한 내용은 예시 아래의 가능한 옵션을 살펴보자):</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;record-rate&quot;,
  &quot;opts&quot;: {
    &quot;rateController&quot;: {
      &quot;type&quot;: &quot;fixed-rate&quot;,
      &quot;opts&quot;: {&quot;tps&quot; : 100}
    },
    &quot;pathTemplate&quot;: &quot;../tx_records_client&lt;C&gt;_round&lt;R&gt;.txt&quot;,
    &quot;outputFormat&quot;: &quot;TEXT&quot;,
    &quot;logEnd&quot;: true
  }
}</code></pre>
<p>이 컨트롤러는 레이트 컨트롤러 <code>type</code>을 <code>record-rate</code>로 설정하여 지정가능하고, 가능한 옵션들 (<code>opts</code> 프로퍼티)는 다음과 같다:</p>
<ul>
<li><p><code>rateController</code> : 임시 레이트 컨트롤러 사양.</p>
</li>
<li><p><code>pathTemplate</code> : 기록된 시간이 저장될 파일 경로 템플릿. 이 경로는 절대 경로 또는 루트 캘리퍼 디렉토리에 대한 상대경로가 될 수 있다.</p>
<p>이 템플릿은 특별한 &quot;variables/placeholders&quot;를 가지고 있어야 한다. 이것들은 특정 환경 프로퍼티들을 참조할 수 있다(아래 비고를 보자). 사용가능한 placeholders는 다음과 같다:</p>
<ul>
<li><code>&lt;C&gt;</code> : 레이트 컨트롤러를 사용하는 현재 워커의 1-based index를 위한 placeholder </li>
<li><code>&lt;R&gt;</code> : 레이트 컨트롤러를 사용하는 현재 라운드의 1-based index를 위한 placeholder</li>
</ul>
</li>
<li><p><code>outputFormat</code> : 선택사항(optional). 기록이 저장될 포맷을 결정한다. 기본 값은 <code>&quot;TEXT&quot;</code>이다. 현재 지원하는 포맷들은 다음과 같다:</p>
<ul>
<li><code>&quot;TEXT&quot;</code> : 각 저장된 타이밍이 분리된 줄의 텍스트로 저장된다.</li>
<li><code>&quot;BIN_BE&quot;</code> : Big Endian encoding 바이너리 포맷.</li>
<li><code>&quot;BIN_LE&quot;</code> : Little Endian encoding 바이너리 포맷.</li>
</ul>
</li>
<li><p><code>logEnd</code> : 선택사항. 로그가 파일에 기록되는지 여부를 가리킨다. 기본 값은 <code>false</code>이다.</p>
</li>
</ul>
<p><strong>템플릿 플레이스홀더:</strong> 캘리퍼는 동일한 동작을 하는 여러 라운드와 여러 워커를 정의하는 간결한 방법을 제공하므로, 워커와 라운드 기록을 구분하는 것이 중요하다. 따라서, 출력 파일 경로는 라운드와 워커 인덱스를 위한 플레이스홀더를 포함할 수 있다 이것은 각 라운드의 각 워커에서 자동으로 resolve된다. 그렇지 않으면, 각 워커가 동일한 파일을 작성해 타이밍과 트랜잭션 ID 간 심각한 충돌이 일어난다.</p>
<p><strong>텍스트 포맷:</strong> 레이트 컨트롤러는 아래 포맷으로 기록을 저장한다(일정한 10TPS 비율을 가정하고 실제 타이밍 노이즈는 무시한다). 로우 <code>i</code>는 <code>i</code>번째 트랜잭션과 대응된다:</p>
<pre><code class="language-json">100
200
300
...</code></pre>
<p><strong>바이너리 포맷:</strong> 바이너리 형식은 X 개의 기록을 X+1 UInt32 숫자로 인코딩한다 (숫자 하나는 배열 길이에 대한 것이고, 나머지는 배열 엘리먼트에 대한 것이다). Little Endian 또는 Big Endian 인코딩이 가능하다: </p>
<pre><code class="language-json">Offset: |0      |4      |8      |12      |16      |...     
Data:   |length |1st    |2nd    |3rd     |4th     |...   </code></pre>
<h2 id="replay-rate-반복-비율">Replay rate (반복 비율)</h2>
<hr>
<p>좋은 벤치마크의 가장 중요한 측면 중 하나는 반복성이다. 즉, 필요할 때 마다 결정적인 방식으로 재실행할 수 있어야 한다. 그러나, 어떤 벤치마크들은 워크로드(예: 유저의 동작)을 확룔적 분포 함수로 정의한다. 이것은 실용적인 관점에서 두 가지 문제를 가진다:</p>
<ol>
<li>반복성: 주어진 확률분포에 대한 랜덤 샘플링은 벤치마크 재 실행 간 다를 수 있다, 이것은 플랫폼 간 비교를 믿기 어렵게 만든다.</li>
<li>효율성: 복잡한 확률 분포 샘플링은 추가 런타임 오버헤드를 발생시킨다. 이것은 부하 비율을 제한하여, 원래 지정한 워크로드에 대한 부하를 일으킬 수 있다. </li>
</ol>
<p>이 레이트 컨트롤러는 오프라인으로 생성된 고정 트랜잭션 로드 프로파일을 재생하여 위와 같은 문제를 완화시키려는 목적이 있다. 이 방법으로 프로파일은 벤치마크 실행 외부에서 한 번만 생성되고, 최소 오버헤드 제약을 가진 동일한 타이밍으로 언제든지 재생될 수 있다.</p>
<p>이 컨트롤러의 간단한 사용 방법은 레코드 컨트롤러로 생성된 트랜잭션 기록을 재생하는 것이다. 하지만 잘 구성된 추적 파일이 이 컨트롤러의 유일한 요구 사항이므로, 모든 툴이나 메서드를 트랜잭션 로드 프로파일 생성에 사용할 수 있다.</p>
<p>아래 예시는 동일 워커-의존적 워크로드 프로파일을 재생하는 레이트 컨트롤러를 지정한다(자세한 사항은 예시 아래에 있는 가능한 옵션들을 보자):</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;replay-rate&quot;,
  &quot;opts&quot;: {
    &quot;pathTemplate&quot;: &quot;../tx_records_client&lt;C&gt;.txt&quot;,
    &quot;inputFormat&quot;: &quot;TEXT&quot;,
    &quot;logWarnings&quot;: true,
    &quot;defaultSleepTime&quot;: 50
    }
}</code></pre>
<p>이 컨트롤러는 레이트 컨트롤러 <code>type</code>을 <code>replay-rate</code>로 설정하여 지정가능하고, 가능한 옵션들 (<code>opts</code> 프로퍼티)는 다음과 같다:</p>
<ul>
<li><p><code>pathTemplate</code> : 트랜잭션 타이밍이 재생될 파일 경로 템플릿. 이 경로는 절대 경로 또는 루트 캘리퍼 디렉토리에 대한 상대경로가 될 수 있다.</p>
<p>이 템플릿은 특별한 &quot;variables/placeholders&quot;를 가지고 있어야 한다. 이것들은 특정 환경 프로퍼티들을 참조할 수 있다. 사용가능한 placeholders는 다음과 같다:</p>
<ul>
<li><code>&lt;C&gt;</code> : 레이트 컨트롤러를 사용하는 현재 워커의 1-based index를 위한 placeholder </li>
<li><code>&lt;R&gt;</code> : 레이트 컨트롤러를 사용하는 현재 라운드의 1-based index를 위한 placeholder</li>
</ul>
</li>
<li><p><code>inputFormat</code> : 선택사항(optional). 트랜잭션 타이밍이 저장될 포맷을 결정한다. 기본 값은 <code>&quot;TEXT&quot;</code>이다. 현재 지원하는 포맷들은 다음과 같다:</p>
<ul>
<li><code>&quot;TEXT&quot;</code> : 각 저장된 타이밍이 분리된 줄의 텍스트로 저장된다.</li>
<li><code>&quot;BIN_BE&quot;</code> : Big Endian encoding 바이너리 포맷.</li>
<li><code>&quot;BIN_LE&quot;</code> : Little Endian encoding 바이너리 포맷.</li>
</ul>
</li>
<li><p><code>logWarnings</code> : 선택사항. 재생할 기록이 더 없을 때 로그를 남기는지, 그래서 <code>defaultSleepTime</code>가 연속된 트랜잭션 사이에서 사용되는지를 가리킨다. 기본값은 false이다. </p>
</li>
<li><p><code>defaultSleepTime</code> : 선택사항. 벤치마크 실행이 지정된 레코딩보다 길 때 트랜잭션 간 sleep 시간을 결정한다. 기본값은 20ms이다.</p>
</li>
</ul>
<p><strong>기록에 대하여:</strong>
지속기간-기반 벤치마크 실행을 사용할 때 특별히 주의해야 한다. 기록에서 지정한 것보다 많은 트랜잭션을 발행할 가능성이 있기 때문이다. 이런 케이스를 위한 안전 조치는 <code>defaultSleepTime</code> 옵션이다. 이것은 오직 결과에 대한 추가 성능 분석 수행 이전에 제거할 수 있는 적은 수의 트랜잭션에만 영향을 끼치는 실행의 마지막 몇 순간에만 일어나야 한다.</p>
<p>추천되는 접근 방식은 트랜잭션 수-기반 라운드 구성이다. 반복해야 하는 트랜잭션 수가 미리 알려져있기 때문이다. 워커의 수가 워커에 의해 제출되는 실제 트랜잭션 수에 영향을 준다는 점에 주의하자.</p>
<h2 id="커스텀-컨트롤러-추가">커스텀 컨트롤러 추가</h2>
<hr>
<p>캘리퍼의 내장 컨트롤러가 아닌 레이트 컨트롤러를 사용하는 것도 가능하다. 테스트 구성 파일에서 레이트 컨트롤러를 지정할 때 (<a href="https://hyperledger.github.io/caliper/v0.4.2/architecture/">아키텍처 문서</a>를 보자) <code>type</code>과 <code>opts</code> 속성을 지정해주어야 한다.</p>
<p><code>type</code> 속성을 설정해 아래 기준을 만족하는 커스텀 js 파일을 가리키도록 할 수 있다:</p>
<ol>
<li><p>파일 또는 모듈이 아래 파라미터들을 가지는 <code>createRateController</code> 펑션을 export한다: </p>
<ol>
<li><p><code>TestMessage</code> 파라미터 : 구성 파일의 <code>opts</code> attribute set의 <code>object</code> representation이다. 레이트 컨트롤러의 커스텀 설정들을 표함한다.</p>
</li>
<li><p><code>TransactionStatisticsCollector</code> object : 현재 워커 트랜잭션 통계에 대한 레이트 컨트롤러 액세스를 제공하는 객체</p>
</li>
<li><p><code>number</code> 타입인 <code>workerIndex</code> 파라미터: 레이트 컨트롤러를 사용하는 워커 프로세스의 0-based index.</p>
<p>함수는 반드시 아래 기준을 만족하는 객체를 반환해야 한다(즉, 해당 레이트 컨트롤러 인스턴스를 리턴해야 한다.)</p>
</li>
</ol>
</li>
<li><p><code>createRateController</code>에 의해 리턴되는 객체는 반드시 <code>/packages/caliper-core/lib/rate-control/rateInterface.js</code>인터페이스를 구현해야 한다. 즉, 반드시 아래 async function을 제공해야 한다.</p>
<ol>
<li><code>applyRateControl</code> : 원하는 시간 동안 (비동기 방식으로) 실행을 &quot;blocking&quot; 하여 실제 레이트 컨트롤을 수행한다.</li>
<li><code>end</code> : 라운드 종료 시 획득한 자원을 제거한다.</li>
</ol>
</li>
</ol>
<p>아래 예시는 컨트롤 역할을 수행하지 않는 레이트 컨트롤의 완전한 구현이다. 즉, 프로그램 실행이 허용하는 한 빠른 트랜잭션 제출을 허용한다 (경고. 많은 워커 프로세스로 실행되는 이 구현은 백엔드 네트워크에 쉽게 오버로드를 줄 수 있으므로 주의해서 사용해야 한다).</p>
<pre><code class="language-javascript">/*
* Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

&#39;use strict&#39;;

const RateInterface = require(&#39;path-to-caliper/caliper-core/lib/rate-control/rateInterface.js&#39;);

/**
 * Rate controller for allowing uninterrupted workload generation.
 *
 * @property {object} options The user-supplied options for the controller. Empty.
 */
class MyRateController extends RateInterface{
    /**
     * Initializes the rate controller instance.
     * @param {TestMessage} testMessage The testMessage passed for the round execution
     * @param {TransactionStatisticsCollector} stats The TX stats collector instance.
     * @param {number} workerIndex The 0-based index of the worker node.
     * @param {number} roundIndex The 0-based index of the current round.
     * @param {number} numberOfWorkers The total number of worker nodes.
     * @param {object} roundConfig The round configuration object.
     */
    constructor(testMessage, stats, workerIndex) {
        super(testMessage, stats, workerIndex);
    }

    /**
     * Doesn&#39;t perform any rate control.
     * @async
     */
    async applyRateControl() {
        // no sleeping is needed, allow the transaction invocation immediately
    }

    /**
     * Notify the rate controller about the end of the round.
     * @async
     */
    async end() { 
        // nothing to dispose of
    }
}

/**
 * Factory for creating a new rate controller instance.
 * @param {TestMessage} testMessage start test message
 * @param {TransactionStatisticsCollector} stats The TX stats collector instance.
 * @param {number} workerIndex The 0-based index of the worker node.
 *
 * @return {RateInterface} The new rate controller instance.
 */
function createRateController(testMessage, stats, workerIndex) {
    return new MyRate(testMessage, stats, workerIndex);
}

module.exports.createRateController = createRateController;</code></pre>
<p>이 구현을 캘리퍼 디렉토리 옆에 있는 <code>myRateController.js</code> 파일에 저장한다고 가정해 보자 (즉, 디렉토리와 파일이 같은 레벨에 있다). 이 테스트 구성 파일에서 아래 방식과 같이 레이트 컨트롤러를 (구성 계층에서 요구되는 위치로) 설정할 수 있다.</p>
<pre><code class="language-yaml">rateControl:
  # relative path from the Caliper directory
- type: ../myRateController.js
  # empty options
  opts: </code></pre>
<h2 id="라이선스">라이선스</h2>
<p>The Caliper codebase is released under the Apache 2.0 license. Any documentation developed by the Caliper Project is licensed under the Creative Commons Attribution 4.0 International License. You may obtain a copy of the license, titled CC-BY-4.0, at <a href="http://creativecommons.org/licenses/by/4.0/">http://creativecommons.org/licenses/by/4.0/</a>.</p>
]]></description>
        </item>
    </channel>
</rss>