<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hyeseong-dev.log</title>
        <link>https://velog.io/</link>
        <description>어제보다 오늘 그리고 오늘 보다 내일...</description>
        <lastBuildDate>Wed, 05 Jul 2023 00:13:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hyeseong-dev.log</title>
            <url>https://images.velog.io/images/hyeseong-dev/profile/7ffa61ee-0c3e-47bc-bd06-b735e8ec6776/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hyeseong-dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hyeseong-dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[프로그래머스(ft. 다항식 더하기)]]></title>
            <link>https://velog.io/@hyeseong-dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4ft.-%EB%8B%A4%ED%95%AD%EC%8B%9D-%EB%8D%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyeseong-dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4ft.-%EB%8B%A4%ED%95%AD%EC%8B%9D-%EB%8D%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 05 Jul 2023 00:13:33 GMT</pubDate>
            <description><![CDATA[<h1 id="다항식-더하기">다항식 더하기</h1>
<h2 id="문제-설명">문제 설명</h2>
<p>한 개 이상의 항의 합으로 이루어진 식을 다항식이라고 합니다. 다항식을 계산할 때는 동류항끼리 계산해 정리합니다. 덧셈으로 이루어진 다항식 polynomial이 매개변수로 주어질 때, 동류항끼리 더한 결괏값을 문자열로 return 하도록 solution 함수를 완성해보세요. 같은 식이라면 가장 짧은 수식을 return 합니다.</p>
<h3 id="제한사항">제한사항</h3>
<ul>
<li><p>0 &lt; polynomial에 있는 수 &lt; 100</p>
</li>
<li><p>polynomial에 변수는 &#39;x&#39;만 존재합니다.</p>
</li>
<li><p>polynomial은 양의 정수, 공백, ‘x’, ‘+&#39;로 이루어져 있습니다.</p>
</li>
<li><p>항과 연산기호 사이에는 항상 공백이 존재합니다.</p>
</li>
<li><p>공백은 연속되지 않으며 시작이나 끝에는 공백이 없습니다.</p>
</li>
<li><p>하나의 항에서 변수가 숫자 앞에 오는 경우는 없습니다.</p>
</li>
<li><p>&quot; + 3xx + + x7 + &quot;와 같은 잘못된 입력은 주어지지 않습니다.</p>
</li>
<li><p>0으로 시작하는 수는 없습니다.</p>
</li>
<li><p>문자와 숫자 사이의 곱하기는 생략합니다.</p>
</li>
<li><p>polynomial에는 일차 항과 상수항만 존재합니다.</p>
</li>
<li><p>계수 1은 생략합니다.</p>
</li>
<li><p>결괏값에 상수항은 마지막에 둡니다.</p>
</li>
<li><p>0 &lt; polynomial의 길이 &lt; 50</p>
</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<table>
<thead>
<tr>
<th>polynomial</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;3x + 7 + x&quot;</td>
<td>&quot;4x + 7&quot;</td>
</tr>
<tr>
<td>&quot;x + x + x&quot;</td>
<td>&quot;3x&quot;</td>
</tr>
</tbody></table>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<h4 id="입출력-예-1">입출력 예 #1</h4>
<ul>
<li>&quot;3x + 7 + x&quot;에서 동류항끼리 더하면 &quot;4x + 7&quot;입니다.</li>
</ul>
<h4 id="입출력-예-2">입출력 예 #2</h4>
<ul>
<li>&quot;x + x + x&quot;에서 동류항끼리 더하면 &quot;3x&quot;입니다.</li>
</ul>
<h3 id="python-코드">python 코드</h3>
<pre><code class="language-python">def solution(polynomial):
    xnum = 0
    const = 0
    for c in polynomial.split(&#39; + &#39;):
        if c.isdigit():
            const+=int(c)
        else:
            xnum = xnum+1 if c==&#39;x&#39; else xnum+int(c[:-1])
    if xnum == 0:
        return str(const)
    elif xnum==1:
        return &#39;x + &#39;+str(const) if const!=0 else &#39;x&#39;
    else:
        return f&#39;{xnum}x + {const}&#39; if const!=0 else f&#39;{xnum}x&#39;


params = [
    &quot;x + x + x&quot;, &quot;3x&quot;
    &quot;3x + 7 + x&quot;, &quot;4x + 7&quot;
]

result = solution(params[0])
print(result)</code></pre>
<h3 id="php-코드">PHP 코드</h3>
<pre><code class="language-php">function solution($polynomial) {
    // 다항식을 &#39; + &#39; 기준으로 분리합니다.
    $terms = explode(&quot; + &quot;, $polynomial);

    // x 항의 계수와 상수항을 저장할 변수를 초기화합니다.
    $xnum = 0;
    $const = 0;

    // 각 항을 순회하며 x 항과 상수항을 분리하여 계산합니다.
    foreach ($terms as $term) {
        if (is_numeric($term)) { // 항이 숫자인 경우(상수항)
            $const += intval($term);
        } else { // 항이 x를 포함하는 경우
            // 항이 &#39;x&#39;인 경우 계수는 1, 그렇지 않은 경우 숫자 부분을 계수로 사용합니다.
            $xnum += ($term == &#39;x&#39;) ? 1 : intval(substr($term, 0, -1));
        }
    }

    // x 항의 계수와 상수항에 따라 결과 문자열을 반환합니다.
    if ($xnum == 0) {
        return strval($const); // x 항이 없는 경우 상수항만 반환합니다.
    } elseif ($xnum == 1) {
        // x 항의 계수가 1인 경우, 상수항이 0인지 아닌지에 따라 다르게 반환합니다.
        return $const != 0 ? &#39;x + &#39;.strval($const) : &#39;x&#39;;
    } else {
        // x 항의 계수가 1보다 큰 경우, 상수항이 0인지 아닌지에 따라 다르게 반환합니다.
        return $const != 0 ? strval($xnum).&#39;x + &#39;.strval($const) : strval($xnum).&#39;x&#39;;
    }
}

// 테스트
$test = &quot;3x + 7 + x&quot;;
echo solution($test); // 결과 출력</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[크롬 브라우저 Storage]]></title>
            <link>https://velog.io/@hyeseong-dev/%ED%81%AC%EB%A1%AC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-Storage</link>
            <guid>https://velog.io/@hyeseong-dev/%ED%81%AC%EB%A1%AC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-Storage</guid>
            <pubDate>Wed, 28 Jun 2023 06:23:34 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기-앞서">들어가기 앞서</h1>
<p>크롬 브라우저에서 개발자도구를 열고 &#39;Application&#39;탭을 클릭하면 SideBar에서 Storage영역이 있다.
여기서 Local Storage, Session Storage, Cookies가 보이는데 뭔지 각각 설명해줘. 그리고 차이점도 설명해. </p>
<h2 id="local-storage">Local Storage</h2>
<p>이것은 웹 스토리지 API의 일부로, 사용자의 웹 브라우저에 데이터를 영구적으로 저장할 수 있게 합니다. <code>즉, 사용자가 웹 사이트를 닫거나 브라우저를 종료하더라도 데이터가 삭제되지 않습니다</code>. 각 웹 사이트는 <code>도메인당 대략 5MB의 데이터를 저장</code>할 수 있습니다. Local Storage에 저장된 데이터는 클라이언트 측 <code>JavaScript에서 접근</code>할 수 있습니다. <code>Local Storage는 쿠키와는 달리 HTTP 요청과 함께 전송되지 않습니다.</code></p>
<h2 id="session-storage">Session Storage</h2>
<p>이것도 웹 스토리지 API의 일부로, Local Storage와 비슷하지만, 한 가지 중요한 차이점이 있습니다. Session Storage에 저장된 데이터는 페이지 세션이 끝날 때 삭제됩니다. 페이지 세션은 브라우저 탭이 열려 있는 동안 지속됩니다. <code>브라우저를 닫거나 탭을 닫으면 페이지 세션이 끝나며, Session Storage에 저장된 모든 데이터가 삭제됩니다</code>.</p>
<h2 id="cookies">Cookies</h2>
<p>쿠키는 클라이언트 측 및 서버 측에서 모두 접근할 수 있는 작은 데이터 조각입니다. 쿠키는 HTTP 요청 헤더를 통해 서버와 클라이언트 간에 전송됩니다. 쿠키에는 만료 날짜가 있으며, 만료 날짜가 지나면 자동으로 삭제됩니다. <code>만료 날짜가 설정되지 않은 쿠키는 세션 쿠키라고 부르며, 브라우저가 닫힐 때 삭제됩니다.</code></p>
<h2 id="차이점">차이점:</h2>
<h3 id="데이터-수명">데이터 수명</h3>
<ul>
<li>Local Storage의 데이터는 수동으로 삭제되지 않는 한 영구적입니다. </li>
<li>Session Storage의 데이터는 페이지 세션이 끝나면 삭제됩니다. </li>
<li>쿠키의 데이터는 만료 날짜나 세션 종료 때 삭제됩니다.</li>
</ul>
<h3 id="데이터-용량">데이터 용량</h3>
<ul>
<li>Local Storage와 Session Storage는 웹 사이트당 5MB를 저장할 수 있습니다. </li>
<li>쿠키는 웹 사이트당 4KB만 저장할 수 있습니다.</li>
</ul>
<h3 id="데이터-접근">데이터 접근</h3>
<ul>
<li>Local Storage와 Session Storage의 데이터는 웹 브라우저에서만 접근할 수 있습니다. </li>
<li>쿠키의 데이터는 웹 브라우저뿐만 아니라 서버에서도 접근할 수 있습니다.</li>
</ul>
<h3 id="데이터-전송">데이터 전송</h3>
<ul>
<li>Local Storage와 Session Storage의 데이터는 HTTP 요청과 함께 전송되지 않습니다. </li>
<li>쿠키의 데이터는 모든 HTTP 요청과 함께 전송됩니다.</li>
</ul>
<p>이들은 모두 사용자 데이터를 클라이언트 측에 저장하는 방법들이지만, 그들의 사용 사례와 용도는 상황에 따라 다릅니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CI3에서 세션 데이터 처리]]></title>
            <link>https://velog.io/@hyeseong-dev/CI3%EC%97%90%EC%84%9C-%EC%84%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hyeseong-dev/CI3%EC%97%90%EC%84%9C-%EC%84%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Wed, 28 Jun 2023 04:45:53 GMT</pubDate>
            <description><![CDATA[<p>CodeIgniter 3에서 세션 데이터는 기본적으로 파일 시스템에 저장됩니다. 디폴트로 설정된 세션 저장 방식은 파일 시스템을 사용하는 것이지만, 필요에 따라 데이터베이스(DB)나 메모리(memory) 등 다른 저장소를 사용할 수도 있습니다.</p>
<p>일반적으로 CodeIgniter 3에서 세션은 다음 경로에 파일로 저장됩니다:</p>
<pre><code class="language-bash">application/cache/session/</code></pre>
<p>위 경로는 application/config/config.php 파일에서 sess_save_path 설정값으로 지정할 수 있습니다. 기본적으로 위 경로에 세션 파일이 생성되며, 이 파일에 세션 데이터가 저장됩니다.</p>
<p>만약 세션 저장 방식을 데이터베이스(DB)로 변경하고자 한다면, CodeIgniter 3에서는 database 라이브러리를 사용하여 DB를 세션 저장소로 설정할 수 있습니다. 이를 위해 config.php 파일에서 sess_driver 값을 &#39;database&#39;로 설정하고, 데이터베이스 연결 정보를 database.php 파일에 구성해야 합니다.</p>
<p>마지막으로, 메모리(memory)를 세션 저장소로 사용하려면 CodeIgniter 3에서는 추가적인 구성이 필요합니다. 이는 기본적으로 제공되는 저장 방식은 아니며, 사용자 정의 저장 방식을 구현해야 합니다. 이를 위해서는 CodeIgniter의 세션 라이브러리를 확장하고 사용자 정의 드라이버를 작성해야 합니다.</p>
<p>참고 : <a href="http://www.ciboard.co.kr/user_guide/kr/libraries/sessions.html">http://www.ciboard.co.kr/user_guide/kr/libraries/sessions.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[두 리스트 합치기]]></title>
            <link>https://velog.io/@hyeseong-dev/%EB%91%90-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%ED%95%A9%EC%B9%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyeseong-dev/%EB%91%90-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%ED%95%A9%EC%B9%98%EA%B8%B0</guid>
            <pubDate>Sat, 17 Jun 2023 19:18:21 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><img src="https://velog.velcdn.com/images/hyeseong-dev/post/1c9eb3b6-432f-4ff9-b174-ff75230688e8/image.png" alt=""></p>
<h2 id="코드1">코드1</h2>
<ul>
<li>itertools.chain과 sorted 함수를 사용</li>
</ul>
<pre><code class="language-python">from itertools import chain

N = int(input())
list1 = list(map(int, input().split()))
M = int(input())
list2 = list(map(int, input().soplit()))

chained = chain(list1, list2)
_sorted = sorted(chained)
_maped = map(str, _sorted)
result = &#39; &#39;.join(_mapped)
print(result)</code></pre>
<h2 id="코드2">코드2</h2>
<pre><code class="language-python">def input_list():
    # 사용자로부터 정수를 입력받아 리스트의 크기를 결정합니다.
    n = int(input())
    # 사용자로부터 정수들을 입력받아 리스트의 원소로 만듭니다.
    return list(map(int, input().split()))

def merge(list1, list2, result, i, j):
    # 두 개의 정렬된 리스트를 받아서 더 작은 원소부터 차례대로 결과 리스트에 추가합니다.
    # i와 j는 각각 list1과 list2에서 현재 확인 중인 원소의 인덱스를 나타냅니다.
    while i &lt; len(list1) and j &lt; len(list2):
        if list1[i] &lt; list2[j]:
            result.append(list1[i])
            i += 1
        else:
            result.append(list2[j])
            j += 1
    return result, i, j

def append_remaining(list, result, index):
    # 주어진 리스트에 남아있는 원소들을 결과 리스트에 추가합니다.
    # index는 현재 확인 중인 원소의 위치를 나타냅니다.
    while index &lt; len(list):
        result.append(list[index])
        index += 1
    return result

def merge_sorted_lists(list1, list2):
    # 두 개의 정렬된 리스트를 받아서 병합한 결과를 반환합니다.
    result = []
    i = j = 0

    # 두 리스트의 원소들을 병합합니다.
    result, i, j = merge(list1, list2, result, i, j)

    # 각 리스트의 남은 원소들을 결과 리스트에 추가합니다.
    result = append_remaining(list1, result, i)
    result = append_remaining(list2, result, j)

    return result

def print_list(lst):
    # 리스트의 원소들을 공백으로 구분하여 출력합니다.
    for x in lst:
        print(x, end=&#39; &#39;)

# 사용자로부터 두 개의 리스트를 입력받습니다.
list1 = input_list()
list2 = input_list()

# 두 개의 리스트를 병합한 결과를 계산하고 출력합니다.
merged_list = merge_sorted_lists(list1, list2)
print_list(merged_list)
</code></pre>
<h2 id="설명">설명</h2>
<pre><code class="language-python">def input_list():</code></pre>
<p>함수 input_list의 정의를 시작합니다. 이 함수는 사용자로부터 리스트의 크기와 원소들을 입력받아 리스트를 반환합니다.</p>
<pre><code class="language-python">n = int(input())</code></pre>
<p>사용자로부터 입력받은 값을 정수로 변환하여 변수 n에 저장합니다. 이 값은 리스트의 크기를 나타냅니다.</p>
<pre><code class="language-python">return list(map(int, input().split()))</code></pre>
<p>사용자로부터 입력받은 문자열을 공백으로 분리하고, 각 부분을 정수로 변환하여 리스트로 만든 후 반환합니다. 이 값들은 리스트의 원소들입니다.</p>
<pre><code class="language-python">def merge(list1, list2, result, i, j):</code></pre>
<p>함수 merge의 정의를 시작합니다. 이 함수는 두 개의 정렬된 리스트와 결과 리스트, 그리고 두 개의 인덱스를 인수로 받아 결과 리스트에 원소들을 병합합니다.</p>
<pre><code class="language-python">5-9. while i &lt; len(list1) and j &lt; len(list2): ...</code></pre>
<p>두 인덱스가 각 리스트의 크기를 넘지 않는 동안 반복합니다. 두 리스트의 현재 원소를 비교하여 더 작은 원소를 결과 리스트에 추가하고 해당 리스트의 인덱스를 증가시킵니다.</p>
<pre><code class="language-python">return result, i, j</code></pre>
<p>병합한 결과 리스트와 갱신된 두 인덱스를 반환합니다.</p>
<pre><code class="language-python">def append_remaining(list, result, index):</code></pre>
<p>함수 append_remaining의 정의를 시작합니다. 이 함수는 리스트와 결과 리스트, 그리고 인덱스를 인수로 받아 남은 원소들을 결과 리스트에 추가합니다.</p>
<pre><code class="language-python">12-15. while index &lt; len(list): ...</code></pre>
<p>인덱스가 리스트의 크기를 넘지 않는 동안 반복합니다. 현재 인덱스의 원소를 결과 리스트에 추가하고 인덱스를 증가시킵니다.</p>
<pre><code class="language-python">return result</code></pre>
<p>원소들이 추가된 결과 리스트를 반환합니다.</p>
<pre><code class="language-python">def merge_sorted_lists(list1, list2):</code></pre>
<p>함수 merge_sorted_lists의 정의를 시작합니다. 이 함수는 두 개의 정렬된 리스트를 인수로 받아 이를 병합한 결과를 반환합니다.</p>
<pre><code class="language-python">result = []</code></pre>
<p>두 리스트를 병합한 결과를 저장할 빈 리스트를 초기화합니다.</p>
<pre><code class="language-python">i = j = 0</code></pre>
<p>두 리스트의 초기 인덱스를 0으로 설정합니다.</p>
<pre><code class="language-python">result, i, j = merge(list1, list2, result, i, j)</code></pre>
<p>함수 merge를 호출하여 두 리스트의 원소들을 병합하고, 갱신된 인덱스를 받습니다.</p>
<pre><code class="language-python">result = append_remaining(list1, result, i)</code></pre>
<p>함수 append_remaining를 호출하여 첫 번째 리스트의 남은 원소들을 결과 리스트에 추가합니다.</p>
<pre><code class="language-python">result = append_remaining(list2, result, j)</code></pre>
<p>함수 append_remaining를 호출하여 두 번째 리스트의 남은 원소들을 결과 리스트에 추가합니다.</p>
<pre><code class="language-python">return result</code></pre>
<p>병합한 결과 리스트를 반환합니다.</p>
<pre><code class="language-python">def print_list(lst):</code></pre>
<p>함수 print_list의 정의를 시작합니다. 이 함수는 리스트의 원소들을 출력합니다.</p>
<p>25-28.</p>
<pre><code class="language-python">for x in lst: ...</code></pre>
<p>리스트의 각 원소에 대해 반복하며, 원소를 출력합니다. 출력된 원소들 사이에는 공백이 들어갑니다.</p>
<pre><code class="language-python">list1 = input_list()</code></pre>
<p>함수 input_list를 호출하여 사용자로부터 첫 번째 리스트를 입력받습니다.</p>
<pre><code class="language-python">list2 = input_list()</code></pre>
<p>함수 input_list를 호출하여 사용자로부터 두 번째 리스트를 입력받습니다.</p>
<pre><code class="language-python">merged_list = merge_sorted_lists(list1, list2)</code></pre>
<p>함수 merge_sorted_lists를 호출하여 두 리스트를 병합한 결과를 계산합니다.</p>
<pre><code class="language-python">print_list(merged_list)</code></pre>
<p>함수 print_list를 호출하여 병합한 결과 리스트를 출력합니다</p>
<h2 id="php-코드">php 코드</h2>
<pre><code class="language-php">&lt;?php

// 주어진 문자열을 공백 기준으로 분리하여 리스트로 변환하는 함수
function input_list(string $list): array {
    return explode(&#39; &#39;, $list);
}

// 두 개의 리스트를 합치는 함수
// list1과 list2의 원소들을 순서대로 비교하여, 더 작은 값을 result 리스트에 추가
function merge(array $list1, array $list2, array $result, int $i, int $j): array {
    while ($i &lt; count($list1) &amp;&amp; $j &lt; count($list2)) {
        if ($list1[$i] &lt; $list2[$j]) {
            array_push($result, $list1[$i]);
            $i++;
        } else {
            array_push($result, $list2[$j]);
            $j++;
        }
    }
    return array($result, $i, $j);
}

// 리스트의 나머지 원소들을 result에 추가하는 함수
function append_remaining(array $list, array $result, int $index): array {
    while ($index &lt; count($list)) {
        array_push($result, $list[$index]);
        $index++;
    }
    return $result;
}

// 두 개의 정렬된 리스트를 병합하는 함수
// 두 개의 리스트를 병합하여 새로운 정렬된 리스트를 반환
function merge_sorted_lists(array $list1, array $list2): array {
    $result = array();
    $i = $j = 0;
    list($result, $i, $j) = merge($list1, $list2, $result, $i, $j);
    $result = append_remaining($list1, $result, $i);
    $result = append_remaining($list2, $result, $j);
    return $result;
}

// 리스트의 원소들을 출력하는 함수
function print_list(array $lst): void {
    foreach ($lst as $x) {
        echo $x . &quot; &quot;;
    }
}

// 예제 실행
$list1 = input_list(&quot;1 3 5&quot;);
$list2 = input_list(&quot;2 3 6 7 9&quot;);
$merged_list = merge_sorted_lists($list1, $list2);
print_list($merged_list);

?&gt;
</code></pre>
<h3 id="설명-1">설명</h3>
<pre><code class="language-php">$N = intval(trim(fgets(STDIN)));</code></pre>
<p><code>fgets(STDIN)</code>: PHP에서는 <code>fgets(STDIN)</code> 함수를 통해 사용자로부터 표준 입력을 받을 수 있습니다. <strong>STDIN</strong>은 <code>표준 입력 스트림을 나타내는 상수</code>입니다.</p>
<p><strong>사용자로부터 입력?</strong></p>
<p>사용자로부터 입력을 받는다는 것은 여러 다양한 상황에서 사용될 수 있습니다.</p>
<ol>
<li><code>프로그램 설정값 입력</code>: 예를 들어 PHP 스크립트가 실행되면서 어떤 작업을 수행해야 하는데, 그 작업의 세부적인 설정값이 사용자에 따라 달라질 수 있습니다. 이런 경우에는 사용자로부터 직접 설정값을 입력받아서 프로그램이 동작하게 할 수 있습니다.</li>
</ol>
<p>예: &quot;검색할 키워드를 입력하세요: &quot; (사용자가 &#39;피자&#39;라고 입력)</p>
<ol start="2">
<li><code>사용자의 선택 입력</code>: 프로그램이 사용자에게 여러 가지 옵션 중에서 하나를 선택하도록 요청할 수 있습니다. 이 경우에도 사용자로부터의 입력을 받습니다.</li>
</ol>
<p>예: &quot;어떤 파일을 열겠습니까? 1. 사진 2. 문서 3. 동영상&quot; (사용자가 &#39;2&#39;라고 입력)</p>
<ol start="3">
<li><code>게임에서의 사용자 입력</code>: 예를 들어 간단한 텍스트 기반 게임을 만든다면, 사용자로부터 게임 진행에 필요한 입력을 받을 수 있습니다.</li>
</ol>
<p>예: &quot;어느 방향으로 이동하시겠습니까? (w: 위, a: 왼쪽, s: 아래, d: 오른쪽)&quot; (사용자가 &#39;d&#39;라고 입력)</p>
<p>이런 식으로, 프로그램이 어떤 데이터를 필요로 하고, 그 데이터가 실행 시점에 결정되는 경우에는 사용자로부터 직접 입력을 받아서 그 값을 사용합니다.</p>
<p><strong>fget 함수</strong></p>
<ul>
<li><p>fgets() 함수는 파일에서 한 줄을 읽어오는 함수</p>
</li>
<li><p>fgets()는 파일 포인터에서 한 줄을 가져와 문자열로 반환</p>
<ul>
<li>이때 &quot;한 줄&quot;이라는 것은 줄바꿈 문자(&quot;\n&quot;), 반환문자(&quot;\r&quot;), 또는 파일의 끝 EOF를 만날 때까지를 의미</li>
</ul>
</li>
<li><p>첫 번째 매개변수로 파일 핸들을 받습니다. 이 파일 핸들은 fopen() 등의 함수를 통해 열린 <code>파일에 대한 참조</code>입니다.</p>
</li>
<li><p>두 번째 매개변수로는 읽어올 최대 길이를 지정할 수 있습니다</p>
<ul>
<li>fgets()는 이 길이보다 긴 줄을 만나더라도 이 길이만큼의 문자만 읽어옵니다. </li>
</ul>
</li>
<li><p>두 번째 매개변수를 지정하지 않으면 줄의 끝까지 전체를 읽어옵니다.</p>
</li>
<li><p>파일의 끝(EOF)에 도달하거나 오류가 발생하면 fgets() 함수는 FALSE를 반환합니다. 이를 통해 파일 읽기가 정상적으로 이루어졌는지를 확인할 수 있습니다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[카드 역배치]]></title>
            <link>https://velog.io/@hyeseong-dev/%EC%B9%B4%EB%93%9C-%EC%97%AD%EB%B0%B0%EC%B9%98</link>
            <guid>https://velog.io/@hyeseong-dev/%EC%B9%B4%EB%93%9C-%EC%97%AD%EB%B0%B0%EC%B9%98</guid>
            <pubDate>Sat, 17 Jun 2023 14:42:53 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><img src="https://velog.velcdn.com/images/hyeseong-dev/post/4e1c06cf-ed25-4e4d-a926-e363828b8870/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hyeseong-dev/post/6a83ee7b-8d33-4756-b3cb-bb5c1d51c5dd/image.png" alt=""></p>
<h2 id="코드">코드</h2>
<pre><code class="language-php">&lt;?php

$li = range(1, 20);

for ($i = 0; $i &lt; 10; $i++) {
    // In real scenario, this should be read from stdin
    $inputs = explode(&#39; &#39;, trim(fgets(STDIN)));
    $m = (int)$inputs[0];
    $n = (int)$inputs[1];

    $a = array_slice($li, 0, $m - 1);
    $b = array_reverse(array_slice($li, $m - 1, $n - $m + 1));
    $c = array_slice($li, $n);

    $li = array_merge($a, $b, $c);
}

foreach ($li as $item) {
    echo $item . &#39; &#39;;
}

?&gt;
</code></pre>
<h2 id="설명">설명</h2>
<pre><code class="language-php">$li = range(1, 20);</code></pre>
<p>이 부분에서는 PHP의 range 함수를 이용하여 1부터 20까지의 숫자를 갖는 배열을 생성하고 li 변수에 할당합니다.</p>
<pre><code class="language-php">for ($i = 0; $i &lt; 10; $i++) {...}</code></pre>
<p>이 for문은 10번의 반복을 수행합니다. 각 반복마다 특정 범위의 숫자를 역순으로 바꾸는 작업이 이루어집니다.</p>
<pre><code class="language-php">$inputs = explode(&#39; &#39;, trim(fgets(STDIN)));</code></pre>
<p>사용자로부터 두 개의 숫자를 입력받아 공백을 기준으로 분리합니다. 이 두 숫자는 역순으로 바꿀 범위를 결정합니다.</p>
<pre><code class="language-php">$m = (int)$inputs[0];
$n = (int)$inputs[1];</code></pre>
<p>입력받은 두 개의 숫자를 각각 m과 n에 할당합니다.</p>
<pre><code class="language-php">$a = array_slice($li, 0, $m - 1);</code></pre>
<p>배열 li에서 처음부터 m-1까지의 요소를 새 배열 a에 저장합니다.</p>
<pre><code class="language-php">$b = array_reverse(array_slice($li, $m - 1, $n - $m + 1));</code></pre>
<p>배열 li에서 m-1부터 n까지의 요소를 선택하고, 이를 역순으로 만들어 새 배열 b에 저장합니다.</p>
<pre><code class="language-php">$c = array_slice($li, $n);</code></pre>
<p>배열 li에서 n 이후의 모든 요소를 새 배열 c에 저장합니다.</p>
<pre><code class="language-php">$li = array_merge($a, $b, $c);</code></pre>
<p>배열 a, b, c를 순서대로 합쳐 새 배열 li를 만듭니다. 이 때, 배열 b는 이미 역순으로 되어 있으므로, 결과적으로 m부터 n까지의 요소가 역순으로 바뀝니다.</p>
<pre><code class="language-php">foreach ($li as $item) {echo $item . &#39; &#39;;}</code></pre>
<p>최종적으로 만들어진 배열 li의 모든 요소를 순서대로 출력합니다. 요소 사이에는 공백이 들어갑니다. 이 부분에서는 PHP의 foreach 반복문을 이용하고 있습니다.
이 코드를 실행하면, 사용자로부터 10번에 걸쳐 두 개의 숫자를 입력받고, 각 입력마다 해당 범위의 숫자를 역순으로 바꾸는 작업을 수행합니다. 모든 입력이 처리된 후에는 최종적으로 얻어진 배열을 출력합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정돈된 코드 유지하기]]></title>
            <link>https://velog.io/@hyeseong-dev/%EC%A0%95%EB%8F%88%EB%90%9C-%EC%BD%94%EB%93%9C-%EC%9C%A0%EC%A7%80%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyeseong-dev/%EC%A0%95%EB%8F%88%EB%90%9C-%EC%BD%94%EB%93%9C-%EC%9C%A0%EC%A7%80%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 13 Jun 2023 08:52:20 GMT</pubDate>
            <description><![CDATA[<h1 id="🎈-part1">🎈 Part1</h1>
<h2 id="📗-chapter1">📗 Chapter1</h2>
<h3 id="정돈된-코드-유지하기">정돈된 코드 유지하기</h3>
<h3 id="📣-질문">📣 질문</h3>
<ol>
<li><p><code>회사의 코딩 표준</code>에 맞추기 위하여 레이아웃을 바꿔야 하는가? 아니면 원작자의 스타일을 그냥 두는 것이 나은가? 그렇다면 이유는 무엇인가?</p>
</li>
<li><p>코드를 <code>리포매팅해주는 도구</code>는 중요한가? 도구는 당신이 사용하는 언어에 얼마나 의존적인가?</p>
</li>
<li><p>코드의 <code>외관</code>과 <code>설계</code> 중 어떤것이 더 중요한가?</p>
</li>
<li><p>현재 프로젝트에서 <code>코드의 일관성</code>은 어떠한가? 어떻게 개선할 수 있는가?</p>
</li>
<li><p><code>탭 vs 스페이스?</code> 선택한 이유는? 이것은 중요한가?</p>
</li>
<li><p><code>언어의 레이아웃</code>과 <code>명명 규칙</code>을 따르는 것이 중요한가? 아니면 표준 라이브러리와 차별화하여 &#39;자신만의 스타일&#39;을 사용하는 것이 유용한가?</p>
</li>
<li><p>화려한 구문 강조 코드 편집기를 사용하면 색상이 코드의 구조를 나타내준다는 이유로, 코드의 레이아웃에 대해 신경 쓰지 않아도 될까?</p>
</li>
</ol>
<h3 id="🎪내용">🎪내용</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[코드에 신경 쓰기 - p1c1]]></title>
            <link>https://velog.io/@hyeseong-dev/book-%EC%BD%94%EB%93%9C%EC%97%90-%EC%8B%A0%EA%B2%BD-%EC%93%B0%EA%B8%B0-p1c1</link>
            <guid>https://velog.io/@hyeseong-dev/book-%EC%BD%94%EB%93%9C%EC%97%90-%EC%8B%A0%EA%B2%BD-%EC%93%B0%EA%B8%B0-p1c1</guid>
            <pubDate>Mon, 12 Jun 2023 00:06:45 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기-전">들어가기 전</h1>
<p> 개발자가 되기 <code>전</code>에도 개발자로 <code>첫 취직을 하고 나서도</code> 좋은 개발자가 되는 방법에 대해 깊은 성찰 없이 구호만 외쳐왔습니다. 이 책을 읽었을지라도 부단히 노력하고 무의식으로 내재하지 않는 한…. 또 공허한 구호만 될 것이 틀림 없으시면서도.</p>
<p>어쨌든 한 번이고, 두 번이고, 세 번이고 의식하며 <code>훌륭한 프로그래머 되는 법</code>에서 소개하는 내용과 질문에 대해 성찰한다면 분명 level Up! 하는 개발자가 되는 것이고 그렇지 않다면 좋은 유희 거리가 되거나 혹은 이 책정도 읽었으니 조금이나마 훌륭한 프로그래머가 되려고 노력했다고 자신을 위안 삼을지도 모르겠습니다.</p>
<p>책(콘텐츠)을 읽는 시점과 독자가 처한 상황에 따라 긁기 읽히는 것은 각양각색일 것입니다. 저의 경우 CS에 대한 지식이 부족하다는 자조적인 생각과 2년 차가 되었지만 여러 회사를 옮겨 깊이 있는 언어의 공부가 부족하다는 생각 그리고 현재 있는 회사에서 유산 언어와 코드 베이스로 경력을 맞아야 한다는 생각에 긍정적인 마음가짐은 아니었습니다. </p>
<p>처음 2021년 개발자가 되는 것이 내 인생의 목표였다면 이제는 정말 훌륭한 프로그래머가 되는 법에 대해 구체적인 접근법을 통해서 행동으로 하나 하나 실행해야 할 차례라고 듭니다.</p>
<p>책의 일부 내용을 일부 인용하자면, 저자 <code>피트</code>는 <code>평범한 프로그래머와 훌륭한 프로그래머의 차이는 바로 &quot;태도&quot;에 있다. 소프트웨어 회사에서 실제로 일어나는 다양한 제약과 압박 속에서도, 프로다운 접근 방식을 취하고 최고의 코드를 작성하려는 태도로부터 훌륭한 프로그래밍은 태어난다.</code></p>
<p>책은 5개 파트와, 총 39 챕텁로 이루어져 있습니다. 
모든 내용을 소개하기 보다는 글을 정리하면서 하나 하나 정리하고 혹은 미래에 다시 볼 경우 다시 되새김질 할 수 있도록 인상 깊은 내용들만 정리하려 합니다. 
책의 내용을 두서 없는 내 글 실력으로 정리 될 지 모르겠지만 말입니다. </p>
<h1 id="🎈-part1">🎈 Part1</h1>
<h2 id="📗-chapter1">📗 Chapter1</h2>
<h3 id="코드에-신경쓰기">코드에 신경쓰기</h3>
<h3 id="📣-질문">📣 질문</h3>
<ol>
<li>코드에 신경 쓰는가? 자신이 만든 결과물에서 그 점이 어떻게 드러나는가?</li>
<li>프로그래머로서 더 나아지고 싶은가? 가장 노력해야 하는 부분은 어떤 부분인가?</li>
<li>코드에 신경 쓰지 않는다면, 왜 이책을 읽고 있는가?</li>
<li>&#39;좋은 프로그래머가 좋은 코드를 작성한다는 것은 셜록 홈즈가 아니라도 알 수 있는 뻔한 사실이다. 나쁜 프로그래머의 경우는 그렇지 않다&#39;는 문장은 얼마나 정확한가? 좋은 프로그래머가 나쁜 코드를 만들 수도 있는가? 어떠헥 그럴수 있는가?</li>
</ol>
<h3 id="🎪내용">🎪내용</h3>
<p><code>지옥에 보내 버려 할 코드 역시 좋은 의도로 포장되어 있다.</code> 훌륭한 프로그래머가 되기 위해서는 좋은 의도를 뛰어넘어 실제로 코드에 주의를 기울여야한다. 즉, <code>긍정적인 관점</code>과 <code>건전한 태도</code>를 품어야 한다. 일단 이 <code>긍정적인 관점, 건전한 태도</code>라는 단어들이 추상적으로 나에게 와닿는다. 조금더 살펴보자. </p>
<p><code>좋은 프로그래머가 좋은 코드를 작성한다. 그렇다면 좋은 코드란 무엇인가?</code> -&gt; <strong>어떤 코딩 환경에서든 단지 작동하는 것처럼 보이는 코딩은 거부해야 한다. 프로그래머는 올바르게 작동하는 훌륭한 코드를 짜도록 노력해야 한다. (올바른지 작동을 증명하는 적절한 테스트도 갖추어야 한다.)</strong></p>
<p>또한 </p>
<ul>
<li>의도가 드러나야한다.(다른 사람이 쉽게 파악하고 이해 할 수 있어야 한다.)</li>
<li>유지 보수 가능해야한다.(본인과 다른 사람이 추후 쉽게 수정 할 수 있어야한다.)</li>
<li>정확해야한다(문제를 해결했음을 증명하는 모든 단계를 통과할 수 있어야함. 기능이 작동하는 것처럼 보이기만 해서는 안된다.</li>
</ul>
<p>어떤 코드든 간에 해당 코드를 만지기 전보다 더 나아지도록 해야한다.</p>
<p>여기서 <code>더 나아짐</code>이란 무엇일까?</p>
<ol>
<li>더 좋은 구조</li>
<li>더 좋은 테스트</li>
<li>더 쉬운 이해등 </li>
</ol>
<p>코드에 대해 계속해서 주의를 기울이면서 지속적으로 새로운 언어나 문법, 기술을 익혀 나가야한다. 하지만 그 새로운 것들을 실제로 적용하는 것은 적절한 상황에 한정해야 한다. </p>
<p>무엇보다 이처럼 코드에 신경 쓰면서도, 즐거운 프로그래밍을 하는 것을 잊어서는 안된다. 복잡한 문제를 풀기 위해 코드를 수정하는 과정을 즐겨야 한다. 자랑할 만한 소프트웨어를 만들어 내자 .</p>
<p><strong>코드에 대한 감정적 반응은 잘못된 것이 아니다. 훌륭한 결과물을 자랑스러워하거나 더러운 코드에 혐오감을 느끼는 것은 자신이 건전하다는 증거다</strong> </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] php 뽀개기-1]]></title>
            <link>https://velog.io/@hyeseong-dev/TIL-php-%EB%BD%80%EA%B0%9C%EA%B8%B0-1</link>
            <guid>https://velog.io/@hyeseong-dev/TIL-php-%EB%BD%80%EA%B0%9C%EA%B8%B0-1</guid>
            <pubDate>Fri, 02 Jun 2023 05:52:13 GMT</pubDate>
            <description><![CDATA[<h2 id="1-현재-경로-위치-확인">1. 현재 경로 위치 확인</h2>
<pre><code class="language-php">$currentPath = getcwd();
echo &quot;현재 위치: &quot; . $currentPath;</code></pre>
<h2 id="2-k번째-약수">2. k번째 약수</h2>
<p><img src="https://velog.velcdn.com/images/hyeseong-dev/post/e359af48-a3f1-4d72-81f4-7c4fdeb0033f/image.png" alt=""></p>
<h3 id="code-1">Code-1</h3>
<pre><code class="language-php">&lt;?php
$input_result = function ($path) {
    $file = fopen($path, &quot;r&quot;);
    $line = fgets($file);
    fclose($file);
    return array_map(&#39;intval&#39;, explode(&#39; &#39;, $line));
};

$output_result = function ($path) {
    $file = fopen($path, &quot;r&quot;);
    $line = fgets($file);
    fclose($file);
    return intval(trim($line));
};

$kth_divisor = function ($n, $k) {
    $cnt = 0;
    for ($i = 1; $i &lt;= $n; $i++) {
        if ($n % $i == 0) {
            $cnt++;
        }
        if ($cnt == $k) {
            return $i;
        }
    }
    echo &quot;return false&quot; . &quot;\n&quot;;
    return false;
};

$test_kth_divisor = function () use ($input_result, $output_result, $kth_divisor) {
    $COMMON_PATH = &quot;inflearn/python/섹션 2/1. k번째 약수/&quot;;
    $INPUT_PATH = &quot;./&quot; . $COMMON_PATH . &quot;in1.txt&quot;;
    $OUTPUT_PATH = &quot;./&quot; . $COMMON_PATH . &quot;out1.txt&quot;;

    list($n, $k) = $input_result($INPUT_PATH);
    $expected = $output_result($OUTPUT_PATH);
    assert($kth_divisor($n, $k) === $expected);

};

$test_kth_divisor();
echo &quot;Test Pass&quot;;
?&gt;</code></pre>
<h3 id="code-2">Code-2</h3>
<pre><code class="language-php">&lt;?php
class KthDivisorTest
{
    private static $COMMON_PATH;
    private static $numTestCases;

    public function __construct()
    {
        self::$COMMON_PATH = &quot;inflearn/python/섹션 2/1. k번째 약수/&quot;;
        self::$numTestCases = 5;
    }

    private function readInput($path)
    {
        $file = fopen($path, &quot;r&quot;);
        $line = fgets($file);
        fclose($file);
        return array_map(&#39;intval&#39;, explode(&#39; &#39;, $line));
    }

    private function readOutput($path)
    {
        $file = fopen($path, &quot;r&quot;);
        $line = fgets($file);
        fclose($file);
        return intval(trim($line));
    }

    private function kthDivisor($n, $k)
    {
        $cnt = 0;
        for ($i = 1; $i &lt;= $n; $i++) {
            if ($n % $i == 0) {
                $cnt++;
            }
            if ($cnt == $k) {
                return $i;
            }
        }
        return -1;
    }

    private function runTestCases()
    {
        for ($i = 1; $i &lt;= self::$numTestCases; $i++) {
            $inputPath = &quot;./&quot; . self::$COMMON_PATH . &quot;in$i.txt&quot;;
            $outputPath = &quot;./&quot; . self::$COMMON_PATH . &quot;out$i.txt&quot;;

            $input = $this-&gt;readInput($inputPath);
            $expected = $this-&gt;readOutput($outputPath);
            $n = $input[0];
            $k = $input[1];

            $result = $this-&gt;kthDivisor($n, $k);
            assert($result === $expected, &quot;Test case $i failed. n=$n, k=$k, expected=$expected&quot;);
        }
    }

    public function testKthDivisor()
    {
        $this-&gt;runTestCases();
    }
}

$test = new KthDivisorTest();
$test-&gt;testKthDivisor();

?&gt;</code></pre>
<h2 id="isset함수">isset함수</h2>
<pre><code class="language-php">define(&#39;ENVIRONMENT&#39;, isset($_SERVER[&#39;CI_ENV&#39;]) ? $_SERVER[&#39;CI_ENV&#39;] : &#39;production&#39;);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[git log 한글 깨짐 현상]]></title>
            <link>https://velog.io/@hyeseong-dev/git-log-%ED%95%9C%EA%B8%80-%EA%B9%A8%EC%A7%90-%ED%98%84%EC%83%81</link>
            <guid>https://velog.io/@hyeseong-dev/git-log-%ED%95%9C%EA%B8%80-%EA%B9%A8%EC%A7%90-%ED%98%84%EC%83%81</guid>
            <pubDate>Fri, 02 Jun 2023 05:20:46 GMT</pubDate>
            <description><![CDATA[<p><code>https://dlagusgh1.tistory.com/278</code> &lt;-- 링크로 지지고 볶고 해도 안되었다. 
혹시나 싶어 전역 설정 파일을 열어 보니 <code>logOutputEncoding = cp949</code>로 되어 있다 이 경우 git log한 경우 한글 깨짐을 해결하는 방법은 값만 UTF8로 바꾸어 주면 된다. </p>
<h3 id="디테일">디테일</h3>
<p>아래 명령어를 이용하여 전역 설정 파일로 갑니다.</p>
<pre><code class="language-bash">git config --global -e</code></pre>
<p><code>logOutputEncoding = utf-8</code>로 되어 있는지 확인하고 안되어 있다면 동일하게 해줍니다. 
그리고 저장하고 다시 git log하면 해결 될 것입니다. 
안되면 ㅠ.ㅠ </p>
<pre><code>[user]
        email = xxx
        name = xxxx
[credential &quot;http://test.com&quot;]
        provider = generic
[i18n]
        commitEncoding = utf-8
        logOutputEncoding = utf-8
[core]
        pager = less -r
~</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[PHP Type]]></title>
            <link>https://velog.io/@hyeseong-dev/%ED%83%80%EC%9E%85%EC%8B%9C%EC%8A%A4%ED%85%9Cft.-php</link>
            <guid>https://velog.io/@hyeseong-dev/%ED%83%80%EC%9E%85%EC%8B%9C%EC%8A%A4%ED%85%9Cft.-php</guid>
            <pubDate>Wed, 31 May 2023 01:15:44 GMT</pubDate>
            <description><![CDATA[<h1 id="🎈-복합타입">🎈 복합타입</h1>
<p>PHP에서 복합 타입(Composite types)은 여러 개의 단순 타입이나 클래스 타입을 결합하여 새로운 타입을 정의하는 것을 의미합니다. 이는 유니온 타입(Union types)과 교차 타입(Intersection types)으로 구분됩니다.</p>
<h2 id="🎆-교차-타입-intersection-types">🎆 교차 타입 (Intersection types):</h2>
<p>교차 타입은 특정 값이 여러 개의 클래스 타입 선언을 모두 만족시켜야 하는 타입입니다. 이 교차 타입을 구성하는 개별 타입들은 <code>&#39;&amp;&#39;</code> 기호로 연결됩니다. 따라서, T, U, V라는 타입들로 구성된 교차 타입은 <code>T&amp;U&amp;V</code>로 표현됩니다.</p>
<h2 id="🎇-유니온-타입-union-types">🎇 유니온 타입 (Union types):</h2>
<p>유니온 타입은 여러 다른 타입의 값들을 받아들일 수 있는 타입입니다. 유니온 타입을 구성하는 개별 타입들은 &#39;|&#39; 기호로 연결됩니다. 따라서, T, U, V라는 타입들로 구성된 유니온 타입은 T|U|V로 표현됩니다. 만약 타입 중 하나가 교차 타입인 경우, 그것을 DNF(Disjunctive Normal Form, 논리합 정규형)으로 작성하기 위해서는 괄호로 묶어주어야 합니다: <code>T|(X&amp;Y)</code>.</p>
<h2 id="🧨-타입-별칭-type-aliases">🧨 타입 별칭 (Type aliases):</h2>
<p>PHP는 두 가지 타입 별칭을 지원합니다: mixed와 iterable입니다.</p>
<p>mixed는 object|resource|array|string|float|int|bool|null 이라는 유니온 타입에 해당합니다. 즉, mixed 타입은 객체, 리소스, 배열, 문자열, 부동 소수점 숫자, 정수, 불린, null 등 여러 가지 다른 타입의 값들을 받아들일 수 있습니다.</p>
<p>iterable은 Traversable|array라는 유니온 타입에 해당합니다. iterable 타입은 순회 가능한 객체나 배열을 받아들일 수 있습니다.</p>
<p>이러한 타입 별칭을 사용하면 코드를 더 명확하게 표현할 수 있으며, 특정 조건을 충족하는 다양한 타입의 값을 처리할 수 있습니다.</p>
<hr>
<h2 id="결론">결론</h2>
<p><code>PHP 7.3에서는 유니온 타입, 교차 타입, 그리고 mixed 타입 별칭이 공식적으로 지원되지 않습니다.</code> 이러한 타입 기능은 다음 버전에서 소개되었습니다.</p>
<p>유니온 타입과 mixed 타입 별칭: PHP 8.0 버전부터 도입되었습니다.
교차 타입: 2021년 9월까지 PHP는 공식적으로 교차 타입을 지원하지 않았습니다. PHP 8.1에서 도입될 예정이었습니다.
따라서 PHP 7.3에서는 이러한 타입 기능을 사용할 수 없습니다. 그러나 PHP의 타입 시스템은 이후 버전에서 확장되었으며, 이러한 기능은 PHP 개발자들에게 더 강력한 타입 체크와 코드의 가독성 향상을 제공합니다. 이러한 이유로 PHP 최신 버전으로 업그레이드하는 것이 좋습니다.</p>
<hr>
<h3 id="null">Null</h3>
<pre><code class="language-php">&lt;?php
$var = NULL;       
?&gt;</code></pre>
<p>이 코드는 $var 변수에 NULL 값을 할당합니다. 이렇게 하면 $var는 값이 없는 상태가 됩니다.</p>
<p>NULL 타입은 PHP의 타입 시스템의 일부로, 특정 변수가 아무런 값도 가지고 있지 않은 상태를 나타내는 데 사용됩니다. NULL 타입의 변수는 is_null() 함수를 사용하여 확인할 수 있습니다.</p>
<h3 id="converting-to-boolean">Converting to boolean</h3>
<p>아래 값은 <code>false</code>로 평가 됩니다. </p>
<ol>
<li>false</li>
<li>0</li>
<li>0.0 and -0.0</li>
<li>&quot;&quot;, &quot;0&quot;</li>
<li>array with zero elements</li>
<li>PHP의 내장 클래스인 SimpleXML은 빈 XML 요소로부터 생성된 객체를 boolean으로 캐스팅할 때 예외적인 동작을 보이는데, 일반적으로 객체는 true를 반환하지만 SimpleXML 객체는 요소가 실제로 비어 있을 경우 false를 반환합니다.</li>
</ol>
<p><strong>Code-1</strong></p>
<pre><code class="language-php">&lt;?php
$x=TRUE;
$y=FALSE;
$z= $y OR $x;
var_dump((bool) $z)
?&gt;

// result
//php main.php
//bool(false)</code></pre>
<ul>
<li><code>$z= $y OR $x;</code> 이 부분을 <code>$z = ($y OR $x);</code>과 같이 처리 해주거나 혹은 <code>$z = $y || $x;</code>과 같이 연산자를 사용해주면 의도한대로 &#39;true&#39;가 반환된다.</li>
</ul>
<h4 id="톺아보기-1">톺아보기-1</h4>
<p><strong>XML이란?</strong>
XML(Extensible Markup Language)은 데이터를 저장하고 전송하는 데 사용되는 <code>마크업 언어</code>입니다. HTML과 유사한 구조를 가지고 있지만, XML은 <code>데이터를 설명</code>하고 <code>표현</code>하는 데 중점을 두며, 사용자가 태그를 정의하여 고유한 데이터 구조를 만들 수 있습니다.</p>
<p>XML은 다음과 같은 특성을 가집니다:</p>
<p><code>플랫폼에 독립적</code>: XML은 플랫폼과 프로그래밍 언어에 독립적이므로, 서로 다른 시스템 간에 데이터를 쉽게 교환할 수 있습니다.</p>
<p><code>자기 설명적</code>: XML 태그는 데이터를 설명하는 역할을 하므로, XML 문서는 자체적으로 데이터 구조를 표현하고 설명합니다.</p>
<p><code>확장 가능</code>: XML은 사용자가 필요에 따라 새로운 태그를 정의할 수 있으므로, 다양한 요구사항과 상황에 맞게 확장할 수 있습니다.</p>
<p><code>계층적 구조</code>: XML 데이터는 트리 구조로 표현되므로, 복잡한 데이터 관계를 표현하는 데 적합합니다.</p>
<pre><code class="language-php">&lt;?php
var_dump((bool) &quot;&quot;);        // bool(false)
var_dump((bool) &quot;0&quot;);       // bool(false)
var_dump((bool) 1);         // bool(true)
var_dump((bool) -2);        // bool(true)
var_dump((bool) &quot;foo&quot;);     // bool(true)
var_dump((bool) 2.3e5);     // bool(true)
var_dump((bool) array(12)); // bool(true)
var_dump((bool) array());   // bool(false)
var_dump((bool) &quot;false&quot;);   // bool(true)
?&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 18108(ft.rust)]]></title>
            <link>https://velog.io/@hyeseong-dev/%EB%B0%B1%EC%A4%80-18108ft.rust</link>
            <guid>https://velog.io/@hyeseong-dev/%EB%B0%B1%EC%A4%80-18108ft.rust</guid>
            <pubDate>Tue, 30 May 2023 00:51:50 GMT</pubDate>
            <description><![CDATA[<h2 id="case-1">case-1</h2>
<pre><code class="language-rust">use std::io;

fn main() {
    const STANDARD : i32 = 543;
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();
    let mut year = input.split_whitespace();
    let  a: i32 = year.next().unwrap().parse().unwrap();
    print!(&quot;{}&quot;, a - STANDARD);

}
</code></pre>
<h3 id="설명">설명</h3>
<ol>
<li><p><code>const STANDARD : i32 = 543;</code> - 불기 연도와 서기 연도의 차이인 543을 상수 STANDARD로 선언합니다. 이 상수는 i32 형태의 정수입니다.</p>
</li>
<li><p><code>let mut input = String::new();</code> - 빈 문자열을 만들어 input 변수에 저장합니다. mut 키워드는 이 변수를 변경할 수 있게 합니다.</p>
</li>
<li><p><code>io::stdin().read_line(&amp;mut input).unwrap();</code> - 사용자로부터 한 줄을 읽어 input 변수에 저장합니다. <code>unwrap()</code>은 <code>에러 처리를 위한 메소드</code>입니다.</p>
<p>3.1. Rust의 <code>split_whitespace()</code> 메소드는 주어진 문자열을 공백<code>(스페이스, 탭, 줄바꿈 등)</code>을 기준으로 분할하고, 이를 통해 생성된 각 문자열에 대한 <code>이터레이터</code>를 <code>반환</code>합니다.</p>
</li>
</ol>
<p>이터레이터는 컬렉션의 요소를 반복적으로 접근할 수 있는 객체로, <code>next() 메소드</code>를 통해 <code>다음 요소를 가져올 수 있습니다</code>. 이 때 next()는 <code>Option을 반환</code>하며, 컬렉션의 <code>요소가 더 이상 없을 때</code>는 <code>None을 반환</code>합니다. 따라서 unwrap() 메소드를 사용해 Option에서 실제 값을 추출하였습니다.</p>
<p>그리고 <code>parse() 메소드</code>를 이용하여 문자열을 <code>원하는 데이터 타입</code>으로 <code>변환</code>하였습니다. 이 경우에는 <code>i32 타입으로 변환</code>하였습니다.</p>
<ol start="4">
<li><code>let mut year = input.split_whitespace()</code>; - input 문자열을 공백으로 분리하고, 이터레이터를 year 변수에 저장합니다.</li>
</ol>
<h2 id="case-2">case-2</h2>
<p><code>iter()</code>를 사용하는 대신 <code>trim()</code> 메서드를 사용하여 입력 문자열의 앞뒤 공백을 제거합니다. 그리고 parse()를 직접 input 문자열에 적용하여 정수로 변환함. </p>
<pre><code class="language-rust">use std::io;

fn main() {
    const STANDARD: i32 = 543;
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();
    let a: i32 = input.trim().parse().unwrap();
    println!(&quot;{}&quot;, a - STANDARD);
}</code></pre>
<h2 id="case-3">case-3</h2>
<pre><code class="language-rust">use std::io;

const THAI_YEAR_OFFSET: i32 = 543;

fn main() {
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();

    let year: i32 = match input.trim().parse::&lt;i32&gt;() {
        Ok(y) =&gt; y - THAI_YEAR_OFFSET,
        Err(_) =&gt; {
            eprintln!(&quot;유효하지 않은 입력입니다. 올바른 연도를 입력하세요.&quot;);
            return;
        }
    };

    println!(&quot;변환된 연도: {}&quot;, year);
}
</code></pre>
<h2 id="case-4">case-4</h2>
<pre><code class="language-rust">use std::io;

const THAI_YEAR_OFFSET: i32 = 543;

fn main() {
    let year: i32 = read_input(&quot;불기 연도를 입력하세요: &quot;);
    let converted_year = convert_to_ad(year);
    println!(&quot;서기 연도로 변환된 결과: {}&quot;, converted_year);
}

fn read_input(prompt: &amp;str) -&gt; i32 {
    println!(&quot;{}&quot;, prompt);

    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();

    input.trim().parse().unwrap()
}

fn convert_to_ad(year: i32) -&gt; i32{
    year - THAI_YEAR_OFFSET
}</code></pre>
<p>이 코드는 함수를 사용하여 입력을 받고, 연도를 변환하는 로직을 분리하였습니다. read_input 함수는 사용자에게 프롬프트를 출력하고 입력을 받아서 변환된 연도를 반환합니다. convert_to_ad 함수는 받은 불기 연도를 서기 연도로 변환합니다.</p>
<p>main 함수에서는 read_input 함수를 호출하여 사용자로부터 불기 연도를 입력받고, convert_to_ad 함수를 사용하여 연도를 변환한 후 출력합니다.</p>
<p>이러한 함수 분리를 통해 코드를 모듈화하고 가독성을 높일 수 있습니다.</p>
<h2 id="php-code">php code</h2>
<pre><code class="language-php">&lt;?php

$handle = fopen(&quot;php://stdin&quot;, &quot;r&quot;);
$year = intval(fgets($handle));  // 입력받은 불기 연도를 정수로 변환
$year = $year - 543;  // 불기 연도에서 543을 빼서 서기 연도로 변환
echo $year;
fclose($handle);

?&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 10926(ft. rust)]]></title>
            <link>https://velog.io/@hyeseong-dev/%EB%B0%B1%EC%A4%80-10926ft.-rust</link>
            <guid>https://velog.io/@hyeseong-dev/%EB%B0%B1%EC%A4%80-10926ft.-rust</guid>
            <pubDate>Tue, 30 May 2023 00:21:50 GMT</pubDate>
            <description><![CDATA[<h1 id="-성공">??! 성공</h1>
<h2 id="문제">문제</h2>
<p>준하는 사이트에 회원가입을 하다가 joonas라는 아이디가 이미 존재하는 것을 보고 놀랐다. 준하는 놀람을 ??!로 표현한다. 준하가 가입하려고 하는 사이트에 이미 존재하는 아이디가 주어졌을 때, 놀람을 표현하는 프로그램을 작성하시오.</p>
<h2 id="입력">입력</h2>
<p>첫째 줄에 준하가 가입하려고 하는 사이트에 이미 존재하는 아이디가 주어진다. 아이디는 알파벳 소문자로만 이루어져 있으며, 길이는 50자를 넘지 않는다.</p>
<h2 id="출력">출력</h2>
<p>첫째 줄에 준하의 놀람을 출력한다. 놀람은 아이디 뒤에 ??!를 붙여서 나타낸다.</p>
<h2 id="예제-입력-1">예제 입력 1</h2>
<p>joonas</p>
<h2 id="예제-출력-1">예제 출력 1</h2>
<p>joonas??!</p>
<h2 id="예제-입력-2">예제 입력 2</h2>
<p>baekjoon</p>
<h2 id="예제-출력-2">예제 출력 2</h2>
<p>baekjoon??!</p>
<h2 id="제한">제한</h2>
<ul>
<li>시간 제한: 1 초</li>
<li>메모리 제한: 256 MB</li>
<li>제출 수: 177946</li>
<li>정답 수: 89820</li>
<li>맞힌 사람 수: 79172</li>
<li>정답 비율: 51.265%</li>
</ul>
<hr>
<h2 id="풀이">풀이</h2>
<h3 id="케이스-1">케이스-1</h3>
<pre><code class="language-rust">use std::io;

fn main() {
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();
    input = input.trim().to_string(); // 이 줄 추가
    input.push_str(&quot;??!&quot;);
    print!(&quot;{}&quot;, input);
}</code></pre>
<h4 id="설명">설명</h4>
<ol>
<li><p>use std::io;: Rust의 표준 입출력 라이브러리를 가져옵니다.</p>
</li>
<li><p>fn main() {: Rust에서 main 함수는 프로그램의 진입점입니다.</p>
</li>
<li><p>let mut input = String::new();: input이라는 가변 문자열을 선언하고, 초기화합니다.</p>
</li>
<li><p>io::stdin().read_line(&amp;mut input).unwrap();: 표준 입력(stdin)에서 한 줄을 읽어 input 문자열에 저장합니다. .unwrap()는 read_line이 반환하는 Result 타입에서 오류가 발생했는지 확인하고, 오류가 없을 경우에만 값을 반환합니다. 만약 오류가 있다면 프로그램이 panic 상태로 진입하게 됩니다.</p>
</li>
<li><p>input = input.trim().to_string();: 입력받은 문자열의 양쪽 끝에서 공백을 제거합니다. trim 함수는 공백을 제거한 새로운 slice를 반환하기 때문에, to_string 메서드를 사용하여 새로운 String 인스턴스를 생성합니다.</p>
</li>
<li><p>input.push_str(&quot;??!&quot;);: 문자열 input의 뒤에 &quot;??!&quot;를 추가합니다.</p>
</li>
<li><p>print!(&quot;{}&quot;, input);: 최종 결과 문자열을 출력합니다.</p>
</li>
</ol>
<h3 id="케이스-2">케이스-2</h3>
<p><code>format!</code> 매크로를 사용하여 풀이할 수 있습니다. 이 방식은 주어진 형식에 따라 문자열을 만드는데 사용됩니다.  </p>
<pre><code class="language-rust">use std::io;

fn main() {
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();
    let input = input.trim();
    let output = format!(&quot;{}??!&quot;, input);
    print!(&quot;{}&quot;, output);
}
</code></pre>
<h4 id="설명-1">설명</h4>
<p>위의 코드에서는 문자열 input에 &quot;??!&quot;를 추가하는 대신, format! 매크로를 사용하여 두 문자열을 결합한 새로운 문자열 output을 만들었습니다. format! 매크로는 주어진 형식에 따라 문자열을 만듭니다. 여기서는 입력 문자열 뒤에 &quot;??!&quot;를 붙여 새 문자열을 만듭니다. 그런 다음 print! 매크로를 사용하여 output을 출력합니다.</p>
<h3 id="케이스-3">케이스-3</h3>
<p>또 다른 접근 방식으로는 println! 매크로를 직접 사용하는 것이 있습니다. 이렇게 하면 별도의 문자열을 만들지 않고도 입력 문자열 뒤에 &quot;??!&quot;를 바로 추가할 수 있습니다. 아래는 그 예시입니다.</p>
<pre><code class="language-rust">use std::io;

fn main() {
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();
    let input = input.trim();
    println!(&quot;{}??!&quot;, input);
}</code></pre>
<p>위의 코드에서는 println! 매크로를 사용하여 입력 문자열과 &quot;??!&quot;를 바로 결합하고 출력합니다. 이 방법은 매우 직관적이고, 중간에 새로운 문자열을 만들 필요가 없으므로 약간 더 효율적일 수 있습니다.</p>
<h2 id="번외-php">번외 php</h2>
<pre><code class="language-php">
$handle = fopen(&quot;php://stdin&quot;, &quot;r&quot;);
$line = fgets($handle);
$line = trim($line); // 이 줄 추가
echo $line.&quot;??!&quot;;
fclose($handle);
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[php를 이용한 파일업로드 기능]]></title>
            <link>https://velog.io/@hyeseong-dev/php%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9D%BC%EC%97%85%EB%A1%9C%EB%93%9C-%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@hyeseong-dev/php%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9D%BC%EC%97%85%EB%A1%9C%EB%93%9C-%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Thu, 25 May 2023 06:10:35 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기-전">들어가기 전</h2>
<p>php를 이용한 프로젝트에 투입되었으며 php의 OOP에 대한 적응을 위한 간단한 기능 구현을 하려함. </p>
<h2 id="구현-사항">구현 사항</h2>
<ul>
<li>OOP의 다형성을 이용하여 파일 업로드 기능 구현 코드 작성</li>
</ul>
<h2 id="코드">코드</h2>
<ul>
<li>전체 코드 <pre><code class="language-php">&lt;?php
interface FileUploaderInterface {
public function handleFileUpload(): string;
public function isFileNotSelected(): bool;
public function isValidExtension(string $extension): bool;
public function isValidSize(int $size): bool;
public function moveUploadedFile(string $tmp_name, string $target_dir): bool;
}
</code></pre>
</li>
</ul>
<p>interface MessageInterface {
  public function showMessage(string $message, string $color): string;
}</p>
<p>class BaseMessage implements MessageInterface {
  protected function wrapMessage(string $message, string $color): string {
    return &#39;<p style="color: ' . $color . ';">&#39; . $message . &#39;</p>&#39;;
  }</p>
<p>  public function showMessage(string $message, string $color): string {
    return $this-&gt;wrapMessage($message, $color);
  }
}</p>
<p>class ErrorMessage extends BaseMessage {
  // No need to override showMessage() method as it is inherited from BaseMessage
}</p>
<p>class SuccessMessage extends BaseMessage {
  // No need to override showMessage() method as it is inherited from BaseMessage
}</p>
<p>class CustomFileUploader implements FileUploaderInterface {
  protected $allowedExtensions;
  protected $uploadDirectory;
  protected $errorMessage;
  protected $successMessage;</p>
<p>  public function __construct(array $allowedExtensions, string $uploadDirectory, MessageInterface $errorMessage, MessageInterface $successMessage) {
    $this-&gt;allowedExtensions = $allowedExtensions;
    $this-&gt;uploadDirectory = $uploadDirectory;
    $this-&gt;errorMessage = $errorMessage;
    $this-&gt;successMessage = $successMessage;
  }</p>
<p>  public function handleFileUpload(): string {
    if ($_SERVER[&#39;REQUEST_METHOD&#39;] !== &#39;POST&#39;) {
      return &#39;&#39;;
    }</p>
<pre><code>if ($this-&gt;isFileNotSelected()) {
  return $this-&gt;errorMessage-&gt;showMessage(&#39;Please choose a file!&#39;, &#39;red&#39;);
}

$file = $_FILES[&#39;upload&#39;];
$file_name = $file[&#39;name&#39;];
$file_size = $file[&#39;size&#39;];
$file_tmp_name = $file[&#39;tmp_name&#39;];

$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);
$file_ext = strtolower($file_ext);

if (!$this-&gt;isValidExtension($file_ext)) {
  return $this-&gt;errorMessage-&gt;showMessage(&#39;Invalid file type!&#39;, &#39;red&#39;);
}

if (!$this-&gt;isValidSize($file_size)) {
  return $this-&gt;errorMessage-&gt;showMessage(&#39;File too large!&#39;, &#39;red&#39;);
}

$target_dir = $this-&gt;uploadDirectory . &#39;/&#39; . $file_name;

if (!is_dir($this-&gt;uploadDirectory)) {
  mkdir($this-&gt;uploadDirectory, 0755, true);
}

if ($this-&gt;moveUploadedFile($file_tmp_name, $target_dir)) {
  return $this-&gt;successMessage-&gt;showMessage(&#39;File uploaded!&#39;, &#39;green&#39;);
} else {
  return $this-&gt;errorMessage-&gt;showMessage(&#39;Failed to move the uploaded file!&#39;, &#39;red&#39;);
}</code></pre><p>  }</p>
<p>  public function isFileNotSelected(): bool {
    return empty($_FILES[&#39;upload&#39;][&#39;name&#39;]);
  }</p>
<p>  public function isValidExtension(string $extension): bool {
    return in_array($extension, $this-&gt;allowedExtensions);
  }</p>
<p>  public function isValidSize(int $size): bool {
    $maxSize = 1000000;
    return $size &lt;= $maxSize;
  }</p>
<p>  public function moveUploadedFile(string $tmp_name, string $target_dir): bool {
    return move_uploaded_file($tmp_name, $target_dir);
  }
}</p>
<p>$allowedExtensions = [&#39;png&#39;, &#39;jpg&#39;, &#39;jpeg&#39;, &#39;gif&#39;];
$uploadDirectory = &#39;uploads&#39;;
$errorMessage = new ErrorMessage();
$successMessage = new SuccessMessage();
$fileUploader = new CustomFileUploader($allowedExtensions, $uploadDirectory, $errorMessage, $successMessage);
$message = $fileUploader-&gt;handleFileUpload();</p>
<p>?&gt;</p>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>File Upload</title>
</head>

<body>
  <form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" method="post" enctype="multipart/form-data">
    Select image to upload:
    <input type="file" name='upload'>
    <input type="submit" value="Submit" name='submit'>
  </form>

  <?php if (isset($message)) echo $message; ?>
</body>

</html>
```

<h2 id="설명">설명</h2>
<h3 id="fileuploaderinterface">FileUploaderInterface</h3>
<pre><code class="language-php">&lt;?php

interface FileUploaderInterface {
  public function handleFileUpload(): string;
  public function isFileNotSelected(): bool;
  public function isValidExtension(string $extension): bool;
  public function isValidSize(int $size): bool;
  public function moveUploadedFile(string $tmp_name, string $target_dir): bool;
}</code></pre>
<ul>
<li>위의 코드는 <code>FileUploaderInterface</code>라는 인터페이스를 정의하는 부분입니다. 이 인터페이스는 파일 업로드와 관련된 동작을 수행하기 위한 메서드를 선언합니다.</li>
</ul>
<ol>
<li><p><code>handleFileUpload(): string</code>: 파일 업로드를 처리하는 메서드입니다. 이 메서드는 파일 업로드 과정을 수행하고, 결과에 대한 문자열을 반환합니다.</p>
</li>
<li><p><code>isFileNotSelected(): bool</code>: 파일이 선택되지 않았는지 확인하는 메서드입니다. 파일이 선택되지 않은 경우 true를 반환하고, 선택된 경우 false를 반환합니다.</p>
</li>
<li><p><code>isValidExtension(string $extension): bool</code>: 주어진 확장자가 유효한지 확인하는 메서드입니다. 매개변수로 확장자를 받아 해당 확장자가 유효한 경우 true를 반환하고, 그렇지 않은 경우 false를 반환합니다.</p>
</li>
<li><p><code>isValidSize(int $size): bool</code>: 주어진 파일 크기가 유효한지 확인하는 메서드입니다. 매개변수로 파일 크기를 받아 해당 크기가 유효한 경우 true를 반환하고, 그렇지 않은 경우 false를 반환합니다.</p>
</li>
<li><p><code>moveUploadedFile(string $tmp_name, string $target_dir): bool</code>: 업로드된 파일을 지정된 디렉토리로 이동하는 메서드입니다. 매개변수로 임시 파일 경로($tmp_name)와 이동할 대상 디렉토리 경로($target_dir)를 받아 파일을 이동하고, 이동이 성공한 경우 true를 반환하고, 실패한 경우 false를 반환합니다.</p>
</li>
<li><p>결론 : 이 인터페이스는 파일 업로드와 관련된 클래스에서 구현될 때, 각 메서드가 정확히 어떤 동작을 수행해야 하는지를 명시하고, 타입 힌트를 통해 메서드의 매개변수와 반환 타입을 명확히 지정할 수 있도록 도와줍니다.</p>
</li>
</ol>
<h4 id="톺아보기-1">톺아보기-1</h4>
<ol>
<li>php에서 <code>interface</code>는 <code>클래스가 구현해야 하는 메서드의 목록을 정의하는 역할</code></li>
<li>메서드 시그니처(이름, 매개변수, 반환값의 형식)만 포함</li>
<li>구현내용은 갖지 않음. </li>
<li>특징
4.1. <code>interface</code>키워드를 사용하여 정의 
4.2. 인터페이스는 class 키워드를 사용하여 구현되는 클래스에 의해 구현됩니다.
4.3. 인터페이스에서 선언된 모든 메서드는 공개(public)로 간주됩니다. 즉, 메서드는 클래스에서 구현될 때 반드시 <code>공개적으로 선언되어야 합니다.</code>
4.4. 클래스는 하나 이상의 인터페이스를 구현할 수 있으며, <code>인터페이스는 다중 상속과 유사한 효과</code>를 제공합니다.
4.5. 인터페이스는 객체 간의 관계를 정의하고 클래스 간의 결합도를 낮출 수 있습니다.</li>
<li>예시 <pre><code class="language-php">interface Animal {
public function makeSound();
}
</code></pre>
</li>
</ol>
<p>class Dog implements Animal {
  public function makeSound() {
    echo &quot;Woof!&quot;;
  }
}</p>
<p>class Cat implements Animal {
  public function makeSound() {
    echo &quot;Meow!&quot;;
  }
}</p>
<p>$dog = new Dog();
$dog-&gt;makeSound(); // 출력: Woof!</p>
<p>$cat = new Cat();
$cat-&gt;makeSound(); // 출력: Meow!</p>
<pre><code>
---

### MessageInterface

```php
interface MessageInterface {
  public function showMessage(string $message, string $color): string;
}

class BaseMessage implements MessageInterface {
  protected function wrapMessage(string $message, string $color): string {
    return &#39;&lt;p style=&quot;color: &#39; . $color . &#39;;&quot;&gt;&#39; . $message . &#39;&lt;/p&gt;&#39;;
  }

  public function showMessage(string $message, string $color): string {
    return $this-&gt;wrapMessage($message, $color);
  }
}

class ErrorMessage extends BaseMessage {
  // No need to override showMessage() method as it is inherited from BaseMessage
}

class SuccessMessage extends BaseMessage {
  // No need to override showMessage() method as it is inherited from BaseMessage
}
</code></pre><h4 id="설명-1">설명</h4>
<ul>
<li>위의 코드는 메시지를 표시하는 기능을 구현하기 위한 인터페이스와 클래스들을 포함하고 있습니다.</li>
</ul>
<ol>
<li><p><code>MessageInterface</code>는 <code>showMessage</code>라는 메서드를 가지는 인터페이스입니다. 이 인터페이스는 메시지와 컬러를 매개변수로 받아서 문자열로 변환하여 반환하는 기능을 정의합니다.</p>
</li>
<li><p><code>BaseMessage</code>는 <code>MessageInterface</code>를 <code>구현한 클래스</code>입니다. BaseMessage는 wrapMessage라는 보호된 메서드를 가지고 있습니다. 이 메서드는 메시지와 컬러를 매개변수로 받아서 특정 형식으로 래핑된 문자열을 반환합니다. BaseMessage는 또한 MessageInterface의 showMessage 메서드를 구현하며, 내부적으로 wrapMessage 메서드를 호출하여 변환된 문자열을 반환합니다.</p>
</li>
<li><p><code>ErrorMessage</code>는 BaseMessage를 상속한 클래스입니다. ErrorMessage는 BaseMessage에서 상속받은 showMessage 메서드를 그대로 사용하며, <code>추가적인 메서드를 구현하지 않습니다.</code></p>
</li>
<li><p><code>SuccessMessage</code>도 BaseMessage를 상속한 클래스로, BaseMessage에서 상속받은 showMessage 메서드를 그대로 사용합니다.</p>
</li>
</ol>
<p>이렇게 구현된 클래스들을 사용하면 메시지와 컬러를 전달하여 특정 형식으로 래핑된 메시지를 얻을 수 있습니다.</p>
<h4 id="톺아보기-2">톺아보기-2</h4>
<p>PHP에서는 <code>protected</code>, <code>public</code>, <code>private</code> 외에도 <code>final</code>과 <code>static</code>을 포함한 몇 가지 가시성 및 특성 키워드를 제공합니다.</p>
<ol>
<li><p><code>protected:</code> protected 키워드는 <code>해당 멤버(프로퍼티 또는 메서드)가 정의된 클래스 내부 및 해당 클래스를 상속받은 자식 클래스 내부에서 접근할 수 있음</code>을 나타냅니다.</p>
</li>
<li><p><code>public: public</code> 키워드는 해당 멤버가 <code>어디에서나 접근</code>할 수 있음을 나타냅니다. 즉, <code>클래스 내부</code>, <code>자식 클래스</code>, <code>클래스 인스턴스</code> 등 <code>어디서든</code> 접근 가능합니다.</p>
</li>
<li><p><code>private</code>: private 키워드는 해당 <code>멤버가 정의된 클래스 내부에서만</code> 접근할 수 있음을 나타냅니다. <code>자식 클래스나 클래스 인스턴스에서는 접근할 수 없습니다.</code></p>
</li>
<li><p><code>final:</code> final 키워드는 클래스를 <code>확장(상속)할 수 없음</code>을 나타냅니다. final로 선언된 클래스는 더 이상 상속할 수 없으며, 메서드를 final로 선언하면 해당 메서드를 오버라이딩할 수 없습니다.</p>
</li>
<li><p><code>static:</code> static 키워드는 클래스 레벨에 속하는 멤버(프로퍼티 또는 메서드)를 나타냅니다. static 멤버는 클래스 인스턴스를 생성하지 않고도 호출할 수 있으며, 인스턴스 간에 공유됩니다.</p>
</li>
</ol>
<h3 id="customfileuploader">CustomFileUploader</h3>
<pre><code class="language-php">class CustomFileUploader implements FileUploaderInterface {
  protected $allowedExtensions;
  protected $uploadDirectory;
  protected $errorMessage;
  protected $successMessage;

  public function __construct(array $allowedExtensions, string $uploadDirectory, MessageInterface $errorMessage, MessageInterface $successMessage) {
    $this-&gt;allowedExtensions = $allowedExtensions;
    $this-&gt;uploadDirectory = $uploadDirectory;
    $this-&gt;errorMessage = $errorMessage;
    $this-&gt;successMessage = $successMessage;
  }</code></pre>
<ul>
<li><p>위의 코드는 CustomFileUploader 클래스의 생성자에 두 개의 인터페이스 형식의 매개변수 errorMessage와 successMessage가 추가되었습니다. 이러한 매개변수를 통해 ErrorMessage 및 SuccessMessage와 같은 메시지 클래스의 인스턴스를 주입할 수 있습니다.</p>
</li>
<li><p>기존에는 CustomFileUploader 클래스 내에서 ErrorMessage와 SuccessMessage를 직접 생성했지만, 이제는 외부에서 이러한 메시지 클래스의 인스턴스를 생성한 후에 CustomFileUploader의 생성자를 통해 주입합니다. 이는 의존성 주입(Dependency Injection) 패턴을 사용하여 클래스 간의 결합도를 낮추고 유연성을 높이는 방식입니다.</p>
</li>
<li><p>이렇게 함으로써, CustomFileUploader 클래스는 어떤 종류의 메시지 클래스도 사용할 수 있게 되며, 새로운 메시지 클래스를 만들어서 주입하는 것도 가능합니다. 이는 코드의 확장성과 유지보수성을 향상시키는 데 도움이 됩니다.</p>
</li>
<li><p>생성자의 시그니처를 변경함으로써, 이제 CustomFileUploader 클래스를 인스턴스화할 때 메시지 클래스의 인스턴스를 제공해야 합니다. 예를 들어:</p>
<pre><code class="language-php">$allowedExtensions = [&#39;png&#39;, &#39;jpg&#39;, &#39;jpeg&#39;, &#39;gif&#39;];
$uploadDirectory = &#39;uploads&#39;;
$errorMessage = new ErrorMessage();
$successMessage = new SuccessMessage();
</code></pre>
</li>
</ul>
<p>$fileUploader = new CustomFileUploader($allowedExtensions, $uploadDirectory, $errorMessage, $successMessage);</p>
<pre><code>
- 위와 같이 ErrorMessage와 SuccessMessage 클래스의 인스턴스를 생성한 후, 이를 CustomFileUploader 클래스의 생성자에 주입합니다. 이제 CustomFileUploader 내에서는 주입된 메시지 클래스의 인스턴스를 사용하여 에러 메시지 및 성공 메시지를 생성할 수 있습니다.

### handleFileUpload

```php
public function handleFileUpload(): string {
    if ($_SERVER[&#39;REQUEST_METHOD&#39;] !== &#39;POST&#39;) {
      return &#39;&#39;;
    }

    if ($this-&gt;isFileNotSelected()) {
      return $this-&gt;errorMessage-&gt;showMessage(&#39;Please choose a file!&#39;, &#39;red&#39;);
    }

    $file = $_FILES[&#39;upload&#39;];
    $file_name = $file[&#39;name&#39;];
    $file_size = $file[&#39;size&#39;];
    $file_tmp_name = $file[&#39;tmp_name&#39;];

    $file_ext = pathinfo($file_name, PATHINFO_EXTENSION);
    $file_ext = strtolower($file_ext);

    if (!$this-&gt;isValidExtension($file_ext)) {
      return $this-&gt;errorMessage-&gt;showMessage(&#39;Invalid file type!&#39;, &#39;red&#39;);
    }

    if (!$this-&gt;isValidSize($file_size)) {
      return $this-&gt;errorMessage-&gt;showMessage(&#39;File too large!&#39;, &#39;red&#39;);
    }

    $target_dir = $this-&gt;uploadDirectory . &#39;/&#39; . $file_name;

    if (!is_dir($this-&gt;uploadDirectory)) {
      mkdir($this-&gt;uploadDirectory, 0755, true);
    }

    if ($this-&gt;moveUploadedFile($file_tmp_name, $target_dir)) {
      return $this-&gt;successMessage-&gt;showMessage(&#39;File uploaded!&#39;, &#39;green&#39;);
    } else {
      return $this-&gt;errorMessage-&gt;showMessage(&#39;Failed to move the uploaded file!&#39;, &#39;red&#39;);
    }
  }</code></pre><ul>
<li>위의 코드는 handleFileUpload() 메서드의 내용입니다. 이 메서드는 파일 업로드를 처리하고 결과에 따라 적절한 메시지를 반환합니다.</li>
</ul>
<p>코드의 실행 흐름은 다음과 같습니다:</p>
<ol>
<li><p><code>$_SERVER[&#39;REQUEST_METHOD&#39;]</code> 값을 확인하여 현재 요청이 <code>POST</code> 메서드인지 확인합니다. 만약 POST 메서드가 아니라면 빈 문자열을 반환하고 메서드 실행을 종료합니다.</p>
</li>
<li><p>파일이 선택되지 않았는지 확인하기 위해 <code>$this-&gt;isFileNotSelected()</code> 메서드를 호출합니다. 파일이 선택되지 않았다면 &#39;Please choose a file!&#39; 메시지와 &#39;red&#39; 색상을 사용하여<code>$this-&gt;errorMessage</code> <code>객체</code>의 <code>showMessage() 메서드</code>를 <code>호출</code>하고 해당 <code>결과를 반환</code>합니다.</p>
</li>
<li><p>선택된 파일이 있다면, 파일의 정보를 변수에 할당합니다. 파일 이름은 <code>$file_name</code>, 파일 크기는 <code>$file_size</code>, 임시 파일 경로는 <code>$file_tmp_name</code>에 저장됩니다.</p>
</li>
<li><p>파일 이름으로부터 <code>확장자를 추출</code>하기 위해 <code>pathinfo()</code> 함수를 사용하고, 추출된 확장자를 <code>소문자로 변환</code>합니다.</p>
</li>
<li><p>추출된 확장자가 유효한지 확인하기 위해 <code>$this-&gt;isValidExtension()</code> 메서드를 <code>호출</code>합니다. 유효하지 않은 경우 &#39;Invalid file type!&#39; 메시지와 &#39;red&#39; 색상을 사용하여 <code>$this-&gt;errorMessage 객체의 showMessage() 메서드를 호출</code>하고 해당 결과를 반환합니다.</p>
</li>
<li><p>파일 크기가 유효한지 확인하기 위해 <code>$this-&gt;isValidSize()</code> 메서드를 <code>호출</code>합니다. 유효하지 않은 경우 &#39;File too large!&#39; 메시지와 &#39;red&#39; 색상을 사용하여 $this-&gt;<code>errorMessage 객체</code>의 <code>showMessage()</code> 메서드를 호출하고 해당 결과를 반환합니다.</p>
</li>
<li><p>업로드된 파일을 저장하기 위한 대상 디렉토리 경로인 <code>$target_dir</code>을 생성합니다. 대상 디렉토리가 존재하지 않는 경우에는 <code>mkdir() 함수</code>를 사용하여 <code>디렉토리를 생성</code>합니다.</p>
</li>
<li><p><code>$this-&gt;moveUploadedFile()</code> 메서드를 호출하여 업로드된 파일을 이동시킵니다. 이동에 성공한 경우 &#39;File uploaded!&#39; 메시지와 &#39;green&#39; 색상을 사용하여 $this-&gt;<code>successMessage 객체</code>의 <code>showMessage()</code> 메서드를 <code>호출하고 해당 결과를 반환</code>합니다.</p>
</li>
<li><p>이동에 실패한 경우 &#39;Failed to move the uploaded file!&#39; 메시지와 &#39;red&#39; 색상을 사용하여 $this-&gt;<code>errorMessage 객체</code>의 <code>showMessage() 메서드를 호출</code>하고 해당 결과를 반환합니다.</p>
</li>
<li><p>결론 :  이렇게하여 <code>handleFileUpload()</code> 메서드는 파일 업로드 처리 후 결과에 따라 <code>적절한 메시지를 반환</code>합니다. 이 메서드는 errorMessage와 successMessage 객체를 사용하여 <code>에러 메시지</code>와 <code>성공 메시지를 생성</code>하며, 이전에 <code>주입된 객체들과 협력하여 동작</code>합니다.</p>
</li>
</ol>
<h4 id="톺아보기-3">톺아보기-3</h4>
<p>php의 built-in으로 제공되는 복합 데이터 타입은?</p>
<ol>
<li>배열 (Array): 여러 개의 값을 하나의 변수에 저장하는 데이터 구조입니다. 인덱스 배열과 연관 배열 두 가지 형태로 사용할 수 있습니다.<pre><code class="language-php">// 인덱스 배열
$numbers = [1, 2, 3, 4, 5];
</code></pre>
</li>
</ol>
<p>// 연관 배열
$person = [
  &#39;name&#39; =&gt; &#39;John&#39;,
  &#39;age&#39; =&gt; 30,
  &#39;email&#39; =&gt; &#39;john@example.com&#39;
];</p>
<pre><code>2. 객체 (Object): 클래스로부터 생성된 인스턴스를 나타내는 데이터 타입입니다. 객체는 속성(프로퍼티)와 메서드로 구성됩니다.
```php
class Person {
  public $name;
  public $age;

  public function __construct($name, $age) {
    $this-&gt;name = $name;
    $this-&gt;age = $age;
  }

  public function greet() {
    echo &quot;Hello, my name is &quot; . $this-&gt;name . &quot; and I&#39;m &quot; . $this-&gt;age . &quot; years old.&quot;;
  }
}

$person = new Person(&quot;John&quot;, 30);
$person-&gt;greet();</code></pre><ol start="3">
<li>리소스 (Resource): 외부 자원에 대한 참조를 나타내는 데이터 타입입니다. 예를 들어, 파일 핸들, 데이터베이스 연결 등이 리소스로 표현될 수 있습니다.<pre><code class="language-php">// 파일 핸들링 예시
$file = fopen(&quot;example.txt&quot;, &quot;r&quot;);
// 파일 핸들 $file을 리소스로 표현
</code></pre>
</li>
</ol>
<p>// 데이터베이스 연결 예시
$connection = mysqli_connect(&quot;localhost&quot;, &quot;username&quot;, &quot;password&quot;, &quot;database&quot;);
// 데이터베이스 연결 리소스 $connection</p>
<pre><code>4. callable: 함수 또는 메서드를 참조할 수 있는 데이터 타입입니다. 함수명, 익명 함수, 클래스의 정적 메서드 등이 callable 타입으로 사용될 수 있습니다.
```php
// 함수 참조
$functionRef = &#39;strlen&#39;;
echo $functionRef(&quot;Hello&quot;);  // 문자열 길이 반환

// 익명 함수
$anonymousFunc = function($x, $y) {
  return $x + $y;
};
echo $anonymousFunc(5, 3);  // 8 반환

// 클래스의 정적 메서드
class Math {
  public static function add($x, $y) {
    return $x + $y;
  }
}
$methodRef = [Math::class, &#39;add&#39;];
echo $methodRef(4, 2);  // 6 반환
</code></pre><ol start="5">
<li>iterable: 반복 가능한 데이터 타입을 나타내는 인터페이스입니다. 배열이나 객체의 경우 반복 가능한 타입으로 사용할 수 있습니다. iterable 타입은 foreach 루프와 같은 반복 작업에 사용됩니다.<pre><code class="language-php">// 배열을 반복 가능한 타입으로 사용
$numbers = [1, 2, 3, 4, 5];
foreach ($numbers as $number) {
echo $number . &quot; &quot;;
}
</code></pre>
</li>
</ol>
<p>// 객체를 반복 가능한 타입으로 사용
class MyIterator implements Iterator {
  private $position = 0;
  private $data = [&#39;A&#39;, &#39;B&#39;, &#39;C&#39;];</p>
<p>  public function rewind() {
    $this-&gt;position = 0;
  }</p>
<p>  public function current() {
    return $this-&gt;data[$this-&gt;position];
  }</p>
<p>  public function key() {
    return $this-&gt;position;
  }</p>
<p>  public function next() {
    ++$this-&gt;position;
  }</p>
<p>  public function valid() {
    return isset($this-&gt;data[$this-&gt;position]);
  }
}</p>
<p>$myIterator = new MyIterator();
foreach ($myIterator as $key =&gt; $value) {
  echo $key . &quot;: &quot; . $value . &quot; &quot;;
}</p>
<pre><code>
### isFileNotSelected

```php
public function isFileNotSelected(): bool {
    return empty($_FILES[&#39;upload&#39;][&#39;name&#39;]);
  }</code></pre><ul>
<li><p><code>isFileNotSelected</code> 메서드는 현재 업로드된 파일이 선택되지 않은 경우를 확인하는 함수입니다.</p>
</li>
<li><p>해당 메서드는 <code>$_FILES[&#39;upload&#39;][&#39;name&#39;]</code> 값을 확인하여 파일 이름이 비어있는지를 검사합니다.</p>
</li>
</ul>
<ol>
<li><p><code>$_FILES[&#39;upload&#39;]</code>는 PHP의 슈퍼글로벌 변수 중 하나로, 파일 업로드 시에 전송된 파일에 대한 정보를 담고 있습니다.</p>
</li>
<li><p><code>&#39;name&#39;</code>은 업로드된 파일의 원본 이름을 나타냅니다.</p>
</li>
</ol>
<ul>
<li><p><code>empty($_FILES[&#39;upload&#39;][&#39;name&#39;])</code>은 <code>$_FILES[&#39;upload&#39;][&#39;name&#39;]</code> 값이 비어있으면 <code>true</code>를 반환하고, 그렇지 않으면 <code>false</code>를 반환합니다.</p>
</li>
<li><p>따라서, <code>isFileNotSelected</code> 메서드는 업로드된 파일이 선택되지 않은 경우 <code>true</code>를 반환하고, 선택된 경우에는 <code>false</code>를 반환합니다.</p>
</li>
</ul>
<h3 id="isvalidextension">isValidExtension</h3>
<pre><code class="language-php">  public function isValidExtension(string $extension): bool {
    return in_array($extension, $this-&gt;allowedExtensions);
  }</code></pre>
<ul>
<li><p><code>isValidExtension</code> 메서드는 주어진 파일 확장자가 허용된 확장자 목록에 포함되어 있는지를 확인하는 함수입니다.</p>
</li>
<li><p>해당 메서드는 $extension 매개변수와 $this-&gt;allowedExtensions 속성을 비교하여 주어진 확장자가 허용된 확장자인지를 판별합니다.</p>
</li>
</ul>
<ol>
<li><p><code>$extension</code>은 확인할 파일의 확장자를 나타냅니다.</p>
</li>
<li><p><code>$this-&gt;allowedExtensions</code>는 $this 객체의 allowedExtensions 속성으로, 허용된 확장자의 배열을 나타냅니다.</p>
</li>
</ol>
<ul>
<li><p><code>in_array($extension, $this-&gt;allowedExtensions)</code>은 <code>$extension</code> 값이 <code>$this-&gt;allowedExtensions</code> 배열에 포함되어 있는지를 확인합니다.</p>
</li>
<li><p><code>in_array()</code> 함수는 주어진 값이 배열에 존재하는지를 확인하고, <code>존재할 경우 true</code>를 반환하고, <code>그렇지 않을 경우 false</code>를 반환합니다.</p>
</li>
</ul>
<p>따라서, <code>isValidExtension</code> 메서드는 주어진 확장자가 허용된 확장자 목록에 포함되어 있는 경우 true를 반환하고, 포함되지 않은 경우에는 false를 반환합니다.</p>
<h3 id="isvalidsize">isValidSize</h3>
<pre><code class="language-php"> public function isValidSize(int $size): bool {
    $maxSize = 1000000;
    return $size &lt;= $maxSize;
  }</code></pre>
<ul>
<li><p>isValidSize 메서드는 주어진 파일 크기가 유효한 크기인지를 확인하는 역할을 합니다.</p>
</li>
<li><p>매개변수로 받은 $size를 $maxSize와 비교하여 유효한 크기인지를 판단합니다. 여기서 $maxSize는 1000000로 설정되어 있습니다.</p>
</li>
<li><p>함수는 주어진 파일 크기 $size가 $maxSize 이하인 경우 true를 반환하고, 그렇지 않은 경우에는 false를 반환합니다. 이를 통해 파일 크기가 허용 범위 내에 있는지를 확인할 수 있습니다.</p>
</li>
</ul>
<h3 id="moveuploadedfile">moveUploadedFile</h3>
<pre><code class="language-php">  public function moveUploadedFile(string $tmp_name, string $target_dir): bool {
    return move_uploaded_file($tmp_name, $target_dir);
  }</code></pre>
<ul>
<li><p><code>moveUploadedFile()</code> 함수는 <code>업로드된 파일</code>을 <code>지정된 대상 디렉토리</code>로 <code>이동시키는 역할</code>을 합니다. 이 함수는 <code>move_uploaded_file() 내장 함수</code>를 <code>호출</code>하여 파일 이동을 수행합니다.</p>
</li>
<li><p>함수는 <code>두 개</code>의 <code>매개변수</code>를 받습니다. 첫 번째 매개변수인 <code>$tmp_name</code>은 업로드된 <code>파일의 임시 경로</code>를 나타내며, 두 번째 매개변수인 <code>$target_dir</code>은 파일이 이동될 <code>대상 디렉토리</code>를 나타냅니다.</p>
</li>
<li><p><code>move_uploaded_file($tmp_name, $target_dir)</code> 함수는 업로드된 파일을 <code>임시 경로</code>에서 <code>대상 디렉토리로 이동</code>시킵니다. 이 과정에서 파일의 <code>권한과 소유자도 유지</code>됩니다. 함수는 파일 이동에 성공하면 true를 반환하고, 파일 이동에 실패하면 false를 반환합니다.</p>
</li>
<li><p>이 코드에서는 move_uploaded_file() 함수의 반환 값을 그대로 반환하므로, 파일 이동에 성공했는지 여부를 호출한 곳에서 확인할 수 있습니다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 - 선택정렬]]></title>
            <link>https://velog.io/@hyeseong-dev/%EC%9E%90%EB%B0%94-%EC%84%A0%ED%83%9D%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@hyeseong-dev/%EC%9E%90%EB%B0%94-%EC%84%A0%ED%83%9D%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Sat, 20 May 2023 17:18:38 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">public class 선택정렬_Ver1 {

  // 두 위치의 원소를 교환
  public static void swap(int[] numbers, int position1, int position2) {
    int temporary = numbers[position1];
    numbers[position1] = numbers[position2];
    numbers[position2] = temporary;
  };

  // 시작 위치부터 가장 작은 원소의 위치 찾기
  public static int findSmallestPosition(int[] numbers, int startFrom) {
    int smallestPosition = startFrom;
    for (int currentIndex = startFrom; currentIndex &lt; numbers.length; currentIndex++) {
      // 현재 위치의 원소가 지금까지 찾은 가장 작은 원소보다 작다면, 그 위치를 저장
      if (numbers[currentIndex] &lt; numbers[smallestPosition]) {
        smallestPosition = currentIndex;
      }
    }
    return smallestPosition;
  }

  // 선택 정렬 알고리즘
  public static void sort(int[] numbers) {
    for (int currentIndex = 0; currentIndex &lt; numbers.length; currentIndex++) {
      // 현재 위치부터 가장 작은 원소의 위치 찾기
      int smallestPosition = findSmallestPosition(numbers, currentIndex);
      // 그 원소와 현재 위치의 원소를 교환
      swap(numbers, currentIndex, smallestPosition);
    }
  }

  // 메인 메소드
  public static void main(String[] args) {
    int[] numbers = { 5, 3, 6, 2, 10, 0, 11 };
    sort(numbers);
    System.out.print(&quot;정렬된 배열: &quot;);
    for (int number : numbers) {
      System.out.print(number + &quot; &quot;);
    }
  }
}
</code></pre>
<h3 id="설명">설명</h3>
<h4 id="라인별">라인별</h4>
<ol>
<li><p><code>public class 선택정렬_Ver1 {:</code> &quot;선택정렬_Ver1&quot;이라는 클래스를 선언합니다. 이 클래스 안에는 모든 정렬 메서드가 포함됩니다.</p>
</li>
<li><p><code>public static void swap(int[] numbers, int position1, int position2) {:</code> swap이라는 메소드를 선언합니다. 이 메서드는 배열 numbers의 position1 위치와 position2 위치에 있는 원소들을 서로 바꿉니다.</p>
</li>
<li><p><code>temporary = numbers[position1];:</code> position1 위치의 원소를 임시 변수 temporary에 저장합니다. 이는 position1 위치에 position2 위치의 원소를 덮어쓰기 전에 position1 위치의 원소를 보존하기 위함입니다.</p>
</li>
<li><p><code>numbers[position1] = numbers[position2];:</code> position2 위치의 원소를 position1 위치에 덮어씁니다.</p>
</li>
<li><p><code>numbers[position2] = temporary;:</code> 이전에 보존해둔 temporary(원래 position1 위치의 원소)를 position2 위치에 덮어씁니다.</p>
</li>
<li><p><code>public static int findSmallestPosition(int[] numbers, int startFrom) {:</code> findSmallestPosition이라는 메서드를 선언합니다. 이 메서드는 배열 numbers의 startFrom 위치부터 시작하여 가장 작은 원소의 위치를 찾아 반환합니다.</p>
</li>
<li><p><code>for (int currentIndex = startFrom; currentIndex &lt; numbers.length; currentIndex++) {...}:</code> `startFrom 위치부터 배열의 끝까지 순회하면서 가장 작은 원소의 위치를 찾는 루프입니다.</p>
</li>
<li><p><code>public static void sort(int[] numbers) {:</code> sort라는 메서드를 선언합니다. 이 메서드는 배열 numbers에 선택 정렬을 수행합니다.</p>
</li>
<li><p><code>for (int currentIndex = 0; currentIndex &lt; numbers.length; currentIndex++) {...}:</code> 배열의 각 위치에 대해 가장 작은 원소를 찾아(findSmallestPosition), 그 위치의 원소와 현재 위치의 원소를 교환합니다(swap).</p>
</li>
<li><p><code>public static void main(String[] args) {:</code> main 메서드를 선언합니다. 이 메서드는 프로그램의 진입점입니다.</p>
</li>
<li><p>int[] numbers = { 5, 3, 6, 2, 10, 0, 11 };: 정렬할 숫자 배열을 선언하고 초기화합니다.</p>
</li>
<li><p><code>sort(numbers);:</code> sort 메서드를 호출하여 배열을 정렬합니다</p>
</li>
</ol>
<h4 id="메서드별">메서드별</h4>
<ul>
<li><p><code>main(String[] args):</code> 이 메소드는 프로그램의 시작점입니다. 먼저 숫자 배열을 생성하고, sort(numbers)를 호출하여 선택 정렬을 수행하고, 그 후에 정렬된 배열을 출력합니다.</p>
</li>
<li><p><code>sort(int[] numbers):</code> 이 메소드는 선택 정렬 알고리즘을 구현합니다. 배열의 각 위치에 대해 가장 작은 원소의 위치를 찾고(findSmallestPosition), 그 위치의 원소와 현재 위치의 원소를 교환합니다(swap).</p>
</li>
<li><p><code>findSmallestPosition(int[] numbers, int startFrom):</code> 이 메소드는 주어진 시작 위치부터 배열의 가장 작은 원소의 위치를 찾습니다. 이를 위해, 시작 위치부터 배열의 끝까지 각 원소를 확인하고, 가장 작은 원소의 위치를 저장합니다. 메소드는 가장 작은 원소의 위치를 반환합니다.</p>
</li>
<li><p><code>swap(int[] numbers, int position1, int position2):</code> 이 메소드는 배열에서 두 원소의 위치를 교환합니다. position1의 원소와 position2의 원소가 서로 교환되며, 이를 위해 임시 변수가 사용됩니다.</p>
</li>
</ul>
<p>전체적으로 보면, main 메소드는 sort 메소드를 호출하여 배열을 정렬하고, sort 메소드는 findSmallestPosition과 swap 메소드를 사용하여 선택 정렬 알고리즘을 구현합니다. 이 과정은 배열의 각 위치에서 가장 작은 원소를 찾아 앞으로 이동시키는 것을 반복하므로, 최종적으로 배열이 오름차순으로 정렬됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot Tutorial for Beginners(Java Framework)]]></title>
            <link>https://velog.io/@hyeseong-dev/Spring-Boot-Tutorial-for-BeginnersJava-Framework</link>
            <guid>https://velog.io/@hyeseong-dev/Spring-Boot-Tutorial-for-BeginnersJava-Framework</guid>
            <pubDate>Thu, 18 May 2023 08:47:59 GMT</pubDate>
            <description><![CDATA[<h1 id="목차">목차</h1>
<ul>
<li><a href="#java-%EC%84%A4%EC%B9%98">java 설치</a><ul>
<li><a href="#wsl-%EC%84%A4%EC%B9%98">WSL 설치</a></li>
<li><a href="#sdkman-%EC%84%A4%EC%B9%98">SDKMAN 설치</a></li>
<li><a href="#sdkman-%EC%84%A4%EC%A0%95">SDKMAN 설정</a></li>
<li><a href="#java-%EC%84%A4%EC%B9%98">Java 설치</a></li>
<li><a href="#maven-%EC%84%A4%EC%B9%98">Maven 설치</a></li>
<li><a href="#%EC%84%A4%EC%B9%98-%ED%99%95%EC%9D%B8">설치 확인</a></li>
</ul>
</li>
<li><a href="#spring-initializr">spring initializr</a><ul>
<li><a href="#1%EB%8B%A8%EA%B3%84">1단계</a></li>
<li><a href="#2%EB%8B%A8%EA%B3%84">2단계</a></li>
<li><a href="#3%EB%8B%A8%EA%B3%84">3단계</a></li>
</ul>
</li>
<li><a href="#%ED%8C%A8%ED%82%A4%EC%A7%80-%EC%83%9D%EC%84%B1">패키지 생성</a></li>
<li><a href="#model-%EC%83%9D%EC%84%B1">model 생성</a></li>
<li><a href="#%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%B0%A8%EC%9D%B4">클래스와 인터페이스 차이</a></li>
<li><a href="#persondao-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%A0%95%EC%9D%98">PersonDao 인터페이스 정의</a></li>
<li><a href="#interface%EC%9D%98-default-%ED%82%A4%EC%9B%8C%EB%93%9C">interface의 default 키워드</a></li>
</ul>
<h2 id="java-설치">java 설치</h2>
<p><strong>참고 WSL에서 SDKMAN을 사용하여 Java와 Maven을 설치 진행</strong></p>
<ol>
<li>WSL 설치 : </li>
</ol>
<ul>
<li>WSL을 사용하기 위해 Windows 10에서 WSL을 설치해야 합니다. 자세한 내용은 Microsoft 공식 문서를 참조.</li>
</ul>
<ol start="2">
<li>SDKMAN 설치:</li>
</ol>
<ul>
<li>WSL 터미널을 열고 다음 명령어를 실행하여 SDKMAN을 설치합니다:<pre><code class="language-bash">curl -s &quot;https://get.sdkman.io&quot; | bash
source &quot;$HOME/.sdkman/bin/sdkman-init.sh&quot;</code></pre>
</li>
</ul>
<ol start="3">
<li>SDKMAN 설정 </li>
</ol>
<ul>
<li>SDKMAN을 초기화하고 적절한 환경 변수를 설정하기 위해 다음 명령어를 실행합니다:</li>
</ul>
<pre><code class="language-bash">source &quot;$HOME/.sdkman/bin/sdkman-init.sh&quot;
</code></pre>
<ol start="4">
<li>Java 설치:</li>
</ol>
<ul>
<li>다음 명령어를 사용하여 설치 가능한 Java 버전 목록을 확인합니다:</li>
</ul>
<pre><code class="language-bash">sdk list java
</code></pre>
<ul>
<li>원하는 Java 버전을 선택하고 다음 명령어를 실행하여 설치합니다(예: Java 11 설치):<pre><code class="language-bash">sdk install java 11.0.13-open
</code></pre>
</li>
</ul>
<pre><code>
5. Maven 설치:

- 다음 명령어를 사용하여 설치 가능한 Maven 버전 목록을 확인합니다:
```bash
sdk list maven</code></pre><ul>
<li>원하는 Maven 버전을 선택하고 다음 명령어를 실행하여 설치합니다(예: Maven 3.8.4 설치):</li>
</ul>
<pre><code class="language-bash">sdk install maven 3.8.4</code></pre>
<ol start="6">
<li>설치 확인:</li>
</ol>
<ul>
<li>Java와 Maven이 정상적으로 설치되었는지 확인하기 위해 다음 명령어를 실행합니다:</li>
</ul>
<pre><code class="language-bash">java -version
mvn -v</code></pre>
<p>위의 단계를 따라하면 WSL에서 SDKMAN을 사용하여 Java와 Maven을 설치할 수 있습니다.</p>
<h2 id="spring-initializr">spring initializr</h2>
<ul>
<li>아래 웹사이트에 접속하여 spring initizlizr를 통하여 프로젝트 구성을 쉽게 할 수 있습니다. </li>
<li><a href="https://start.spring.io/">https://start.spring.io/</a></li>
</ul>
<h3 id="1단계">1단계</h3>
<ul>
<li>원하는 사항들을 선택하여 프로젝트를 구성함
<img src="https://velog.velcdn.com/images/hyeseong-dev/post/bf4eaef1-0395-4e8a-a8ca-b527d361f1b9/image.png" alt=""></li>
</ul>
<h3 id="2단계">2단계</h3>
<ul>
<li>구성 설정을 마무리 하기 위해 화면 하단의 <code>GENERATE</code> 버튼을 클릭하여 프로젝트 환경구성 압축 파일을 받아줍니다. 
<img src="https://velog.velcdn.com/images/hyeseong-dev/post/15d4768c-1fb5-426d-97fe-674ddff95f0a/image.png" alt=""></li>
</ul>
<h3 id="3단계">3단계</h3>
<p><strong>참고</strong> 
WSL에서 프로젝트를 구성하니. 다운받은 파일이 있는 디렉토리를 윈도우 탐색기 창으로 열어 줍니다. 그리고 WSL 터미널을 열고 Drag and Drop으로 터미널에 붙여 넣어주면 경로가 표시됩니다. 이를 리눅스 명령어를 통해서 옮겨주고 압축해제해 줍니다.</p>
<ul>
<li>zip 파일을 압축해제 하여 intellJ를 통하여 압축해제된 디렉토리를 선택하여 프로젝트 개발을 시작합니다. </li>
</ul>
<h2 id="패키지-생성">패키지 생성</h2>
<pre><code class="language-bash">└── com
    └── example
        └── demo
            ├── DemoApplication.java
            ├── api
            ├── dao
            ├── model
            └── service</code></pre>
<h2 id="model-생성">model 생성</h2>
<pre><code class="language-java">package com.example.demo.model;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.UUID;

public class Person {
    private final UUID id;
    private final String name;

    public Person(
        @JsonProperty(&quot;id&quot;) UUID id,
        @JsonProperty(&quot;name&quot;) String name
    ){
        this.id = id;
        this.name = name;
    }

    public UUID getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}
</code></pre>
<p>위의 코드는 com.example.demo.model 패키지에 있는 Person 클래스를 정의하는 예시입니다. 해당 클래스는 다음과 같은 멤버 변수와 생성자, 메서드를 가지고 있습니다:</p>
<ul>
<li><p>private final UUID id: UUID 타입의 변수로, 객체의 고유 식별자를 나타냅니다. final 키워드로 선언되어 값을 변경할 수 없습니다.
private final String name: 문자열 타입의 변수로, 객체의 이름을 나타냅니다. final 키워드로 선언되어 값을 변경할 수 없습니다.
클래스에는 다음과 같은 생성자와 메서드가 정의되어 있습니다:</p>
</li>
<li><p>생성자: @JsonProperty 어노테이션을 사용하여 JSON 속성과 클래스의 멤버 변수를 매핑합니다. 생성자의 인자로 id와 name을 받아 초기화합니다. 이를 통해 JSON 데이터를 Person 객체로 역직렬화할 때 속성의 값을 할당할 수 있습니다.</p>
</li>
<li><p>getId() 메서드: 객체의 id 값을 반환합니다.</p>
</li>
<li><p>getName() 메서드: 객체의 name 값을 반환합니다.</p>
</li>
</ul>
<p>이렇게 정의된 Person 클래스는 UUID와 name 속성을 가지고 있는 데이터 모델을 나타냅니다. @JsonProperty 어노테이션을 사용하여 JSON 데이터와의 매핑을 지정하고, getId()와 getName() 메서드를 통해 속성 값을 조회할 수 있습니다. 이러한 모델 클래스는 데이터를 표현하고 가공하는 데 사용됩니다.</p>
<h2 id="톺아보기---1">톺아보기 - 1</h2>
<h3 id="클래스와-인터페이스-차이">클래스와 인터페이스 차이</h3>
<ul>
<li>자바에서 클래스와 인터페이스는 둘 다 객체 지향 프로그래밍에서 중요한 개념이지만, 목적과 구조에서 차이가 있습니다.</li>
</ul>
<h3 id="클래스">클래스</h3>
<p><strong>클래스(Class):</strong></p>
<ul>
<li>클래스는 객체를 생성하기 위한 템플릿 또는 청사진(blueprint) 역할을 합니다.</li>
<li>객체의 속성을 나타내는 필드(Field)와 객체의 동작을 나타내는 메서드(Method)를 포함할 수 있습니다.</li>
<li>인스턴스화(Instantiation)하여 객체를 생성하고 해당 객체의 상태와 동작을 정의할 수 있습니다.</li>
<li>단일 클래스나 클래스 계층 구조로 구성되어 있으며, 다중 상속을 지원하지 않습니다.</li>
<li>클래스는 다른 클래스를 상속받아 기능을 확장하고 재사용할 수 있습니다.</li>
<li>클래스의 인스턴스는 클래스의 타입으로 사용될 수 있습니다.</li>
</ul>
<h3 id="인터페이스interface">인터페이스(Interface)</h3>
<ul>
<li>인터페이스는 클래스가 구현해야 하는 동작의 명세(specification)를 정의하는 역할을 합니다.</li>
<li>클래스가 인터페이스를 구현(Implement)함으로써 해당 인터페이스의 동작을 갖는 것을 보장합니다.</li>
<li>인터페이스는 추상 메서드(Abstract Method)와 상수(Constant)로 구성됩니다. (Java 8부터는 디폴트 메서드(Default Method)와 정적 메서드(Static Method)도 포함할 수 있습니다.)</li>
<li>클래스는 다중 인터페이스를 구현할 수 있으며, 이를 통해 다중 상속의 일부 기능을 대체할 수 있습니다.</li>
<li>인터페이스는 동작의 일관성을 유지하고 클래스 간의 결합도를 낮추는 역할을 합니다.</li>
<li>인터페이스의 인스턴스는 인터페이스의 타입으로 사용될 수 있습니다. (다형성)</li>
</ul>
<p><code>클래스</code>는 <code>객체</code>의 <code>구조와 동작</code>을 직접 <code>정의``하고</code>구현<code>하는 데 사용되는 반면,</code>인터페이스`는 클래스가 가져야 할 동작을 정의하고 해당 동작을 구현하는 클래스에서 사용됩니다. 클래스는 상속을 통해 확장하고 재사용할 수 있으며, 인터페이스는 다중 구현을 통해 클래스의 동작을 보장하고 일관성을 유지합니다. 또한, 인터페이스를 사용함으로써 클래스 간의 결합도를 낮추고 유연성을 높일 수 있습니다.</p>
<h2 id="persondao-인터페이스-정의">PersonDao 인터페이스 정의</h2>
<ul>
<li><pre><code class="language-java">package com.example.demo.dao;
</code></pre>
</li>
</ul>
<p>import com.example.demo.model.Person;</p>
<p>import java.util.UUID;</p>
<p>public interface PersonDao {
    int insertPerson(UUID id, Person person);
    default int insertPerson(Person person){
        UUID id = UUID.randomUUID();
        return insertPerson(id, person);
    }
}</p>
<pre><code>
- 인터페이스는 PersonDao 이름으로 선언되었으며, com.example.demo.dao 패키지에 위치합니다.

- 인터페이스에는 두 개의 메서드가 포함되어 있습니다:

  - insertPerson(UUID id, Person person): 이 메서드는 UUID 타입의 id와 Person 객체를 매개변수로 받고 정수를 반환합니다. 이 메서드는 주어진 id와 person 데이터를 데이터 저장소에 삽입하는 역할을 합니다. 이 메서드의 구현은 이 인터페이스를 구현하는 클래스에서 제공되어야 합니다.

  - default int insertPerson(Person person): 이는 인터페이스에 정의된 디폴트 메서드입니다. 이 메서드는 Person 객체 하나만 매개변수로 받습니다. 메서드 내에서는 UUID.randomUUID()를 사용하여 랜덤한 UUID를 생성하고, 생성된 id와 제공된 person 객체를 가지고 insertPerson(UUID id, Person person) 메서드(첫 번째 메서드)를 호출합니다. 이 메서드의 디폴트 구현은 내부적으로 고유한 UUID를 생성하여 사람을 삽입하는 편리한 방법을 제공합니다.

이 인터페이스를 사용하면 요구사항에 맞게 PersonDao 인터페이스를 구현하는 다른 클래스의 구현을 정의할 수 있습니다. 각각의 구현은 데이터 저장소에 사람을 삽입하는 자체 로직을 제공할 수 있습니다. 디폴트 메서드 insertPerson(Person person)는 그대로 사용하거나 구현 클래스에서 오버라이드하여 사용할 수 있습니다.

## 톺아보기-2 
### interface의 default 키워드?

`default` 키워드는 `자바 8`에서 `도입`된 인터페이스의 기능 중 하나입니다. 인터페이스 내에서 default 키워드를 사용하여 `메서드에 기본 구현`을 제공할 수 있습니다.

기본적으로 인터페이스는 `추상 메서드만`을 `포함`하며, `구현체`에서 이러한 메서드를 `구현`해야 합니다. 그러나 `자바 8부터`는 `default` 키워드를 사용하여 `인터페이스 내에서 메서드에 기본 구현을 제공`할 수 있게 되었습니다. `이는 인터페이스를 구현하는 클래스에서 해당 메서드를 오버라이딩하지 않아도 되는 편의성을 제공`합니다.

인터페이스의 default 메서드는 다음과 같은 특징을 가지고 있습니다:

1. default 메서드는 인터페이스 내에 구현됩니다. 따라서 인터페이스를 구현하는 클래스에서 이 메서드를 오버라이딩할 필요가 없습니다.

2. default 메서드는 일반적인 메서드 구문을 사용하여 정의됩니다. 즉, 메서드 시그니처와 본문을 가질 수 있습니다.

3. default 메서드는 인터페이스 내에서 호출될 수 있으며, 구현체에서도 선택적으로 호출할 수 있습니다.

4. 클래스가 여러 인터페이스를 구현하고 있을 때, 동일한 시그니처를 가진 default 메서드가 여러 인터페이스에 존재하는 경우, 해당 클래스에서 명시적으로 오버라이딩하여 어느 인터페이스의 메서드를 사용할지 지정해야 합니다.

default 메서드는 기본적인 구현을 제공하면서도 인터페이스의 호환성을 유지하고 새로운 기능을 추가하는 데 유용합니다. 이를 통해 인터페이스를 변경하여 기존의 구현체에 영향을 주지 않고 새로운 기능을 추가할 수 있습니다.


## FakePersonDataAccessService dao 정의

```java
package com.example.demo.dao;

import com.example.demo.model.Person;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class FakePersonDataAccessService implements PersonDao{
    private static List&lt;Person&gt; DB = new ArrayList&lt;&gt;();

    @Override
    public int insertPerson(UUID id, Person person){
        DB.add(new Person(id, person.getName()));
        return 1;
    }
}</code></pre><p>위의 코드는 <code>com.example.demo.dao</code> 패키지에 <code>FakePersonDataAccessService</code> 클래스를 정의하는 예시입니다. 해당 클래스는 PersonDao 인터페이스를 구현하고 있습니다. 코드를 간단히 설명하면 다음과 같습니다:</p>
<ul>
<li><p>import com.example.demo.model.Person;: com.example.demo.model.Person 클래스를 사용하기 위해 해당 패키지를 import하고 있습니다.</p>
</li>
<li><p>import java.util.ArrayList;: java.util.ArrayList 클래스를 사용하기 위해 해당 패키지를 import하고 있습니다.</p>
</li>
<li><p>import java.util.List;: java.util.List 인터페이스를 사용하기 위해 해당 패키지를 import하고 있습니다.</p>
</li>
<li><p>import java.util.UUID;: java.util.UUID 클래스를 사용하기 위해 해당 패키지를 import하고 있습니다.</p>
</li>
<li><p>public class FakePersonDataAccessService implements PersonDao: FakePersonDataAccessService 클래스가 PersonDao 인터페이스를 구현하고 있음을 선언하고 있습니다.</p>
</li>
<li><p>private static List<Person> DB = new ArrayList&lt;&gt;();: List<Person> 타입의 DB라는 이름의 private 정적 변수를 선언하고, ArrayList 객체를 생성하여 초기화하고 있습니다.</p>
</li>
<li><p>@Override: insertPerson 메서드가 PersonDao 인터페이스의 추상 메서드를 오버라이딩함을 표시하고 있습니다.</p>
</li>
<li><p>public int insertPerson(UUID id, Person person): UUID와 Person 객체를 매개변수로 받아서 Person 객체를 DB에 추가하는 메서드를 정의하고 있습니다. @Override 키워드를 사용하여 PersonDao 인터페이스의 insertPerson 메서드를 구현하고 있습니다.</p>
</li>
<li><p>DB.add(new Person(id, person.getName()));: DB에 새로운 Person 객체를 생성하여 추가하는 코드입니다. 생성된 Person 객체는 매개변수로 전달받은 id와 person 객체의 이름으로 초기화됩니다.</p>
</li>
<li><p>return 1;: 메서드가 성공적으로 실행되었음을 나타내기 위해 1을 반환합니다.</p>
</li>
</ul>
<p>위의 코드는 FakePersonDataAccessService 클래스가 PersonDao 인터페이스를 구현하여 insertPerson 메서드를 재정의하고, 해당 메서드를 사용하여 Person 객체를 DB에 추가하는 기능을 제공합니다. 이를 통해 데이터 액세스 서비스를 가짜로 구현하여 테스트나 개발 환경에서 사용할 수 있습니다.</p>
<h2 id="service-정의">Service 정의</h2>
<pre><code class="language-java">package com.example.demo.service;

import com.example.demo.dao.PersonDao;
import com.example.demo.model.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class PersonService {

    private final PersonDao personDao;

    @Autowired
    public PersonService(@Qualifier(&quot;fakeDao&quot;) PersonDao personDao) {
        this.personDao = personDao;
    }

    public int addPerson(Person person){
        return personDao.insertPerson(person);
    }
}
</code></pre>
<p>위의 코드는 com.example.demo.service 패키지에 있는 PersonService 클래스를 정의하는 예시입니다. 해당 클래스는 다음과 같은 멤버 변수와 생성자, 메서드를 가지고 있습니다:</p>
<ul>
<li><p>private final PersonDao personDao: PersonDao 인터페이스를 구현한 객체를 저장하기 위한 변수입니다.</p>
</li>
<li><p>@Autowired 어노테이션을 사용한 생성자: PersonDao 구현체를 주입받습니다. @Qualifier 어노테이션을 사용하여 PersonDao의 구현체 중 &quot;fake&quot;로 지정된 빈을 주입받습니다. 이렇게 주입받은 PersonDao 객체는 personDao 멤버 변수에 저장됩니다.</p>
</li>
<li><p>addPerson(Person person) 메서드: Person 객체를 인자로 받아 personDao.insertPerson() 메서드를 호출하여 해당 객체를 저장합니다. 저장한 결과를 반환합니다.</p>
</li>
</ul>
<p>PersonService 클래스는 비즈니스 로직을 담당하는 서비스 클래스입니다. PersonDao 인터페이스를 주입받아 데이터 저장에 사용하고, addPerson(Person person) 메서드를 통해 Person 객체를 저장하는 기능을 제공합니다. @Service 어노테이션을 사용하여 스프링에게 해당 클래스가 서비스 역할을 수행하는 빈임을 알려줍니다. 이렇게 분리된 서비스 계층은 컨트롤러와 데이터 액세스 계층 간의 중간 역할을 수행하며, 비즈니스 로직을 캡슐화하여 유지보수와 테스트를 용이하게 합니다.</p>
<hr>
<h2 id="controller-정의">Controller 정의</h2>
<pre><code class="language-java">package com.example.demo.api;

import com.example.demo.model.Person;
import com.example.demo.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping(&quot;/api/v1/persion&quot;)
@RestController
public class PersonController {
    private final PersonService personService;

    @Autowired
    public PersonController(PersonService personService) {
        this.personService = personService;
    }

    @PostMapping
    public void addPerson(
            @RequestBody Person person){
        personService.addPerson(person);
    }
}
</code></pre>
<h3 id="설명">설명</h3>
<h4 id="임포트">임포트</h4>
<ul>
<li><p>com.example.demo.api 패키지에 PersonController 클래스가 속해 있음을 나타냅니다.</p>
</li>
<li><p>com.example.demo.model.Person과 com.example.demo.service.PersonService를 import 하고 있습니다. 이는 해당 클래스들을 사용하기 위해 필요한 import 문입니다.</p>
</li>
<li><p>@RestController 어노테이션은 해당 클래스가 REST 컨트롤러임을 나타냅니다. 이 어노테이션은 Spring MVC에서 제공하는 @Controller와 @ResponseBody 어노테이션의 결합입니다.</p>
</li>
<li><p>@RequestMapping(&quot;/api/v1/person&quot;) 어노테이션은 해당 컨트롤러의 엔드포인트 경로를 /api/v1/person으로 설정합니다. 이는 클라이언트가 해당 경로로 HTTP 요청을 보낼 때 이 컨트롤러에서 처리할 수 있음을 의미합니다.</p>
</li>
<li><p>PersonService를 주입받기 위해 생성자 인젝션을 사용합니다. @Autowired 어노테이션은 Spring에게 PersonService의 인스턴스를 주입하도록 지시합니다.</p>
</li>
<li><p>@PostMapping 어노테이션은 해당 메서드가 POST 요청을 처리함을 나타냅니다. 클라이언트가 POST 요청을 /api/v1/person 엔드포인트로 보낼 때 이 메서드가 호출됩니다.</p>
</li>
<li><p>@RequestBody 어노테이션은 요청 본문에 포함된 데이터를 Person 객체로 매핑합니다. 이는 클라이언트가 POST 요청 본문에 JSON 형식으로 Person 객체를 전송할 수 있도록 합니다.</p>
</li>
<li><p>addPerson 메서드는 PersonService의 addPerson 메서드를 호출하여 Person 객체를 추가합니다. 이는 비즈니스 로직을 PersonService에서 처리하도록 위임하는 역할을 합니다.</p>
</li>
</ul>
<p>위의 코드는 클라이언트가 POST 요청을 /api/v1/person 엔드포인트로 보내면 요청 본문에 포함된 Person 객체를 PersonService를 통해 추가하는 기능을</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] java - 2]]></title>
            <link>https://velog.io/@hyeseong-dev/TIL-java-2</link>
            <guid>https://velog.io/@hyeseong-dev/TIL-java-2</guid>
            <pubDate>Wed, 17 May 2023 02:15:31 GMT</pubDate>
            <description><![CDATA[<h3 id="1-q-생성자는-메서드의-일부분이야">1. Q 생성자는 메서드의 일부분이야?</h3>
<h4 id="네-생성자는-자바에서-메서드의-일부분입니다-생성자는-클래스-내부에-선언되는-특수한-종류의-메서드이며-객체인스턴스를-초기화하는-역할을-합니다">네, 생성자는 자바에서 메서드의 일부분입니다. 생성자는 클래스 내부에 선언되는 특수한 종류의 메서드이며, 객체(인스턴스)를 초기화하는 역할을 합니다.</h4>
<p>생성자와 일반적인 메서드의 차이점은 다음과 같습니다:</p>
<p>이름: 생성자의 이름은 클래스의 이름과 동일합니다. 이를 통해 생성자는 객체를 생성할 때 호출되며, 객체를 초기화하는 역할을 수행합니다. 메서드는 임의의 이름을 가지며, 호출될 때 해당 메서드의 이름을 사용합니다.</p>
<p>반환 값: 생성자는 반환 값이 없는 특징을 가지고 있습니다. 반환 타입을 명시적으로 선언하지 않으며, 생성자가 호출될 때에는 생성된 객체(인스턴스) 자체를 반환합니다. 일반적인 메서드는 반환 타입을 명시하고, 해당 타입의 값을 반환합니다.</p>
<p>호출 방법: 생성자는 new 키워드를 사용하여 객체를 생성할 때 자동으로 호출됩니다. 객체를 생성할 때 생성자를 명시적으로 호출해야 하며, 생성자가 호출되면 해당 객체의 초기화 작업이 수행됩니다. 일반적인 메서드는 호출 시에 메서드 이름을 사용하여 호출합니다.</p>
<p>오버로딩: 생성자는 오버로딩(Overloading)을 통해 여러 개의 생성자를 가질 수 있습니다. 오버로딩은 같은 이름의 메서드나 생성자를 매개변수의 유형 또는 개수에 따라 다르게 선언하는 것을 의미합니다. 이를 통해 다양한 초기화 방법을 제공할 수 있습니다.</p>
<p>따라서 생성자는 메서드의 일종으로, 클래스 내부에 선언되어 객체의 초기화를 담당하는 특수한 형태의 메서드입니다.</p>
<h3 id="2-q-생성자는-몇개를-최대-가질수-있어">2. Q 생성자는 몇개를 최대 가질수 있어?</h3>
<h4 id="자바에서-클래스는-여러-개의-생성자를-가질-수-있습니다-생성자는-클래스-내부에-선언되는-특별한-메서드이며-객체인스턴스를-초기화하는-역할을-수행합니다">자바에서 클래스는 여러 개의 생성자를 가질 수 있습니다. 생성자는 클래스 내부에 선언되는 특별한 메서드이며, 객체(인스턴스)를 초기화하는 역할을 수행합니다.</h4>
<p>클래스는 기본적으로 아무 생성자도 선언하지 않았을 경우, 컴파일러에 의해 기본 생성자(default constructor)가 자동으로 생성됩니다. 그러나 클래스에 명시적으로 생성자를 선언하면 기본 생성자가 자동으로 생성되지 않습니다.</p>
<p>클래스는 필요한 만큼의 생성자를 가질 수 있으며, 다양한 매개변수 유형 및 개수를 가진 생성자를 정의할 수 있습니다. 이를 통해 다양한 초기화 방법을 제공하고 객체 생성 시에 필요한 값을 전달할 수 있습니다.</p>
<p>예를 들어, 다음과 같이 클래스에 여러 개의 생성자를 선언할 수 있습니다:</p>
<pre><code class="language-java">public class MyClass {
    // 기본 생성자
    public MyClass() {
        // 초기화 작업
    }

    // 매개변수가 있는 생성자
    public MyClass(int value) {
        // 초기화 작업
    }

    // 다른 매개변수 유형을 가진 생성자
    public MyClass(String name, int age) {
        // 초기화 작업
    }
}</code></pre>
<p>위의 예시에서 MyClass 클래스는 기본 생성자와 매개변수가 있는 생성자, 그리고 다른 매개변수 유형을 가진 생성자를 가지고 있습니다. 이와 같이 클래스는 필요한 만큼의 생성자를 가질 수 있으며, 객체 생성 시에 적합한 생성자를 사용하여 초기화 작업을 수행할 수 있습니다.</p>
<h3 id="3-q-자바에서-생성자의-오버로딩이-뭐야">3. Q 자바에서 생성자의 오버로딩이 뭐야?</h3>
<p>자바에서 생성자의 오버로딩(Overloading)은 같은 이름을 가지면서 매개변수의 유형 또는 개수가 다른 여러 개의 생성자를 정의하는 것을 말합니다.</p>
<p>생성자 오버로딩을 통해 동일한 클래스 내에서 다양한 초기화 방법을 제공할 수 있습니다. 매개변수의 유형 또는 개수에 따라 호출될 생성자가 자동으로 선택되며, 객체 생성 시에 필요한 인자를 전달하여 생성자를 호출할 수 있습니다.</p>
<p>예를 들어, 다음은 생성자 오버로딩을 사용하여 여러 개의 생성자를 정의하는 예시입니다:</p>
<pre><code class="language-java">public class MyClass {
    private int value;

    public MyClass() {
        // 매개변수가 없는 생성자
        this.value = 0;
    }

    public MyClass(int value) {
        // int 타입의 매개변수를 받는 생성자
        this.value = value;
    }

    public MyClass(String name, int age) {
        // String과 int 타입의 매개변수를 받는 생성자
        this.value = age * 10;
    }
}</code></pre>
<p>위의 예시에서 MyClass 클래스는 여러 개의 생성자를 가지고 있으며, 매개변수의 유형과 개수에 따라 생성자를 오버로딩하였습니다. 각 생성자는 다양한 초기화 방법을 제공하며, 객체 생성 시에 호출될 생성자는 전달하는 인자의 유형과 개수에 따라 자동으로 선택됩니다.</p>
<p>예를 들어, 다음과 같이 생성자 오버로딩을 활용하여 객체를 생성할 수 있습니다:</p>
<pre><code class="language-java">MyClass obj1 = new MyClass();                // 매개변수가 없는 생성자 호출
MyClass obj2 = new MyClass(10);              // int 타입의 매개변수를 받는 생성자 호출
MyClass obj3 = new MyClass(&quot;John&quot;, 25);      // String과 int 타입의 매개변수를 받는 생성자 호출</code></pre>
<p>각 객체 생성 시에 전달하는 인자에 맞는 생성자가 자동으로 선택되어 호출되며, 해당 생성자에 정의된 초기화 작업이 수행됩니다. 생성자 오버로딩을 통해 다양한 초기화 방법을 제공할 수 있습니다.</p>
<h3 id="4-q-오버로딩이-지원-no">4. Q 오버로딩이 지원 NO</h3>
<ul>
<li><p><code>변수와 필드</code>: 변수나 필드의 경우, 같은 이름을 가지고 매개변수의 유형 또는 개수가 다르더라도 오버로딩이 지원되지 않습니다. 변수나 필드는 유일한 식별자로서 중복될 수 없습니다.</p>
</li>
<li><p><code>지역 변수</code>: 메서드나 블록 내에서 선언된 지역 변수의 경우, 같은 이름을 가지고 매개변수의 유형 또는 개수가 다른 형태로 오버로딩할 수 없습니다. 지역 변수도 유일한 식별자로서 중복될 수 없습니다.</p>
</li>
<li><p><code>메서드 반환 유형</code>: 메서드의 반환 유형은 오버로딩에 영향을 주지 않습니다. 즉, 같은 이름의 메서드는 매개변수의 유형 또는 개수가 다르더라도 반환 유형이 다른 경우에도 오버로딩이 지원되지 않습니다.</p>
</li>
<li><p><code>예외 타입</code>: 메서드가 예외를 던지는 경우, 예외 타입만 다르고 매개변수의 유형 또는 개수가 동일한 메서드를 오버로딩할 수 없습니다. 예외 타입은 메서드 시그니처의 일부가 아니기 때문에 오버로딩에 영향을 주지 않습니다.</p>
</li>
<li><p><code>생성자와 메서드의 매개변수 이름</code>: 오버로딩에는 매개변수의 이름이 영향을 주지 않습니다. 같은 이름의 매개변수를 가지고 매개변수의 유형 또는 개수가 다른 생성자나 메서드를 오버로딩할 수 없습니다.</p>
</li>
</ul>
<h3 id="5-q-오버로딩-지원-ok">5. Q 오버로딩 지원 OK</h3>
<ul>
<li><code>생성자</code>: 클래스 내의 생성자는 같은 이름을 가지고 매개변수의 유형 또는 개수가 다르면 오버로딩될 수 있습니다.</li>
<li><code>메서드</code> : 클래스 내의 메서드도 같은 이름을 가지고 매개변수의 유형 또는 개수가 다르면 오버로딩될 수 있습니다.</li>
<li><code>연산자</code> : 특정 연산자에 대해서도 오버로딩이 가능합니다. 예를 들어, + 연산자는 정수형, 실수형, 문자열 등 다양한 유형에 대해서 오버로딩될 수 있습니다.</li>
<li><code>캐스팅</code> : 타입 캐스팅에 대해서도 오버로딩이 가능합니다. 다양한 유형 간의 캐스팅 연산을 오버로딩할 수 있습니다.</li>
<li><code>가변 인자</code>:  메서드 매개변수에 가변 인자(varargs)를 사용할 경우, 해당 메서드는 인자 개수에 따라 오버로딩될 수 있습니다.</li>
<li><code>생성자와 메서드의 접근 제어자</code> : 생성자와 메서드의 접근 제어자도 오버로딩에 영향을 줄 수 있습니다. 같은 이름을 가지고 매개변수의 유형 또는 개수가 다르면서, 접근 제어자가 다른 생성자나 메서드를 정의할 수 있습니다.</li>
<li><code>예외 던지기</code> : 메서드가 예외를 던지는 경우, 예외 타입에 따라 다른 메서드를 오버로딩할 수 있습니다.</li>
<li><code>정적 메서드</code>: 정적 메서드도 같은 이름을 가지고 매개변수의 유형 또는 개수가 다르면 오버로딩될 수 있습니다.</li>
<li><code>인터페이스 메서드</code> : 인터페이스 내에 메서드도 오버로딩이 가능합니다. 동일한 인터페이스 내에서 같은 이름의 메서드를 매개변수의 유형 또는 개수가 다른 형태로 정의할 수 있습니다.</li>
<li><code>추상 클래스의 추상 메서드</code> : 추상 클래스 내에도 오버로딩이 가능한 추상 메서드를 정의할 수 있습니다. 동일한 추상 클래스 내에서 같은 이름의 추상 메서드를 매개변수의 유형 또는 개수가 다른 형태로 정의할 수 있습니다.</li>
<li><code>상속 관계에서의 메서드 오버로딩</code> : 상속 관계에 있는 클래스들 간에도 메서드 오버로딩이 가능합니다. 부모 클래스의 메서드와 동일한 이름의 메서드를 자식 클래스에서 매개변수의 유형 또는 개수가 다른 형태로 정의할 수 있습니다.</li>
<li><code>내부 클래스</code> : 내부 클래스도 오버로딩이 가능합니다. 내부 클래스는 외부 클래스 내에서 정의되며, 같은 이름을 가지고 매개변수의 유형 또는 개수가 다른 여러 개의 내부 클래스를 정의할 수 있습니다.</li>
<li><code>제네릭 메서드</code> : 제네릭 메서드는 메서드 시그니처에 타입 매개변수를 포함하는 메서드입니다. 제네릭 메서드도 매개변수의 유형 또는 개수에 따라 오버로딩될 수 있습니다.</li>
<li><code>배열</code> : 배열도 오버로딩이 가능합니다. 다른 유형의 배열을 선언하여 오버로딩된 형태로 사용할 수 있습니다.</li>
<li><code>열거형</code> : 열거형은 오버로딩이 가능합니다. 열거형 상수를 처리하기 위한 다양한 메서드를 오버로딩하여 정의할 수 있습니다.</li>
<li><code>Setter와 Getter 메서드</code> : 클래스에서 인스턴스 변수에 대한 값을 설정하거나 가져오는 Setter와 Getter 메서드도 오버로딩될 수 있습니다. 동일한 이름을 가지고 매개변수의 유형 또는 개수가 다른 Setter와 Getter 메서드를 정의할 수 있습니다.</li>
<li><code>생성자의 체이닝</code>:생성자를 체이닝하여 오버로딩할 수 있습니다. 한 생성자에서 다른 생성자를 호출하여 초기화하는 방식으로 오버로딩을 구현할 수 있습니다.</li>
<li><code>Lambda 표현식</code>: 자바 8 이상에서는 Lambda 표현식을 사용하여 함수형 인터페이스를 구현할 수 있습니다. Lambda 표현식은 매개변수의 유형 또는 개수에 따라 오버로딩될 수 있습니다.</li>
<li><code>메서드 참조</code> : 메서드 참조는 메서드의 참조를 전달하는 방식으로 동작합니다. 메서드 참조는 매개변수의 유형 또는 개수에 따라 오버로딩될 수 있습니다.</li>
<li><code>제네릭 클래스</code> : 제네릭 클래스를 정의할 때, 동일한 클래스 이름을 가지고 매개변수의 유형이나 개수에 따라 다른 유형의 클래스를 생성할 수 있습니다.</li>
</ul>
<h3 id="6-q-기본-생성자를-호출하지-않고-클래스를-이용할-수-있는지">6. Q. 기본 생성자를 호출하지 않고 클래스를 이용할 수 있는지?</h3>
<p>기본 생성자를 호출하지 않고 클래스를 사용하는 것은 일반적으로는 불가능합니다. 생성자는 객체의 초기화를 담당하며, 객체를 생성할 때 반드시 호출되어야 합니다. 따라서 클래스를 사용하려면 해당 클래스의 생성자를 호출하여 객체를 생성해야 합니다.</p>
<p>그러나 클래스의 정적(static) 멤버들(정적 변수 또는 정적 메서드)은 객체의 인스턴스화 없이 사용할 수 있습니다. 정적 멤버들은 클래스 수준에서 존재하므로, 클래스 이름을 통해 직접 접근할 수 있습니다.</p>
<p>예를 들어, 다음과 같이 클래스의 정적 멤버를 사용할 수 있습니다:</p>
<pre><code class="language-java">public class MyClass {
    public static int myStaticVariable = 10;
    public static void myStaticMethod() {
        System.out.println(&quot;Static method called&quot;);
    }
}

// 정적 멤버 사용
int value = MyClass.myStaticVariable;
MyClass.myStaticMethod();</code></pre>
<p>위의 코드에서는 MyClass의 객체를 생성하지 않고도 myStaticVariable과 myStaticMethod()을 직접 사용할 수 있습니다. 하지만 일반적인 멤버 변수나 인스턴스 메서드를 사용하려면 해당 클래스의 객체를 생성해야 합니다.</p>
<h3 id="7-q-자바에서-접근-지정자가-유용하게-사용되는-케이스는">7. Q 자바에서 접근 지정자가 유용하게 사용되는 케이스는?</h3>
<p>자바에서 접근 지정자는 클래스 멤버들의 접근을 제어하는 데 사용됩니다. 다른 클래스나 모듈에서 어떤 멤버에 접근 가능한지를 명확하게 정의함으로써 코드의 가독성, 유지 보수성, 안정성을 향상시킬 수 있습니다. 아래는 접근 지정자가 유용하게 사용되는 몇 가지 케이스입니다:</p>
<ul>
<li><p><code>캡슐화(Encapsulation)</code>: 접근 지정자를 사용하여 클래스의 내부 상태를 숨길 수 있습니다. 클래스의 내부 구현 세부 사항을 외부로부터 감추고, 필요한 기능에 대한 인터페이스만 노출함으로써 객체 지향적인 캡슐화를 달성할 수 있습니다.</p>
</li>
<li><p><code>정보 은닉(Information Hiding)</code>: 접근 지정자를 통해 멤버 변수를 private로 선언하고, 해당 변수에 대한 접근을 제한할 수 있습니다. 외부에서 직접적으로 접근하지 못하도록 함으로써 데이터의 무결성과 보안을 유지할 수 있습니다.</p>
</li>
<li><p><code>모듈화(Modularity)</code>: 접근 지정자를 사용하여 클래스의 내부를 모듈화할 수 있습니다. 클래스를 작은 부분으로 분할하고, 각 부분에 필요한 접근 수준을 부여함으로써 코드의 모듈화와 재사용성을 높일 수 있습니다.</p>
</li>
<li><p><code>상속과 다형성 관리</code>: 접근 지정자를 사용하여 상속과 다형성을 관리할 수 있습니다. protected 접근 지정자를 사용하여 서브클래스에서만 접근 가능한 멤버를 정의하거나, public 접근 지정자를 사용하여 외부에서 접근 가능한 메서드를 정의할 수 있습니다.</p>
</li>
<li><p><code>API 설계</code>: 접근 지정자를 사용하여 외부에 노출되는 클래스, 메서드 및 변수를 명시적으로 정의할 수 있습니다. API의 일부를 public으로 선언하여 외부에서 사용 가능한 인터페이스를 제공하고, 나머지 부분은 private으로 선언하여 내부 구현을 감출 수 있습니다.</p>
</li>
</ul>
<p>위의 케이스들은 접근 지정자를 사용하여 <code>코드를 더 구조화하고, 다른 개발자들과의 협업을 원활하게 만들며, 코드의 유지 보수성을 향상</code>시키는 데 도움을 줍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] java - 1]]></title>
            <link>https://velog.io/@hyeseong-dev/TIL-%EC%9E%84%EC%8B%9C%EA%B8%80</link>
            <guid>https://velog.io/@hyeseong-dev/TIL-%EC%9E%84%EC%8B%9C%EA%B8%80</guid>
            <pubDate>Tue, 16 May 2023 00:25:13 GMT</pubDate>
            <description><![CDATA[<p>[TIL] 임시글</p>
<h2 id="질문-1">질문-1</h2>
<h3 id="q--파일-다운로드를-할-경우-가끔식-msi-확장자가-있는데-뭐야">Q : 파일 다운로드를 할 경우 가끔식 msi 확장자가 있는데 뭐야?</h3>
<ul>
<li><code>MSI</code>는 <code>&quot;Microsoft Installer&quot;의 약자</code>로, Microsoft Windows 운영 체제에서 사용되는 <code>설치 프로그램 파일 형식</code>입니다. MSI 파일은 소프트웨어를 설치, 구성 및 제거하기 위해 사용됩니다.</li>
</ul>
<p>MSI 파일은 일반적으로 소프트웨어 회사나 개발자가 제공하는 공식 설치 프로그램입니다. 이 파일을 실행하면 해당 소프트웨어를 설치하고 컴퓨터 시스템에 필요한 파일과 설정을 자동으로 구성합니다.</p>
<p>MSI 파일은 다른 설치 파일 형식에 비해 일반적으로 더욱 편리하고 간편하게 사용할 수 있습니다. 사용자는 단순히 MSI 파일을 더블 클릭하여 설치 프로세스를 시작할 수 있으며, 설치 마법사가 안내하는 단계를 따라 소프트웨어를 설치할 수 있습니다.</p>
<p>따라서, 파일 다운로드 시 확장자가 MSI인 경우 해당 파일은 소프트웨어를 설치하기 위한 파일일 가능성이 높습니다.</p>
<h2 id="질문---2">질문 - 2</h2>
<h3 id="java-hello-world-찍기-오류">java hello world 찍기 오류</h3>
<pre><code class="language-bash">C:\Users\DT-PC-30\workSpace\src\Main.java:3:19
java: cannot find symbol
  symbol:   method prinln(java.lang.String)
  location: variable out of type java.io.PrintStream\</code></pre>
<ul>
<li><p>주어진 오류 메시지를 보면 <code>java: cannot find symbol</code>이며, <code>symbol: method prinln(java.lang.String)</code>과 <code>location: variable out of type java.io.PrintStream</code>이라고 나와 있습니다. <code>이 오류는 prinln 메서드를 찾을 수 없다는 의미입니다.</code></p>
<p>오류 메시지에서 <code>prinln</code>이라고 적혀 있는데, <code>실제로는 println으로 적어야 합니다.</code> 따라서, println 메서드를 사용해야 합니다. 메서드명에 오타가 있는 것 같습니다.</p>
<p>이 오류를 수정하려면 println 메서드를 올바르게 사용하도록 코드를 수정해야 합니다. println 메서드는 System.out 객체를 사용하여 호출할 수 있습니다. 다음과 같이 코드를 수정해 보세요:</p>
</li>
</ul>
<pre><code class="language-java">System.out.println(&quot;Hello, World!&quot;);</code></pre>
<h2 id="질문---3">질문 - 3</h2>
<h3 id="질문---2에서-말하는-symbol이란">질문 - 2에서 말하는 symbol이란?</h3>
<ul>
<li>symbol은 프로그래밍에서 변수, 함수, 클래스, 인터페이스 등과 같은 개체를 가리키는 일반적인 용어입니다. 코드에서 symbol은 특정한 개체를 식별하고 참조하는 데 사용됩니다.</li>
</ul>
<p>cannot find symbol 오류는 컴파일러가 소스 코드에서 참조하려는 식별자를 찾을 수 없을 때 발생합니다. 즉, 해당 식별자(변수, 함수 등)에 대한 정의가 없거나 오타가 있는 경우에 이 오류가 발생합니다. 컴파일러는 소스 코드를 분석하여 심볼 테이블(symbol table)에 등록된 식별자를 찾습니다. 그러나 해당 식별자가 정의되지 않았거나 올바르게 입력되지 않은 경우에는 cannot find symbol 오류가 발생합니다.</p>
<h2 id="질문---3-1">질문 - 3</h2>
<h3 id="질문---2에서-말하는-symbol이란-1">질문 - 2에서 말하는 symbol이란?</h3>
<ul>
<li><p>symbol은 프로그래밍에서 변수, 함수, 클래스, 인터페이스 등과 같은 개체를 가리키는 일반적인 용어입니다. 코드에서 symbol은 특정한 개체를 식별하고 참조하는 데 사용됩니다.</p>
<p><code>cannot find symbol</code> 오류는 컴파일러가 소스 코드에서 참조하려는 <code>식별자</code>를 찾을 수 없을 때 발생합니다. 즉, 해당 식별자(<code>변수, 함수 등</code>)에 대한 <code>정의가 없거나</code> <code>오타</code>가 있는 경우에 이 오류가 발생합니다. 컴파일러는 소스 코드를 분석하여 <code>심볼 테이블(symbol table)에 등록된 식별자를 찾습니다</code>. 그러나 해당 식별자가 정의되지 않았거나 올바르게 입력되지 않은 경우에는 cannot find symbol 오류가 발생합니다.</p>
</li>
</ul>
<h2 id="질문---4">질문 - 4</h2>
<h3 id="윈도우-단축키-중-현재-활성화된-창을-최소화-하는-단축키는">윈도우 단축키 중 현재 활성화된 창을 최소화 하는 단축키는?</h3>
<ul>
<li><code>Alt + Space, N</code> : 현재 창을 최소화합니다.</li>
</ul>
<h2 id="질문---5">질문 - 5</h2>
<h3 id="자바에서-사용하는-데이터-타입들은-뭐가-있을까">자바에서 사용하는 데이터 타입들은 뭐가 있을까?</h3>
<h4 id="기본-데이터-타입-primitive-data-types">기본 데이터 타입 (Primitive Data Types)</h4>
<ol>
<li>boolean: 참(true) 또는 거짓(false) 값을 나타내는 논리적인 데이터 타입.</li>
<li>byte: 8비트 정수 값을 나타내는 데이터 타입.</li>
<li>short: 16비트 정수 값을 나타내는 데이터 타입.</li>
<li>int: 32비트 정수 값을 나타내는 데이터 타입.</li>
<li>long: 64비트 정수 값을 나타내는 데이터 타입.</li>
<li>float: 단정밀도 32비트 부동소수점 값을 나타내는 데이터 타입.</li>
<li>double: 배정밀도 64비트 부동소수점 값을 나타내는 데이터 타입.</li>
<li>char: 유니코드 문자를 나타내는 16비트 문자 데이터 타입.</li>
</ol>
<h4 id="참조-데이터-타입-reference-data-types">참조 데이터 타입 (Reference Data Types)</h4>
<ol>
<li>String: 문자열을 나타내는 클래스로, 문자열 데이터를 다루는 데 사용됩니다.</li>
<li>Array: 배열을 나타내는 데이터 타입으로, 동일한 타입의 여러 값을 저장하는 데 사용됩니다.</li>
<li>Class: 클래스를 나타내는 데이터 타입으로, 객체의 특성과 동작을 정의하는데 사용됩니다.</li>
<li>Interface: 인터페이스를 나타내는 데이터 타입으로, 구현 클래스에 대한 규약을 정의하는데 사용됩니다.</li>
<li>Enum: 열거형을 나타내는 데이터 타입으로, 사전에 정의된 상수 집합을 표현하는데 사용됩니다.</li>
<li>Object: 모든 클래스의 최상위 부모 클래스로, 모든 객체를 참조하기 위한 데이터 타입입니다.
이러한 데이터 타입들은 Java에서 다양한 용도로 사용됩니다. 기본 데이터 타입은 원시 값을 저장하고, 참조 데이터 타입은 객체나 복잡한 데이터 구조를 다룰 때 사용됩니다.</li>
</ol>
<h2 id="질문---6">질문 - 6</h2>
<h3 id="java에서-final키워드는-뭐야">java에서 <code>final</code>키워드는 뭐야?</h3>
<h4 id="변수">변수</h4>
<ul>
<li>final 변수는 한 번 초기화된 후에는 <code>변경할 수 없는 상수</code>입니다.
초기화된 이후에는 <code>다른 값을 할당할 수 없으며, 읽기 전용</code>으로 사용됩니다.
final 변수는 보통 <code>대문자와 언더스코어(_)를 사용</code>하여 이름을 지정합니다.
예를 들어: final int MAX_COUNT = 10;</li>
</ul>
<h4 id="메소드">메소드</h4>
<ul>
<li>final 메소드는 하위 클래스에서 <code>오버라이드(재정의)할 수 없는 메소드</code>입니다.
final 메소드는 상위 클래스에서 이미 정의되고 구현된 메소드를 그대로 사용합니다.
<code>하위 클래스에서 동일한 시그니처(메소드 이름, 매개변수 유형, 반환 유형)로</code> <code>메소드를 오버라이드하는 것을 방지</code>합니다.
예를 들어: <code>public final void printMessage() { /* 메소드 구현 */ }</code></li>
</ul>
<h4 id="클래스">클래스</h4>
<ul>
<li>final 클래스는 <code>상속될 수 없는 클래스</code>입니다.
final 클래스는 다른 클래스가 이를 상속하여 확장할 수 없도록 제한합니다.
예를 들어: public final class MyClass { /* 클래스 내용 */ }</li>
</ul>
<p>final 키워드를 사용하여 변수, 메소드 또는 클래스를 선언하면 해당 요소는 변경되거나 상속될 수 없습니다. 이를 통해 상수, 불변성, 보안성 또는 메소드/클래스의 안정성을 보장할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] Rust 문자열]]></title>
            <link>https://velog.io/@hyeseong-dev/TIL-Rust-%EB%AC%B8%EC%9E%90%EC%97%B4</link>
            <guid>https://velog.io/@hyeseong-dev/TIL-Rust-%EB%AC%B8%EC%9E%90%EC%97%B4</guid>
            <pubDate>Mon, 15 May 2023 08:47:19 GMT</pubDate>
            <description><![CDATA[<h2 id="1-질문-바이트-문자열의-타입은-왜-u8이야">1. 질문: 바이트 문자열의 타입은 왜 u8이야?</h2>
<p>바이트 문자열의 타입이 u8인 이유는 u8이 <code>0에서 255</code>까지의 값을 나타낼 수 있는 가장 작은 <code>unsigned 정수형</code>이기 때문입니다.</p>
<p><code>바이트 문자열</code>은 기본적으로 <code>ASCII 문자</code>의 <code>시퀀스</code>로, 각 <code>ASCII 문자</code>는 <code>0에서 255 사이의 값</code>으로 나타낼 수 있습니다. 따라서 각 <code>ASCII 문자를 나타내는 데 u8 타입</code>을 사용하는 것이 <code>가장 효율적</code>입니다.</p>
<p>또한, <code>u8은 컴퓨터에서 가장 기본적인 데이터 단위</code>인 <code>바이트와 일치</code>합니다. 이것은 <code>바이트 문자열</code>이 <code>이진 데이터를 처리하고 저장하는 데 유용</code>하게 만듭니다.</p>
<p>Rust에서 바이트 문자열은 b&quot;Hello&quot;와 같이 b 접두사를 사용하여 표현되고, 이것은 [u8; 5] 타입을 가진 <code>배열로 저장</code>됩니다. 이는 <code>각 문자가 ASCII로 인코딩된 u8 값으로 변환되어</code> <code>배열에 저장</code>되기 때문입니다.</p>
<h2 id="2-질문-바이트-문자열이-자동으로-배열로-형변환-된다는-말이야">2. 질문: 바이트 문자열이 자동으로 배열로 형변환 된다는 말이야?</h2>
<p>네, 맞습니다. <code>Rust에서 바이트 문자열</code>은 <code>바이트 배열</code>로 처리됩니다. <code>b 접두사를 사용</code>하여 <code>문자열을 선언</code>하면, <code>컴파일러는 이를 바이트 배열로 자동 변환</code>합니다. 예를 들어,</p>
<pre><code class="language-rust">let hello = b&quot;Hello&quot;;</code></pre>
<p>이 코드는 hello 변수를 바이트 배열로 선언합니다. 이 배열의 타입은 [u8; 5]입니다. 이는 각 ASCII 문자가 u8 타입의 값으로 변환되어 배열에 저장되기 때문입니다. 이 배열은 각각 &#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39; 문자의 ASCII 값으로 채워집니다.</p>
<p>따라서 바이트 문자열은 자동으로 바이트 배열로 변환되며, 이는 <code>Rust의 타입 시스템에서 자동으로 처리</code>됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[러스트로 만드는 채팅앱]]></title>
            <link>https://velog.io/@hyeseong-dev/%EB%9F%AC%EC%8A%A4%ED%8A%B8%EB%A1%9C-%EB%A7%8C%EB%93%9C%EB%8A%94-%EC%B1%84%ED%8C%85%EC%95%B1</link>
            <guid>https://velog.io/@hyeseong-dev/%EB%9F%AC%EC%8A%A4%ED%8A%B8%EB%A1%9C-%EB%A7%8C%EB%93%9C%EB%8A%94-%EC%B1%84%ED%8C%85%EC%95%B1</guid>
            <pubDate>Thu, 11 May 2023 08:35:26 GMT</pubDate>
            <description><![CDATA[<p>참고 : <a href="https://book.async.rs/tutorial/specification.html">https://book.async.rs/tutorial/specification.html</a></p>
<h1 id="specification--getting-started">Specification &amp; Getting Started</h1>
<h2 id="specification">Specification</h2>
<p>채팅은 <code>TCP</code>를 통한 간단한 <code>텍스트</code> <code>프로토콜</code>을 사용합니다. 프로토콜은 <code>\n</code>으로 구분된 <code>utf-8</code> 메시지로 구성됩니다.</p>
<p>client는 server에 연결하고 login을 첫 번째 라인으로 보냅니다. 이 후 client는 다음 구문을 사용하여 다른 client에 메시지를 보낼 수 있습니다.</p>
<pre><code>login1, login2, ... loginN: message
</code></pre><p>각 클라이언트는 <code>from login: message</code> 메시지를 수신합니다.</p>
<p>터미널 화면에서 아래와 같이 비슷하게 보일겁니다. </p>
<pre><code class="language-text">On Alice&#39;s computer:   |   On Bob&#39;s computer:

&gt; alice                |   &gt; bob
&gt; bob: hello               &lt; from alice: hello
                       |   &gt; alice, bob: hi!
                           &lt; from bob: hi!
&lt; from bob: hi!        |
</code></pre>
<p>채팅 서버가 해야하는 중요한 일들 중 하나는 <code>많은 동시 연결을 추적하는 것</code>입니다. 채팅 client의 주요 과제는 <code>동시 발신 메시지</code>, <code>수신 메시지</code> 및 <code>사용자 입력</code>을 <code>관리</code>하는 것입니다.</p>
<h2 id="getting-started">Getting Started</h2>
<p>카고를 이용하여 프로젝트를 생성합니다. </p>
<pre><code class="language-rust">$ cargo new a-chat
$ cd a-chat</code></pre>
<p><code>Cargo.toml</code>파일에 아래 라인을 추가합니다. </p>
<pre><code class="language-toml">[dependencies]
futures = &quot;0.3.0&quot;
async-std = &quot;1&quot;
</code></pre>
<h2 id="writing-an-accept-loop">Writing an Accept Loop</h2>
<p><strong>서버 구현을 위한 작업을 하겠습니다.</strong> </p>
<ul>
<li>TCP 소켓을 address에 bind고 연결 수락을 시작하는 loop. </li>
</ul>
<pre><code class="language-rust">#![allow(unused)]

use async_std::{
    io::{BufReader, BufWriter},
    net::{TcpListener, TcpStream, ToSocketAddrs},
    prelude::*,
    task,
};

type Result&lt;T&gt; = std::result::Result&lt;T, Box&lt;dyn std::error::Error + Send + Sync&gt;&gt;;</code></pre>
<ul>
<li><code>async_std</code> 라이브러리에서 비동기 작업을 수행하기 위한 일부 모듈과 네트워킹 관련 <code>모듈을 가져오는 코드</code>입니다. 이를 통해 러스트에서 비동기 소켓 프로그래밍을 할 수 있습니다.</li>
</ul>
<ol start="0">
<li><p><code>#![allow(unused)]</code> 는 러스트의 컴파일러에게 컴파일러가 <code>사용하지 않은</code> <code>변수</code>, <code>함수</code> 또는 <code>모듈 등</code>이 있어도 <code>경고를 표시하지 않도록 지시하는 디렉티브</code>입니다. 이를 사용하면 코드에 특정 부분에 대해 경고를 표시하지 않아도 되기 때문에, <code>개발자는 불필요한 경고 메시지를 제거하고 더 깨끗한 코드를 유지</code>할 수 있습니다.</p>
<p>0.1. <code>use 키워드</code>를 사용하여 다른 crate들의 모듈을 가져올 수 있습니다.</p>
<ul>
<li><code>extern</code>은 Rust에서 <code>다른 crate에 있는 함수나 데이터를 가져와서 사용</code>하거나, <code>라이브러리를 링크</code>하는 데 사용되는 <code>예약어</code>입니다. extern crate 문은 이전에 사용되었으며, <code>Rust 2018 Edition 이후</code>로는 더 이상 <code>필요하지 않습니다</code>. Rust 2018 Edition부터는 crate들이 라이브러리로서 불러오지 않고 모듈로서 불러와져서, <code>extern crate 대신</code> </li>
</ul>
</li>
<li><p><code>prelude</code>: <code>표준 라이브러리의 prelude는 자주 사용되는 트레잇(trait)들을 쉽게 사용할 수 있게 해주는 모듈</code>입니다. async_std의 prelude는 주로 비동기 I/O 작업에 필요한 트레잇들을 제공합니다.</p>
<p>1.1. <code>자주 사용되는 타입</code> &amp; <code>트레잇</code>을 쉽게 사용할 수 있도록 가져오는 모듈. 
Rust 표준 라이브러리에는 <code>std::prelude</code>라는 모듈이 있으며, 이 모듈은 <code>Vec, Option, Result</code> 등과 같은 <code>기본적인 타입</code>들과, <code>ToString, Into, From</code> 등과 같은<code>트레잇</code>들을 내부적으로 가져옵니다. 이렇게 prelude 모듈을 사용하면, <code>매번 긴 경로를 사용하여 타입이나 트레잇을 가져올 필요 없이</code>, <code>간단하게 use crate::prelude::*; 구문</code>을 사용하여 필요한 타입이나 트레잇들을 가져올 수 있습니다. 이는 코드의 <code>가독성</code>을 높이고, 개발자의 <code>생산성을 향상</code>시킵니다.</p>
</li>
</ol>
<ol start="2">
<li><p><code>task</code>: 이 모듈은 비동기 작업을 생성하고 실행하는 데 필요한 <code>함수와 트레잇을 제공</code>합니다.</p>
</li>
<li><p><code>net</code>: 이 모듈은 <code>네트워킹에 필요한 기능들을 제공</code>합니다. 여기서는 <code>TcpListener</code>와 <code>ToSocketAddrs</code>를 가져왔는데, 이들은 각각 <code>TCP 연결을 수신</code>하고 <code>주소를 소켓 주소로 변환</code>하는 데 사용됩니다.</p>
</li>
<li><p><code>BufReader</code>와 <code>BufWriter</code>: 입력과 출력을 위한 버퍼링 기능을 제공합니다.</p>
</li>
<li><p><code>type Result&lt;T&gt; = ...</code> 코드를 통해서 type aliasing이 사용되고 있어요. </p>
<p>5.1. Result<T>: 이것은 러스트에서 에러 처리를 위해 사용되는 <code>enum 타입</code>입니다. <code>Ok(T)</code> 또는 <code>Err(E)</code> 두 가지 값을 가질 수 있으며, 여기서 T는 연산의 성공 결과 타입, E는 실패했을 때의 에러 타입을 나타냅니다. 이 코드에서는 <code>Result 타입에 대한 별칭을 설정</code>하고 있으며, <code>에러 타입</code>은<code>Box&lt;dyn std::error::Error + Send + Sync&gt;로 설정</code>되어 있습니다.</p>
<p>5.2. <code>Box&lt;dyn std::error::Error + Send + Sync&gt;</code>: 이것은 <code>힙</code>에 할당된 <code>동적 타입의 에러</code>를 의미합니다. 여기서 <code>Send</code>와 <code>Sync</code>는 <code>멀티스레딩 환경에서 이 에러 타입이 안전하게 전송(Send)되거나 공유(Sync)될 수 있음을 보장</code>합니다.</p>
<p>5.3. 이러한 타입 별칭은 코드의 가독성을 높이고, 반복적으로 긴 타입 선언을 작성하는 것을 줄이는데 도움이 됩니다. 이렇게 설정한 <code>Result 타입은 이후 비동기 네트워킹 코드</code>에서 <code>에러를 반환할 때 사용</code>될 것입니다.</p>
</li>
</ol>
<p><strong>이제 서버의 루프를 작성합니다.</strong></p>
<pre><code class="language-rust">async fn accept_loop(addr: impl ToSocketAddrs) -&gt; Result&lt;()&gt; {
    let listener: TcpListener = TcpListener::bind(addr).await?;
    let mut incoming: async_std::net::Incoming = listener.incoming();
    while let Some(stream) = incoming.next().await {
        let stream: TcpStream = stream?;
        println!(&quot;Accepting from: {}&quot;, stream.peer_addr()?);
        let _handle: task::JoinHandle&lt;()&gt; = spawn_and_log_error(connection_loop(stream));
    }
    Ok(())
}</code></pre>
<ul>
<li>이 코드는 TCP 소켓 서버의 <code>메인 루프를 설정</code>하는 <code>함수</code>입니다. 이 함수는 주어진 <code>주소에서 들어오는 TCP 연결을 수신</code>하고, 각 <code>연결</code>에 대해 <code>처리를 수행</code>합니다.</li>
</ul>
<ol>
<li><p><code>async fn accept_loop(addr: impl ToSocketAddrs) -&gt; Result&lt;()&gt;:</code> accept_loop 함수는 비동기 함수(async fn)로 선언되어 있습니다. 이 함수는 소켓 주소로 변환될 수 있는 어떤 타입의 <code>addr</code> 인자를 받습니다(impl ToSocketAddrs). 이 함수는 <code>Result&lt;()&gt; 타입을 반환</code>하며, 이는 함수가 <code>성공</code>적으로 수행되면 <code>Ok(())</code>를 반환하고, <code>에러</code>가 발생하면 <code>Err(E)</code>를 반환함을 의미합니다. 여기서 <code>E</code>는 이전에 정의한 <code>Box&lt;dyn std::error::Error + Send + Sync&gt; 타입</code>입니다.</p>
</li>
<li><p><code>impl ToSocketAddrs</code> : 이 문법은 함수의 매개변수로 특정 트레잇(Trait)을 구현하는 어떤 타입이든 받을 수 있음을 나타냅니다. ToSocketAddrs는 러스트 표준 라이브러리의 트레잇으로, 소켓 주소로 변환될 수 있는 여러 가지 타입들에 대해 구현되어 있습니다.</p>
<p>즉, <code>accept_loop(addr: impl ToSocketAddrs)</code> 함수는 <code>ToSocketAddrs 트레잇</code>을 구현하는 어떤 타입의 addr도 인자로 받을 수 있습니다. 이는 <code>String, &amp;str, SocketAddr, (IpAddr, u16), (Ipv4Addr, u16), (Ipv6Addr, u16), (str, u16), (String, u16), [SocketAddr; N], &amp;[SocketAddr]</code> 등과 같은 다양한 타입을 <code>인자로 받을 수 있음</code>을 의미합니다.</p>
<p>2.1. <code>impl</code> 키워드
2.1.1. <code>특정 타입에 대해 trait을 구현 할 때</code></p>
<ul>
<li>impl 키워드는 특정 타입에 대해 트레잇(trait)를 구현하는 데 사용. 예를 들어, 아래 코드는 Display 트레잇을 MyStruct 타입에 대해 구현하고 있습니다.<pre><code class="language-rust">struct MyStruct {
value: i32,
}
</code></pre>
</li>
</ul>
<p>impl std::fmt::Display for MyStruct {
   fn fmt(&amp;self, f: &amp;mut std::fmt::Formatter) -&gt; std::fmt::Result {</p>
<pre><code>   write!(f, &quot;MyStruct {{ value: {} }}&quot;, self.value)</code></pre><p>   }
}</p>
<pre><code>
2.1.2. `특정 타입에 메소드를 추가할 때`:
impl 키워드는 특정 타입에 메소드를 추가하는 데도 사용됩니다. 예를 들어, 아래 코드는 MyStruct 타입에 new라는 메소드를 추가하고 있습니다
```rust
struct MyStruct {
 value: i32,
}

impl MyStruct {
   fn new(value: i32) -&gt; Self {
       Self { value }
   }
}
</code></pre></li>
</ol>
<ol start="3">
<li><p><code>let listener = TcpListener::bind(addr).await?;</code>: 이 줄은 주어진 주소에 TCP 소켓을 바인딩합니다. bind 함수는 비동기 함수이므로, .await 키워드를 사용하여 함수의 완료를 기다립니다. 만약 bind 함수가 에러를 반환하면 ? 연산자를 통해 에러를 바로 반환합니다.</p>
<p>3.1. Q. 만약 <code>await</code>을 사용하지 않으면 <code>Future</code>를 반환함. Future는 Rust에서 비동기 연산을 나타내는 타입입니다. 이 <code>Future는 결과가 준비될 때까지 대기</code>하고, <code>준비된 결과</code>를 <code>가져오는 기능</code>을 제공합니다.</p>
<p><code>.await을 사용</code>하면, <code>Future의 결과가 준비될 때까지 현재의 비동기 작업을 일시 중지</code>하고 <code>다른 비동기 작업이 실행될 수 있도록</code> 합니다. 그리고 <code>Future의 결과가 준비</code>되면, <code>.await을 사용한 비동기 작업</code>이 <code>다시 실행</code>되어 결과를 가져옵니다.</p>
<p>따라서 .await을 사용하지 않고 Future를 그대로 반환하거나 저장하는 것도 가능합니다. 그러나 이 Future의 결과를 가져오려면 어떤 방식으로든 .await을 사용해야 합니다.</p>
<p>3.2. <code>TcpListener::bind</code>메서드는 비동기 소켓 서버를 작성할 때 사용됩니다. 이 메서드는 소켓 주소를 매개변수로 받아 TcpListener 인스턴스를 바인딩합니다. 이것은 서버가 클라이언트의 연결 요청을 수신할 수 있도록 합니다.</p>
<p>3.2.1. 매개변수 타입:</p>
<p><code>addr: impl ToSocketAddrs - ToSocketAddrs</code> 트레잇을 구현하는 타입. 이것은 소켓 주소를 나타내는 값으로, TcpListener를 바인딩할 위치를 지정합니다. 예를 들어, 문자열 <code>&quot;127.0.0.1:8080&quot;</code> 또는 <code>튜플 (&quot;127.0.0.1&quot;, 8080) 등</code>이 될 수 있습니다.</p>
</li>
</ol>
<p>  3.2.2. 반환 타입:
  <code>Result&lt;TcpListener&gt;</code> - 이 메서드는 Result를 반환하는데, 이것은 함수가 성공적으로 수행되면 <code>Ok(TcpListener)</code>를 반환하고, 에러가 발생하면 Err를 반환합니다. TcpListener는 TCP 소켓 서버를 나타내며, 클라이언트의 연결 요청을 수신하는 역할을 합니다.</p>
<p>  따라서, 아래와 같이 bind 메서드를 호출하면, 주어진 주소에 TCP 소켓 서버가 바인딩되고, 이 서버를 나타내는 TcpListener 인스턴스가 반환됩니다.</p>
<pre><code class="language-rust">  let listener = TcpListener::bind(&quot;127.0.0.1:8080&quot;).await?;
</code></pre>
<ol start="4">
<li><p><code>?</code> : 연산자는 <code>에러 처리를 간결하게</code> 표현하는 데 사용됩니다. 이 연산자는 <code>Result 타입의 값을 처리</code>하며, <code>Ok</code>인 경우에는 내부 값을 언랩하여 <code>반환</code>하고, <code>Err</code>인 경우에는 <code>현재 함수에서 바로 에러를 반환</code>합니다.</p>
<p>4.1. 예시</p>
<pre><code class="language-rust">fn some_function() -&gt; Result&lt;(), SomeError&gt; {
   let result = could_fail()?;
   // Do something with result
   Ok(())
}
</code></pre>
<p>여기서 <code>could_fail</code> 함수는 <code>Result&lt;T, SomeError&gt;</code>를 반환합니다. <code>?</code> 연산자는 이 <code>Result 값을 처리</code>하며, <code>could_fail가 성공(Ok)</code>하면 내부 값을 언랩하여 <code>result</code>에 <code>저장</code>하고, <code>실패(Err)</code>하면 <code>some_function</code>에서 바로 <code>에러를 반환</code>합니다.</p>
<p>이렇게 ? 연산자를 사용하면, <code>match</code>나 <code>if let</code>을 사용하여 <code>Result를 수동으로 처리하는 번거로움</code> 없이 에러를 간결하게 처리할 수 있습니다. 이는 Rust에서 에러 처리를 간단하면서도 안전하게 만드는 중요한 기능 중 하나입니다.</p>
</li>
<li><p><code>let mut incoming:async_std::net::Incoming = listener.incoming();</code> <code>소켓 서버</code>에서 들어오는 <code>TCP 연결 요청</code>을 처리하는 <code>스트림을 생성</code>합니다.</p>
<p><code>listener.incoming()</code>는 들어오는 클라이언트 연결의 <code>무한 스트림을 반환</code>하는데, 이 스트림의 각 항목은 <code>Result&lt;TcpStream&gt; 타입</code>입니다. Result는 성공 시에 Ok(TcpStream)을 반환하고, 연결 시도 중에 오류가 발생하면 Err를 반환합니다. TcpStream은 클라이언트와의 TCP 연결을 나타냅니다.</p>
<ol start="6">
<li><code>while let Some(stream) = incoming.next().await { // 3
  // TODO
}</code> : 
6.1.  <code>incoming</code> <code>스트림</code>에서 <code>새로운 항목</code>(여기서는 <code>TCP 연결</code>)을 비동기적으로 <code>가져오는 작업</code>을 반복적으로 <code>수행</code>합니다. 이 작업은 incoming.next().await를 통해 수행되며, 이는 <code>incoming 스트림</code>의 <code>다음 항목</code>을 비동기적으로 가져옵니다.</li>
</ol>
<p><code>while let Some(stream) = incoming.next().await</code> 구문은 <code>incoming.next().await</code> 호출이 <code>Some 값을 반환</code>하는 한 <code>계속</code>해서 <code>루프를 수행</code>합니다. <code>Some 값은 incoming 스트림에서 새로운 TCP 연결을 성공적으로 가져왔음을 의미</code>합니다. stream 변수에는 이 TCP 연결이 할당됩니다.</p>
<p>만약 <code>incoming.next().await</code> 호출이 <code>None</code>을 <code>반환</code>하면, 이는 incoming 스트림에 <code>더 이상 처리할 TCP 연결이 없음</code>을 의미하고, while 루프는 종료됩니다.</p>
<p>6.2. <code>await 키워드 오른쪽에 코드 블록을 사용하는 경우</code>, async fn에서 Future의 구현체를 직접 만들 때입니다.</p>
<p>예를 들어, 다음과 같이 <code>async fn</code>에서 <code>Future</code>의 구현체를 만들어서 <code>await</code> 키워드로 실행할 수 있습니다.</p>
<pre><code class="language-rust">async fn foo() -&gt; i32 {
 let x = async {
     // 비동기적으로 실행되는 코드 블록
     42
 };

 x.await
}
#[tokio::main]
async fn main() {
     let result = foo().await;
     println!(&quot;{}&quot;, result);
 }</code></pre>
<p>여기서 x는 async 블록이며, 이는 Future의 구현체입니다. x.await는 이 Future가 완료될 때까지 현재 태스크를 블록하고, Future의 결과를 반환합니다.</p>
<p>이러한 코드 블록은 async fn에서만 사용할 수 있습니다. async fn을 호출한 코드에서 await 키워드를 사용하는 경우에는 코드 블록 대신 Future의 인스턴스를 반환하는 것이 일반적입니다.</p>
</li>
<li><p><code>let stream: TcpStream = stream?;</code>
<code>stream</code> 변수에 <code>TcpStream 값을 할당</code>하되, 만약 값이 <code>Err</code>일 경우에는 해당 Err을 <code>리턴</code>한다는 뜻입니다.</p>
</li>
<li><p><code>println!(&quot;Accepting from: {}&quot;, stream.peer_addr()?);</code>
<code>stream.peer_addr()</code> 메서드는 해당 <code>TCP 연결의 소켓 주소를 반환</code>합니다. 이 소켓 주소는 <code>std::net::SocketAddr</code> 형식입니다. println! 매크로에서 {}를 사용하여 해당 소켓 주소를 문자열로 포맷팅하여 출력하고 있습니다. <code>?</code> 연산자는 이 표현식의 결과를 <code>Result 타입으로 반환</code>하고, 이 표현식에서 오류가 발생한 경우에는 <code>Ok(())</code> 대신 <code>Err</code> 값을 반환하여 <code>accept_loop 함수에서 예외 처리를 수행</code>하게 됩니다.</p>
</li>
<li><p><code>let _handle: task::JoinHandle&lt;()&gt;</code>는 비동기적으로 실행될 <code>connection_loop</code> 함수를 호출하는 <code>spawn_and_log_error</code> 함수의 반환값입니다.</p>
<p><code>spawn_and_log_error</code> 함수는 <code>제네릭 타입 F</code>를 입력으로 받으며, 이는 <code>Future 트레이트</code>를 구현한 반환값을 가져야 합니다. <code>spawn_and_log_error</code> 함수는 async move 블록으로 구현되어 있으며, <code>반환값의 타입</code>은 <code>task::JoinHandle&lt;()&gt;</code> 입니다.</p>
<p>따라서, <code>let _handle: task::JoinHandle&lt;()&gt;</code>은 <code>spawn_and_log_error(connection_loop(stream))</code>의 반환값을 할당받는 변수입니다. <code>connection_loop(stream)</code>은 비동기적으로 실행되는 함수이며, 이를 수행하는 핸들러를 _handle 변수에 할당합니다. _handle 변수는 반환값이 없으며, 핸들러를 정상적으로 수행하기 위해 사용됩니다.</p>
</li>
<li><p><code>Ok(())</code> :  Rust에서 성공적으로 끝난 함수의 반환값</p>
<p>Rust의 <code>Result 타입은 함수가 성공적으로 완료되거나 (Ok) 또는 오류(Err)로 종료</code>될 수 있음을 나타냅니다. <code>Ok</code>와 <code>Err</code>는 <code>Result 타입의 두 가지 variant</code>입니다.</p>
<p>Ok(())에서 괄호 안의 ()는 <code>unit type</code>을 의미하며, <code>특별한 값이 없음</code>을 나타냅니다. 이것은 함수가 <code>특정 값을 반환하지 않지만 성공적으로 완료</code>되었음을 나타내는 일반적인 방법입니다.</p>
<p>따라서, Ok(())는 이 함수가 성공적으로 종료되었고 반환할 <code>특별한 값이 없음</code>을 나타냅니다.</p>
</li>
</ol>
<pre><code class="language-rust">// main
fn run() -&gt; Result&lt;()&gt; {
    let fut = accept_loop(&quot;127.0.0.1:8080&quot;);
    task::block_on(fut)
}</code></pre>
<ul>
<li>Rust에서는 비동기 함수를 호출해도 코드가 실행되지 않는다는 것입니다. 비동기가 아닌 함수에서 퓨처를 실행하는 방법은 퓨처를 executor에게 전달하는 것입니다. 이 경우 <code>task::block_on</code>을 사용하여 <code>현재 스레드</code>에서 <code>future</code>를 <code>실행</code>하고 <code>완료될 때까지 차단</code>합니다.</li>
</ul>
<h2 id="receiving-message">Receiving message</h2>
<p>프로토콜의 메시지 수신부를 구현해봅시다. </p>
<ol>
<li>수신을 <code>TcpStream</code> <code>split</code>하고 <code>\n</code>바이트를 <code>utf-8</code>로 디코딩</li>
<li>첫 번째 줄을 로그인으로 해석</li>
<li>나머지 줄을 다음과 같이 구문 분석합니다. <code>login: messa</code></li>
</ol>
<pre><code class="language-rust">    use async_std::{
        io::BufReader,
        net::TcpStream,
    };

fn spawn_and_log_error&lt;F&gt;(fut: F) -&gt; task::JoinHandle&lt;()&gt;
where
    F: Future&lt;Output = Result&lt;()&gt;&gt; + Send + &#39;static,
{
    task::spawn(async move {
        if let Err(e) = fut.await {
            eprintln!(&quot;{}&quot;, e)
        }
    })
}

async fn accept_loop(addr: impl ToSocketAddrs) -&gt; Result&lt;()&gt; {
    let listener: TcpListener = TcpListener::bind(addr).await?;
    let mut incoming: async_std::net::Incoming = listener.incoming();
    while let Some(stream) = incoming.next().await {
        let stream: TcpStream = stream?;
        println!(&quot;Accepting from: {}&quot;, stream.peer_addr()?);
        let _handle: task::JoinHandle&lt;()&gt; = spawn_and_log_error(connection_loop(stream));
    }
    Ok(())
}

async fn connection_loop(mut stream: TcpStream) -&gt; Result&lt;()&gt; {
    let reader: BufReader&lt;&amp;TcpStream&gt; = BufReader::new(&amp;stream);
    let mut writer: BufWriter&lt;&amp;TcpStream&gt; = BufWriter::new(&amp;stream);

    let name: String = match reader.lines().next().await {
        None =&gt; Err(&quot;peer disconnected immediately&quot;)?,
        Some(line) =&gt; line?,
    };
    println!(&quot;name = {}&quot;, name);

    // write response to client
    let response: &amp;str = &quot;HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, world!&quot;;
    writer.write_all(response.as_bytes()).await?;
    writer.flush().await?;

    Ok(())
}</code></pre>
<ol>
<li><p><code>spawn_and_log_error</code> 함수는 <code>제네릭 함수</code>로, <code>F</code>라는 타입 파라미터를 가지고 있습니다. 이 함수는 F가 Future trait를 구현하고, 결과값이 Result&lt;()&gt; 타입인 것을 요구합니다. 함수는 task::spawn 함수를 호출하여, async move 블록에서 fut을 실행시킵니다. 만약 fut 실행 중 오류가 발생하면 eprintln!(&quot;{}&quot;, e) 코드를 실행하여 오류 메시지를 출력합니다. task::spawn 함수는 실행된 태스크의 핸들을 반환합니다. 반환된 핸들은 현재 사용하지 않는 것으로 _handle 변수에 할당됩니다.</p>
</li>
<li><p><code>task::JoinHandle&lt;()&gt;</code>은 async_std 런타임에서 생성된 future가 실행을 완료한 후 반환하는 <code>결과값이 없는 스레드 핸들을 나타내는 타입</code>입니다.</p>
<p>좀 더 자세하게 설명하면, <code>task::JoinHandle</code>은 <code>실행 중인 future</code>의 실행을 <code>추적</code>하고 <code>제어</code>하는 <code>스레드 핸들러</code>입니다. 이 핸들러는 <code>async_std::task::spawn</code> <code>함수</code>에 의해 <code>반환</code>됩니다.</p>
<p><code>반환 타입</code>으로 <code>()</code>을 사용하면 해당 <code>future의 결과값이 없음</code>을 나타내며, 이는 <code>Result 타입을 사용하여 오류 처리를 수행할 필요가 없음</code>을 의미합니다. 따라서 <code>반환값이 없는 future를 실행하는 경우</code>에는 <code>task::JoinHandle&lt;()&gt;</code>을 <code>반환</code>하는 것이 일반적입니다</p>
</li>
<li><p><code>where 키워드</code>는 <code>제네릭 타입 매개변수</code>에 <code>제약 조건을 지정</code>하는 데 사용됩니다.</p>
<p>이 코드에서 <code>F</code>는 <code>Future 트레이트를 구현</code>해야 하고, 그 출력은 <code>Result&lt;()&gt;</code> 타입이어야 하며, <code>Send</code> <code>트레이트</code>를 구현하고, <code>&#39;static</code> <code>수명</code>을 가지도록 제한됩니다.</p>
<p>즉, <code>spawn_and_log_error 함수의 매개변수 F는 Future 트레이트를 구현하며, Result&lt;()&gt; 타입을 출력하는 것이 보장되어야 하며, 다른 스레드로 전송할 수 있도록해야 하며, 수명은 &#39;static이어야 합니다.</code></p>
<p>3.1.<code>좀 더 알아보기</code></p>
<ul>
<li><code>where</code> 제네릭 타입 <code>매개변수</code>에 대한 <code>제약 조건</code>(constraint)을 명시하는 키워드</li>
<li>제네릭 <code>함수</code>나 제네릭 <code>구조체</code>에서 <code>타입 매개변수</code>의 <code>제약 조건</code>을 명시할 때 사용</li>
<li>where를 사용하여 특정 트레잇을 구현한 타입만 타입 매개변수로 받도록 제약을 걸거나, 타입 매개변수 간의 관계를 명시 </li>
</ul>
<p>3.2. <code>다른 예시</code></p>
<pre><code class="language-rust">fn foo&lt;T, U&gt;(x: T, y: U) -&gt; T
where
 T: std::fmt::Debug,
 U: std::fmt::Debug,
{
 println!(&quot;x = {:?}&quot;, x);
 println!(&quot;y = {:?}&quot;, y);
 x
}

fn main() {
 let s = String::from(&quot;hello&quot;);
 let n = 42;
 let result = foo(s, n);
 println!(&quot;result = {:?}&quot;, result);
}
</code></pre>
<ul>
<li>foo 함수는 제네릭 타입 매개변수 T와 U를 가지고 있습니다. 하지만 이 함수는 반환값으로 T 타입만을 가집니다.</li>
<li>따라서 T 타입은 std::fmt::Debug trait을 구현해야 합니다. 이 제약조건을 where 키워드를 사용하여 명시했습니다.</li>
<li>함수를 호출할 때 foo(s, n)로 호출하면 <code>T</code> 타입은 <code>String</code> 타입으로 추론됩니다. <code>String</code>은 <code>std::fmt::Debug</code> <code>trait</code>을 구현하므로 제약조건을 만족합니다. <code>U</code> 타입은 <code>i32</code>로 추론됩니다.</li>
</ul>
<p>3.3. <code>where</code></p>
<pre><code> `F: Future&lt;Output = Result&lt;()&gt;&gt; + Send + &#39;static,`</code></pre><ul>
<li><code>F: Future&lt;Output = Result&lt;()&gt;&gt;</code>: 이 부분은 제네릭 타입 매개변수 F가 Future 트레이트를 구현해야 한다는 것을 나타냅니다. Future 트레이트는 어떤 비동기 계산을 나타내며, 결국에는 값을 반환합니다. 여기서는 Result&lt;()&gt; 타입을 반환하는 것으로 지정되어 있어요. 이는 성공 시 Ok(())를 반환하거나 에러를 나타내는 Result 타입을 반환함을 의미합니다.</li>
</ul>
<ul>
<li><p><code>+ Send</code>: 이 부분은 제네릭 타입 F에 대한 추가적인 트레이트 제약을 나타냅니다. Send 트레이트는 해당 타입이 스레드 간에 안전하게 공유될 수 있음을 의미합니다. 즉, 다른 스레드로 이동시킬 수 있고 동시에 접근할 수 있는 타입을 나타냅니다.</p>
</li>
<li><p><code>&#39;static</code>: 이 부분은 라이프타임 &#39;static을 의미합니다. <code>&#39;static</code>은 <code>프로그램 전체 동안 유지되는 수명</code>을 나타내는데, 여기서는 F 타입이 &#39;static 수명을 갖는다는 것을 명시합니다. 이는 <code>F 타입이 프로그램 전체에서 유효하고 제한 없이 사용될 수 있음</code>을 의미합니다.</p>
</li>
<li><p>따라서, <code>F 타입</code>은 <code>Future 트레이트를 구현</code>하고 <code>Result&lt;()&gt;</code>을 <code>반환</code>하며, <code>Send 트레이트</code>를 <code>만족</code>하고 <code>&#39;static 수명을 갖는 타입</code>이어야 합니다.</p>
</li>
<li><p><code>질문</code> : <code>F:Future&lt;Output = Result&lt;()&gt;&gt; + Send</code> 구문에서 Send는 F와 결합하는거야? &#39;Future&lt;Output = Result&lt;()&gt;&gt; &#39;과 결합하는 거야?</p>
</li>
<li><p><code>답변</code> : 
<code>F: Future&lt;Output = Result&lt;()&gt;&gt; + Send</code> 구문에서 <code>Send</code>는 <code>F</code>와 <code>결합</code>하는 것입니다. 이는 <code>F</code>가 <code>Send</code> <code>트레이트를 구현</code>해야 한다는 것을 의미합니다. <code>F 타입은 Future&lt;Output = Result&lt;()&gt;&gt;과는 별개로 결합</code>하는 것이며, 두 가지 조건을 동시에 충족해야 합니다.</p>
</li>
</ul>
</li>
<li><pre><code class="language-rust">{
     task::spawn(async move {
         if let Err(e) = fut.await {
             eprintln!(&quot;{}&quot;, e)
         }
     })
 }</code></pre>
<ul>
<li>해당 코드는 <code>task::spawn</code> 함수를 사용하여 <code>비동기 작업</code>을 <code>실행</code>하는 부분입니다.</li>
</ul>
</li>
</ol>
<ul>
<li><code>task::spawn</code> 함수는 <code>비동기 클로저</code>나 <code>비동기 함수</code>를 인자로 받아서 <code>백그라운드에서 실행</code>합니다.
<code>async move { ... }</code>은 비동기 클로저를 정의하는 부분입니다. <code>move 키워드</code>는 <code>클로저</code>가 <code>외부</code>의 <code>변수</code>를 <code>소유</code>할 수 있도록 합니다.
<code>if let Err(e) = fut.await { ... }</code>는 <code>fut.await</code> 표현식의 결과가 <code>Err</code>일 경우 실행됩니다. 이 부분은 <code>fut</code>가 완료될 때까지 대기하고, Err 값을 가지면 해당 에러를 출력합니다.
<code>eprintln!(&quot;{}&quot;, e)</code>은 에러를 표준 오류 출력에 출력하는 부분입니다.
즉, 해당 코드는 <code>비동기 클로저</code>를 <code>task::spawn</code> <code>함수로 실행</code>하여 <code>비동기 작업을 백그라운드에서 실행</code>하고, <code>작업이 완료되면 에러가 발생했을 경우 해당 에러를 출력하는 역할</code>을 합니다. </li>
</ul>
<h3 id="톺아보기-1">톺아보기-1</h3>
<p><strong>러스트 함수</strong></p>
<ol>
<li><p>일반함수(일반 함수(Non-Generic Function)</p>
<ul>
<li>인자와 반환값을 명시하며 호출 시 타입 파라미터가 사용되지 않음</li>
<li>예시: <code>fn add(a: i32, b: i32) -&gt; i32 { a + b }</code></li>
</ul>
</li>
<li><p>제네릭 함수(Generic Function)</p>
<ul>
<li>인자나 반환값이 타입 파라미터를 사용하는 함수</li>
<li>예시: <code>fn get_first&lt;T&gt;(list: &amp;[T]) -&gt; Option&lt;&amp;T&gt;</code>{ list.first() }</li>
<li>만약 함수의 타입을 정하지 않고 매개변수에만 제네릭 타입을 이용한다면 컴파일에러가 발생함. 이유는 컴파일러는 해당 타입을 알수 없기 때문임.</li>
</ul>
</li>
<li><p>클로저(Closure)
3.1. non-generic closure</p>
<pre><code class="language-rust">let add_one = |x| x + 1;</code></pre>
<p>3.2. generic closure</p>
<pre><code class="language-rust">let map = |arr: &amp;[i32], op: fn(i32) -&gt; i32| -&gt; Vec&lt;i32&gt; {
 arr.iter().map(|&amp;x| op(x)).collect()
};
</code></pre>
</li>
<li><p><code>while let Some(line) = lines.next().await {</code> </p>
<p>4.1. <code>while let</code>은 루프에서 <code>패턴 매칭</code>을 수행하며, 주어진 패턴에 일치하는 값으로 <code>반복을 진행</code>하거나 <code>종료</code>하는 제어 구조
4.1.1. <code>문법</code></p>
<pre><code class="language-rust">   while let 패턴 = 값 {
     // 패턴과 일치하는 경우 실행되는 코드
}</code></pre>
<p>4.2. <code>Some(line) = lines.next().await</code>는 <code>lines</code>라는 <code>이터레이터</code>에서 <code>다음 값</code>을 가져오고, <code>해당 값이 Some</code>이면 <code>패턴 매칭</code>을 통해 값을 <code>분해</code>하여 <code>line 변수에 할당</code>하는 구문입니다.
4.2.1. <code>Some()</code> : Rust의 Option 열거형의 하나인 Some 변형</p>
</li>
<li><p><code>let mut reader: BufReader&lt;&amp;TcpStream&gt; = BufReader::new(&amp;stream);</code>
<code>let mut writer: BufWriter&lt;&amp;TcpStream&gt; = BufWriter::new(&amp;stream);</code>
: 
5.1. TCP 스트림을 읽고 쓰기 위해 BufReader와 BufWriter를 사용하는 부분입니다.
  <code>let mut reader: BufReader&lt;&amp;TcpStream&gt; = BufReader::new(&amp;stream);</code>
5.1.1. <code>BufReader</code>버퍼링된 읽기를 지원하는 타입
5.1.2. <code>BufReade</code>r는 주어진 reader(&amp;TcpStream)를 감싸고 버퍼링된 읽기 기능을 제공합니다.
5.1.3. <code>&amp;stream</code>은 <code>TcpStream</code>에 대한 <code>불변 참조</code>를 의미합니다. <code>BufReader</code>는 <code>TcpStream</code>을 읽기 위해 참조를 사용합니다.
5.1.4. <code>reader</code> 변수는 <code>BufReader&lt;&amp;TcpStream&gt;</code> 타입으로 선언되어 있습니다. 이는 <code>TcpStream</code>에서 읽기 작업을 수행하기 위한 버퍼링된 리더 객체를 나타냅니다.</p>
</li>
</ol>
<h3 id="톺아보기-2">톺아보기-2</h3>
<h4 id="버퍼링-데이터를-임시로-저장하는-메모리-영역">버퍼링: 데이터를 임시로 저장하는 메모리 영역</h4>
<p><strong>특징</strong></p>
<ol>
<li>입출력 작업을 효율적으로 처리하는 기술</li>
<li>데이터를 읽거나 쓸 때에는 한 번에 작은 블록 단위로 처리하는 것보다 큰 블록 단위로 처리하는 것이 효율적</li>
<li>사용시 데이터를 작은 블록 단위로 입출력 장치와 직접 통신하는 대신, ``메모리에 임시로 저장한 후<code></code>더 큰 블록 단위로 입출력 장치와 통신<code>할 수 있습니다. 이렇게 함으로써 입출력 장치와의 통신 횟수 줄이고,</code>메모리와 입출력 장치 간의 속도 차이를 완화`할 수 있습니다.</li>
<li>버퍼링은 입출력 작업의 성능을 향상시키고 응용 프로그램의 처리량을 개선하는 데 도움을 줍니다. 또한, 버퍼링은 데이터의 흐름을 관리하여 원활한 입출력 처리를 가능하게 합니다.</li>
</ol>
<ul>
<li>예를 들어 파일을 읽을 때 버퍼링을 사용하면 한 번에 더 많은 데이터를 읽어 메모리에 저장한 후 응용 프로그램에서 필요한 만큼의 데이터를 처리할 수 있습니다. 마찬가지로 데이터를 쓸 때에도 버퍼에 쌓아둔 후 한 번에 출력하므로 출력 작업의 효율성을 높일 수 있습니다.</li>
</ul>
<hr>
<ol start="6">
<li><p>`let name: String = match reader.lines().next().await {</p>
<pre><code> None =&gt; Err(&quot;peer disconnected immediately&quot;)?,
 Some(line) =&gt; line?,</code></pre><p> };`</p>
<ul>
<li><code>connection_loop</code> 함수 내부에서 클라이언트로부터 받은 첫 번째 줄을 읽어서 변수 <code>name</code>에 <code>저장</code>하는 부분입니다.</li>
</ul>
<p>6.1. <code>reader.lines().next().await</code>는 <code>reader</code>에서 비동기적으로 <code>한 줄씩 읽어오는 작업</code>입니다. lines()는 <code>BufReader</code>의 메서드로, <code>스트림</code>을 줄 단위로 읽을 수 있게 해줍니다.
6.2. <code>next().await</code>는 비동기적으로 다음 줄을 읽어오는 작업입니다. <code>next()</code>는 <code>lines()</code>에서 반환되는 Stream의 다음 아이템을 가져오는 메서드입니다.
6.3. <code>match</code> 표현식을 사용하여 결과를 처리합니다. <code>reader.lines().next().await</code>의 결과가 None이면 클라이언트가 즉시 연결을 끊었다는 의미이므로 에러를 반환합니다.
6.4. <code>Some(line) =&gt; line?</code>은 성공적으로 줄을 읽었을 때 해당 줄을 <code>line</code> 변수에 바인딩하고, <code>line?</code>을 사용하여 <code>Result 타입의 값</code>을 얻습니다. 만약 <code>line의 값이 Err일 경우 에러를 반환</code>합니다.</p>
</li>
<li><p><code>let response: &amp;str = &quot;HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, world!&quot;;</code>
7.1. <code>&amp;str</code> 타입을 사용한 이유. </p>
<ul>
<li><p>효율성: &amp;str은 <code>불변한 문자열 슬라이스</code>를 나타내므로 <code>메모리를 효율적</code>으로 사용할 수 있습니다. 응답 본문과 같은 작은 크기의 문자열 데이터에 적합합니다.</p>
</li>
<li><p><code>문자열 리터럴과의 호환성</code>: &amp;str은 Rust에서 문자열 리터럴의 타입입니다. 따라서 문자열 리터럴을 &amp;str으로 사용하면 쉽게 응답 본문과 같은 문자열을 나타낼 수 있습니다.</p>
</li>
</ul>
<ul>
<li><p><code>스트링 슬라이스의 편의성</code>: &amp;str은 문자열 슬라이스를 나타내므로 다양한 <code>문자열 조작 및 처리 작업에 유용</code>합니다. <code>문자열의 일부분에 대한 참조</code>를 쉽게 가져올 수 있습니다.</p>
</li>
<li><p><code>자동 변환</code>: <code>&amp;str은 String과 상호 변환</code>될 수 있습니다. 필요한 경우 <code>String을 &amp;str으로 변환</code>하거나, <code>&amp;str을 String으로 변환</code>할 수 있습니다.</p>
</li>
<li><p>위의 이유들로 인해 &amp;str은 보편적인 문자열 표현 방식으로 많이 사용됩니다.</p>
</li>
</ul>
</li>
<li><p><code>writer.write_all(response.as_bytes()).await?;</code></p>
<ul>
<li>응답을 클라이언트에게 전송하기 위해 writer를 사용하는 부분입니다.</li>
</ul>
<p>8.1. <code>writer</code>는 <code>BufWriter</code>로 생성된 쓰기 인터페이스입니다. <code>BufWriter</code>는 내부적으로 버퍼링을 수행하여 효율적인 쓰기 작업을 도와줍니다.</p>
<p>8.2. <code>write_all</code>은 비동기적으로 주어진 <code>바이트 슬라이스</code>를 전송하는 메서드입니다. <code>response.as_bytes()</code>는 <code>response 문자열</code>을 <code>바이트 슬라이스로 변환</code>하여 전송할 준비를 합니다.</p>
<p>8.3. await는 비동기 작업이 완료될 때까지 기다리는 키워드입니다. <code>write_all 메서드는 전송 작업이 완료</code>면 <code>Result 타입을 반환</code>합니다.</p>
<p>8.4. <code>?</code>는 <code>Result 타입의 값을 처리</code>하는 문법으로, <code>반환된 Result 값을 확인</code>하고, <code>에러가 발생한 경우 함수에서 바로 에러를 반환</code>합니다.</p>
<p>8.5. 즉, <code>writer.write_all(response.as_bytes()).await?;</code> 코드는 <code>비동기적으로 응답</code>을 <code>클라이언트에게 전송</code>하는 작업을 수행합니다. 전송이 <code>성공</code>적으로 완료되면 <code>계속 진행</code>하고, 전송 중에 <code>에러가 발생</code>하면 함수에서 <code>에러를 반환</code>합니다.</p>
</li>
<li><p><code>writer.flush().await?;</code>: <code>writer</code>를 사용하여 <code>버퍼에 남아있는 데이터</code>를 <code>비동기적으로 클라이언트에게 전송</code>하는 작업을 수행합니다. </p>
</li>
<li><p><code>8</code>, <code>9</code>의 차이
<code>writer.write_all(response.as_bytes()).await?;:</code>
이 코드는 response 문자열을 바이트 슬라이스로 변환한 후, 해당 데이터를 비동기적으로 클라이언트에게 전송합니다.</p>
<p><code>write_all</code> 메서드는 <code>버퍼에 데이터를 쓰는 작업</code>이므로, 데이터는 <code>버퍼에 임시로 저장</code>됩니다.
전송 작업이 완료되기 전까지는 실제로 클라이언트에게 전송되지 않습니다.</p>
<p>await는 비동기 작업이 완료될 때까지 기다리는 키워드입니다.</p>
<p><code>?</code>는 Result 타입의 값을 처리하는 문법으로, 반환된 Result 값을 확인하고, 에러가 발생한 경우 함수에서 바로 에러를 반환합니다.</p>
<p><code>writer.flush().await?;:</code></p>
<p>이 코드는 <code>버퍼에 저장된 데이터</code>를 목적지로 <code>비우는 작업</code>을 비동기적으로 수행합니다.</p>
<p>flush 메서드는 버퍼에 남아있는 데이터를 목적지로 전송합니다. <code>전송 작업이 완료되고 버퍼가 비워질 때까지 기다립니다.</code></p>
<p>따라서, <code>writer.write_all(response.as_bytes()).await?;</code>은 <code>버퍼에 데이터를 쓰고 전송 작업</code>을 시작하는 반면, <code>writer.flush().await?</code>는 <code>버퍼에 저장된 데이터를 목적지로 비우는 작업</code>을 수행합니다. <code>실제 전송</code>은 <code>flush 메서드를 호출할 때 이루어지며, 버퍼가 비워질 때까지 기다립니다.</code></p>
</li>
</ol>
<h2 id="sending-message">Sending Message</h2>
<p><code>Sending</code>을 구현하는 가장 확실한 방법은 각 클라이언트의 TcpStream 쓰기에 대한 각 connection_loop 액세스 권한을 부여하는 것입니다. 그런 식으로 클라이언트는 수신자에게 직접 <code>.write_all()</code>로 메시지를 보낼 수 있습니다. </p>
<p> 하지만  Alice가 <code>bob: foo</code>를 보내고 Charley가 <code>bob: bar</code>를 보내면 Bob은 어쩌면 <code>fobaor</code>를 받을 수 있습니다. 소켓을 통해 메시지를 보내려면 여러 시스템 호출이 필요하며이는 두 개의 동시 .write_all이 서로 간섭할 수도 있습니다!</p>
<p>일반적으로 단일 작업만 각 <code>TcpStream</code>에 써야 합니다. 이제 채널을 통해 메시지를 수신하고 소켓에 쓰는 <code>connection_writer_loop</code> 작업을 만들어 보겠습니다. 이 작업은 메시지 직렬화 합니다. Alice와 Charley가 동시에 두 개의 메시지를 Bob에게 보내면 Bob은 메시지가 채널에 도착한 순서대로 메시지를 보게 됩니다.</p>
<pre><code class="language-rust">use futures::channel::mpsc; // 1
use futures::sink::SinkExt;
use std::sync::Arc;

type Sender&lt;T&gt; = mpsc::UnboundedSender&lt;T&gt;; // 2
type Receiver&lt;T&gt; = mpsc::UnboundedReceiver&lt;T&gt;;

async fn connection_writer_loop(
    mut messages: Receiver&lt;String&gt;,
    stream: Arc&lt;TcpStream&gt;, // 3
) -&gt; Result&lt;()&gt; {
    let mut stream = &amp;*stream;
    while let Some(msg) = messages.next().await {
        stream.write_all(msg.as_bytes()).await?;
    }
    Ok(())
}</code></pre>
<h3 id="설명">설명</h3>
<ol>
<li><p><code>futures::channel::mpsc</code> : 다중 생산자 단일 소비자(multi-producer, single-consumer) 비동기 채널을 제공합니다.</p>
</li>
<li><p><code>SinkExt</code> trait는 <code>Sink</code> trait에 대한 확장을 제공합니다. Sink trait는 비동기적으로 요소를 소비하는 타입을 정의하는데 사용됩니다. SinkExt는 Sink를 확장하여 추가적인 유용한 메서드들을 제공합니다.</p>
<p>SinkExt trait는 다양한 <code>비동기</code>적인 <code>메서드</code>들을 제공하여 요소를 <code>소비하고 처리</code>하는 데 도움을 줍니다. 예를 들면, SinkExt를 통해 <code>요소를 비동기적으로 전송</code>하고 <code>버퍼링하는 기능</code>, <code>요소를 변환</code>하고 <code>필터링</code>하는 기능, <code>여러 소비자를 조합</code>하는 기능 등을 사용할 수 있습니다.</p>
<p>이 경우, SinkExt trait를 사용하여 Sink를 확장하고 비동기적인 작업을 수행하는 메서드들을 사용할 수 있게 됩니다. 따라서 futures::sink::SinkExt를 가져오면 비동기적인 소비자와 관련된 다양한 기능을 활용할 수 있게 됩니다.</p>
</li>
<li><p><code>use std::sync::Arc;</code>: <code>Arc</code>는 <code>&quot;Atomic Reference Counting&quot;</code>의 약자로, 다중 스레드 간에 공유되는 데이터를 안전하게 소유하고 참조하는 데 사용되는 <code>스마트 포인터</code>입니다. Arc는 여러 스레드에서 안전하게 공유될 수 있는 Rc (Reference Counting)의 상호 배타적인 버전입니다.</p>
<p><code>Arc</code>는 <code>Arc&lt;T&gt;</code> 형태로 사용되며, T는 Arc로 공유되는 타입입니다. <code>Arc</code>는 <code>데이터의 소유권</code>을 나타내며, 참조자의 수를 추적하여 데이터의 소유권이 필요 없을 때 데이터를 자동으로 정리합니다.</p>
<p><code>Arc는 주로 다중 스레드 환경에서 데이터를 안전하게 공유해야 하는 경우 사용</code>됩니다. 다중 스레드 환경에서 Arc로 감싼 데이터를 여러 스레드에서 동시에 접근하고 참조할 수 있으며, 소유권 규칙을 준수하여 안전하게 데이터를 사용할 수 있게 됩니다.</p>
<p>따라서, use std::sync::Arc;를 통해 std::sync 모듈의 Arc 타입을 가져오면 다중 스레드 환경에서 안전하게 데이터를 공유하기 위한 Arc 스마트 포인터를 사용할 수 있습니다.</p>
</li>
</ol>
<h3 id="스마트-포인터란">스마트 포인터란?</h3>
<ul>
<li>모리 관리를 돕는 타입</li>
<li><code>포인터와 유사한 동작</code>을 제공하면서도 <code>추가적인 기능</code>과 <code>보안을 제공</code>하는 <code>래퍼 타입</code><ul>
<li>메모리 소유, 참조 카운팅, 빌림 규칙 등과 관련 </li>
</ul>
</li>
</ul>
<h4 id="특징">특징</h4>
<ol>
<li><p><code>소유권 관리</code> : 스마트 포인터는 자체적으로 데이터의 소유를 관리합니다. 데이터를 생성하거나 소멸할 때 적절한 시점에 소유권을 이전하거나 해제함으로써 메모리 안전성을 유지합니다.</p>
</li>
<li><p><code>빌림 규칙</code> : 스마트 포인터는 Rust의 빌림 규칙을 적용하여 데이터에 대한 동시 접근을 제한합니다. 빌림 규칙은 컴파일러가 런타임 오류를 방지하기 위해 데이터에 대한 가변 참조자의 수와 라이프타임을 추적합니다.</p>
</li>
<li><p><code>추가적인 기능</code> : 스마트 포인터는 일반적인 포인터보다 많은 기능을 제공합니다. 예를 들어, 메모리 할당 및 해제, 소유권 전달, 참조 카운팅, 스레드 안전성 등을 처리할 수 있습니다.</p>
</li>
</ol>
<h4 id="스마트-포인터-요약-">스마트 포인터 요약 :</h4>
<ol>
<li><p>Rust에서는 다양한 종류의 스마트 포인터가 제공됩니다. <code>가장 일반적인 스마트 포인터</code>로는 <code>Box, Rc, Arc, Cell, RefCell, Mutex, Ref, RefMut 등</code>이 있습니다. 각각의 스마트 포인터는 특정한 상황에 사용될 수 있으며, 메모리 관리와 동시성을 다루는 다양한 요구 사항에 맞게 선택하여 사용할 수 있습니다.</p>
</li>
<li><p>스마트 포인터는 Rust의 안전성과 효율성을 높이는 데 중요한 역할을 합니다. 개발자가 메모리 관리에 대해 직접적인 관여 없이 안전하게 코드를 작성할 수 있도록 도와주며, 메모리 누수나 데드락과 같은 일반적인 문제들을 방지하는 데 도움을 줍니다.</p>
</li>
</ol>
<hr>
<table>
<thead>
<tr>
<th>스마트 포인터</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>Box&lt;T&gt;</code></td>
<td>힙(heap)에 데이터를 할당하고 소유하는 가장 간단한 스마트 포인터입니다.</td>
</tr>
<tr>
<td><code>Rc&lt;T&gt;</code></td>
<td>참조 카운팅(reference counting) 스마트 포인터로, 여러 개의 소유자를 허용합니다.</td>
</tr>
<tr>
<td><code>Arc&lt;T&gt;</code></td>
<td>원자적(atomic) 참조 카운팅 스마트 포인터로, 다중 스레드 환경에서 안전하게 공유될 수 있습니다.</td>
</tr>
<tr>
<td><code>Cell&lt;T&gt;</code></td>
<td>내부 가변성(mutable interior)을 제공하여 값을 변경할 수 있는 스마트 포인터입니다. 스레드 간에는 안전하지 않습니다.</td>
</tr>
<tr>
<td><code>RefCell&lt;T&gt;</code></td>
<td>내부 가변성(mutable interior)을 제공하여 값을 변경할 수 있는 스마트 포인터입니다. 여러 개의 소유자를 허용합니다.</td>
</tr>
<tr>
<td><code>Mutex&lt;T&gt;</code></td>
<td>동시 접근을 제어하기 위해 스레드 간에 안전하게 데이터에 상호 배타적인 접근을 제공하는 스마트 포인터입니다.</td>
</tr>
</tbody></table>
<hr>
<h3 id="스마트-포인터-사용-예시">스마트 포인터 사용 예시</h3>
<ol>
<li><code>Box&lt;T&gt;:</code><pre><code class="language-rust">fn main() {
 let my_box: Box&lt;i32&gt; = Box::new(42);
 println!(&quot;Value: {}&quot;, *my_box);
}</code></pre>
</li>
</ol>
<p>1.1. <code>let my_box: Box&lt;i32&gt; = Box::new(42);:</code></p>
<ul>
<li><p><code>Box::new(42)</code>를 사용하여 <code>힙</code>에 정수 <code>42</code>를 할당하고, <code>my_box</code>라는 변수에 <code>Box&lt;i32&gt; 타입</code>으로 <code>바인딩</code>합니다.
<code>Box 스마트 포인터</code>는 데이터를 <code>힙에 할당</code>하고, 해당 데이터의 <code>소유권</code>을 갖습니다.
println!(&quot;Value: {}&quot;, *my_box);:</p>
<p><code>*my_box</code>를 사용하여 <code>my_box</code>의 <code>소유권을 해제</code>하고 <code>힙에 할당된 값을 가져옵니다.</code>
println! 매크로를 사용하여 값을 출력합니다.
위 코드는 Box 스마트 포인터를 사용하여 힙에 정수 값을 할당하고 이를 안전하게 소유하며 접근하는 예시입니다. Box 스마트 포인터는 힙에 할당된 데이터의 소유권을 가지고 있기 때문에 메모리 안전성을 보장하면서 힙 데이터에 접근할 수 있습니다.</p>
</li>
</ul>
<h3 id="톺아보기-3">톺아보기-3</h3>
<h4 id="소유권">소유권</h4>
<ul>
<li>Rust에서 메모리 관리와 생명주기를 제어하는 개념입니다. Rust는 소유권 규칙을 통해 메모리 안전성을 보장하면서 <code>자원의 생성, 소멸 및 이동을 관리</code>합니다. 소유권의 라이프사이클은 다음과 같은 단계로 구성됩니다</li>
</ul>
<ul>
<li><p><code>소유권의 생성:</code></p>
<p>소유권은 값이 생성되면서 처음으로 소유자에게 부여됩니다.
이때, let 키워드를 사용하여 변수에 값을 할당하거나 스마트 포인터를 생성함으로써 소유권이 생성됩니다.</p>
</li>
<li><p><code>소유권의 이전 (Transfer):</code></p>
<p>Rust에서는 한 번에 하나의 소유자만이 값을 소유할 수 있습니다.
소유권을 다른 변수나 함수로 이전하는 과정을 소유권의 이전이라고 합니다.
이때, 이전된 변수는 이후에 사용할 수 없습니다.</p>
</li>
<li><p><code>소유권의 대여 (Borrowing):</code></p>
<p>소유권이 이전된 변수나 함수는 대여자(Borrower)로서 소유권을 갖지 않으면서 값을 참조할 수 있습니다.
대여자는 불변 참조자(immutable reference) 또는 가변 참조자(mutable reference)를 통해 값을 읽거나 변경할 수 있습니다.</p>
</li>
<li><p><code>소유권의 소멸:</code></p>
<p>소유권이 소멸되는 시점은 소유권을 가진 변수가 스코프를 벗어날 때입니다.
변수의 스코프가 종료되면, 할당된 메모리는 자동으로 해제되고 리소스가 반환됩니다.
이때, 스마트 포인터의 소멸자(destructor)가 호출되어 자원의 정리나 추가적인 작업을 수행할 수 있습니다.
위의 life cycle을 예시 코드로 설명해보겠습니다:</p>
</li>
<li><p>소유권 예시 코드 </p>
</li>
</ul>
<pre><code class="language-rust">fn main() {
    let my_box: Box&lt;i32&gt; = Box::new(42); // 소유권의 생성

    {
        let borrowed_value: &amp;i32 = &amp;*my_box; // 소유권의 대여
        println!(&quot;Borrowed Value: {}&quot;, borrowed_value);
    } // borrowed_value가 스코프를 벗어나면서 대여 종료

    // 다른 작업 수행 가능

} // my_box가 스코프를 벗어나면서 소유권의 소멸 및 메모리 해제
</code></pre>
<ul>
<li><p><code>&amp;*my_box</code> : 
1) <code>*</code> 연산자를 사용하여 <code>my_box</code>가 <code>소유한 힙에 저장된 값을 가져옵니다</code>. 따라서 *my_box는 힙에 저장된 i32 값에 접근합니다.</p>
<p>2) <code>&amp;*my_box</code>: <code>&amp;</code> 연산자를 사용하여 <code>*my_box</code>의 값을 참조하는 <code>불변 참조자</code>(immutable reference)를 <code>생성</code>합니다. 이렇게 생성된 참조자는 <code>my_box</code>의 값에 대한 <code>대여자</code>가 되어 <code>값을 읽을 수 있습니다. 참조자를 통해</code>my_box<code>의</code>값을 변경할 수는 없습니다.`</p>
<p>3) <code>&amp;*my_box</code>는 my_box가 소유한 힙의 i32 값을 대여하여 참조하는 <code>불변 참조자를 생성</code>합니다. 이렇게 생성된 참조자를 사용하여 값을 읽을 수 있습니다.</p>
</li>
</ul>
<ol start="2">
<li><code>Rc&lt;T&gt;:</code><pre><code class="language-rust">use std::rc::Rc;
</code></pre>
</li>
</ol>
<p>fn main() {
    let shared_value: Rc<i32> = Rc::new(42);
    println!(&quot;Value: {}&quot;, *shared_value);
}</p>
<pre><code>
  - `let shared_value: Rc&lt;i32&gt; = Rc::new(42);`:
  `Rc::new(42)`를 사용하여 `정수 42를 포인터로 감싸고`, `shared_value`라는 변수에 `Rc&lt;i32&gt;` 타입으로 `바인딩`합니다. 이렇게 하면 `shared_valu`e가 `42 값을 공유`하는 스마트 포인터가 됩니다.

    - `Rc` 스마트 포인터는 `참조 카운팅`을 사용하여 `여러` 개의 `소유자를 허용`합니다. 각 소유자는 `Rc의 복사본`을 가지고 있으며, `소유자 수에 대한 카운트가 유지`됩니다. `카운트가 0`이 되면 `리소스가 자동으로 해제`됩니다.

 - 이 코드는 Rc 스마트 포인터를 사용하여 정수값을 공유합니다. Rc는 여러 소유자가 동일한 데이터를 공유하면서 데이터의 수명을 추적하고 메모리 누수를 방지하는 데 사용됩니다.

3. `Arc&lt;T&gt;:`

```rust
use std::sync::Arc;
use std::thread;

fn main() {
    let shared_value: Arc&lt;i32&gt; = Arc::new(42);

    let thread1 = thread::spawn({
        let shared_value = Arc::clone(&amp;shared_value);
        move || {
            println!(&quot;Thread 1: {}&quot;, *shared_value);
        }
    });

    let thread2 = thread::spawn({
        let shared_value = Arc::clone(&amp;shared_value);
        move || {
            println!(&quot;Thread 2: {}&quot;, *shared_value);
        }
    });

    thread1.join().unwrap();
    thread2.join().unwrap();
}
</code></pre><ul>
<li><p><code>Arc</code>를 사용하여 <code>정수값 42</code>를 <code>공유</code>하는 예시. <code>두 개의 스레드가 생성</code>되어 <code>shared_value</code>를 <code>공유</code>하고 <code>동시에 접근</code>하여 <code>값을 출력</code>합니다.</p>
<ul>
<li><p><code>Arc::new(42)</code>를 사용하여 정수 42를 Arc 스마트 포인터로 감싸서 생성합니다.</p>
</li>
<li><p><code>Arc::clone(&amp;shared_value)</code>을 사용하여 <code>shared_value</code>의 <code>참조 카운트를 증가</code>시키고, <code>새로운 Arc 포인터를 생성</code>합니다.</p>
<ul>
<li><code>Arc::clone()</code> 함수에 <code>&amp;shared_value</code>와 같이 <code>참조자</code>(reference)를 전달하는 이유는 <code>Arc 스마트 포인터의 복제(clone) 동작</code>을 <code>수행</code>하기 위해서입니다.</li>
</ul>
</li>
<li><p>각 스레드의 클로저에서 <code>Arc를 이동</code>시키고, 클로저 내부에서 <code>*shared_value</code>를 통해 <code>값을 참조</code>하여 출력합니다.</p>
</li>
<li><p><code>thread::spawn()</code>을 통해 <code>스레드를 생성</code>하고, <code>join()</code>을 사용하여 <code>스레드의 실행이 완료될 때까지 기다립니다.</code></p>
</li>
</ul>
</li>
</ul>
<h3 id="톺아보기-4">톺아보기-4</h3>
<h4 id="참조자와-스마트-포인터">참조자와 스마트 포인터</h4>
<ul>
<li><p>참조자와 스마트 포인터는 Rust에서 데이터에 대한 참조를 제공하는 두 가지 다른 개념입니다.</p>
<ul>
<li><p><code>참조자 (Reference)</code>:</p>
<p>하나. 참조자는 값을 소유하지 않고, 다른 값에 대한 참조를 만듭니다.</p>
<p>둘. 참조자는 <code>&amp;</code> 기호를 사용하여 생성되며, 변수 또는 값에 대한 불변 또는 가변 참조를 나타냅니다.</p>
<p>셋. 참조자는 빌림(Borrowing) 개념으로, 데이터의 소유권을 가져가지 않고도 값을 빌려올 수 있습니다.</p>
<p>넷. 참조자는 스코프를 벗어날 때까지 유효하며, 동일한 데이터에 대한 여러 참조자가 존재할 수 있습니다.</p>
</li>
<li><p><code>스마트 포인터 (Smart Pointer)</code>:</p>
<p>하나. 스마트 포인터는 값을 <code>소유</code>하고, 값을 가리키는 <code>포인터 역할</code>을 합니다.</p>
<p>둘. 스마트 포인터는 일반적으로 특정한 동작 또는 소유권 규칙을 갖춘 데이터 구조입니다.</p>
<p>셋. 스마트 포인터는 주로 메모리 관리, 동시성, 참조 카운팅 등의 작업을 수행하기 위해 사용됩니다.</p>
<p>넷. Rust에서의 스마트 포인터에는 Box, Rc, Arc, Cell, RefCell, Mutex 등이 있습니다.</p>
</li>
</ul>
</li>
</ul>
<ol start="4">
<li><code>Cell&lt;T&gt;:</code></li>
</ol>
<pre><code class="language-rust">use std::cell::Cell;

fn main() {
    let my_cell: Cell&lt;i32&gt; = Cell::new(42);
    let value = my_cell.get();
    println!(&quot;Value: {}&quot;, value);

    my_cell.set(24);
    let new_value = my_cell.get();
    println!(&quot;New Value: {}&quot;, new_value);
}</code></pre>
<ul>
<li><p><code>Cell&lt;T&gt;</code>은 내부 값을 변경 가능한 셀을 제공하는 스마트 포인터 타입입니다</p>
</li>
<li><p><code>my_cell</code>은 <code>Cell&lt;i32&gt;</code> 타입으로 생성되었고, 초기값으로 42가 설정되었습니다. <code>my_cell.get()</code>을 사용하여 셀의 값을 가져와서 <code>value</code> 변수에 할당하고, 그 값을 출력합니다. 그리고 <code>my_cell.set(24)</code>를 사용하여 셀의 값을 변경하여 24로 설정한 후, <code>my_cell.get()</code>을 사용하여 변경된 값을 가져와서 <code>new_value</code> 변수에 할당하고, 그 값을 출력합니다.</p>
</li>
</ul>
<ol start="5">
<li><code>RefCell&lt;T&gt;:</code> </li>
</ol>
<pre><code class="language-rust">use std::cell::RefCell;

fn main() {
    let my_ref_cell: RefCell&lt;i32&gt; = RefCell::new(42);
    let value = my_ref_cell.borrow();
    println!(&quot;Value: {}&quot;, *value);

    *my_ref_cell.borrow_mut() = 24;
    let new_value = my_ref_cell.borrow();
    println!(&quot;New Value: {}&quot;, *new_value);
}
</code></pre>
<ul>
<li><p>이 코드는 <code>RefCell&lt;i32&gt;</code>를 사용하여 <code>가변성을 런타임에 관리</code>합니다. <code>RefCell</code>은 <code>불변 참조(borrow())</code>와 <code>가변 참조(borrow_mut())</code>를 제공하므로, <code>같은 스코프 내에서도 해당 데이터에 대해 가변 참조를 가질 수 있습니다</code>.</p>
<ul>
<li>RefCell에 42를 저장합니다.</li>
<li>42를 읽어서 출력합니다.</li>
<li>42를 24로 변경합니다.</li>
<li>24를 읽어서 출력합니다.</li>
</ul>
</li>
<li><p>이렇게 <code>RefCell</code>을 사용하면 컴파일 시점이 아닌 <code>런타임 시점에 데이터의 가변성을 관리</code>할 수 있습니다. 이는 <code>Rust의 소유권 모델에서 일반적으로 허용되지 않는 동작을 가능</code>하게 하지만, <code>RefCell은 런타임에 borrow 규칙을 체크하여 안전성을 보장</code>합니다. 이러한 유연성 때문에 <code>RefCell은 여러 공유 상태를 가진 복잡한 데이터 구조를 다루는 데 유용</code>합니다.</p>
</li>
</ul>
<ol start="6">
<li><code>Mutex&lt;T&gt;:</code></li>
</ol>
<pre><code class="language-rust">use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let shared_value: Arc&lt;Mutex&lt;i32&gt;&gt; = Arc::new(Mutex::new(42));

    let thread1 = thread::spawn({
        let shared_value = Arc::clone(&amp;shared_value);
        move || {
            let mut value = shared_value.lock().unwrap();
            *value += 10;
            println!(&quot;Thread 1: {}&quot;, *value);
        }
    });

    let thread2 = thread::spawn({
        let shared_value = Arc::clone(&amp;shared_value);
        move || {
            let mut value = shared_value.lock().unwrap();
            *value -= 5;
            println!(&quot;Thread 2: {}&quot;, *value);
        }
    });

    thread1.join().unwrap();
    thread2.join().unwrap();
}
</code></pre>
<ul>
<li>이 코드는 <code>두 개</code>의 <code>스레드를 사용</code>하여 <code>공유 상태를 동시에 업데이트</code>하는 예제입니다. Rust의 std::sync::{Arc, Mutex}와 std::thread를 사용하여 공유 상태를 안전하게 처리하고 있습니다.</li>
</ul>
<ul>
<li><p>하나. <code>Arc&lt;Mutex&lt;i32&gt;&gt;</code> 타입의 <code>shared_value</code>를 생성하고 초기값으로 <code>42</code>를 설정합니다. Arc는 Atomic Reference Counting을 사용하여 여러 스레드 간에 안전하게 공유할 수 있는 <code>참조 카운팅 포인터</code>입니다. <code>Mutex</code>는 상호 배제를 위한 동기화 프리미티브로, <code>한 번에 하나의 스레드만 데이터에 액세스</code>할 수 있게 합니다.</p>
</li>
<li><p>둘. <code>첫 번째 스레드(thread1)를 생성</code>하고 <code>실행</code>합니다. <code>Arc::clone(&amp;shared_value)</code>를 통해 shared_value의 참조 카운터를 증가시키고, 이를 새로운 스레드에서 사용할 수 있게 합니다. 스레드는 shared_value의 Mutex를 잠그고(lock()), 성공적으로 잠금을 획득하면 값을 10만큼 증가시키고 출력합니다.</p>
</li>
<li><p>셋. <code>두 번째 스레드(thread2)</code>를 <code>생성하고 실행</code>합니다. thread1과 마찬가지로 Arc::clone(&amp;shared_value)를 사용하여 참조 카운터를 증가시키고, 스레드에서 사용할 수 있게 합니다. 스레드는 shared_value의 Mutex를 잠그고(lock()), 성공적으로 잠금을 획득하면 값을 5만큼 감소시키고 출력합니다.</p>
</li>
<li><p>넷. <code>join().unwrap()</code>을 사용하여 두 스레드가 모두 완료될 때까지 기다립니다. 이렇게 하면 모든 스레드 작업이 완료되기 전에 메인 스레드가 종료되지 않도록 합니다.</p>
</li>
</ul>
<h3 id="톺아보기-5">톺아보기-5</h3>
<pre><code class="language-rust">let shared_value: Arc&lt;Mutex&lt;i32&gt;&gt; = Arc::new(Mutex::new(42));
</code></pre>
<ul>
<li><p>이 코드에서는 두 가지 주요 개념, 즉 <code>Arc</code>와 <code>Mutex</code>가 사용됩니다.</p>
</li>
<li><p><code>Arc (Atomic Reference Counting)</code>: 이는 참조 카운팅 포인터로, <code>여러 스레드간에 안전하게 공유</code>될 수 있습니다. 참조 카운터는 <code>Arc의 복사본이 생성될 때마다 증가</code>하고, <code>복사본이 drop될 때마다 감소</code>합니다. 참조 카운터가 0이 되면, <code>Arc는 자신이 소유하는 메모리를 정리(cleanup)</code>합니다.</p>
</li>
<li><p><code>Mutex (Mutual Exclusion)</code>: 이는 상호 배제를 제공하는 동기화 프리미티브입니다. Mutex는 <code>한 번에 하나의 스레드만</code>이 <code>데이터에 액세스</code>하도록 보장합니다. 이는 데이터 레이스(data race)를 방지하는 데 사용되며, 두 개 이상의 스레드가 동시에 동일한 데이터를 변경하려고 할 때 발생할 수 있는 문제를 해결합니다.</p>
</li>
<li><p>그런 다음 두 개의 스레드가 생성됩니다. 각 스레드는 <code>Arc::clone(&amp;shared_value)</code>를 통해 shared_value에 대한 <code>참조를 복제(clone)</code>합니다. 이렇게 하면 <code>각 스레드가 shared_value에 독립적으로 액세스</code>할 수 있습니다.</p>
</li>
<li><p>스레드 내부에서는 <code>lock().unwrap()</code> 메서드를 사용하여 <code>Mutex의 잠금을 획득하려고 시도</code>합니다. 이 메서드는 두 가지 가능한 결과를 반환합니다:</p>
<ul>
<li><p><code>잠금</code>을 성공적으로 <code>획득한 경우</code>: 이는 Mutex가 현재 다른 스레드에 의해 잠겨있지 않음을 의미합니다. 이 경우, 해당 스레드는 <code>Mutex가 보호하는 데이터에 액세스</code>할 수 있습니다.</p>
</li>
<li><p><code>잠금</code>을 <code>획득</code>하지 <code>못한 경우</code>: 이는 Mutex가 현재 다른 스레드에 의해 잠겨있음을 의미합니다. 이 경우, lock() 메서드는 <code>현재 스레드를 블록(block)</code>하여, 잠금을 획득할 수 있을 때까지 <code>대기</code>하게 합니다.</p>
</li>
</ul>
</li>
<li><p>unwrap() 메서드는 Result 타입을 처리하는 데 사용되며, 이는 lock() 메서드가 실패하면 패닉(즉, 프로그램 종료)을 유발합니다.</p>
</li>
<li><p><code>join().unwrap()</code> 메서드는 각 스레드가 완료될 때까지 <code>메인 스레드가 대기하도록</code> 합니다. <code>join() 메서드</code>는 <code>Result 타입을 반환</code>하는데, 이는 <code>스레드가 패닉 상태</code>에서 종료된 <code>경우 Err를 반환</code>합니다. unwrap()는 이 Result를 처리하며, Err인 경우 프로그램을 패닉 상태로 만듭니다.</p>
</li>
<li><p>스레드가 완료되면 Mutex의 잠금이 자동으로 해제되고, 다른 스레드가 잠금을 획득할 수 있게 됩니다. 이렇게 하면 여러 스레드가 동시에 동일한 데이터에 액세스하려고 할 때 발생할 수 있는 데이터 레이스 조건을 방지합니다.</p>
</li>
<li><p>이러한 방식으로, Rust의 Arc와 Mutex는 여러 스레드에서 공유되는 데이터에 대한 동시 액세스를 안전하게 관리합니다. 이는 Rust의 메모리 안전성 보장에 중요한 역할을 합니다. 또한, 이를 통해 스레드 간에 데이터를 안전하게 공유하고 동기화하는 복잡한 작업을 수행할 수 있습니다.</p>
</li>
</ul>
<p><strong>아래 코드 설명중이었음</strong></p>
<pre><code class="language-rust">use futures::channel::mpsc; // 1
use futures::sink::SinkExt;
use std::sync::Arc;

type Sender&lt;T&gt; = mpsc::UnboundedSender&lt;T&gt;; // 2
type Receiver&lt;T&gt; = mpsc::UnboundedReceiver&lt;T&gt;;

async fn connection_writer_loop(
    mut messages: Receiver&lt;String&gt;,
    stream: Arc&lt;TcpStream&gt;, // 3
) -&gt; Result&lt;()&gt; {
    let mut stream = &amp;*stream;
    while let Some(msg) = messages.next().await {
        stream.write_all(msg.as_bytes()).await?;
    }
    Ok(())
}</code></pre>
<ol start="4">
<li><code>type Sender&lt;T&gt; = mpsc::UnboundedSender&lt;T&gt;;
type Receiver&lt;T&gt; = mpsc::UnboundedReceiver&lt;T&gt;;</code></li>
</ol>
<p>4.1. <code>mpsc::UnboundedSender&lt;T&gt;</code>와 <code>mpsc::UnboundedReceiver&lt;T&gt;</code>는 <code>futures</code> 라이브러리의 <code>mpsc (multi-producer, single-consumer)</code> 채널을 나타냅니다. 이것은 <code>여러 생성자(데이터를 보내는 스레드)와 단일 소비자(데이터를 받는 스레드)가 있는 비동기 메시지 패싱 채널</code>입니다.</p>
<p>4.2. <code>Sender&lt;T&gt; = mpsc::UnboundedSender&lt;T&gt;;</code> 와 <code>type Receiver&lt;T&gt; = mpsc::UnboundedReceiver&lt;T&gt;;</code> 는 코드를 간결하게 만드는 편의성을 제공하는 <code>타입 별칭</code>입니다. 이를 통해 <code>mpsc::UnboundedSender&lt;T&gt;</code>와 <code>mpsc::UnboundedReceiver&lt;T&gt;</code> 대신 간단히 <code>Sender&lt;T&gt;</code>와 <code>Receiver&lt;T&gt;</code>를 사용할 수 있습니다.</p>
<p>4.3. <code>UnboundedSender&lt;T&gt;</code>와 <code>UnboundedReceiver&lt;T&gt;</code>는 <code>&quot;unbounded&quot;</code> <code>채널</code>을 나타냅니다. 이는 <code>채널이 버퍼링된 메시지의 수에 대한 상한선이 없음</code>을 의미합니다. <code>이런 종류의 채널은 메시지가 전송되는 속도가 수신되는 속도보다 빠를 때 유용</code>하지만, <code>메모리 사용에 주의</code>해야 합니다. <code>메시지를 무한히 보낼 수 있기 때문</code>에, <code>메시지를 소비하는 속도가 충분히 빠르지 않으면 메모리 부족이 발생</code>할 수 있습니다.</p>
<ol start="5">
<li><p><code>함수 선언:</code></p>
<p>5.1. <code>async fn connection_writer_loop(mut messages: Receiver&lt;String&gt;, stream: Arc&lt;TcpStream&gt;) -&gt; Result&lt;()&gt;</code></p>
<p>5.2. 이 함수는 비동기적으로 실행되며, 두 개의 인자를 받습니다: 메시지를 받는 Receiver<String>와 TcpStream에 대한 <code>Arc(Atomic Reference Counting)</code> 포인터. 이 함수는 Result&lt;()&gt;를 반환합니다.</p>
</li>
<li><p><code>스트림 레퍼런스 얻기</code>   </p>
<p>6.1. <code>let mut stream = &amp;*stream;</code>
6.2. 이 코드는 <code>Arc 포인터</code>를 디레퍼런스하여 TcpStream에 대한 <code>뮤터블 참조를 얻습니다.</code></p>
</li>
<li><p><code>메시지 처리 루프:</code> 
7.1. <code>while let Some(msg) = messages.next().await {...}</code>
7.2. 이 코드는 메시지를 가져와서 처리하는 비동기 루프입니다. <code>Receiver&lt;String&gt;</code>에서 다음 메시지를 가져오는 <code>messages.next().await</code>가 비동기적으로 실행되며, 메시지가 도착하면 <code>Some(msg)</code> 패턴에 바인딩되어 <code>루프 내부로 들어갑니다.</code></p>
</li>
<li><p><code>메시지를 스트림에 쓰기:</code></p>
<p>8.1. <code>stream.write_all(msg.as_bytes()).await?;</code></p>
<p>8.2. 이 코드는 msg의 내용을 <code>스트림에 비동기적으로 쓰는 작업</code>을 수행합니다. <code>write_all</code>은 <code>주어진 바이트를 모두 쓸 때까지 계속적으로 호출</code>되며, <code>작업이 완료</code>되면 <code>메시지의 모든 바이트</code>가 <code>네트워크 스트림에 쓰여지게 됩니다</code>. 이 작업이 실패하면, 함수는 에러를 반환합니다(? 연산자 때문에).</p>
</li>
<li><p><code>함수의 끝:</code>
9.1. <code>Ok(())</code>
9.2. 모든 메시지가 처리되고 나면, 함수는 Ok(())를 반환하여 성공적으로 완료되었음을 나타냅니다.</p>
</li>
<li><p>이 함수는 <code>비동기</code>적으로 동작하므로, <code>네트워크 I/O 작업을 기다리는 동안</code> <code>다른 작업을 실행</code>할 수 있습니다. 이는 서버가 여러 클라이언트를 효율적으로 처리할 수 있게 해주는 중요한 특성입니다.</p>
</li>
</ol>
<h2 id="connecting-readers-and-writers">Connecting Readers and Writers</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1002(ft. rust)]]></title>
            <link>https://velog.io/@hyeseong-dev/%EB%B0%B1%EC%A4%80-1002ft.-rust</link>
            <guid>https://velog.io/@hyeseong-dev/%EB%B0%B1%EC%A4%80-1002ft.-rust</guid>
            <pubDate>Wed, 10 May 2023 09:15:29 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>두 정수 A와 B를 입력받은 다음, A×B를 출력하는 프로그램을 작성하시오.</p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 A와 B가 주어진다. (0 &lt; A, B &lt; 10)</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 A×B를 출력한다.</p>
<h3 id="예제-입력1">예제 입력1</h3>
<p>1 2</p>
<h3 id="예제-출력1">예제 출력1</h3>
<p>2</p>
<h3 id="예제-입력2">예제 입력2</h3>
<p>3 4</p>
<h3 id="예제-출력2">예제 출력2</h3>
<p>12</p>
<h3 id="case-1">Case-1</h3>
<pre><code class="language-rust">use std::io;

fn main() {
    println!(&quot;Please enter two numbers: &quot;);
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();

    let numbers: Vec&lt;i32&gt; = input
        .split_whitespace()
        .map(|s| s.parse().unwrap())
        .collect();

    let a:i32 = numbers[0];
    let b:i32 = numbers[1];

    println!(&quot;{}&quot;, a * b);
}
</code></pre>
<h3 id="case-2">Case-2</h3>
<pre><code class="language-rust">use std::io;

fn main() {
    println!(&quot;Please enter two numbers: &quot;);
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();

    let mut numbers = input.split_whitespace();
    let a: i32 = numbers.next().unwrap().parse().unwrap();
    let b: i32 = numbers.next().unwrap().parse().unwrap();

    println!(&quot;{}&quot;, a * b);
}</code></pre>
<p><code>case-1</code> 코드와 유사하지만, 입력을 처리하는 방식에 약간의 차이가 있습니다. 이 버전에서는 <code>split_whitespace()</code>로 <code>분리한 숫자</code>들을 <code>numbers 이터레이터</code>에 <code>저장</code>한 뒤, <code>next() 메서드</code>를 사용하여 <code>각 숫자를 차례대로 가져와</code>서 <code>a와 b에 할당</code>합니다. 이후 곱셈 결과를 출력합니다.</p>
<h4 id="설명">설명</h4>
<ol>
<li><code>next()</code> 메서드 : </li>
</ol>
<ul>
<li>컬렉션의 다음 요소를 차례대로 가져옴</li>
<li><code>Option&lt;T&gt;</code> 타입을 반환</li>
<li>T는 이터레이터가 생성하는 항목의 타입</li>
<li>이터레이터에 더 이상 요소가 없을 경우, next() 메서드는 <code>None을 반환</code></li>
<li>그렇지 않으면, <code>Some(T)를 반환</code>합니다. 이를 통해 이터레이터가 더 이상 요소를 가지고 있지 않음을 알 수 있습니다.</li>
<li>결국, <code>Option&lt;T&gt;</code> 타입을 반환 -&gt; <code>None</code> or <code>Some(T)</code> 일수 있음.</li>
</ul>
<ol start="2">
<li><p><code>unwrap메서드</code> : 
 2.1. Option 타입:</p>
<ul>
<li>Some(T): 내부에 값 T를 포함합니다.</li>
<li>None: 값이 없음을 나타냅니다.</li>
</ul>
<p> 2.2. Result 타입:</p>
<ul>
<li>Ok(T): 성공적인 결과를 포함하는 값 T를 가집니다.</li>
<li>Err(E): 에러를 나타내는 값 E를 포함합니다.</li>
</ul>
<p> 2.3. unwrap() 메서드:</p>
<ul>
<li>Option 또는 Result 타입에서 사용할 수 있습니다.</li>
<li>Option의 경우:<ul>
<li>Some(T)일 때: 내부의 값 T를 반환합니다.</li>
<li>None일 때: 패닉을 일으키고 프로그램이 종료됩니다.</li>
</ul>
</li>
<li>Result의 경우:<ul>
<li>Ok(T)일 때: 내부의 값 T를 반환합니다.</li>
<li>Err(E)일 때: 패닉을 일으키고 프로그램이 종료됩니다.</li>
</ul>
</li>
</ul>
<p> 2.4. 안전한 에러 처리:</p>
<ul>
<li>unwrap() 메서드보다는 match 문이나 if let 구문을 사용하여 에러 처리를 명시적으로 하는 것이 좋습니다.</li>
<li>이를 통해 프로그램의 안정성을 높일 수 있습니다.</li>
</ul>
</li>
</ol>
<h3 id="case---3">Case - 3</h3>
<pre><code class="language-rust">use std::io;

fn main() {
    println!(&quot;Please enter two numbers: &quot;);
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).unwrap();

    let numbers: Vec&lt;i32&gt; = input
        .split_whitespace()
        .map(|s| s.parse().unwrap())
        .collect();

    let (a, b) = (numbers[0], numbers[1]);

    println!(&quot;{}&quot;, a * b);
}
</code></pre>
<h4 id="설명-1">설명</h4>
<ol>
<li><p>destructuring or pattern matching</p>
<ul>
<li>tuple 내의 요소를 각각 변수 a와 b에 할당</li>
</ul>
</li>
<li><p><code>destructuring</code>을 쓸수 있는 데이터 타입. </p>
</li>
<li><ol>
<li>튜플: 여러 개의 다른 타입의 값을 묶어 하나로 만든 데이터 구조</li>
</ol>
</li>
<li><ol start="2">
<li>배열: 동일한 타입의 여러 개의 값을 묶어 하나로 만든 데이터 구조</li>
</ol>
</li>
<li><ol start="3">
<li>Structs: 용자 정의 데이터 타입으로, 여러 개의 다른 타입의 값을 묶어 하나로 만든 데이터 구조</li>
</ol>
</li>
<li><ol start="4">
<li>Enums: 여러 개의 다른 타입의 값을 묶어 하나로 만든 데이터 구조로, 각각의 값은 Enum의 다른 variant에 해당합니다.</li>
</ol>
</li>
<li><p><code>destructuring</code> 예시 코드 </p>
</li>
<li><ol>
<li><p>튜플 destructuring</p>
<pre><code class="language-rust">fn main() {
let tuple = (1, &quot;hello&quot;, 3.14);
let (a, b, c) = tuple;

println!(&quot;{}, {}, {}&quot;, a, b, c); // 출력: 1, hello, 3.14
}
</code></pre>
</li>
</ol>
</li>
</ol>
<pre><code>3.2. 배열 destructuring
```rust
fn main() {
    let array = [1, 2, 3];
    let [a, b, c] = array;

    println!(&quot;{}, {}, {}&quot;, a, b, c); // 출력: 1, 2, 3
}
</code></pre><p>3.3. <code>Struct</code> destructuring</p>
<pre><code class="language-rust">struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 3, y: 4 };
    let Point { x, y } = point;

    println!(&quot;{}, {}&quot;, x, y); // 출력: 3, 4
}
</code></pre>
<p>3.4. <code>Enum</code> destructuring</p>
<pre><code class="language-rust">enum Animal {
    Dog { name: String, age: i32 },
    Cat { name: String, age: i32 },
}

fn main() {
    let animal = Animal::Dog {
        name: String::from(&quot;Buddy&quot;),
        age: 3,
    };

    match animal {
        Animal::Dog { name, age } =&gt; println!(&quot;Dog named {} is {} years old&quot;, name, age),
        Animal::Cat { name, age } =&gt; println!(&quot;Cat named {} is {} years old&quot;, name, age),
    }
}
</code></pre>
<ol start="4">
<li><code>enum</code>, 열거형?
열거형(enum)은 Rust에서 다양한 값을 묶어서 하나의 타입으로 표현할 수 있는 사용자 정의 데이터 타입입니다. 열거형은 여러 개의 variant로 구성되며, 각 variant는 열거형의 이름을 사용하여 접근할 수 있습니다. 각 variant는 다른 타입과 값을 가질 수 있으며, 그 값들은 괄호 안에 표시됩니다.</li>
</ol>
<p>4.1. 구조 </p>
<pre><code class="language-rust">enum EnumName {
    Variant1(Type1),
    Variant2(Type2),
    Variant3(Type3),
    // ...
}
</code></pre>
<ul>
<li>예. 여러 종류의 도형을 표현하는 열거형</li>
</ul>
<pre><code class="language-rust">enum Shape {
    Circle(f64),          // 원
    Rectangle(f64, f64),  // 사각형
    Square(f64),          // 정사각형
}
</code></pre>
<ul>
<li><p>이 열거형에서 Shape는 열거형의 이름이며, Circle, Rectangle, Square는 각각의 variant입니다. 각 variant는 다른 타입의 값을 가집니다. 원은 반지름을 나타내는 f64 타입의 값을 가지며, 사각형은 가로와 세로 길이를 나타내는 두 개의 f64 타입 값을 가집니다. 정사각형은 한 변의 길이를 나타내는 f64 타입 값을 가집니다.</p>
</li>
<li><p>예. 아래는 Shape 열거형의 Rectangle variant를 사용하는 코드 예시입니다. Shape 열거형을 선언하고, 함수를 사용하여 사각형의 면적을 계산하는 예제입니다.</p>
</li>
</ul>
<pre><code class="language-rust">enum Shape {
    Circle(f64),
    Rectangle(f64, f64),
    Square(f64),
}

fn area(shape: &amp;Shape) -&gt; f64 {
    match shape {
        Shape::Circle(radius) =&gt; std::f64::consts::PI * radius * radius,
        Shape::Rectangle(width, height) =&gt; width * height,
        Shape::Square(side) =&gt; side * side,
    }
}

fn main() {
    let rectangle = Shape::Rectangle(4.0, 6.0);
    let rectangle_area = area(&amp;rectangle);
    println!(&quot;The area of the rectangle is: {:.2}&quot;, rectangle_area);
}
</code></pre>
<ul>
<li>이 예제에서 Shape 열거형을 선언한 후, area 함수를 사용하여 도형의 면적을 계산합니다. main 함수에서 Shape::Rectangle variant를 사용하여 사각형을 생성하고, 그 면적을 계산한 뒤 출력합니다.</li>
</ul>
<h3 id="case---4">Case - 4</h3>
<pre><code class="language-rust">use std::io;
use std::str::FromStr;

fn main() {
    println!(&quot;Please enter two numbers: &quot;);
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).expect(&quot;Failed to read line&quot;);

    let mut numbers = input.split_whitespace().map(|s| {
        i32::from_str(s).unwrap_or_else(|_| {
            eprintln!(&quot;Error: Invalid input&quot;);
            std::process::exit(1);
        })
    });

    let a = numbers.next().expect(&quot;Missing number&quot;);
    let b = numbers.next().expect(&quot;Missing number&quot;);

    println!(&quot;{}&quot;, a * b);
}</code></pre>
<h4 id="설명-2">설명</h4>
<ol>
<li>이 코드는 <code>parse()</code> 대신 <code>from_str()</code>을 사용하여 에러 메시지를 표시하고 프로그램을 종료하는 처리를 추가했습니다. 또한, 입력 문자열을 공백으로 분리하고 각각의 숫자를 추출하는 과정을 조금 더 명확하게 표현하였습니다.</li>
</ol>
<h3 id="case-5">Case-5</h3>
<pre><code class="language-rust">fn main() {
    let (a, b): (i32, i32);
    std::io::stdin().read_line(&amp;mut String::new()).map(|s| s.trim().parse().unwrap()).collect();
    println!(&quot;{}&quot;, a * b);
}
</code></pre>
<h4 id="설명-3">설명</h4>
<ol>
<li>이 코드는 공백을 기준으로 두 정수를 입력받고, 곱셈 결과를 출력합니다. 하지만 이렇게 코드를 짧게 작성하는 것이 항상 좋은 것은 아니며, 이해하기 어렵고 유지보수하기 힘들 수 있습니다. 가독성과 에러 처리를 고려한 코드를 작성하는 것이 더 바람직합니다.</li>
</ol>
<h3 id="case-6">Case-6</h3>
<pre><code class="language-rust">use std::io;

fn main() {
    println!(&quot;Please enter 2 numbers separated by a space:&quot;);

    let input = read_input();
    let parse_input(&amp;input);

    if let Some((a, b)) = numbers {
        println!(&quot;the product of the 2 numbers is: {}&quot;, a * b);
    } else {
        eprintln!(&quot;Error: Invalid input&quot;);
    }

}

fn read_input()-&gt; String {
    let mut input = String::new();
    io::stdin().read_line(&amp;mut input).expect(&quot;Failed to read line from stdin&quot;);
    input
}

fn parse_input(input: &amp;str) {
    let mut numbers = input.split_whitespace().map(|s| i32::from_str(s));

    match (numbers.next(), numbers.next()) {
        (Some(Ok(a)), Some(Ok(b))) =&gt; Some((a, b)),
        _ =&gt; None,
    }
}</code></pre>
<h4 id="설명-4">설명</h4>
<ol>
<li><p><code>let input = read_input();</code>: read_input() 함수를 호출하여 사용자로부터 입력을 받아 변수 input에 저장합니다.</p>
</li>
<li><p><code>let parse_input(&amp;input);</code>: parse_input() 함수를 호출하고 input 변수를 인자로 전달합니다. parse_input() 함수는 숫자 두 개를 반환합니다.</p>
</li>
<li><p><code>if let Some((a, b)) = numbers { ... }</code>: parse_input() 함수에서 반환한 숫자들이 유효한지 확인하고, 유효한 경우 두 숫자를 곱한 결과를 출력합니다.</p>
</li>
<li><p><code>else { eprintln!(&quot;Error: Invalid input&quot;); }</code>: parse_input() 함수에서 반환한 숫자들이 유효하지 않은 경우, 에러 메시지를 출력합니다.</p>
</li>
<li><p><code>fn read_input()-&gt; String { ... }</code>: 사용자로부터 입력을 받기 위한 함수입니다. io::stdin().read_line() 메소드를 사용하여 콘솔에서 입력을 받습니다.</p>
</li>
<li><p><code>fn parse_input(input: &amp;str) { ... }</code>: 사용자 입력 문자열을 받아 숫자를 추출하고, 추출한 숫자를 반환하는 함수입니다. 
<code>input.split_whitespace().map(...)</code> 메소드를 사용하여 입력 문자열에서 공백으로 구분된 숫자들을 추출합니다. 추출한 숫자를 정수로 변환한 후, (a, b) 형태의 튜플로 반환합니다.</p>
</li>
<li><p><code>parse_input</code>:</p>
<p>7.1. 매개변수 : </p>
<ul>
<li><p><code>input: &amp;str</code> : 
매개변수 input의 원형 타입은 <code>String</code>입니다. 하지만 <code>&amp;String</code>과 같은 문자열 레퍼런스는 문자열슬라이스(&amp;str)과 호환됨. 따라서  <code>&amp;String</code>를 <code>&amp;str</code>로 변환할 때는 별도의 변환 과정이 필요하지 않습니다. 이를 더 일반화해서 말하면, Rust에서는 <code>&quot;Deref coercion&quot;</code>이라는 기능을 제공하는데, 이를 통해 컴파일러가 암시적으로 타입을 변환해줍니다.</p>
<ul>
<li><code>Deref coercion</code>을 한글로는 <code>&quot;역참조 강제 변환&quot;</code> 또는 <code>&quot;간접 참조 강제 변환&quot;</code>으로 번역할 수 있습니다. <code>&quot;Deref&quot;는 &quot;역참조&quot;를 의미</code>하는데, 이는 <code>포인터나 레퍼런스 타입의 값을 직접 참조할 때 사용하는 연산자인 *와 유사</code>한 개념입니다. 따라서 &quot;Deref coercion&quot;는 <code>암시적으로 역참조 연산을 수행</code>하여 <code>타입을 변환</code>하는 것을 의미합니다. </li>
</ul>
</li>
<li><p><code>input: &amp;String</code>도 가능</p>
<ul>
<li><p>Q. <code>parse_input</code> 메서드의 매개변수 타입으로 input: &amp;String 도 가능하다. 그렇다면 <code>&amp;str, &amp;String 어느것을 쓰는것이 더 효율적</code>?</p>
</li>
<li><p>A. 실제 메모리 상에서는 <code>&amp;str이 더 효율적</code>입니다. 이는 &amp;String을 사용하면 <code>추가적인 메모리 할당과 복사가 발생</code>하기 때문입니다. 따라서 가능하다면, <code>함수의 매개변수나 구조체의 필드 등</code>에서 <code>문자열을 참조하는 경우</code>에는 <code>&amp;str을 사용하는 것이 좋습니다</code>. </p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<ul>
<li></li>
</ul>
<p>7.2. 반환 타입 : </p>
<ul>
<li>Option&lt;(i32, i32)&gt;</li>
</ul>
<ol start="8">
<li><code>(numbers.next(), numbers.next())</code> 튜플</li>
<li><code>_</code> 와일드카드 패턴 : <ul>
<li><code>모든 값과 매치된다는 것</code></li>
<li><code>패턴 매칭에서 해당 패턴과 일치하는 값이 없는 경우를 처리</code></li>
<li>보통 match 표현식의 <code>마지막 분기에 사용</code>되며, 일치하는 <code>패턴이 없는 경우</code> <code>디폴트 값</code>을 지정할 때 유용</li>
</ul>
</li>
<li><code>map메서드 클로저</code></li>
</ol>
<ul>
<li>클로저는 특정한 코드 블록을 의미`</li>
<li>특별한 문법을 사용하여 표현<ul>
<li><code>|s| i32::from_str(s)</code> 에서 <code>|s|</code> 는 클로저의 <code>인자를 정의</code>하고, <code>i32::from_str(s)</code> 는 클로저의 <code>반환값</code>을 정의합니다. </li>
</ul>
</li>
<li>클로저는 함수처럼 값을 반환할 수 있고, 이 반환값은 map 함수가 호출된 컨텍스트에서 사용될 수 있음. </li>
<li>따라서 <code>map(|s| i32::from_str(s))</code> 는 각각의 문자열에 대해 <code>i32::from_str</code> 함수를 호출하여 결과 값을 반환하는 클로저를 생성하고, 이 클로저를 각각의 문자열에 적용하여 <code>새로운 Iterator 를 생성</code>합니다.</li>
</ul>
<h2 id="힙-할당-타입">힙 할당 타입</h2>
<ul>
<li><p>알아야 하는 이유: 자동으로 형 변환이 가능하기 때문 </p>
<ul>
<li><p>Rust에서 Deref coercion은 Deref 트레이트를 구현하는 타입에서 사용됩니다. </p>
</li>
<li><p>Deref 트레이트는 <code>*</code> 연산자의 동작을 오버로드하는데 사용됩니다. 이를 통해 해당 타입에 대한 <code>참조가 아닌 해당 타입 자체에 대한 작업을 수행</code>할 수 있습니다.</p>
</li>
<li><p><code>Deref coercion</code>은 <code>Deref 트레이트를 구현하는 모든 타입에서 사용할 수 있습니다</code>. 일반적으로 <code>Box, Vec, String, Rc, Arc와 같은 힙 할당 타입</code>에서 많이 사용됩니다. 또한, <code>참조자(&amp;) 타입</code>에 대해서도 사용할 수 있습니다.</p>
</li>
</ul>
</li>
</ul>
<h3 id="box">Box</h3>
<pre><code class="language-rust">let boxed_num: Box&lt;i32&gt; = Box::new(42);
let num_ref: &amp;i32 = &amp;boxed_num;
assert_eq!(*num_ref, 42);
</code></pre>
<h3 id="vec">Vec</h3>
<pre><code class="language-rust">let vec_of_strings = vec![&quot;hello&quot;, &quot;world&quot;];
let slice_of_strings: &amp;[&amp;str] = &amp;vec_of_strings;
assert_eq!(slice_of_strings[0], &quot;hello&quot;);</code></pre>
<h3 id="string">String</h3>
<pre><code class="language-rust">let s = String::from(&quot;hello&quot;);
let s_ref: &amp;str = &amp;s;
assert_eq!(s_ref, &quot;hello&quot;);</code></pre>
<h3 id="rc">Rc</h3>
<pre><code class="language-rust">use std::rc::Rc;
let rc_num: Rc&lt;i32&gt; = Rc::new(42);
let num_ref: &amp;i32 = &amp;rc_num;
assert_eq!(*num_ref, 42);</code></pre>
<h3 id="arc">Arc</h3>
<pre><code class="language-rust">use std::sync::Arc;
let arc_num: Arc&lt;i32&gt; = Arc::new(42);
let num_ref: &amp;i32 = &amp;arc_num;
assert_eq!(*num_ref, 42);</code></pre>
<hr>
<h2 id="heap-할당-타입--참조자-타입">heap 할당 타입 &amp; 참조자(&amp;) 타입</h2>
<ol>
<li><p>힙 할당 타입은 메모리 할당이 동적으로 이루어지는 타입을 의미합니다. 러스트에서는 힙에 메모리를 할당하는 타입으로 <code>Box, Vec, String, Rc, Arc</code> 등이 있습니다. 이러한 타입은 값이 스택이 아닌 힙에 저장되기 때문에 <code>포인터로 스택에 참조</code>됩니다.</p>
</li>
<li><p><code>참조자(&amp;) 타입</code>은 <code>다른 값을 참조</code>하는 데 <code>사용</code>되는 <code>타입</code>으로, 해당 값을 <code>소유하지 않습니다</code>. 대신 참조된 값의 <code>메모리 위치를 가리키는 포인터 역할</code>을 합니다. 이러한 타입은 <code>불변 참조자(&amp;T)</code>와 <code>가변 참조자(&amp;mut T)</code>가 있습니다. 불변 참조자는 값을 읽을 수만 있으며 변경할 수 없고, 가변 참조자는 값을 읽고 변경할 수 있습니다. <code>참조자는 스택에 저장</code>되며, <code>스택에서 값을 참조</code>하기 때문에 <code>값이 소유한 메모리 공간의 크기에 영향을 받지 않습니다.</code></p>
</li>
</ol>
<p>2.1. <strong>Q. <code>참조자 타입을 사용 할 수 없는 타입</code>은 뭐야? 혹은 <code>사용 할 수 없는 경우</code>가 있어?</strong></p>
<p>2.1. <strong>A.</strong> 참조자(&amp;)는 <code>소유권을 빌릴 수 있는 타입</code>에서만 사용할 수 있습니다. 
2.1.1. <strong>소유권을 가지지 않는 타입</strong>에서는 <strong>참조자를 사용할 수 없습니다.</strong> </p>
<ul>
<li>기본 타입인 숫자 타입들(<code>i32, u8, f64 등), 불(bool) 타입, 복합 타입 중 하나 이상의 필드가 소유권을 가지는 타입(</code>Box, Vec, String` 등)이 포함됩니다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>