<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>soo-im.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 12 Feb 2025 15:00:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>soo-im.log</title>
            <url>https://velog.velcdn.com/images/soo-im/profile/8a2ee14c-7b22-4479-8e4d-d45c1f4a732d/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. soo-im.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/soo-im" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[2025-02 TIL]]></title>
            <link>https://velog.io/@soo-im/2025-02-TIL</link>
            <guid>https://velog.io/@soo-im/2025-02-TIL</guid>
            <pubDate>Wed, 12 Feb 2025 15:00:55 GMT</pubDate>
            <description><![CDATA[<p>야근 지옥에서 내가 돌아왔다!!!
다시 야근을 하더라도 오늘의 나는 또 새 게시글을 쓴다 🍎 (그냥 월 표기를 없앨까봐... 연 표기로 할까...)</p>
<p>가물가물한 리액트의 기억을 더듬어서 다시 시작 ㅠㅠ</p>
<h2 id="2025-02-12">2025-02-12</h2>
<h3 id="컴포넌트-대문자">컴포넌트 대문자</h3>
<p>리액트에서 컴포넌트 명칭은 꼭! 대문자로 써야 한다. 앞에서도 언급했지만... 소문자로 쓰면 HTML 태그로 인식해서 렌더링이 안된다.</p>
<pre><code class="language-jsx">    function Hello() {
      return (
          &lt;div&gt;
            &lt;h1&gt;Hello&lt;/h1&gt;
          &lt;/div&gt;
      );
    }
    function world() {
      return (
          &lt;div&gt;
            &lt;h1&gt;World&lt;/h1&gt;
          &lt;/div&gt;
      );
    }
    function App() {
      return (
          &lt;div&gt;
            &lt;Hello /&gt; // 컴포넌트 호출
            &lt;world /&gt; // 컴포넌트 호출 안 됨
          &lt;/div&gt;
      );
    }</code></pre>
<p>소문자 <code>world</code> 컴포넌트는 안 나온다. 뭔가 이상하다 싶으면 대소문자 체크하기~!</p>
<h2 id="2025-02-18">2025-02-18</h2>
<h3 id="함수-매개변수를-전달하는-방식---python-vs-js-vs-jsx">함수 매개변수를 전달하는 방식 :  Python vs. JS vs. JSX</h3>
<h3 id="1-python">1. Python</h3>
<ol>
<li>키워드 전달<pre><code class="language-python">def my_func(a, b): ...
my_func(a=1, b=2)
my_func(b=2, a=1) # 순서와 무관
# my_func(c=1, b=2) # 정의되지 않은 키워드를 전달하면 에러</code></pre>
</li>
<li>위치 전달<pre><code class="language-python">def my_func(a, b): ...
my_func(1, 2) # 순서대로 전달</code></pre>
</li>
<li>객체 전달<pre><code class="language-python">def my_func(a, b): ...
my_func({&quot;a&quot; : 1, &quot;b&quot; : 2})</code></pre>
</li>
<li>동적으로 객체 전달<pre><code class="language-python">def my_func(**kwargs): ...
my_func({&quot;a&quot; : 1, &quot;b&quot; : 2})</code></pre>
</li>
</ol>
<h3 id="2-javascript">2. JavaScript</h3>
<p>Python과 다르게 키워드 전달 방식은 없다.</p>
<ol>
<li>위치 전달<pre><code class="language-js">function myFunction(a, b) {
 console.log(a);
 console.log(b);
}
MyComp(1, 2);
// 출력 결과 :
// 1
// 2</code></pre>
</li>
<li>강제로 객체를 전달하면? → 객체를 통째로 첫 번째 위치 인자에 할당해버린다!<pre><code class="language-js">function myFunction(a, b) {
 console.log(a);
 console.log(b);
}
myFunction({ a: 1, b: 2 });  
// 출력 결과 :
// {a:1, b:2}
// undefined
// a=1, b=2로 할당되는 게 아니라 a에 {a:1, b:2} 객체가 통째로 할당되고 b에는 아무것도 할달되지 않는다. </code></pre>
</li>
<li>객체로 전달하는 방법 &quot;객체 구조 분해 할당 destructuring&quot;<pre><code class="language-js">function myFunction({a, b}) { // 위와 다르게 { }로 객체를 명시
 console.log(a);
 console.log(b);
}
myFunction({ a: 1, b: 2 });
// 출력 결과 :
// 1
// 2
// 이렇게 하면 a, b 각각 할당이 가능하다!</code></pre>
</li>
</ol>
<h3 id="3-jsx">3. JSX</h3>
<ol>
<li>JSX도 분해 할당이 가능하다.<pre><code class="language-jsx">function MyComponent({ a, b }) {
 return &lt;div&gt;{a} {b}&lt;/div&gt;;
}
</code></pre>
</li>
</ol>
<p>// 개별 속성으로 전달 가능
<MyComponent a={1} b={2} /></p>
<p>// 객체로 한 번에 전달 가능
const props = { a: 1, b: 2 };
&lt;MyComponent {...props} /&gt; </p>
<pre><code>2. JSX의 `props`는 Python `**kwargs`와 유사하게 동작한다. 항상 객체를 받아서 자동으로 할당해준다. 
```jsx
function MyComponent(props) {
    return &lt;div&gt;{props.a} {props.b}&lt;/div&gt;;
}

// 전달 방법 : 1번 예시의 두 가지 모두 가능</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2024-11 TIL]]></title>
            <link>https://velog.io/@soo-im/2024-11-TIL</link>
            <guid>https://velog.io/@soo-im/2024-11-TIL</guid>
            <pubDate>Mon, 04 Nov 2024 16:03:38 GMT</pubDate>
            <description><![CDATA[<h2 id="2024-11-04-파생상품">2024-11-04 &lt;파생상품&gt;</h2>
<p>오늘은 레버리지 관련 파생상품을 알아보자... 빨리 끝낼 수 있을까 ㅠ.ㅠ </p>
<h3 id="바이낸스-spot-margin">바이낸스 Spot Margin</h3>
<p><img src="https://velog.velcdn.com/images/soo-im/post/21768cfc-e922-461f-8b44-a7e3cafa5054/image.png" alt="">
거래하고자 하는 계좌 유형을 선택하세요. 
교차: 자산은 모든 페어 거래에 사용할 수 있으며 위험은 전체 계좌가 부담합니다. 
격리: 자산은 특정 트레이딩 페어에만 사용되며 위험은 특정 트레이딩 페어 계정 내에서 감수합니다.
<img src="https://velog.velcdn.com/images/soo-im/post/bddebe9b-1568-4cae-a225-5674b4ff4285/image.png" alt="">
<img src="https://velog.velcdn.com/images/soo-im/post/a9a874d9-0adc-4bbb-9528-a50438b77f0a/image.png" alt="">
여기에서 자산을 빌릴 수 있습니다. 최대 대출 금액은 담보와 특정 코인에 대한 개별 대출 한도에 따라 결정됩니다.
<img src="https://velog.velcdn.com/images/soo-im/post/81274359-71c7-46eb-8d59-b17081d69768/image.png" alt="">
<img src="https://velog.velcdn.com/images/soo-im/post/023aeef3-cfd5-4794-8366-78b44b130de5/image.png" alt=""></p>
<ol>
<li><p>Margin Account 개설
최초로 Margin Trade에 진입해서 Margin 계좌를 개설한다.</p>
</li>
<li><p>Auto-Transfer
Spot 계좌에서 Margin 계좌로 자동 이체할 수 있다.</p>
</li>
<li><p>Trade with Auto-Borrow
Borrow 탭에서 매매가와 수량을 입력해서 실행하면 -&gt; 내 자산과 빌린 금액을 합쳐 (부족한 금액은 자동으로 대출) 매매를 한다.
<img src="https://velog.velcdn.com/images/soo-im/post/cbe680ea-3a97-4359-b5e8-1651b47d8efe/image.png" alt=""></p>
</li>
<li><p>Repay with Auto-Repay
Repay 탭에서 매매가와 수량을 입력해서 실행하면 -&gt; 매매 금액에서 자동으로 빌린 금액이 상환이 된다. (상환을 어떤 기준으로 하는지는 아직 모르겠네... 수량으로 하나? 그러면 Cross에서는 못할 것 같은데)
<img src="https://velog.velcdn.com/images/soo-im/post/40024d07-1bed-4b27-909e-081041b6d47b/image.png" alt=""></p>
</li>
</ol>
<p>예시)</p>
<ol>
<li>BTC 가격이 1,000원이고 나는 Spot 지갑에 1,000원 보유하고 있음</li>
<li>3배를 땡기고 싶어서 2,000원 빌려서 Margin 지갑에 3,000원을 만듦</li>
<li>3,000원으로 3개 BTC를 삼</li>
<li>시나리오 1) BTC가 100원 올라서 3,300원을 매도하고 2,000원을 갚음. 결국 1,000원에서 1,300원이 됨. 나는 100원 이득이 아니라 3배인 300원 이득을 본 셈.</li>
<li>시나리오 2) BTC가 100원 떨어져서 2,700원을 매도하고 2,000원을 갚음. 나는 100원 손해가 아니라 300원 손해를 본 셈.</li>
<li>시나리오 3) BTC가 400원 떨어져서 1,800원을 매도함. 이렇게 전부 청산해도 빌린 금액을 갚을 수 없을 때에는 자동 상환이 되지 않고 임의로 갚아야 함.</li>
</ol>
<p>궁금한 점)</p>
<ol>
<li>매수/매도 모두 대출이 될까?</li>
<li>자동 상환은 언제/얼마를 갚나? 일단 매수-매도 한 세트 돌면 무조건 빌린 돈부터 상환하고 보나?</li>
<li>시나리오 3 (자동 상환 불가) 경우는 자산 가격에 따라서 수시로 상황이 변할텐데 어떻게 판단하나?</li>
</ol>
<h2 id="2024-11-24-회계원리">2024-11-24 &lt;회계원리&gt;</h2>
<p>드디어 출근 안 하는 주말이 왔다 ㅠㅠ
대출 관련 서비스를 위해 회계원리를 배워보자 으자자 🔥</p>
<p>강의 : <a href="https://youtu.be/1evJVf2zcUE?si=ouH-j-5r15ulAd0w">사진찍는회계사 유튜브</a></p>
<h3 id="부기">부기</h3>
<ol>
<li>부기
부기(簿記)는 거래를 기록하는 것을 말한다. 부(簿)가 &#39;장부&#39; 할 때 부.</li>
<li>단식부기
흔히 생각하는 가계부를 쓰는 방식. 거래로 인한 입금/출금을 기록한다.</li>
<li>단식부기의 맹점
기본적으로 입금 = 수익, 출금 = 비용으로 보기 때문에, 각 거래의 특성을 보기 어렵다.
만약 자동차를 사서 출금이 일어나면 그 달에 큰 비용이 발생한 것으로 기록 된다. 하지만 자동차는 수 년간 사용하기 때문에 특정 달의 비용으로 보기는 어렵다.
<img src="https://velog.velcdn.com/images/soo-im/post/1346cb93-6dd0-44ef-9026-a914e5466108/image.png" alt=""></li>
<li>거래의 이중성
위 예시처럼 입금/출금은 수익/비용으로만 볼 수 없다. 입금은 수익 뿐 아니라 자산의 감소 (예: 차를 팔았다) 혹은 부채의 증가 (예: 돈을 빌렸다)로 볼 수도 있고, 출금도 반대로 볼 수 있다.
<img src="https://velog.velcdn.com/images/soo-im/post/b409a34b-a233-462d-87e9-8580471aabcf/image.png" alt=""></li>
<li>복식부기
차변/대변을 동시에 기록하는 방식.
차변 : 자산, 비용 / 대변 : 부채, 자본, 수익 -&gt; 해당 위치에 적으면 +, 반대 위치에 적으면 -.
예를 들어 자산을 차변에 적으면 자산이 증가한 것이고, 대변에 적으면 자산이 감소한 것이다.
<img src="https://velog.velcdn.com/images/soo-im/post/401212a7-4882-4165-af20-99af4096c24e/image.png" alt="">
<img src="https://velog.velcdn.com/images/soo-im/post/d3609a24-76bd-4050-8b28-82fa023e6475/image.png" alt=""></li>
<li>자산, 부채, 자본
자산을 차변에 적고, 부채/자본을 대변에 적는 이유는 -&gt; 자산이 늘어나거나 줄었을 때, 그 돈이 어디에서 나왔는지 확인하기 위함이다.
<img src="https://velog.velcdn.com/images/soo-im/post/21576de2-79c6-4438-9c8d-f4f7311889b1/image.png" alt="">
자산을 산 경우, 단식부기에서는 5억의 비용만 기록하는 반면 / 복식부기에서는 자산이 증가했고, 그 돈의 출처는 무엇인지까지 기록한다.
<img src="https://velog.velcdn.com/images/soo-im/post/ac3e4d6f-3c15-42fe-896b-1f8c54b1e867/image.png" alt=""></li>
<li>비용, 수익
그러면 왜 비용은 차변 (좌)이고 수익은 대변 (우)일까? 
돈을 벌어서 자산이 늘어났다고 가정하면, 차변에 자산을 기록한다. 그러면 이 자산이 어디에서 기인했는지를 기록하는 것이 대변이기 때문에 &#39;수익&#39;이 와야 하므로 수익이 대변에 오는 것이다.
반대로 자산이 줄었을 때에는 비용이 발생한 것이기 때문에 비용이 차변에 온다.
<img src="https://velog.velcdn.com/images/soo-im/post/bb56dea7-2faa-4e9f-bd17-6a921813be01/image.png" alt="">
수익이 차변 (좌)에 오거나 비용이 대변 (우)에 오는 것은 수익의 감소 혹은 비용의 감소인데 이건 나중에 알아본다. (이건 무슨 상황이지...)</li>
</ol>
<h3 id="재무제표-요소">재무제표 요소</h3>
<ol>
<li>저량, 유량
저량 Stock : 한 시점의 값 (X) → 자산, 자본, 부채
유량 Flow : 일정 기간의 값 (dX/dt) → 수익, 비용</li>
<li>재무제표의 목적
재무상태를 측정하는 것이 재무상태표 (저량)이고
성과를 측정하는 것이 손익계산서 (유량)이다.</li>
<li>재무상태
&#39;과거 사건의 결과&#39;는 이미 일어난 일만 다루고, 앞으로 발생할 일은 재무상태에 반영하지 않음을 의미한다. (내일 월급이 들어온다고 해서 그걸 자산으로 반영하지는 않는다.)</li>
</ol>
<table>
<thead>
<tr>
<th>용어</th>
<th>정의</th>
<th>예</th>
</tr>
</thead>
<tbody><tr>
<td>자산</td>
<td>과거 사건의 결과로 현재 소유 중이고, 미래의 현금 (및 현금성자산) 유입에 기여할 것으로 기대하는 자원.</td>
<td>현금, 토지, 채권, 지적재산권</td>
</tr>
<tr>
<td>부채</td>
<td>과거 사건의 결과로 현재 가지는 의무. 자원의 유출로 이행해야 하는 의무.</td>
<td>차입금, 외상, 용역 제공 의무 (A/S)</td>
</tr>
<tr>
<td>자본</td>
<td>자산에서 부채를 차감한 잔여 지분.</td>
<td>-</td>
</tr>
</tbody></table>
<ol start="4">
<li>성과</li>
</ol>
<table>
<thead>
<tr>
<th>용어</th>
<th>정의</th>
<th>예</th>
</tr>
</thead>
<tbody><tr>
<td>수익</td>
<td>자산의 증가/부채의 감소로 &#39;자본&#39;이 증가하는 것</td>
<td>판매 수익, 매매 차익</td>
</tr>
<tr>
<td>비용</td>
<td>자산의 감소/부채의 증가로 &#39;자본&#39;이 감소하는 것</td>
<td>생산 비용, 매매 차손</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>차익/차손</strong>
3천 원 짜리 차를 1천 원에 팔았으면 → 자본은 1천 원 늘었지만 &#39;수익&#39;이 아니고 &#39;비용&#39; (차손)으로 봐야 한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024-10 TIL]]></title>
            <link>https://velog.io/@soo-im/2024-10-TIL</link>
            <guid>https://velog.io/@soo-im/2024-10-TIL</guid>
            <pubDate>Tue, 01 Oct 2024 13:27:04 GMT</pubDate>
            <description><![CDATA[<h2 id="2024-10-01-리액트">2024-10-01 &lt;리액트&gt;</h2>
<p>강의 듣다 보니... =&gt; 이런 당황스러운 문법이 나와서;; 잠시 멈추고 알아봤다.</p>
<h3 id="arrow-function">Arrow Function</h3>
<p>Arrow function은 JS E6S에서 쓸 수 있는 함수 표현식이다.</p>
<pre><code class="language-js">function my_func(a){
  return a + 1;
}

var my_func = function(a){
  return a + 1;
}

var my_var = (a) =&gt; {return a + 1}

var my_var = a =&gt; return a + 1;</code></pre>
<p>네 개 모두 함수를 표현한다.</p>
<p>Arrow Function 표현식에서 파라미터를 감싸는 괄호 ()나 return을 감싸는 중괄호 {}는 안의 내용이 단일 값일 때 생략이 가능하다.</p>
<h3 id="assigning-function">Assigning Function</h3>
<p>위의 예시를 보면 <code>var my_var = function()</code>, function 자체가 변수에 할당된 것을 볼 수 있다.
생소해 보이지만 Python에서 lambda function이랑 비슷하게 생각하면 된다. <code>my_var = lambda a: print(a)</code></p>
<p>그럼 <code>var my_var = function</code> (할당 assigning)가 <code>function my_func</code> (선언 declaring)와 비교해 다른 점은 무엇일까?</p>
<ol>
<li>호이스팅 제외
JS에서는 함수를 코드 내 어디에서 선언하더라도 무조건 함수를 맨 먼저 선언하기 때문에 (hoisting = 끌어올리다), <code>function my_func</code> 선언보다 먼저 <code>my_func</code>을 호출해도 상관없다. 
하지만 변수에 assign하면 호이스팅에서 제외되어, 변수가 선언되어야 함수도 생성된다.</li>
<li>익명 함수 (이름 없음)
헷갈릴 수도 있는데... 위에서 함수를 할당받은 <code>my_var</code>은 <strong>함수 이름이 아니다.</strong> <code>my_var</code>은 변수 이름이고, 변수에 할당된 함수는 이름이 있을 수도 있고 없을 수도 있다.
<code>var my_var = function ()</code> 이면 함수 이름이 없는 것이고, 
<code>var my_var = function my_func()</code> 이면 함수 이름이 <code>my_func</code>인 것이다.</li>
</ol>
<p>Declaring 대신 Assigning을 쓰는 게 좋은 케이스는 아직 못 찾았다. 예시를 봐도 declaring에 비해 큰 이점은 없는 것 같아서... 굳이 찾자면 호이스팅 제어 정도?</p>
<p>공부하다보면 더 유리한 케이스를 찾겠지 🙄</p>
<h2 id="2024-10-02-리액트">2024-10-02 &lt;리액트&gt;</h2>
<h3 id="jsx-1---요소-만들기">JSX (1) - 요소 만들기</h3>
<p>이전 시간에 JS 안에서 바로 인터랙티브 요소를 만들고 HTML로 렌더링하는 것을 배웠다.</p>
<pre><code class="language-js">// 요소 생성
const btn = React.createElement(&quot;button&quot;, {
  id: &quot;btn&quot;,
  onClick: ()=&gt; console.log(&quot;I&#39;m Clicked&quot;),
}, &quot;Click me&quot;);

// 렌더링
const root = document.getElementById(&quot;root&quot;);
ReactDOM.render(btn, root);</code></pre>
<p>이걸 HTML과 유사한 문법으로 쓰도록 도와주는 것이 JSX 문법이다. 동작은 동일하고, 그냥 표기 방법만 달라진다.</p>
<p>JSX 문법으로 쓰면 문법 변환기를 통해 자동으로 위의 코드로 해석돼서 실행이 된다.</p>
<pre><code class="language-jsx">// 요소 생성 with JSX
const Btn = (&lt;button
             id=&quot;btn&quot;
             onClick={()=&gt; console.log(&quot;I&#39;m Clicked&quot;)}&gt;
             Click me&lt;/button&gt;);</code></pre>
<p>((내가 HTML을 자주 안 써서 그런지 오히려 윗 방식이 더 편리해보이는 것 같기도...))</p>
<h2 id="2024-10-09-리액트">2024-10-09 &lt;리액트&gt;</h2>
<h3 id="jsx-2---요소-마트료시카">JSX (2) - 요소 마트료시카</h3>
<p>React에서 한 요소 안에 여러 요소를 넣을 때에는 createElement의 마지막 인자인 &#39;content&#39;에 내부 요소를 리스트로 전달해준다.</p>
<pre><code class="language-js">const btn = //...
const title = //...

const container = React.createElement(&quot;div&quot;, null, [title, btn])</code></pre>
<p>이걸 JSX로 표현하면 똑같이 HTML 문법처럼 div안에 내부 요소를 집어 넣으면 된다.</p>
<pre><code class="language-html">&lt;!--HTML--&gt;
&lt;div&gt;
  &lt;title&gt;I&#39;m a Title&lt;/title&gt;
  &lt;button&gt;Clcik me&lt;/button&gt;
&lt;/div&gt;</code></pre>
<pre><code class="language-jsx">// JSX
const Container = () =&gt; (
        &lt;div&gt;
            &lt;Title /&gt;
            &lt;Btn /&gt;
        &lt;/div&gt;
    );
ReactDOM.render(&lt;Container /&gt;, root);</code></pre>
<p>그런데 이렇게 할 때 주의할 점이 있다.</p>
<p>첫째, 생성한 컴포넌트는 대문자로 시작해야 한다. (<code>Title</code>, <code>Btn</code>) 소문자로 시작하면 HTML 태그로 인식을 해버린다.</p>
<p>둘째, 내부 요소는 함수로 만들어야 한다. <code>const Btn = ( &lt;button&gt;&lt;/button&gt;)</code> 가 아니라 </p>
<pre><code class="language-jsx">const Btn = () =&gt; ( ... );
function Btn () { return ... };</code></pre>
<p>로 해주어야 한다.</p>
<p>즉 <code>div</code> 태그 안에서 호출하는 <code>&lt;Title /&gt;</code>은 요소가 아니라 함수가 되어야 한다.</p>
<blockquote>
<p><code>&lt;Title /&gt;</code><strong>은 무슨 뜻?</strong>
HTML 태그 열면서 닫는 그 태그다.</p>
</blockquote>
<blockquote>
<p><strong>왜 함수로 만들어야 해?</strong>
그냥 <code>const Title = (&lt;h3&gt;Title&lt;/h3&gt;);</code>로 만들고 호출하면 안되나? 싶지만...
Python에 비유하자면 변수를 만드는 것과 class를 만드는 것의 차이이다.
class로 <code>Title</code>을 만들면 호출될 때마다 새로운 인스턴스가 만들어지고, 인스턴스는 필요에 따라 각각 바뀔 수 있다.
하지만 변수로 <code>Title</code>을 만들면 고정된 값이기 때문에 무조건 하나의 값만 사용이 가능하다.
그래서 <code>Title</code>을 함수로 생성하는 것이다.</p>
</blockquote>
<blockquote>
<p><strong>난 인스턴스 필요없는데...</strong>
그러면 고정된 요소로 만들고 다음과 같이 쓰면 된다.</p>
</blockquote>
<pre><code class="language-jsx">const Container = (
  &lt;div&gt;
    {Title}
    {Title}
  &lt;/div&gt;
);</code></pre>
<h2 id="2024-10-12-리액트">2024-10-12 &lt;리액트&gt;</h2>
<h3 id="state-1">state (1)</h3>
<p>state는 데이터에 따라 변동하는 UI를 만들 때 사용하는 도구이다.</p>
<p>정확한 설명은 나중에 배우고... 우선 React JS에서 state 없이 데이터를 바꾸는 단순한 방법부터 배워보자.</p>
<ul>
<li>Vanilla JS<pre><code class="language-js">// 텍스트 넣을 span 태그 가져오기
const my_span = document.querySelector(&quot;span&quot;);
</code></pre>
</li>
</ul>
<p>// counter를 업데이트하고 span text를 다시 쓰기
function handleClick () {
      counter = counter + 1;
      my_span.innerText = <code>Total Clicks: ${counter}</code>;
};</p>
<p>button.addEventListener(&quot;click&quot;, handleClick);</p>
<pre><code>

- React JS
```js
let counter = 0;

// counter를 업데이트하고 다시 렌더링 하기
function handleClick() {
  counter = counter + 1;
  render();
}

// 만든 요소를 렌더링하기
function render() {
  ReactDOM.render(&lt;Container /&gt;, root);
};

// 함수가 포함된 요소 만들기
const Container = () =&gt; (
  &lt;h3&gt;Total Clicks: {counter}&lt;/h3&gt; 
  &lt;button onClick={handleClick}&gt;Click&lt;/button&gt;
);

// 최초 렌더링하기
render();</code></pre><p>Vanilla JS는 span 안의 text를 직접 바꾸고,
React JS는 요소 자체는 그대로 두되 다시 렌더링하는 방식으로 진행한다.</p>
<p>Vanilla JS는 텍스트 전체가 다시 업데이트되는 데 반해, React JS는 <code>counter</code>만 업데이트가 된다. 변경되는 부분만 업데이트 하다보니 성능 상 도움이 된다.</p>
<p>다만 위의 예시는 요소가 바뀌는 함수마다 <code>render()</code>를 호출해주어야 하는 귀찮음이 있어... 다음 강의에서는 이 점을 해소한다.</p>
<h2 id="2024-10-13-리액트">2024-10-13 &lt;리액트&gt;</h2>
<h3 id="state-2">state (2)</h3>
<p>React JS는 _데이터의 변화_를 감지하면 자동으로 _요소를 리렌더링_시키는 hook을 제공한다. 이게 <code>state</code>다!</p>
<ol>
<li><p>어떻게 만드나?
<code>React.useState()</code>로 생성한다.
이건 <code>[state, modifier]</code> 배열을 반환하는데 state는 변화를 감지할 데이터, modifier은 state를 변화를 만드는 함수이다.
위 예시에서 state는 <code>counter</code>, modifier은 <code>counter = counter +1</code>과 같다고 보면 된다.</p>
</li>
<li><p>어떻게 사용하나?
state에 변동하는 값을 넣고, modifier에 바뀔 state 값을 전달하면 된다. 예를 들어 <code>function(1)</code>을 하면 state의 값은 1로 바뀐다. (보통 modifier 이름은 <code>set_CounterName</code>으로 한다.)
주의할 점은 state와 modifier을 생성할 때에는 해당 _<strong>state가 들어가는 요소 내부에서 생성</strong>_해야 한다는 것이다.
hook을 만들 때 그 hook이 어디에 있는지 알려주어야 해서 그렇다.</p>
</li>
</ol>
<pre><code class="language-jsx">// 올바른 방법. (요소 내부에서 생성)
// 이렇게 해야 리액트가 &#39;내가 감시할 게 &#39;Container&#39; 컴포넌트 안에 있구나! 라고 알 수 있다.

function Container() {
  // useState(0) -&gt; state 초기값을 0 으로 선언한다.
  const [counter, setCounter] = React.useState(0); 
  // onClick 발생하면 counter를 +1 로 바꾼다.
  const onClick = () =&gt; {
    setCounter(counter + 1);
  };
  return (
    &lt;div&gt;
      &lt;h3&gt;Total Clicks : {counter}&lt;/h3&gt;
      &lt;button onClick={onClick}&gt;Click me&lt;/button&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<pre><code class="language-jsx">// 잘못된 방법. (요소 외부에서 생성)
// 이렇게 하면 React JS가 어느 컴포넌트를 감시해야 할 지 모른다.

const [counter, setCounter] = React.useState(0);
const onClick = () =&gt; {
  setCounter(counter + 1);
};

function Container() {       
  return (
    &lt;div&gt;
      &lt;h3&gt;Total Clicks : {counter}&lt;/h3&gt;
      &lt;button onClick={onClick}&gt;Click me&lt;/button&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<ol start="3">
<li>어떻게 작동하나?
위 예시에서 modifier (<code>setCounter</code>)가 동작하면 (즉 state (<code>counter</code>)가 바뀌면) 리액트는 <code>Container</code> 컴포넌트를 리렌더링한다.
즉 이전처럼 매번 함수 끝에 렌더링을 호출할 필요 없이, 바뀌는 변수와 그 함수를 hook으로 생성해서 렌더링을 자동으로 하는 것이다.</li>
</ol>
<blockquote>
<p><strong>const [a, b] = [1, 2]</strong>
이렇게 하면 <code>const a = 1</code>, <code>const b = 2</code>를 한 것과 같다. JS의 이 문법을 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">Destructing assignment</a> (구조 분해 할당...?)라고 한다.
위 예시에서 <code>const [counter, setCounter] = React.useState(0);</code>도 마찬가지로 state를 <code>counter</code>로, function을 <code>setCounter</code>로 할당한 것과 같다.</p>
</blockquote>
<blockquote>
<p><strong><code>setCounter(0)</code> 해도 리렌더링 되나?</strong>
0과 같이 고정값을 전달해서 Counter 값이 전과 동일하다 해도 리렌더링은 실행된다. 즉 값 자체의 변화 여부보다 modifier가 동작했는지가 중요하다.</p>
</blockquote>
<h2 id="2024-10-17-리액트">2024-10-17 &lt;리액트&gt;</h2>
<p><code>setCounter(counter + 1)</code>로 counter에 1을 더했지만, 이건 사실 권장하는 방법은 아니다.
강의에서는 <code>setCounter(counter =&gt; counter + 1)</code>을 사용해야 <strong><em>최신의 counter 값을 쓸 수 있다</em></strong>고 설명한다.</p>
<p>이게 무슨 소리야? 뭐가 최신이라는거야? 하고 이해가 안 됐는데...
<a href="https://yeoulcoding.tistory.com/169?category=806488">이 글</a>을 읽으니 이해하는 데 도움이 되었다. 정리해보자.</p>
<h3 id="direct-vs-functional-state-update">Direct vs. Functional State Update</h3>
<p>앞의 방식 (<code>counter + 1</code>)은 counter에 직접 1을 더해서 direct, 뒤의 방식은 (<code>counter =&gt; counter + 1</code>)은 함수를 사용해서 functional이다.</p>
<pre><code class="language-jsx">// initial value of counter = 0

// direct
function onClick1() {
setCounter(counter + 1);
setCounter(counter + 1);
setCounter(counter + 1);
}

// functional
function onClick2() {
setCounter(current =&gt; current + 1);
setCounter(current =&gt; current + 1);
setCounter(current =&gt; current + 1);

}</code></pre>
<p>놀랍게도(!) 둘의 결과는 다르다. dircet는 1이 나오고, functional은 3이 나온다.</p>
<p>이 차이는 리액트의 비동기 처리 때문에 발생한다. (정확히는 작업을 완전히 완료하지 않고, 예약만 해 둔 다음 나중에 모아서 처리하는 것 때문에)</p>
<p>direct에서 우리가 <code>counter +1</code>을 세 번 하지만, 리액트는 계산한 결과를 곧이곧대로 <code>counter</code>에 저장하지 않는다. 성능을 위해서 임의의 간격 (batch)을 두고 변화를 모아놨다가 한꺼번에 업데이트를 한다.
첫 번째로 호출했을 때에는 counter는 즉시 업데이트가 되지 않고, 미래의 상태만 예약이 된다. 즉 counter는 그대로 0이고 미래의 counter의 값이 1로 &#39;예약&#39;만 된다. 그래서 두 번째로 호출을 했을 때에도 여전히 counter는 0이기 때문에, 또다시 counter는 1로 &#39;예약&#39;이 된다. 이걸 반복하기 때문에 첫 번째 함수의 결과는 1이 되는 것이다.</p>
<p>즉, direct 방식은 직전 단계에서 변화를 주더라도 비동기 처리 때문에 그 변화는 변수에 반영이 되지 않는다. 그래서 앞의 변화는 무시가 되고, 마지막 것만 수행이 된 것 같은 결과가 나온다.
(만약 맨 마지막에 <code>counter + 10</code>을 했으면 결과는 10이 나올 것이다.)</p>
<p>그럼 functional은 비동기가 아닌걸까? functional도 비동기이다. 다만 direct와 다르게 &#39;예약&#39; 된 값을 최신 값으로 본다. 즉 첫 번째 수행할 때 1을 예약하고, 두 번째 수행할 때 &#39;예약된 1&#39;을 가져와서 2를 예약한다. 그래서 결과가 3이 나오는 것이다.</p>
<p>그래서 강의에서 functional이 &quot;<em><strong>최신의 counter 값을 쓰는</strong></em>&quot; 방식이라고 표현한 것이다.
이런 이유로 강의에서는 direct 보다 functional을 권장하고 있다.</p>
<h2 id="2024-10-20">2024-10-20</h2>
<h3 id="vscode-jsx---태그-자동-닫힘-설정">VSCODE JSX - 태그 자동 닫힘 설정</h3>
<p>VSCODE에서 JSX 쓸 때 html 태그가 자동으로 안 닫힐 때가 있다.</p>
<p>해결 방법:</p>
<ol>
<li>에디터 우측 하단을 보면 html로 설정이 되어있는데</li>
<li>이걸 클릭한 다음</li>
<li>JavaScript JSX로 바꿔주면 포매팅 잘 된다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/soo-im/post/73def18b-3a1d-4e25-a7a0-349aacc80a02/image.png" alt="">
<img src="https://velog.velcdn.com/images/soo-im/post/400b007d-32a3-4937-ae81-7f4dbf137586/image.png" alt=""></p>
<p>(혹시 이것만 했을 때 안 되면 Auto Close Tag Extension 설치해보기...)</p>
<h2 id="2024-10-24-리액트">2024-10-24 &lt;리액트&gt;</h2>
<p>강의 하나를 지금 몇 번째 보는건지... 하지만 깊게 배웠죠? (제발)</p>
<h3 id="controlled-component-vs-uncontrolled-component">Controlled Component vs. Uncontrolled Component</h3>
<p>사용자가 input으로 입력하는 값을 실시간으로 state로 받는 상황을 가정하자.
이 때, 입력하는 값은 실시간으로 state로 저장이 되고 &amp; input 필드 내의 값은 입력한 state 값이 계속 보여야 한다.</p>
<p>앞은 알겠는데 뒤는 뭔소리야? 당연히 input에는 사용자가 입력한 값이 보이지? 라고 생각했는데... 코드를 보자.</p>
<pre><code class="language-jsx">const [minutes, setMinutes] = React.useState(0);
const onChange = (event) =&gt; {
        setMinutes(event.target.value);
};
return (
  &lt;div&gt;
    &lt;label htmlFor=&quot;minutes&quot;&gt;Minutes&lt;/label&gt;
    &lt;input
      id=&quot;minutes&quot;
      value={minutes} // 이걸 사용하면 Controlled, 사용하지 않으면 Uncontrolled
      onChange={onChange}
      /&gt;
    &lt;h3&gt;You are typing {minutes}&lt;/h3&gt;
);</code></pre>
<p>주석 처리한 <code>value</code> 속성을 보자. 초기값 0으로 렌더링했다가 -&gt; 값을 입력하면 -&gt; <code>minutes</code>가 바뀌고 -&gt; 그 값이 다시 input 필드에 보인다.</p>
<p>그런데 사실 <code>value</code> 속성을 주석 처리해도 (당연하게도) input 필드에는 똑같이 내가 입력한 값이 보인다.
안 해줘도 되는데 왜 굳이 저 속성을 주는걸까?</p>
<p>그 답은 React가 가지는 state와, DOM이 관리하는 input을 동일하게 유지하기 위해서다. (Functional과 유사한 이 느낌...)
만약 <code>value={minutes}</code> 속성을 주지 않았다고 가정해보자. </p>
<ol>
<li>사용자가 5를 입력한다. (보이는 값 =5)</li>
<li>5를 state에 저장한다. (state = 5)</li>
<li>그런데 서버에서 state를 10으로 바꿔버린다. (state = 10)</li>
<li>하지만 사용자는 여전히 본인이 입력한 5가 보인다. (보이는 값=5)</li>
</ol>
<p>즉 보이는 값과 state가 일치하지 않을 수도 있다. 이럴 때 보이는 값 (input=5)을 Uncontrolled component라고 한다. input은 DOM (대충 말하면 HTML 문서 그 자체)가 관리하는 것이고, state는 리액트가 관리하는 것이기 때문에 <code>value={minutes}</code>로 연결해주지 않으면 둘의 불일치가 날 수 있는 것이다.</p>
<p>저렇게 <code>value</code>를 연결함으로써 input을 항상 리액트가 관리할 수 있는 Controlled Component로 만드는 것이다.</p>
<h2 id="2024-10-30-리액트">2024-10-30 &lt;리액트&gt;</h2>
<p>헤매다 왠지 열받아서 적어두는 리스트</p>
<ol>
<li>html 태그의 속성이 간혹 JS 문법이랑 충돌하는 경우가 있다. 이런 경우는 JSX에서 html 속성을 다르게 주어야 한다.
예를 들어 <code>class</code>는 (JS 문법에서 쓰는 것이라서) <code>className</code>, <code>for</code>는 <code>htmlFor</code>... 이런 식.</li>
<li><code>False</code>는 못 쓴다. <code>false</code>만 가능. (왜 안돼...)</li>
<li>컴포넌트를 반환할 때에는 무조건 하나만 반환해야 한다.
<code>&lt;div&gt;Hello&lt;/div&gt;&lt;div&gt;world&lt;/div&gt;</code> 이렇게 두 개의 <code>&lt;div&gt;</code> 태그를 내보내면 에러가 난다.
상위 <code>&lt;div&gt;&lt;/div&gt;</code> 혹은 React Fragment <code>&lt;&gt;&lt;/&gt;</code>로  감싸서 내보내야 한다.</li>
</ol>
<h2 id="2024-10-31-리액트">2024-10-31 &lt;리액트&gt;</h2>
<h3 id="state로-컴포넌트-속성을-바꾸기">state로 컴포넌트 속성을 바꾸기</h3>
<p>state를 이용해서 컴포넌트의 속성, 예를 들어 value, id, ... 심지어 콘텐츠까지 바꿀 수 있다.</p>
<p>아래는 분 (min)을 입력하면 시간 (hour)으로, 시간을 입력하면 분으로 변환하는 코드이다.</p>
<p>여기서 <code>value={flipped ? amount : amount * 60}</code> 문법을 보면, <code>flipped</code> 라는 state의 true/false 여부에 따라 input 박스에 표시할 값이 달라지는 것을 볼 수 있다.</p>
<p>같은 방식으로 <code>disabled={!flipped}</code>을 통해 input의 입력 가능 여부를 통제하고,
<code>&lt;button&gt;{flipped ? &quot;Mins to Hours&quot; : &quot;Hours to Mins&quot;}&lt;/button&gt;</code>으로 버튼의 콘텐츠 (텍스트)도 바꿀 수도 있다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/aa39e059-2c43-4bb4-96aa-d7dfc5e8fcca/image.gif" alt=""></p>
<pre><code class="language-jsx">    function App() {
      const [amount, setAmount] = React.useState(0);
      const [flipped, setFlipped] = React.useState(true);

      const reset = () =&gt; setAmount(0);

      const onChange = (event) =&gt; {
        setAmount(event.target.value);
      };

      const onFlip = () =&gt; {
        reset();
        setFlipped((flipped) =&gt; !flipped);
      };

      return (
        &lt;&gt;
          &lt;div&gt;
            &lt;h1&gt;Minutes and Hours Converter&lt;/h1&gt;
            &lt;label htmlFor=&quot;minutes&quot;&gt;Minutes&lt;/label&gt;
            &lt;input
              id=&quot;minutes&quot;
              type=&quot;number&quot;
              value={flipped ? amount : amount * 60} // IF 문법과 동일
              onChange={onChange}
              placeholder=&quot;minutes&quot;
              disabled={!flipped}
            /&gt;
          &lt;/div&gt;
          &lt;div&gt;
            &lt;label htmlFor=&quot;hours&quot;&gt;Hours&lt;/label&gt;
            &lt;input
              id=&quot;hours&quot;
              type=&quot;number&quot;
              value={flipped ? Math.round(amount / 60) : amount}
              onChange={onChange}
              placeholder=&quot;hours&quot;
              disabled={flipped}
            /&gt;
          &lt;/div&gt;
          &lt;div&gt;
            &lt;button onClick={reset}&gt;Reset&lt;/button&gt;
            &lt;button onClick={onFlip}&gt;
              {flipped ? &quot;Mins to Hours&quot; : &quot;Hours to Mins&quot;}
            &lt;/button&gt;
          &lt;/div&gt;
        &lt;/&gt;
      );
    }

    const root = document.getElementById(&quot;root&quot;);
    ReactDOM.render(&lt;App /&gt;, root);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024-09 TIL]]></title>
            <link>https://velog.io/@soo-im/2024-09-TIL</link>
            <guid>https://velog.io/@soo-im/2024-09-TIL</guid>
            <pubDate>Mon, 02 Sep 2024 14:56:45 GMT</pubDate>
            <description><![CDATA[<p>9월! 네트워크 책도 거의 다 읽어간다 (❁´◡´❁)
다음 주제도 슬슬 고민 중...</p>
<ol>
<li>Figma + DB로 네이버웹툰 앱 따라만들기</li>
<li>UX 이론</li>
<li>네이티브 앱 (이건 뭘 해야 할지 잘 모름)</li>
</ol>
<h2 id="2024-09-02-네트워크">2024-09-02 &lt;네트워크&gt;</h2>
<h3 id="udp">UDP</h3>
<p>TCP가 정확한 통신이라면 UDP는 효율적인 통신이다. 확인 응답 없이도 데이터를 연달아 보내고, 네트워크 내의 여러 대상자에게도 한 번에 보낼 수 있다 (브로드캐스트).</p>
<p>지난 <a href="https://velog.io/@soo-im/2024-08-TIL#2024-08-26-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC">TCP-UDP 비교에</a> 이어, 좀 더 자세히 보면 다음과 같다.</p>
<table>
<thead>
<tr>
<th></th>
<th>TCP (연결형 통신)</th>
<th>UDP (비연결형 통신)</th>
</tr>
</thead>
<tbody><tr>
<td>우선 순위</td>
<td>정확성</td>
<td>효율성</td>
</tr>
<tr>
<td>헤더</td>
<td>확인응답에 필요한 정보 포함 (일련번호, 확인 응답 번호, window size 등)</td>
<td>확인응답에 필요한 정보 불포함 (목적지 정보인 포트 넘버 정도만 있음)</td>
</tr>
<tr>
<td>속도</td>
<td>느림</td>
<td>빠름</td>
</tr>
<tr>
<td>브로드캐스트 적합성</td>
<td>부적합</td>
<td>적합</td>
</tr>
<tr>
<td>예시</td>
<td>파일 전달, 이메일, 웹브라우징</td>
<td>동영상 스트리밍, 게임, DNS</td>
</tr>
</tbody></table>
<h3 id="4단계-응용-계층-어플리케이션-서버와-클라이언트-간의-데이터-전송-규칙">4단계. 응용 계층: 어플리케이션 서버와 클라이언트 간의 데이터 전송 규칙</h3>
<blockquote>
<p>어플리케이션이 네트워크와 상호 작용하는 계층.</p>
</blockquote>
<p>앞의 단계들은 데이터가 하드웨어 (1단계) - LAN (2단계) - 다른 네트워크 (3단계)를 거쳐 전달되는 과정, 그리고 정확하게 전달이 되었는지 점검하는 과정 (4단계)을 다루었다.
5단계는 이 과정을 다 거쳐서, 어플리케이션 단에서 어떻게 네트워크와 정보를 주고 받는지 (예: 서버와 클라이언트가 어떻게 통신을 해야 하는지) 규칙을 정하는 계층이다.</p>
<p>좀 더 와닿는 예시를 들면...</p>
<ol>
<li>브라우저 (어플)에서 <a href="https://velog.io/@soo-im">https://velog.io/@soo-im</a> 을 입력</li>
<li>브라우저가 응용 계층에서 HTTP/HTTPS 프로토콜을 이용해 웹사이트 호스팅 서버에 요청을 보냄</li>
<li>호스팅 서버가 요청에 웹페이지를 응답</li>
</ol>
<p>→ 이 과정에서 응용 계층은 브라우저가 요청을 보내고-웹페이지를 구성하는 데이터 (JS, HTML 등)를 받는 방법을 정의한다.</p>
<h2 id="2024-09-03-네트워크">2024-09-03 &lt;네트워크&gt;</h2>
<h3 id="dns-서버">DNS 서버</h3>
<p>응용 계층의 대표적인 예시가 DNS 서버이다. DNS 서버는 웹브라우저 간의 데이터 전송을 지원한다.</p>
<ol>
<li>DNS 서버의 역할<ul>
<li><a href="https://velog.io/@soo-im">https://velog.io/@soo-im</a> (도메인)을 요청하면 해당하는 IP 주소를 반환한다.</li>
</ul>
</li>
<li>그런 건 누가 설치하나?<ul>
<li>DNS 서버는 ISP가 함께 공급한다. KT, SKT... 이런 데서 관리하는 DNS 서버가 있다. (원한다면 DNS 서버를 바꿀 수도 있다.)</li>
<li>만약 DNS 서버가 모르는 도메인을 받으면 TLD 라는 상위 DNS 서버로 보내 주소를 찾아온다.</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/soo-im/post/4b728df6-b83a-4fd2-8df1-07ce2f979384/image.png" alt=""></p>
<blockquote>
<p><strong>DNS에 관한 웃픈 이야기</strong>
보통 통신사들이 DNS를 관리하다보니, DNS를 가지고 장난(...)을 치는 경우도 있다.
사용자 간의 트래픽 공유를 막으려고 (망이용료 때문에 이런 걸 쓰는 서비스들이 있다하더라) 악성코드를 심는 경우도 있었다고. 싱글벙글 <a href="https://youtu.be/h-PWth26dPo?si=4LwIW75LLJFHBxnu">코딩애플 유튜브</a> 참고.</p>
</blockquote>
<h2 id="2024-09-05-네트워크">2024-09-05 &lt;네트워크&gt;</h2>
<h3 id="smtp-pop3">SMTP, POP3</h3>
<p>메일을 보내고 (SMTP) 받는 (POP3) 프로토콜.
굳이 다른 프로토콜을 쓰는 이유는 절차가 약간 다르기 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/ed0d09d7-55e5-4e1f-a598-430526f4b924/image.png" alt=""></p>
<ol>
<li>SMTP를 통한 메일 전송 절차
※ 보내는 쪽 (예: 송신 컴퓨터)이 받는 쪽 (예: Gmail 서버)에게 통지한다.<ol>
<li>세션 시작을 통지한다.</li>
<li>송신자의 메일 주소를 통지한다.</li>
<li>발신자의 메일 주소를 통지한다.</li>
<li>메일 본문 전송을 통지한다.</li>
<li>메일 본문을 보낸다.</li>
<li>세션 종료를 통지한다.</li>
</ol>
</li>
<li>POP3
※ 받는 쪽 (수신 컴퓨터)이 주는 쪽 (Naver 서버)에게 통지한다.<ol>
<li>세션 시작을 통지한다. (앱을 켜거나 해서)</li>
<li>수신자 이름을 통지한다.</li>
<li>수신자 비밀번호를 통지한다.</li>
<li>수신된 메일이 있는지 묻는다.</li>
<li>수신된 메일이 있다면 전송을 요청한다.</li>
<li>세션 종료를 통지한다.</li>
</ol>
</li>
</ol>
<h2 id="2024-09-06-네트워크">2024-09-06 &lt;네트워크&gt;</h2>
<h3 id="ping">ping</h3>
<p>갑자기 끼어드는 ping 이야기!
ping은 응용 계층보다는 네트워크 계층에 좀 더 가깝다. (책에서 왜 이 챕터에서 언급했는지는 모르겠지만...)</p>
<p>이전 회사 다닐 때 ping을 하도 많이 듣느라 반가워서 좀 더 찾아봤다.</p>
<ol>
<li>ping이란?<ul>
<li>서로 다른 네트워크나, 동일한 네트워크에 연결된 기기가 통신할 수 있는지 확인하는 도구이다.</li>
</ul>
</li>
<li>사용법<ul>
<li><code>ping (IP주소)</code> 혹은 <code>ping www.google.com</code> 처럼 사용할 수 있다</li>
<li>컴퓨터와 WIFI에 연결된 휴대폰으로도 할 수 있다.<pre><code>  1. (갤럭시 기준) WIFI 설정에 들어가서 맨 밑의 IP 주소를 확인한다.
  2. cmd 창을 열어 `ping (IP주소)`를 입력한다.
  3. 받음 = 4가 나오며 통신이 잘 된다. ![](https://velog.velcdn.com/images/soo-im/post/f2e1ed38-79ae-47d6-ad4b-598d161dd654/image.png)</code></pre></li>
</ul>
</li>
<li>동작 원리<ul>
<li>ping을 실행하면 상대방에 ICMP (Internet Control Message Protocol)이라는 패킷을 보낸다.</li>
<li>ICMP 패킷은 &#39;여보세요? 들려요?&#39; (echo request)정도의 간단한 인사라고 보면 된다.</li>
<li>상대방이 패킷을 수신하면 echo response를 보내며 왕복에 걸린 시간도 함께 보내준다.</li>
<li>그 결과는 위의 스크린샷으로 보여진다. <code>바이트</code>는 패킷 크기, <code>시간</code>은 왕복 시간을 나타낸다. (<code>TTL</code>은 패킷의 남은 수명(?)같은 거라는데... 지금은 자세히 알 필요는 없을 것 같다.)</li>
</ul>
</li>
</ol>
<blockquote>
<p><strong>번외: 데이터를 쓸 때에는 왜 ping이 안 돌아올까?</strong>
핸드폰을 WIFI에 연결하면 echo response가 잘 돌아오지만, 데이터에 연결하면 돌아오지 않을 가능성이 높다.
그 이유는 데이터를 쓸 때에는 공용 IP를 사용하기 때문이다. 공용 IP에 연결되면 보안상의 이유로 방화벽으로 ping이 차단된다.</p>
</blockquote>
<h2 id="2024-09-10-네트워크">2024-09-10 &lt;네트워크&gt;</h2>
<p>모든 계층을 다 봤다! 이제 복습하고 마무리하자 😁</p>
<h3 id="osi-요약">OSI 요약</h3>
<p>지난 달에 썼던 표 복습!</p>
<table>
<thead>
<tr>
<th>순서</th>
<th>계층</th>
<th>설명</th>
<th>관련 키워드</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>물리</td>
<td>데이터를 전기 신호로 변환</td>
<td>랜 케이블, 허브</td>
</tr>
<tr>
<td>2</td>
<td>데이터 링크</td>
<td>랜에서 데이터 전달</td>
<td>이더넷, MAC, 스위치</td>
</tr>
<tr>
<td>3</td>
<td>네트워크</td>
<td>다른 네트워크 목적지로 데이터 전달</td>
<td>IP, 라우터, 서브넷</td>
</tr>
<tr>
<td>4</td>
<td>전송</td>
<td>목적지에 정확하게 데이터를 전달</td>
<td>TCP, UDP</td>
</tr>
<tr>
<td>5</td>
<td>응용</td>
<td>어플리케이션에 데이터 전달</td>
<td>웹 서버, DNS</td>
</tr>
</tbody></table>
<p>이걸 실제 상황에 적용하면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/9c7b5bc9-26f0-4bbb-b110-33d9b0509645/image.png" alt=""></p>
<h2 id="2024-09-23">2024-09-23</h2>
<p>명절을 잘 즐기고 2주만에 온 나... 😊</p>
<p>네트워크 마무리했으니 다음 주제 정할 차례! (10월 첫째 주까지 네트워크 게시글은 따로 쓰는 게 목표)</p>
<p>앱 -&gt; Figma 순으로 급한 듯 하여 아래 순서로 진행.</p>
<ul>
<li>개발: (노마드코더) React JS -&gt; React Native</li>
<li>Figma: (주제가 없다!)</li>
</ul>
<h2 id="2024-09-24-리액트">2024-09-24 &lt;리액트&gt;</h2>
<h3 id="vanilla-js-vs-react-js-1">Vanilla JS vs. React JS (1)</h3>
<ul>
<li><strong>React JS란?</strong>
JS 내에서 사용하는 라이브러리로, import 해서 쓰면 된다.</li>
<li><strong>하는 역할은?</strong>
JS에서 만든 element를 HTML로 랜더링해준다.</li>
<li><strong>무슨 뜻인지...</strong>
Vanilla JS와 React JS에서 인터랙티브 UI를 만드는 과정을 비교하면 다음과 같다.</li>
</ul>
<table>
<thead>
<tr>
<th>작업 순서</th>
<th>Vanilla JS</th>
<th>React JS</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>HTML 파일에서 element 생성</td>
<td>JS 파일에서 element 생성</td>
</tr>
<tr>
<td>2</td>
<td>JS 파일에서 element를 선택 (Selector)</td>
<td>-</td>
</tr>
<tr>
<td>3</td>
<td>JS 파일에서 인터랙티브 함수 생성</td>
<td>좌동</td>
</tr>
<tr>
<td>4</td>
<td>JS 파일에서 HTML element 업데이트</td>
<td>React DOM으로 HTML element 랜더링</td>
</tr>
</tbody></table>
<h2 id="2024-09-30-리액트">2024-09-30 &lt;리액트&gt;</h2>
<h3 id="vanilla-js-vs-react-js-2">Vanilla JS vs. React JS (2)</h3>
<p>다음은 Vanilla JS에서 인터랙티브 버튼을 만드는 방법이다.</p>
<ol>
<li>html에서 element 생성<pre><code class="language-html">&lt;button id=&quot;btn&quot;&gt;Click me&lt;/button&gt;</code></pre>
</li>
<li>JS에서 element 읽은 뒤 해당 element에 함수 부여<pre><code class="language-js">const btn = document.getElementById(&quot;btn&quot;);
function btnClick() {
console.log(&quot;I&#39;m clicked&quot;);
}
btn.addEventListener(&quot;click&quot;, btnClick);</code></pre>
</li>
</ol>
<hr>
<p>이걸 React JS에서 만들면 이렇게 할 수 있다.
(실제로는 더 쉬운 방법이 있어서 이 방법을 쓰지는 않지만... 이게 핵심이라 먼저 짚고 넘어간다.)</p>
<ol>
<li>html에서 element를 넣을 공간 생성<pre><code class="language-html">&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code></pre>
</li>
<li>JS에서 함수가 부여된 button 생성한 뒤 랜더링 <pre><code class="language-js">const root = document.getElementById(&quot;root&quot;);
</code></pre>
</li>
</ol>
<p>const btn = React.createElement(&quot;button&quot;, {
  id: &quot;btn&quot;,
  onclick: ()=&gt; console.log(&quot;I&#39;m Clicked&quot;),
}, &quot;Click me&quot;);
ReactDOM.render(btn,root);</p>
<p>```</p>
<p>React JS를 사용하면 <code>getElementById</code>, <code>addEventListener</code> 같은 걸 쓸 필요 없이 요소를 만들 때 인터랙티브를 넣어버리면 된다.</p>
<blockquote>
<p><strong><code>createElement</code> 파라미터</strong>
첫 번째, element tag (예: div, button...) 
두 번째, properties (예: id, onClick...), 이 때 on 뒤에 오는 것은 기존 EventListener와 동일한 명칭을 쓰면 된다.
세 번째, contents</p>
</blockquote>
<blockquote>
<p><strong>인터랙티브와 eventListener</strong>
사용자 동작에 반응하는 요소를 만들기 위해서는 &#39;eventListener&#39;가 핵심이다. 즉 React JS는 &#39;eventListener&#39;를 더 쉽게 해주는 데에 가치가 있는 것.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024-08 TIL]]></title>
            <link>https://velog.io/@soo-im/2024-08-TIL</link>
            <guid>https://velog.io/@soo-im/2024-08-TIL</guid>
            <pubDate>Tue, 13 Aug 2024 22:21:39 GMT</pubDate>
            <description><![CDATA[<p>두 번의 이직을 거쳐 다시 서비스 기획자로 돌아왔다.
이제 놓았던 공부를 다시 시작할 때가 왔다!</p>
<p>흥미를 잃지 않기 위해 산만하더라도 여러 주제를 TIL에 다 때려넣을 계획이다.
그러다 특정 주제의 내용이 모이면 따로 발행을 하려고 한다.</p>
<ol>
<li>네트워크</li>
<li>네이티브 앱 관련</li>
<li>Figma</li>
<li>금융 상품</li>
<li>UX</li>
</ol>
<p>(이러다 파이썬 다 까먹겠는데...)</p>
<p>우선 하다가 필요한 거 생기면 거기로 넘어가자 😅
일단 오늘은 네트워크!</p>
<h2 id="2024-07-25-네트워크">2024-07-25 &lt;네트워크&gt;</h2>
<h3 id="lan-vs-wan">LAN VS. WAN</h3>
<table>
<thead>
<tr>
<th></th>
<th>LAN (Local Area Network)</th>
<th>WAN (Wide Area Network)</th>
</tr>
</thead>
<tbody><tr>
<td>범위</td>
<td>좁은 지역 (예: 집, 사무실)</td>
<td>넓은 지역 (예: 도시)</td>
</tr>
<tr>
<td>속도</td>
<td>빠름</td>
<td>느림</td>
</tr>
<tr>
<td>접속 방식</td>
<td>유선 랜 (컴퓨터에 꽂는 그 선) 혹은 무선 랜 (공유기)</td>
<td>ISP (인터넷 서비스 공급자)의 모뎀, 라우터</td>
</tr>
</tbody></table>
<h3 id="isp는-어떻게-우리-집-컴퓨터를-인터넷에-연결해주나">ISP는 어떻게 우리 집 컴퓨터를 인터넷에 연결해주나?</h3>
<ol>
<li>ISP 네트워크: ISP는 인터넷에 연결하는 네트워크 인프라 (예: 타워, 케이블 등)을 갖추고 있다. 
마치 전선을 통해 전기가 들어오는 것처럼, ISP의 네트워크가 특정 방식을 통해 집으로 연결된다. 무선 타워, 케이블, 안테나 접시...</li>
<li>모뎀: 모뎀은 일종의 번역기로, 디지털 신호와 아날로그 신호를 modulation/demodulation 하는 장치이다. 이게 라우터와 결합하면 집 안에 로컬 네트워크 (LAN)가 생긴다.</li>
<li>라우터: 라우터는 데이터 배달부에 비유할 수 있다. 데이터를 보내려는 주소까지의 최적 경로를 찾고 배달한다.
만약 직접 갈 수 있는 구간 (LAN)이면 직접 가져다 주고, 멀리 있는 구간 (WAN)이면 데이터를 ISP 라우터로 전달한다. 각각의 라우터들이 데이터를 패스하면서 멀리 있는 구간까지 연결이 되는 것이다.
덧붙여 공용 IP와 개인 IP를 필요에 따라 전환하는 NAT (Network Addrress Translation) 역할도 한다. 이렇게 하면 효율적으로 주소를 관리할 수 있다. 국제 우편을 보낼 때 처음부터 세부 주소를 알 필요가 없으니 국가만 먼저 보고, 나중에는 국가 주소 대신 세부 주소를 보는 것과 비슷한 것.</li>
</ol>
<h2 id="2024-07-27-네트워크">2024-07-27 &lt;네트워크&gt;</h2>
<h3 id="통신-규격-모델-osi-tcpip은-왜-알아야-하나">통신 규격 모델 (OSI, TCP/IP)은 왜 알아야 하나?</h3>
<p>OSI는 7계층, TCP/IP는 4계층으로 구성되어 있다.
각 계층을 배우기 전에... &#39;그래서 이걸 왜 알아야 하나?&#39; 하는 의문이 들었다.
정리해보면 다음과 같다.</p>
<ol>
<li>통신 규격이 없던 시절에는 같은 회사 안에서만 통신이 되던 때도 있었다.</li>
<li>이에 ISO가 통신 규격인 OSI 모델을 만들었고, 현재는 TCP/IP 모델을 사용한다.</li>
<li>각 계층을 이해해야 나중에 문제가 발생했을 때 어느 계층의 문제인지 (예: 하드웨어 문제-물리적 계층-인지, 라우팅 문제-네트워크 계층-인지) 파악을 할 수 있다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/soo-im/post/57679a1d-4abc-4fcd-b84e-40e91c00f14a/image.png" alt="">
이미지 출처: <a href="https://ming9mon.tistory.com/2">https://ming9mon.tistory.com/2</a></p>
<p>감을 잡기 위해 각 계층의 챕터 제목만 가져왔다.</p>
<table>
<thead>
<tr>
<th>계층</th>
<th>설명</th>
<th>관련 키워드</th>
</tr>
</thead>
<tbody><tr>
<td>물리</td>
<td>데이터를 전기 신호로 변환</td>
<td>랜 케이블, 리피터, 허브</td>
</tr>
<tr>
<td>데이터 링크</td>
<td>랜에서 데이터 전송</td>
<td>이더넷, MAC, 스위치</td>
</tr>
<tr>
<td>네트워크</td>
<td>목적지에 데이터 전달</td>
<td>IP, 라우터, 서브넷</td>
</tr>
<tr>
<td>전송</td>
<td>신뢰할 수 있는 데이터 전송</td>
<td>연결/비연결형 통신, TCP, UDP</td>
</tr>
<tr>
<td>응용</td>
<td>애플리케이션에 데이터 전송</td>
<td>웹 서버, DNS</td>
</tr>
</tbody></table>
<h3 id="캡슐화역캡슐화">캡슐화/역캡슐화</h3>
<p>데이터 송신측과 수신측은 둘 다 위의 계층을 거쳐서 데이터를 보내고 받는다. 
위 계층을 지나기 위해서는 각각 맞는 헤더를 붙이거나 (송신 측) 떼야 (수신 측) 한다. 이 과정을 캡슐화/역캡슐화 라고 부른다.
참고로 데이터 링크 계층에서는 데이터 앞에 붙는 헤더, 뒤에 붙는 트레일러 둘 다가 붙거나 떨어진다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/61928b86-172a-408e-b37c-c4cf8e170b40/image.png" alt=""></p>
<h2 id="2024-07-28-네트워크">2024-07-28 &lt;네트워크&gt;</h2>
<h3 id="1단계-물리-계층-데이터를-전기-신호로-변환">1단계. 물리 계층: 데이터를 전기 신호로 변환</h3>
<p>(하드웨어 제품 할 때 들어본 용어가 나온다 ㅎ... 반갑네 😇)</p>
<blockquote>
<p>물리적 매체를 통해 디지털 신호를 광학/전기 신호로 변환하는 계층. </p>
</blockquote>
<p>주요한 장비는 아래와 같다.</p>
<ol>
<li>랜 카드
디지털 신호 (0/1)와 전기 신호를 변환하는 장비. 예를 들면 0은 0V, 1은 +5V... 이런 식으로 변환한다. 전기 -&gt; 디지털 역방향도 마찬가지.</li>
<li>랜 케이블
다이랙트 케이블/크로스 케이블 두 종류가 있다. 케이블 양 끝단의 구리선이 곧게 연결되어 있냐 중간에 꼬아놓았느냐 차이인데... 아주 자세하게 알 필욘 없을 것 같다. 용도가 다르다고 함. (보통 다이렉트는 서로 다른 종류의 장비끼리, 크로스는 같은 종류의 장비끼리 연결할 때 사용)<ul>
<li>다이렉트: 컴퓨터-스위치/허브, 라우터-스위치...</li>
<li>크로스: 컴퓨터-컴퓨터, 스위치-스위치...</li>
</ul>
</li>
<li>리피터
전기 신호 증폭 장치. 요즘은 잘 안 쓴다고.</li>
<li>허브
리피터와 유사하지만 포트가 여러 개여서 한 번에 여러 장비로 보낼 수 있다. (추억의 PoE 허브 ^^는 전력까지 공급해주는 허브.)</li>
</ol>
<h2 id="2024-07-30-네트워크">2024-07-30 &lt;네트워크&gt;</h2>
<h3 id="2단계-데이터-링크-계층-lan에서-데이터-전송">2단계. 데이터 링크 계층: LAN에서 데이터 전송</h3>
<blockquote>
<p>네트워크 장비 간에 신호를 주고받는 규칙을 정하는 계층. 대표적인 규칙이 랜에서 사용하는 이더넷.</p>
</blockquote>
<p>(다른 사람들은 이 설명만 보고 이해를 할 수 있는건가?...)</p>
<p>비유를 하자면 편지 (데이터)를 우체국을 통해 보낼 때, 봉투의 역할을 하는 게 데이터 링크 계층이다.
랜 통신망에서 케이블을 통해 데이터를 주고 받는 규칙인 이더넷을 예시로 들어 설명하면 아래와 같다.</p>
<ol>
<li>프레이밍
아주 많은 편지를 적당히 나누고 (패킷 조각) 봉투에 넣는다. 봉투에 주소 (Media Access Control 주소-IP 주소)를 쓴다.</li>
<li>데이터 송신
편지(프레이밍된 패킷)가 우체국 차(랜 케이블)를 타고 이동한다.</li>
<li>주소 지정
우체국 (허브/스위치)에서 주소를 보고 어디인지 식별하고, 적절히 분류해서 잘 이동할 수 있게 한다.</li>
<li>오류 감지 및 처리
수신된 편지가 송신한 편지와 일치하는지 확인하고, 오류가 있으면 재전송을 요청한다.</li>
<li>흐름 제어
운송 과정에서 과부하가 걸리거나, 같은 통로를 쓰는 편지들 사이에 충돌이 일어나지 않도록 관리한다.
(예전에는 데이터를 송신하기 전에 케이블에 신호가 흐르는지 체크하는 CSMA/CD라는 방식을 사용했는데, 요즘은 잘 사용하지 않는다고 함.)</li>
</ol>
<h2 id="2024-08-05-네트워크">2024-08-05 &lt;네트워크&gt;</h2>
<h3 id="2단계-데이터-링크-계층-이어서">2단계. 데이터 링크 계층 (이어서)</h3>
<ol>
<li><p>MAC 주소</p>
</li>
<li><p>1 MAC 주소란?</p>
<ul>
<li>물리 계층에서 언급한 &#39;랜 카드&#39;는 제조 당시에 고유한 MAC 주소를 받는다.</li>
</ul>
</li>
<li><p>2 그걸 왜 지금 다루나?</p>
<ul>
<li>데이터 링크 계층에서 MAC 주소를 다루는 이유는 랜에서 데이터를 전송할 때 이 주소를 기준으로 전송하기 때문이다.</li>
</ul>
</li>
<li><p>3 이더넷 헤더/트레일러</p>
<ul>
<li>위의 캡슐화 과정을 보면 데이터 링크 계층에서 데이터의 앞뒤로 &#39;링크 헤더&#39;와 &#39;링크 트레일러&#39;가 붙는다. 이더넷 통신을 예로 들면 &#39;이더넷 헤더&#39;와 &#39;트레일러&#39;이다. 
이 때 이더넷 헤더에 목적지의 MAC 주소, 출발지의 MAC 주소 정보가 붙는다. (여기에 추가로 IPv4...같은 프로토콜 번호도 붙는데 이건 몰라도 된대서 패스)</li>
</ul>
</li>
<li><p>프레임
전에 &#39;프레이밍&#39;을 편지를 봉투에 넣는 과정으로 비유했다. 위의 이더넷 헤더와 트레일러를 추가하는 과정을 &#39;프레이밍&#39;이라고 할 수 있다. 이 둘이 추가된 데이터를 &#39;프레임&#39;이라고 부른다.</p>
</li>
<li><p>프레임의 전송
허브는 들어온 데이터를 모든 포트로 내보내지만, 포트를 구별해서 보낼 수는 없다. 이 때 프레임을 이용하면 원하는 곳으로만 보낼 수 있다.
(1) 전송자가 목적지 MAC 주소를 이용해서 데이터를 프레임으로 만든다. (캡슐화)
(2) 이 프레임이 물리 계층을 통해 전기 신호로 바뀐다.
(3) 허브는 모든 포트로 신호를 전송한다.
(4) 그 중 목적지가 아닌 컴퓨터는 자신의 MAC 주소로 온 게 아닌 프레임을 파기한다.
(5) 목적지인 컴퓨너는 반대로 전기 신호를 프레임으로, 프레임을 데이터로 만든다. (역캡슐화)
<img src="https://velog.velcdn.com/images/soo-im/post/7d09bf3b-5bec-4224-bdf0-7c41e0d7d6c0/image.jpg" alt=""></p>
</li>
</ol>
<h2 id="2024-08-09-금융-상품">2024-08-09 &lt;금융 상품&gt;</h2>
<p>증권 1도 모르는 인간이 거래소에 들어온 🍯잼 실화</p>
<h3 id="호가의-세계">호가의 세계</h3>
<p><img src="https://velog.velcdn.com/images/soo-im/post/1d4e3132-3092-448e-87cd-7ec0eab32c77/image.png" alt=""></p>
<p>&#39;호가&#39;를 알고 난 뒤로 다른 용어를 익히기가 더 수월해졌다.
그래서 오늘은 호가와 관련 용어 시리즈!</p>
<ul>
<li>호가: 문자 그대로 &#39;부르는 값&#39;. 매수자가 부르는 사려는 값 (매수 호가), 매도자가 부르는 팔려는 값 (매도 호가)으로 나뉜다. 단순히 값 그 자체라기 보다는 그 가격에 사고 팔겠다는 주문의 의미도 내포한다. 당근마켓으로 비유하면 사요<del>팔아요</del> 게시글을 올리는 것.</li>
<li>오더북: 주문 order을 모아놓았다고 해서 오더북. 증권 앱 같은 데에서 &#39;호가&#39; 쪽을 누르면 보인다 (위 사진). 가운데 가격을 중심으로 좌측에 좀 더 비싸게 팔고 싶은 매도 호가의 수량, 우측에 좀 더 싸게 사고 싶은 매수 호가의 수량이 보인다. 보통 히스토그램으로 표시도 같이 해준다. 이걸 보면 어느 매수/매도 호가에 물량이 많이 있는지, 매수/매도 중에 누가 더 우세한지 등을 볼 수 있다.</li>
<li>스프레드: 매도 호가와 매수 호가의 차이. 선물거래에서 스프레드 차익을 얻는다... 이런 설명도 나오지만 우선 중요한 건 가격의 차이 그 자체다. 시장에서 스프레드가 과하게 커지면 매수자와 매도자 간의 거래 성립이 어려워질 수 있다. 당근마켓에서 사는 사람이 100원, 파는 사람이 500원으로 제시하면 거래 성립이 안 되는 것과 마찬가지.</li>
<li>유동성: 의미는 다양하겠지만 여기서는 얼마나 &#39;이 주식을 잘 살 수 있고 팔 수 있는지&#39;, 즉 현금과 얼마나 쉽게 교환이 가능한지를 나타내는 척도라고 정의하겠다. (경제가 사랑하는 단어 중 하나인 듯) 스프레드 개념과 연결해보면 스프레드가 클수록 유동성은 낮다. 유동성은 거래소의 중요한 지표 중 하나이다. 많은 사람이 쓰는 당근마켓과, 회사 직원들끼리 사용하는 사내 중고장터를 비교하면 당근마켓의 유동성이 더 높다.</li>
<li>마켓메이커 &amp; 마켓테이커: 호가가 촘촘해야 스프레드가 작아지고 유동성이 높아진다. 그리고 유동성이 높아야 고객이 더 많이 찾아오기 때문에 (사내 중고장터보다 당근마켓을 더 많이 찾는 것처럼) 거래소는 유동성을 높이기 위해 &#39;마켓메이커&#39;라는 제도를 둔다. 마켓메이커는 스프레드 사이에 호가를 만들어서 스프레드가 과도하게 벌어지지 않고 거래가 원활하게 일어나도록 만든다. 이들은 시세차익도 얻지만 거래소로부터 인센티브를 받기도 한다. &#39;오더북에 오더를 만들어내는 쪽&#39;이라는 의미에서 마켓&#39;메이커&#39;.
마켓테이커는 반대로 오더북에서 오더를 바로 빼가는 쪽이다. 즉시 주문이 체결되는 시장가 주문이 그 예시. 이 쪽은 유동성을 줄이는 쪽이기 때문에 그다지 인센티브를 받지 못한다.
그래서 거래소 등에서 메이커 수수료와 테이커 수수료를 구분하기도 한다. 유동성을 공급하는 메이커를 우대하는 목적으로 메이커 수수료가 더 낮다.</li>
</ul>
<h2 id="2024-08-13-네트워크">2024-08-13 &lt;네트워크&gt;</h2>
<h3 id="스위치">스위치</h3>
<p>스위치도 허브와 동일하게 여러 장비를 포트로 연결하는 장비이다. 요즘은 아래 장점 때문에 허브보다 스위치를 많이 쓴다.</p>
<table>
<thead>
<tr>
<th></th>
<th>허브</th>
<th>스위치</th>
</tr>
</thead>
<tbody><tr>
<td>MAC 인식</td>
<td>포트별 MAC을 알 수 없어 들어온 데이터를 모든 포트로 전송한다.</td>
<td>포트별 MAC 주소를 기록하는 &#39;MAC 주소 테이블&#39;을 가지고 있어 지정한 포트에만 데이터를 전송한다.</td>
</tr>
<tr>
<td>통신 방식</td>
<td>반이중 통신 (회선 하나에서 송신/수신 번갈아)</td>
<td>전이중 통신 (송신/수신 회선 따로)</td>
</tr>
<tr>
<td>데이터 충돌</td>
<td>반이중 통신 때문에 동시에 데이터를 보내면 충돌 발생</td>
<td>전이중 통신 때문에 동시에 데이터를 보내도 충돌 없음</td>
</tr>
<tr>
<td>충돌 도메인 (데이터 충돌이 미치는 범위)</td>
<td>허브에 연결된 모든 컴퓨터</td>
<td>충돌이 일어난 컴퓨터</td>
</tr>
</tbody></table>
<h2 id="2024-08-14-네트워크">2024-08-14 &lt;네트워크&gt;</h2>
<h3 id="3단계-네트워크-계층-wan에서-데이터-전송">3단계. 네트워크 계층: WAN에서 데이터 전송</h3>
<blockquote>
<p>라우터를 통해 서로 다른 네트워크 간의 통신을 하는 계층.</p>
</blockquote>
<p>설명만 보면 데이터 계층과 네트워크 계층이 무슨 차이인지 감이 잘 안 온다.
둘을 비교하면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/f65f9714-dde6-49a2-9008-9b64cd55903e/image.png" alt=""></p>
<table>
<thead>
<tr>
<th></th>
<th>2단계 데이터 (링크) 계층</th>
<th>3단계 네트워크 계층</th>
</tr>
</thead>
<tbody><tr>
<td>통신 대상</td>
<td>하나의 네트워크 안에서 네트워크 장비 간의 통신</td>
<td>여러 개의 네트워크 간의 통신</td>
</tr>
<tr>
<td>장비</td>
<td>허브, 스위치</td>
<td>라우터</td>
</tr>
<tr>
<td>주소</td>
<td>MAC 주소 (어느 컴퓨터)</td>
<td>IP 주소 (어느 네트워크의 어느 컴퓨터)</td>
</tr>
<tr>
<td>캡슐화</td>
<td>이더넷 헤더를 붙인 프레임</td>
<td>IP 헤더를 붙인 IP 패킷</td>
</tr>
</tbody></table>
<p>여기서 IP는 Internet Protocol의 약자로, 어느 네트워크의 어느 컴퓨터인지를 지정하는 주소이다.</p>
<p>데이터 계층에서 MAC 주소를 넣은 &#39;이더넷 헤더&#39;를 붙여 &#39;프레임&#39;을 만든 것처럼, 네트워크 계층에서도 IP 주소를 넣은 &#39;IP 헤더&#39;를 붙여 &#39;IP 패킷&#39;을 만든다.</p>
<h3 id="ip-주소">IP 주소</h3>
<p>IP 주소는 &#39;공인 IP 주소&#39;와 &#39;사설 IP 주소&#39;로 나뉜다.
공인 IP 주소는 인터넷 서비스 제공자 (ISP)가 인터넷에 직접 연결되는 라우터/컴퓨터에 할당하는 주소이고,
사설 IP 주소는 라우터에 연결된 랜 내부에서 랜 네트워크 관리자가 할당하는 주소이다. 라우터의 DHCP (Dynamic Host Configuration Protocol)를 이용해서 자동으로 할당하기도 한다.</p>
<p>워낙 컴퓨터가 많다보니 IP 주소가 모자라서 이렇게 한 것. 참고로 모자란 건 IPv4 기준이고, (32비트, 만들 수 있는 주소 43억 개) 그걸 보완하려고 IPv6를 만들었다. 이건 128비트라서 340조의 1조 배의 1조 배...라고 한다.</p>
<blockquote>
<p><strong>IP 주소는 왜 192.168.X.XX... 이렇게 생겼을까?</strong>
32비트의 IPv4 주소를 여덟 자리씩 끊어 총 네 개의 십진수로 표현해서 그렇다. 예를 들어 IP 주소가 1100000010101000.... 이런 모양이면 이걸 여덟 자리씩 끊는다. 11000000/10101000/... 이렇게 끊은 구간을 십진수로 변환해서 192.168.1.10 이런 식으로 표현하는 것.</p>
</blockquote>
<h2 id="2024-08-15-네트워크">2024-08-15 &lt;네트워크&gt;</h2>
<h3 id="서브넷">서브넷</h3>
<ol>
<li><p>네트워크 ID, 호스트 ID
IP 주소는 네트워크 ID와 호스트 ID 두 개로 구성이 된다. 위에서 IP 주소를 여덟 자리씩 끊어 총 네 개의 구간으로 나눈다고 했는데, 이 때 앞의 일부는 네트워크 ID, 나머지는 호스트 ID가 된다.
<img src="https://velog.velcdn.com/images/soo-im/post/0fdeed6b-6760-46b4-a437-896b36520600/image.png" alt="">
이 때 몇 개를 나누어 가지느냐에 따라 &#39;클래스&#39;가 정해진다. 네트워크 ID가 앞의 하나이면 A 클래스, 두 개이면 B 클래스, 세 개이면 C 클래스.</p>
</li>
<li><p>네트워크 주소, 브로드캐스트 주소
네트워크 주소는 일종의 대표 주소이고, 브로드캐스트 주소는 해당 대표 주소 하위에 있는 모든 호스트에게 전송하기 위한 주소이다.
네트워크 주소는 호스트 ID가 0이고, 브로드캐스트 주소는 호스트 ID가 255이다.
<img src="https://velog.velcdn.com/images/soo-im/post/44f9644d-d61e-4f3c-a243-07a36d58bc23/image.png" alt="">
예를 들어 192.168.1.1, 192.168.1.2,... 주소를 가진 컴퓨터는 모두 192.168.1.0 이라는 네트워크 하위에 속한다. 그리고 만약 누군가 192.168.1.255 브로드캐스트 주소로 데이터를 보내면 192.168.1.0 하위에 있는 모든 컴퓨터에 데이터가 전송이 된다.
그래서 개별 컴퓨터는 자신의 호스트 ID를 0이나 255로 설정하지 않는다.</p>
</li>
<li><p>서브넷
서브넷은 네트워크를 더 작은 규모로 분할하는 것을 말한다. 무슨 소리이냐면...
C 클래스는 네트워크 ID가 24비트, 호스트 ID가 8비트이다. 즉 호스트를 총 2^8=256개를 설정할 수 있다는 뜻이다. 하지만 그 공간에 컴퓨터가 256개보다 적으면 그만큼 호스트 ID를 낭비하게 된다.
그래서 이 낭비를 막기 위해 호스트 ID에서 일부 비트를 빌려 네트워크 ID로 사용하고, 이렇게 빌려온 비트로 만든 네트워크를 &#39;서브넷&#39;이라고 한다.
위의 경우에서 호스트 ID에서 3비트를 빌리면 2^3=8개의 작은 서브넷을 구성할 수 있게 되고, 각 서브넷은 남은 5비트를 이용해 2^5=32개의 호스트를 가질 수 있다.
이런 식으로 사용자의 상황에 맞게 네트워크를 서브넷으로 만들어 효율적으로 IP 주소를 쓸 수 있다. (어떻게든 방법을 찾는 저 발상... 대단하다)</p>
<blockquote>
<p><strong>서브넷 마스크</strong>
다만 이렇게 분할하면 어디부터 어디까지가 네트워크 ID이고 호스트 ID인지 분간하기가 어려워진다. 그래서 이걸 알려주는 게 서브넷 마스크.
말 그대로 네트워크면 1, 호스트면 0으로 마스킹한다. 예를 들어 32비트 중 첫 25비트가 네트워크/뒤의 7비트가 호스트이면 → <code>11111111.11111111.11111111.10000000</code> 이렇게 표현하는 것이다. 이걸 십진수로 바꾸면 <code>255.255.255.128</code>.</p>
</blockquote>
</li>
</ol>
<h2 id="2024-08-21-네트워크">2024-08-21 &lt;네트워크&gt;</h2>
<h3 id="라우터">라우터</h3>
<p>라우터는 네트워크 간 통신을 가능하게 한다. 3단계 설명이 &#39;라우터를 통해 서로 다른 네트워크 간의 통신을 하는 계층&#39;이었던 만큼 가장 중요한 장비이다.</p>
<ol>
<li>라우터가 하는 일
라우터는 네트워크를 여러 개로 분리하고, 다른 네트워크와 송수신할 때 최적의 경로를 찾아 보내고/받는 역할을 한다 (라우팅). 라우팅 과정에는 네트워크 경로를 찾는 일 뿐만 아니라 트래픽 관리, IP 주소 변환도 포함된다.</li>
<li>무슨 말인지 모르겠다!
IP 주소를 분리해주고, 분리한 주소를 이용해서 네트워크 간의 통신을 해주는 것이다.
<img src="https://velog.velcdn.com/images/soo-im/post/77100be2-031e-4d4b-aa4e-556b3a982c62/image.png" alt="">
이런 상황을 가정해보자. 라우터 A가 인터넷이랑 연결이 되어있고, 라우터 B와 C는 라우터 A에 연결이 되어있다.
(1) 라우터 A는 외부 IP와 내부 IP를 분리해서 관리한다. (외부 IP 주소는 ISP에서 공급받은 것)
(2) 라우터 A는 192.168.0.1을 각각 192.168.1.1과 192.168.2.1로 분리해서 라우터 B, C에 나누어준다.
(2) 라우터 B와 라우터 C는 각각 받은 IP 주소를 다시 분리해서 컴퓨터에게 각각 나누어준다.</li>
<li>저러면 어떻게 통신하나?</li>
</ol>
<ul>
<li>3.1 첫 번째 컴퓨터가 인터넷과 통신하는 경우
<img src="https://velog.velcdn.com/images/soo-im/post/20990c15-3e7e-4e57-8559-1c8278978a4c/image.png" alt="">
컴퓨터 192.168.1.2가 라우터 B에 요청 → 라우터 B가 요청을 보내는 IP를 192.168.1.1로 변경해 라우터 A에 요청 → 라우터 A가 외부 IP 203.0.113.5로 인터넷에 요청 → 인터넷이 203.0.113.5로 라우터 A에게 응답 → 라우터 A가 내부 IP 192.168.1.1로 라우터 B에게 전달 → 라우터 B가 응답을 컴퓨터 192.168.1.2로 전달.</li>
<li>3.2 첫 번째 컴퓨터가 같은 라우터 B에 연결된 두 번째 컴퓨터와 통신하는 경우
<img src="https://velog.velcdn.com/images/soo-im/post/43c74f42-62a4-489c-af54-97454735fa55/image.png" alt="">
컴퓨터 192.168.1.2가 라우터 B에 요청 → 라우터 B가 자신의 서브넷 (192.168.1.X)에 있음을 인식해서 컴퓨터 192.168.1.3로 전달.</li>
<li>3.3 첫 번째 컴퓨터가 다른 라우터 C에 연결된 세 번째 컴퓨터와 통신하는 경우
<img src="https://velog.velcdn.com/images/soo-im/post/1b520569-a764-4653-b7f7-b050b7b86b54/image.png" alt="">
컴퓨터 192.168.1.2가 라우터 B에 요청 → 라우터 B가 자신의 서브넷 (192.168.1.X)에 없음을 인식해서 라우터 A에 전달 → 라우터 A가 라우터 C로 전달 → 라우터 C가 컴퓨터 192.168.2.2로 전달.</li>
</ul>
<ol start="4">
<li>3.2, 3.3은 스위치랑 헷갈린다?!
<img src="https://velog.velcdn.com/images/soo-im/post/60f736cc-e2f7-4ca5-a7a6-95dc59620205/image.png" alt="">
<img src="https://velog.velcdn.com/images/soo-im/post/9d3f650f-a00f-4da5-b189-0cb1d26efde5/image.png" alt="">
앞에서 배운 스위치랑 연결된 모양은 비슷하지만 통신 방식이 다르다.
스위치는 동일한 네트워크 안에서 (LAN) MAC 주소를 기반으로 프레이밍을 거쳐 2단계 통신을 하는 장비이고,
라우터는 다른 네트워크 사이에서 IP 주소를 기반으로 라우팅을 거쳐 3단계 통신을 하는 장비이다.</li>
</ol>
<blockquote>
<p><strong>왜 라우터는 내부 IP와 외부 IP를 구분하나?</strong></p>
</blockquote>
<p>1) IPv4가 모자라서 주소를 아끼려고
2) 내부 네트워크 구조를 숨겨서 (외부에는 내부 IP가 안 보임) 보안을 강화하려고</p>
<blockquote>
<p><strong>라우터 하나에 연결하는 대신 하위 라우터로 나누는 이유는?</strong></p>
</blockquote>
<p>1) 라우터 하나가 큰 네트워크를 관리하면 성능이 떨어져서
2) 라우터에 장애가 났을 때 위험을 분산하려고
3) 라우터 하나만 털리면 다 털릴 수도 있으니 보안을 강화하려고</p>
<h2 id="2024-08-24-인지심리-ux">2024-08-24 &lt;인지심리 (UX)&gt;</h2>
<p>오늘은 UX 특강 듣는 날!</p>
<ol>
<li>쾌락적 편집 (Hedonic Editing)<ul>
<li>긍정적인 내용은 따로따로 말한다. (예: 5만 원 혜택을 받았어요, 2만 원 혜택도 받았어요 &gt; 7만 원 혜택을 받았어요)</li>
<li>부정적인 내용은 합쳐서 말한다.</li>
<li>합쳐서 부정적인 내용을 상쇄시킬 수 있으면 그렇게 한다. (예: 3만 원 벌었어요 &gt; 5만 원 벌고 2만 원 썼어요)</li>
<li>합쳐도 부정적인 내용이 더 크다면 차라리 긍정적인 것을 뒤에 말한다. (예: 5만 원 썼지만 2만 원 벌었어요 &gt; 3만 원 썼어요)</li>
</ul>
</li>
<li>손실 회피 (Loss Averstion)<ul>
<li>손실을 회피하려는 경향. (예: 지금 떠나면 10만 원 잃어요 &gt; 1시간 더 하면 10만 원 더 얻어요)</li>
</ul>
</li>
<li>Affordance<ul>
<li>특정 행동으로 유도하는 패턴. (예: &#39;취소&#39; 버튼은 색 없음, &#39;다음&#39; 버튼은 색 있음)</li>
</ul>
</li>
<li>선택 설계 (Choice Architecture)<ul>
<li>유도하려는 것을 default로 제공한다. (예: XX에 참여하지 않겠습니까? &gt; XX에 참여하시겠습니까?)</li>
<li>대체로 주어진 선택지 안에서 선택하려고 하기 때문에, 주고 싶지 않은 선택지는 숨긴다. (예: 멤버십 level 1~3 중에서 고르세요. &gt; 멤버십에 가입하시겠습니까?)</li>
<li>상대적으로 이득인 것처럼 보이는 미끼를 넣는다. (예: 온라인 구독 50달러/오프라인 구독 100달러/온+오프라인 구독 100달러 → 미끼 상품인 오프라인 구독은 아무도 하지 않고, 온+오프라인 구독을 선택한다. 반대로 미끼 상품을 제거하면 온라인 구독을 선택한다.)</li>
<li>사람은 중간 급의 상품을 자주 선택한다. (예: 상대적으로 비싼 상품을 추가하면 중간급 상품이 더 많이 팔린다.)</li>
</ul>
</li>
</ol>
<h2 id="2024-08-26-네트워크">2024-08-26 &lt;네트워크&gt;</h2>
<h3 id="4단계-전송-계층-데이터의-오류-점검">4단계. 전송 계층: 데이터의 오류 점검</h3>
<blockquote>
<p>데이터를 올바르게 전달하였는지, 오류가 없는지 점검하는 계층</p>
</blockquote>
<p>데이터를 전송하는 1~3 단계와 달리, 4단계는 이렇게 전송된 데이터가 올바르게 도착하는지를 확인한다. 크게 두 가지를 점검한다.</p>
<ol>
<li>오류가 있는지?</li>
<li>전송된 데이터의 목적지가 어느 어플리케이션인지?
그냥 보내기만 하면 동영상 데이터가 채팅 앱에 나와야 할지, 비디오 플레이어에 나와야 할 지 모른다. 이 때 전송 계층이 &#39;포트 번호&#39;를 확인해서 올바른 앱으로 보내준다.
예를 들어 80은 HTTP, 443은 HTTPS, 25는 이메일 (SMTP)... 등이다.</li>
</ol>
<p>전송 계층은 위를 점검할 때 상황에 맞게 연결형 통신과 비연결형 통신 중 하나를 선택한다.</p>
<table>
<thead>
<tr>
<th></th>
<th>연결형 통신</th>
<th>비연결형 통신</th>
</tr>
</thead>
<tbody><tr>
<td>프로토콜</td>
<td>TCP</td>
<td>UDP</td>
</tr>
<tr>
<td>우선 순위</td>
<td>정확성</td>
<td>효율성</td>
</tr>
<tr>
<td>예시</td>
<td>전화 (쌍방)</td>
<td>문자 (일방)</td>
</tr>
<tr>
<td>통신 방식</td>
<td>시작 → &quot;지금 통화 되나요?&quot; → &quot;네&quot; → &quot;XXX 입니다&quot; → &quot;네 이해했습니다&quot; → &quot;네 확인했습니다&quot; → 종료</td>
<td>시작 → &quot;XXX 입니다&quot; → 종료</td>
</tr>
</tbody></table>
<h2 id="2024-08-29-네트워크">2024-08-29 &lt;네트워크&gt;</h2>
<h3 id="tcp">TCP</h3>
<p>TCP는 정확도를 중시하는 연결형 통신 프로토콜이다. 데이터 통신 전에 연결이 잘 되었는지, 데이터가 잘 전달되었는지, 통신이 끝나면 연결이 잘 끊어졌는지 확인하는 절차를 거친다.</p>
<ol>
<li>3-way Handshake (연결 확립)
통신하기 전에 두 컴퓨터의 연결을 확립하는 과정이다. 3-way라고 부르는 이유는 아래의 세 번의 과정을 거치기 때문이다.<ul>
<li>발신자가 수신자에게 &quot;SYN&quot;(동기화) 메시지를 보낸다.</li>
<li>수신자가 &quot;SYN-ACK&quot;(동기화 승인) 메시지로 응답한다.</li>
<li>발신자가 &quot;ACK&quot;(승인) 메시지로 응답한다.</li>
</ul>
</li>
<li>Acknowledgments (도착 확인)<ul>
<li>발신자가 세그먼트를 보낸다. (세그먼트: TCP 헤더를 붙인 데이터 패킷)</li>
<li>수신자가 수신한 모든 세그먼트에 대해 &quot;ACK&quot; 메시지를 보낸다.</li>
<li>만약 발신자가 일정 시간 내에 &quot;ACK&quot;를 받지 못하면 세그먼트가 손상되었다고 가정하고 다시 보낸다.</li>
</ul>
</li>
<li>4-way Handshake (연결 종료)
통신이 끝난 후 연결을 종료하는 과정이다.<ul>
<li>발신자가 수신자에게 &quot;FIN&quot; (연결 종료 요청) 메시지를 보낸다.</li>
<li>수신자가 발신자에게 &quot;ACK&quot; (연결 종료 요청 확인) 메시지를 보낸다.</li>
<li>수신자도 발신자에게 &quot;FIN&quot; 메시지를 보낸다.</li>
<li>발신자도 수신자에게 &quot;ACK&quot; 메시지를 보내면서 연결을 완전히 종료한다.</li>
</ul>
</li>
</ol>
<blockquote>
<p><strong>왜 4-way까지 하나? FIN 보내면 바로 끊으면 되잖아</strong>
아직 보낼 데이터가 남아있을 수 있기 때문이다.
&quot;다 보냈으니까 FIN 보낸 거 아니야?&quot; 라고 생각할 수도 있지만, 여기서 &#39;발신자&#39;와 &#39;수신자&#39;라고 써서 그렇지 실제로는 둘 다 데이터를 보낼 수 있기 때문이다.
그래서 A가 &quot;나 다 보냈어. 끊어도 돼.&quot; 라고 할 때 B는 &quot;알았어... 근데 나는 아직 덜 보냈어.&quot; 같은 상황 때문에 서로 FIN 요청을 보내는 것.</p>
</blockquote>
<h2 id="2024-08-30-네트워크">2024-08-30 &lt;네트워크&gt;</h2>
<h3 id="tcp-헤더">TCP 헤더</h3>
<p>위에서 TCP 헤더를 붙인 데이터 패킷을 &#39;세그먼트&#39;라고 언급했다.
TCP 헤더 안에는 여러 숫자가 있는데, 각각 데이터 전송 과정에서 아래의 역할을 한다.</p>
<table>
<thead>
<tr>
<th>명칭</th>
<th>역할</th>
<th>비유</th>
</tr>
</thead>
<tbody><tr>
<td>일련번호 sequence num</td>
<td>세그먼트의 순서를 식별하는 번호. 하나의 데이터가 여러 세그먼트로 쪼개져서 보내지기 때문에, 각 세그먼트가 몇 번째인지 알기 위해 사용한다.</td>
<td>낱장 단위로 보내는 편지의 페이지 번호</td>
</tr>
<tr>
<td>확인 응답 번호 ack. num</td>
<td>세그먼트가 올바르게 수신되었는지 확인하는 번호. 수신자가 데이터의 seq. num을 보고, 발신자에게 다음에 받고 싶은 seq. num을 ack. num으로 보낸다.</td>
<td>&quot;편지 1~10 페이지 받았어요. 다음에는 11 페이지부터 보내주세요.&quot;</td>
</tr>
<tr>
<td>window size</td>
<td>수신자가 받을 수 있는 세그먼트의 최대 용량. 매번 세그먼트를 주고 받을 때마다 요청-응답을 하면 너무 오래 걸리기 때문에, 수신자가 최대로 받을 수 세그먼트 용량을 미리 파악한다. 이러면 그 용량을 넘지 않게 여러 세그먼트를 연달아 보낼 수 있다.</td>
<td>최대 50장까지 받을 수 있는 우편함이 있으면, 1<del>10페이지, 11</del>20페이지, ... 41~50페이지는 ack. num을 받기 전에도 연달아 보낼 수 있다.</td>
</tr>
<tr>
<td>port num</td>
<td>데이터의 목적지 (어플리케이션)를 식별하는 번호. 해당 데이터가 어느 어플리케이션으로 가야 하는지 알려준다. (전송 계층은 이런 역할까지 한다!)</td>
<td>아파트 (수신자)에 살고 있는 주민의 집 호수 (포트).</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[파이썬으로 구글챗 보내기 (초보자용)]]></title>
            <link>https://velog.io/@soo-im/%EA%B5%AC%EA%B8%80%EC%B1%97%EC%9D%80-%EB%B3%B4%EB%82%B4%EA%B3%A0-%EC%8B%B6%EC%A7%80%EB%A7%8C-%EA%B5%AC%EA%B8%80%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C%EB%8A%94-%EB%AA%A8%EB%A5%B4%EA%B2%A0%EC%96%B4</link>
            <guid>https://velog.io/@soo-im/%EA%B5%AC%EA%B8%80%EC%B1%97%EC%9D%80-%EB%B3%B4%EB%82%B4%EA%B3%A0-%EC%8B%B6%EC%A7%80%EB%A7%8C-%EA%B5%AC%EA%B8%80%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C%EB%8A%94-%EB%AA%A8%EB%A5%B4%EA%B2%A0%EC%96%B4</guid>
            <pubDate>Tue, 04 Jul 2023 15:18:34 GMT</pubDate>
            <description><![CDATA[<p>파이썬으로 구글챗을 보내고 싶었을 뿐인데 구글클라우드의 벽은 나에게 너무 높았다...</p>
<p>파이썬 코드만 <del>작성</del>복붙할 줄 아는 초보자가 파이썬으로 구글챗 보내는 방법을 정리했다 🤗</p>
<h1 id="중요-문서">중요 문서</h1>
<ol>
<li><a href="https://developers.google.com/chat/api/guides/v1/messages/create?hl=ko">Google Chat API(메시지 작성)</a></li>
<li><a href="https://developers.google.com/chat/how-tos/apps-develop?hl=ko">Google Chat 앱 빌드</a></li>
</ol>
<h1 id="1-구글-클라우드-프로젝트-만들기">1. 구글 클라우드 프로젝트 만들기</h1>
<ol>
<li><a href="https://console.cloud.google.com/">구글 클라우드 콘솔</a>로 접속한 다음, &#39;새 프로젝트 만들기&#39;을 클릭해 프로젝트를 만든다. 구글 클라우드를 처음 접속했다면 어딘가 잘 보이는 데 있을 것이다...(아마)</li>
<li>프로젝트를 만들고 나면 상단 드롭다운에 프로젝트명이 보인다.
<img src="https://velog.velcdn.com/images/soo-im/post/ebe64dba-57cd-41df-937f-95aa0f88d321/image.png" alt=""></li>
</ol>
<h1 id="2-google-chat-api-허용">2. Google Chat API 허용</h1>
<ol>
<li>좌측 상단의 햄버거 버튼 <code>≡</code>을 클릭하고 &#39;API 및 서비스&#39;의 &#39;라이브러리&#39;에 들어간다.</li>
<li>검색란에 &#39;Google Chat&#39;을 검색하고, 검색 결과에서 &#39;Google Chat API&#39;를 클릭한다.
<img src="https://velog.velcdn.com/images/soo-im/post/3d33147a-6947-4a6e-8e4a-a8ac180f3202/image.png" alt=""></li>
<li>Google Chat API 세부정보 창에서 &#39;사용(아마도 이 비스무리한 단어)&#39;를 클릭한다. 그렇게 하면 아래와 같이 &#39;API 사용 설정됨&#39; 문구가 나온다.
<img src="https://velog.velcdn.com/images/soo-im/post/ad14a29c-5898-472c-b349-26fe50e4a1ac/image.png" alt=""></li>
</ol>
<h1 id="3-서비스-계정-등록">3. 서비스 계정 등록</h1>
<ol>
<li>좌상단 햄버거 버튼&gt;&#39;API 및 서비스&#39;&gt;&#39;사용자 인증 정보&#39;에 들어간다.</li>
<li>상단의 &#39;+ 사용자 인증 정보 만들기&#39;를 클릭하고 &#39;서비스 계정&#39;을 클릭한다.
<img src="https://velog.velcdn.com/images/soo-im/post/049181b2-7d81-48c1-a5ba-6c24ca8955b7/image.png" alt=""></li>
<li>이름 등등 간단한 정보를 입력하고(나는 선택사항은 따로 설정하지 않았다) 완료를 클릭한다.
<img src="https://velog.velcdn.com/images/soo-im/post/fac1949a-dbe8-4ccb-8851-d4a51cb7707e/image.png" alt=""></li>
<li>자동으로 서비스 키(JSON 혹은 P12) 다운로드가 나온다면 그것을 받고, 만약 그런 화면이 나오지 않으면 &#39;API 및 서비스&#39;&gt;&#39;사용자 인증 정보&#39; 화면의 &#39;서비스 계정&#39; 리스트에서 방금 만든 서비스 계정을 클릭한다. 그리고 &#39;키&#39; 탭으로 이동해 &#39;키 추가&#39;를 눌러 JSON을 선택하고 &#39;만들기&#39;를 눌러 파일을 다운로드 받는다.
<img src="https://velog.velcdn.com/images/soo-im/post/a6fdd5a4-bdec-468b-9aa1-210d5ed31f2d/image.png" alt=""></li>
</ol>
<h1 id="4-chat-앱-만들기">4. Chat 앱 만들기</h1>
<p>엉? 갑자기 무슨 앱이야? 싶지만 이게 있어야만 다음 단계로 갈 수 있다!! 안내에 따라 웹에서 쉽게 만들 수 있다.</p>
<ol>
<li>Chat 앱을 만드는 여러 방법은 <a href="https://developers.google.com/chat/how-tos/apps-develop?hl=ko">이 문서</a>에 있지만, 가장 쉬운 <a href="https://support.google.com/appsheet/answer/12923581?hl=ko">방법</a>인 AppSheet를 사용했다.</li>
<li>위 안내에 따라 <a href="https://www.appsheet.com/Home/Apps?sjid=14391771614796254086-AP">AppSheet</a> 사이트에 접속해서 로그인한다. 이 때 구글 클라우드와 Google Chat을 사용하는 계정으로 로그인하는 걸 추천한다.</li>
<li>기존 앱을 복사해 새로운 앱을 만든다(Copy the Sample App 구간 참고).</li>
<li>1~3단계에서 Next를 계속 클릭해주면 마지막에 &#39;Share&#39; 구간이 나오는데 여기서 &#39;Share app with individual users&#39;를 클릭한다.
<img src="https://velog.velcdn.com/images/soo-im/post/c96b1554-029c-4ce6-8da4-91d90a527555/image.png" alt=""></li>
<li>팝업 하단의 &#39;Copy sharing links&#39;를 클릭한 후 &#39;Browser Link&#39;를 복사한다.
<img src="https://velog.velcdn.com/images/soo-im/post/90af8673-7166-4001-9878-201f94f281ee/image.png" alt=""></li>
</ol>
<h1 id="5-google-chat-api-구성하기">5. Google Chat API 구성하기</h1>
<ol>
<li>다시 클라우드 콘솔로 돌아와서 &#39;API 및 서비스&#39;&gt;&#39;사용 설정된 API 및 서비스&#39;를 클릭한다.</li>
<li>하단 리스트에서 &#39;Google Chat API&#39;를 클릭한다.</li>
<li>&#39;구성&#39; 탭을 클릭한다.</li>
<li>앱 상태를 설정하는 란이 있다. 앱 이름과 이미지 URL, 설명에 적당한 값을 입력한다.</li>
<li>(여기는 필요에 따라 설정하면 되는 듯 하다) 양방향 기능 사용 설정을 켜고, 1:1 메시지 수신과 스페이스 및 그룹 대화 참여 중 필요한 기능을 켠다.</li>
<li>연결 설정에서 &#39;앱 URL&#39;을 선택하고 4번 단계에서 AppSheet에서 복사한 &#39;Browser Link&#39;를 붙여넣는다.
<img src="https://velog.velcdn.com/images/soo-im/post/7773ea21-f935-407a-9493-78c0b7aac286/image.png" alt=""></li>
<li>필요한 경우 슬래시 명령어, 링크 미리보기, 공개 상태를 설정하고(만약 회사나 모임 등 그룹이 있으면 공개 상태를 true로 하는 것이 편할 듯하다) 하단의 저장을 클릭한다.</li>
</ol>
<h1 id="6-google-chat-스페이스에-chat-앱-추가하기">6. Google Chat 스페이스에 Chat 앱 추가하기</h1>
<ol>
<li>이제 Chat 앱을 내가 메시지를 보내고 싶은 스페이스에 추가하면 준비가 끝난다. 구글 챗에서 원하는 스페이스로 들어간다.</li>
</ol>
<p>** 코드가 &#39;스페이스&#39; 기준으로 되어있기 때문에 &#39;채팅&#39;도 이렇게 설정할 수 있는지는 잘 모르겠다
2. 스페이스 이름을 눌러 &#39;앱 및 통합&#39;을 클릭한다.
3. 팝업 하단의 &#39;+ 앱 추가&#39;를 클릭한다.
4. 4번 단계에서 만든 앱 이름을 검색하면 우리가 등록한 앱이 나온다! 이 때 상자 모양이 아니라 내 AppSheet를 만든 계정의 프로필 이미지로 된 앱(하단)을 선택하고 &#39;추가&#39;를 클릭한다.
만약 상자 모양의 앱만 나오면 지금 구글챗을 접속한 계정과 AppSheet에 로그인한 계정이 달라서 그럴 수 있다. 그런 경우에는 AppSheet에 로그인한 계정으로 구글챗에 들어가서 다시 시도한다.
<img src="https://velog.velcdn.com/images/soo-im/post/03997d2c-b8f5-4b42-a24b-1e758249c99a/image.png" alt="">
5. 정상적으로 추가가 되었으면 &#39;xxx님이 {My Chat App} 앱님을 추가했습니다.&#39; 라는 안내 메시지가 나온다.
6. 만약 4번에서 계정을 안 바꾸고 그냥 상자 모양 앱을 추가했으면 &quot;To view this app, you need to sign in&quot;이 나온다. 이건 우리가 원하는 앱을 넣은 게 아니니 다시 4번으로 돌아가서 올바른 앱을 추가한다.
<img src="https://velog.velcdn.com/images/soo-im/post/0fa3f73a-925c-4804-9a20-1b40b1c2cb23/image.png" alt="">
7. 만약 4번에서 계정은 동일하지만 상자 모양 앱을 추가했으면 내가 설정한 기억이 없는 안내 메시지가 나온다. (아마 복사해 온 앱 메시지가 그대로 뜨는 듯) 이 경우에도 잘못된 앱을 넣은 것이니 4번으로 돌아가서 올바른 앱을 추가한다.
<img src="https://velog.velcdn.com/images/soo-im/post/1a4f1639-ab04-4192-afe8-f47c0c2f97ed/image.png" alt=""></p>
<h1 id="7-google-chat-메시지-보내는-python-코드-작성">7. Google Chat 메시지 보내는 Python 코드 작성</h1>
<ol>
<li>드디어 파이썬을 쓸 시간!! <a href="https://developers.google.com/chat/api/guides/v1/messages/create?hl=ko">공식 문서</a>에 따라 google 클라이언트 라이브러리를 설치하고 코드를 작성한다. (나는 &#39;<a href="https://developers.google.com/chat/api/guides/v1/messages/create?hl=ko#create-text-app-auth">앱 인증으로 문자 메시지 만들기</a>&#39; 코드를 사용했다)<pre><code class="language-python"># 출처: Google Chat API 공식 문서
from httplib2 import Http
from oauth2client.service_account import ServiceAccountCredentials
from apiclient.discovery import build
</code></pre>
</li>
</ol>
<h1 id="specify-required-scopes">Specify required scopes.</h1>
<p>SCOPES = [&#39;<a href="https://www.googleapis.com/auth/chat.bot&#39;%5D">https://www.googleapis.com/auth/chat.bot&#39;]</a></p>
<h1 id="specify-service-account-details">Specify service account details.</h1>
<p>CREDENTIALS = ServiceAccountCredentials.from_json_keyfile_name(
    &#39;service_account.json&#39;, SCOPES) # &lt;------- &#39;~.json&#39;에 3번 단계에서 받은 json 파일 경로 입력</p>
<h1 id="build-the-uri-and-authenticate-with-the-service-account">Build the URI and authenticate with the service account.</h1>
<p>chat = build(&#39;chat&#39;, &#39;v1&#39;, http=CREDENTIALS.authorize(Http()))</p>
<h1 id="create-a-chat-message">Create a Chat message.</h1>
<p>result = chat.spaces().messages().create(</p>
<pre><code>parent=&#39;spaces/SPACE&#39;, # &lt;------- &#39;SPACE&#39;에 스페이스 URL 맨 뒤의 문자열 입력

# The message to create.
body={&#39;text&#39;: &#39;Hello, world!&#39;}</code></pre><p>).execute()</p>
<p>print(result)</p>
<p>```
2. &#39;service_account.json&#39;은 3번 단계에서 받은 json 파일을 말한다. 여기에 본인이 받은 json 파일 경로를 입력한다.
3. &#39;SPACE&#39;에 브라우저에서 구글챗 채팅방(스페이스)에 접속했을 때 URL 맨 끝에 붙는 문자열을 입력한다.
<img src="https://velog.velcdn.com/images/soo-im/post/65b23258-0e3c-4691-bc9d-77004b3976d5/image.png" alt=""></p>
<h1 id="8-실행">8. 실행!</h1>
<p>우리 애가 말을 한다!
<img src="https://velog.velcdn.com/images/soo-im/post/6bbf9176-518f-490c-a392-f2c14d6d2e11/image.png" alt=""></p>
<h1 id="9-403-오류">9. 403 오류</h1>
<p>어지간한 오류는 챗지피티에게 물어보면 해결이 되지만, 유달리 해결방법을 찾기 힘든 메시지가 403 이었다.
<code>&lt;HttpError 403 when requesting https://chat.googleapis.com/v1/spaces/{SPACE}/messages?alt=json returned &quot;This Chat app is not a member of this space&quot;. Details: &quot;This Chat app is not a member of this space&quot;&gt;</code></p>
<p>이 에러가 났다면 Chat 앱을 안 만들었거나(4번 단계), Chat 앱의 링크를 Google Chat API의 구성에 올바르게 넣지 않았거나(5번 단계), Chat 앱을 스페이스에 넣지 않았을(6번 단계) 확률이 높다. 그러니 403을 만나면 4번 단계부터 다시 해보면 도움이 될지도 모른다 🙃</p>
<h1 id="능히-할수잇다">능히 할수잇다!</h1>
<p>나같은 바보는 흔치 않겠지만... 누군가(와 미래의 나)에게 도움이 되길 바라며 이번 글 마무리!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[5월~6월 TIL]]></title>
            <link>https://velog.io/@soo-im/5%EC%9B%94-TIL</link>
            <guid>https://velog.io/@soo-im/5%EC%9B%94-TIL</guid>
            <pubDate>Sat, 13 May 2023 14:50:39 GMT</pubDate>
            <description><![CDATA[<p>반 년 공백을 딛고 부활!
AI 코칭 스터디 들어갔으니 3주간 화이팅 🙌</p>
<h2 id="2023-05-13">2023-05-13</h2>
<h3 id="cross-validation-out-of-fold-prediction">Cross Validation Out of Fold Prediction</h3>
<ol>
<li><p>Cross Validation
데이터를 여러 개의 fold로 나누어 validation 성능을 측정하는 방식. (이전에도 기록했으니까 자세한 설명은 패스)</p>
</li>
<li><p>Out of Fold Prediction
fold마다 학습한 모델로 데이터를 예측하고 그 결과를 앙상블하여 최종 예측값으로 사용하는 방식. (대기과학에서 쓰는 방법이랑 비슷하다)
<img src="https://velog.velcdn.com/images/soo-im/post/5c77d452-2969-4faa-a488-cd977f2692b3/image.png" alt="Cross Validation Out of Fold Prediction"></p>
</li>
</ol>
<pre><code class="language-python">&#39;&#39;&#39;
코드 출처: BoostCourse
&#39;&#39;&#39;

    # 테스트 데이터 예측값을 저장할 변수
    test_preds = np.zeros(x_test.shape[0]) 

    # Out Of Fold Validation 예측 데이터를 저장할 변수
    y_oof = np.zeros(x_train.shape[0])

    # 폴드별 평균 Validation 스코어를 저장할 변수
    score = 0

    # 피처 중요도를 저장할 데이터 프레임 선언
    fi = pd.DataFrame()
    fi[&#39;feature&#39;] = features

    # Stratified K Fold 선언
    skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=SEED)

    for fold, (tr_idx, val_idx) in enumerate(skf.split(x_train, y)):
        # train index, validation index로 train 데이터를 나눔
        x_tr, x_val = x_train.loc[tr_idx, features], x_train.loc[val_idx, features]
        y_tr, y_val = y[tr_idx], y[val_idx]

        print(f&#39;fold: {fold+1}, x_tr.shape: {x_tr.shape}, x_val.shape: {x_val.shape}&#39;)

        # LightGBM 데이터셋 선언
        dtrain = lgb.Dataset(x_tr, label=y_tr)
        dvalid = lgb.Dataset(x_val, label=y_val)

        # LightGBM 모델 훈련
        clf = lgb.train(
            model_params,
            dtrain,
            valid_sets=[dtrain, dvalid], # Validation 성능을 측정할 수 있도록 설정
            categorical_feature=categorical_features,
            verbose_eval=200
        )

        # Validation 데이터 예측
        val_preds = clf.predict(x_val)

        # Validation index에 예측값 저장 
        y_oof[val_idx] = val_preds # &lt;-- 이번 fold로 학습한 모델에 validation data를 넣고 예측한 결과를 y_oof에 저장한다.

        # 폴드별 Validation 스코어 측정
        print(f&quot;Fold {fold + 1} | AUC: {roc_auc_score(y_val, val_preds)}&quot;)
        print(&#39;-&#39;*80)

        # score 변수에 폴드별 평균 Validation 스코어 저장
        score += roc_auc_score(y_val, val_preds) / folds # &lt;-- 이번 fold의 성적을 평균해서 저장한다.

        # 테스트 데이터 예측하고 평균해서 저장
        test_preds += clf.predict(x_test) / folds

        # 폴드별 피처 중요도 저장
        fi[f&#39;fold_{fold+1}&#39;] = clf.feature_importance()

        del x_tr, x_val, y_tr, y_val
        gc.collect()

    print(f&quot;\nMean AUC = {score}&quot;) # 폴드별 Validation 스코어 출력
    print(f&quot;OOF AUC = {roc_auc_score(y, y_oof)}&quot;) # Out Of Fold Validation 스코어 출력
    # &lt;-- **이 부분 이해가 잘 안가서 질문 남겨둔 상태** (y랑 y_oof랑 모양이 다른 것 같은데... y는 (n_samples), y_oof는 (n_samples, n_folds) 인 것 같은데 두 개 모양이 다른데 어떻게 이걸 할 수 있는건지??

    # 폴드별 피처 중요도 평균값 계산해서 저장 
    fi_cols = [col for col in fi.columns if &#39;fold_&#39; in col]
    fi[&#39;importance&#39;] = fi[fi_cols].mean(axis=1)</code></pre>
<h3 id="early-stopping">Early Stopping</h3>
<ol>
<li><p>Early Stopping
과적합을 방지하기 위해 학습을 적합한 시점에서 종료하는 방법. 반복학습을 하는 머신러닝 모델에서 validation 성능이 가장 좋은 하이퍼파라미터가 나온 학습 단계에서 학습을 더 진행하지 않고 종료하는 방법이다. </p>
</li>
<li><p>방법
LightGBM, XGBoost 등은 <code>early_stopping_rounds</code> 파라미터로 이 옵션을 제공한다. 부여한 횟수만큼 학습을 하고 그 횟수동안 성능이 개선이 되지 않으면 학습을 종료한다.
예를 들어 <code>n_estimators = 10000</code>, <code>early_stopping_rounds = 100</code>으로 잡은 경우, 1<del>100회를 마친 성능이 0.8인데 101</del>200회 학습 중에서 성능이 0.8보다 높은 것이 하나도 없으면 학습을 종료한다.</p>
</li>
</ol>
<h2 id="2023-05-14">2023-05-14</h2>
<h3 id="feature-engineering">Feature Engineering</h3>
<ol>
<li><p>Feature Engineering?
원본 데이터로부터 문제 해결에 적합한 feature을 생성, 변환하고 머신러닝 모델에 적합한 형식으로 변환하는 작업을 말한다. 딥러닝은 모델이 데이터의 feature을 찾아낼 수 있지만 머신러닝은 사람이 feature를 찾아야 하기 때문에 feature engineering은 머신러닝 성능에 큰 영향을 준다.</p>
</li>
<li><p>방법
특별히 새로운 방법이 있는 것은 아니다. sum, min, max, std, skew 등의 통계치를 이용해 새로운 feature을 만드는 것도 freature engineering의 일종이다.</p>
</li>
<li><p>예시
nunique: 집계하는 condition에서 unique한 값이 몇 번 나왔는지 집계한다.</p>
<pre><code class="language-python">agg_func = [&#39;mean&#39;,&#39;max&#39;,&#39;min&#39;,&#39;sum&#39;,&#39;count&#39;,&#39;std&#39;,&#39;skew&#39;, &#39;nunique&#39;]
test_agg = df.groupby([&#39;customer_id&#39;]).agg(agg_func)</code></pre>
</li>
</ol>
<h3 id="feature-중요도">Feature 중요도</h3>
<ol>
<li><p>feature 중요도?
타겟 변수를 예측하는 데 각 feature가 얼마나 중요한지 측정하는 방법.</p>
</li>
<li><p>Model-specific vs. Model-agnostic
전자는 머신러닝 라이브러리에서 제공하는 feature 중요도를 사용하는 방법, 후자는 머신러닝 기능을 사용하지 않고 학습 이후에 계산하는 방법을 말한다.</p>
</li>
<li><p>Permutation feature 중요도
Model-specific은 각 라이브러리에서 제공하는 기능을 사용하면 된다. 그렇다면 Model-agnostic은 어떻게 계산할까? 강의에서는 예시로 Permutation feature 중요도를 설명한다.
Permutation feature 중요도 방식은 feature의 값을 랜덤하게 섞은 뒤에 학습한 뒤 정상적인 학습 결과와 비교하는 방식이다. 중요한 feature라면 정상적인 학습 결과에 비해 에러가 커질 것이고, 그렇지 않다면 에러에 큰 차이가 나지 않을 것이다.
<code>sklearn.inspection</code>의 <code>permutation_importance</code> 매소드를 이용해서 수행할 수 있다.</p>
</li>
</ol>
<h3 id="feature-selection">Feature Selection</h3>
<ol>
<li><p>Feature Selection?
머신러닝 모델에서 사용할 feature을 선택하는 것을 말한다. 유용하지 않은 feature을 제거하여 과적합을 방지하고 모델 성능을 향상시킬 수 있다.
대표적으로 Filter Method, Wrapper Method, Embedded Method가 있다.</p>
</li>
<li><p>Filter Method
feature의 통계 특성을 이용하는 방법이다. feature 간의 correlation을 구해 상관계수가 높은 feature을 제거하거나, 각 feature의 variance를 구해 분산이 낮은 feature을 제거하는 등의 방식이 있다.</p>
</li>
<li><p>Wrapper Method
예측 모델에 여러 feature subset을 넣어가면서 실험하는 방식이다. (비용은 많이 들겠다)</p>
</li>
<li><p>Embedded Method
Filter Method와 Wrapper Method를 합친 방식으로 학습 알고리즘 자체에서 feature selection을 수행하는 방식이다.</p>
</li>
</ol>
<h2 id="2023-05-15">2023-05-15</h2>
<h3 id="hyperparameter-튜닝">Hyperparameter 튜닝</h3>
<ol>
<li><p>Hyperparameter vs. Parameter
Hyperparamter: 모델 학습 전 사람이 통제하는 값
Parameter: 모델이 학습하면서 스스로 설정하는 값</p>
</li>
<li><p>Hyperparameter 튜닝
모델에 적합한 하이퍼파라미터를 찾는 것을 하이퍼파라미터 튜닝이라고 한다. 아래 세 가지가 대표적인 방법이다.</p>
</li>
<li><p>1 Grid Search
테스트 가능한 하이퍼파라미터를 모두 실험하는 방법. 하이퍼파라미터 개수가 늘어나면 그다지 효율적인 방법은 아니다. 아래 그림에서 균일한 grid로 초록색 격자마다 실험한 것이다.
<img src="https://velog.velcdn.com/images/soo-im/post/bcdfb58c-ebcb-41f4-9f64-fd88762ec350/image.png" alt="Grid Search"></p>
</li>
<li><p>2 Random Search
테스트 가능한 하이퍼파라미터 범위에서 랜덤하게 실험하는 방법. Grid Search보다 효율이 좋다. 아래 그림에서 랜덤하게 실험한 것이다.
<img src="https://velog.velcdn.com/images/soo-im/post/e0a6abdb-dab8-435d-b23e-b043c0f13111/image.png" alt="Random Search"></p>
</li>
<li><p>3 Bayesian Optimization
최초에는 랜덤한 하이퍼파라미터를 실험하고, 실험을 거듭할수록 성능이 잘 나오는 하이퍼파라미터를 집중적으로 실험한다. 아래 그림에서 성능이 높은 파란색 구간을 집중적으로 실험한 것이다.
<img src="https://velog.velcdn.com/images/soo-im/post/e07c5042-dab6-4d8e-838c-cebbc17127f1/image.png" alt="Bayesian Optimization"></p>
</li>
</ol>
<ol start="3">
<li>Optuna
하이퍼파라미터 튜닝을 지원하는 프레임워크.
아래는 목적함수 $(x-2)^2$의 값을 최소로 하는 $x$ 값을 -10~10의 탐색 범위에서 찾는 코드이다. <pre><code class="language-python">import optuna
</code></pre>
</li>
</ol>
<p>def objective(trial):
    x = trial.suggest_uniform(&#39;x&#39;, -10, 10) # &lt;-- -10~10을 uniform하게 탐색
    return (x-2)**2</p>
<p>study = optuna.create_study()
study.optimize(objective, n_trials=100)</p>
<p>study.best_params</p>
<p>&#39;&#39;&#39;
출력 예시
&#39;x&#39; : 2.001285023
&#39;&#39;&#39;</p>
<pre><code>LightGBM에서 optuna로 하이퍼파라미터 튜닝을 하는 예시는 아래와 같다.
```python

&#39;&#39;&#39;
고정된 parameter 주는 경우

model_params = {
    &#39;objective&#39;: &#39;binary&#39;, # 이진 분류
    &#39;boosting_type&#39;: &#39;gbdt&#39;,
    &#39;metric&#39;: &#39;auc&#39;, # 평가 지표 설정
    &#39;feature_fraction&#39;: 0.8, # 피처 샘플링 비율
    &#39;bagging_fraction&#39;: 0.8, # 데이터 샘플링 비율
    &#39;bagging_freq&#39;: 1,
    &#39;n_estimators&#39;: 10000, # 트리 개수
    &#39;early_stopping_rounds&#39;: 100,
    &#39;seed&#39;: SEED,
    &#39;verbose&#39;: -1,
    &#39;n_jobs&#39;: -1,    
}
&#39;&#39;&#39;
def objective(trial, label=label_2011_11):
    lgb_params = {
        &#39;objective&#39;: &#39;binary&#39;,
        &#39;boosting_type&#39;: &#39;gbdt&#39;,
        &#39;num_leaves&#39;: trial.suggest_int(&#39;num_leaves&#39;, 2, 256), # num_leaves 값을 2-256까지 정수값 중에 사용
        &#39;max_bin&#39;: trial.suggest_int(&#39;max_bin&#39;, 128, 256), # max_bin 값을 128-256까지 정수값 중에 사용
        # min_data_in_leaf 값을 10-40까지 정수값 중에 사용
        &#39;min_data_in_leaf&#39;: trial.suggest_int(&#39;min_data_in_leaf&#39;, 10, 40),
        # 피처 샘플링 비율을 0.4-1.0까지 중에 uniform 분포로 사용
        &#39;feature_fraction&#39;: trial.suggest_uniform(&#39;feature_fraction&#39;, 0.4, 1.0),
        # 데이터 샘플링 비율을 0.4-1.0까지 중에 uniform 분포로 사용
        &#39;bagging_fraction&#39;: trial.suggest_uniform(&#39;bagging_fraction&#39;, 0.4, 1.0),
        # 데이터 샘플링 횟수를 1-7까지 정수값 중에 사용
        &#39;bagging_freq&#39;: trial.suggest_int(&#39;bagging_freq&#39;, 1, 7),
        &#39;n_estimators&#39;: 10000,
        &#39;early_stopping_rounds&#39;: 100,
        # L1 값을 1e-8-10.0까지 로그 uniform 분포로 사용
        &#39;lambda_l1&#39;: trial.suggest_loguniform(&#39;lambda_l1&#39;, 1e-8, 10.0),
        # L2 값을 1e-8-10.0까지 로그 uniform 분포로 사용
        &#39;lambda_l2&#39;: trial.suggest_loguniform(&#39;lambda_l2&#39;, 1e-8, 10.0),
        &#39;seed&#39;: SEED,
        &#39;verbose&#39;: -1,
        &#39;n_jobs&#39;: -1,    
    }

    # oof prediction 함수 호출해서 out of fold validation 예측값을 얻어옴
    y_oof, test_preds, fi = make_lgb_oof_prediction(train, y, test, features, model_params=lgb_params)

    # Validation 스코어 계산
    val_auc = roc_auc_score(label, y_oof)

    return val_auc

study = optuna.create_study(direction=&#39;maximize&#39;)  # val_auc가 가장 크게 나오도록(maximize) 하이퍼파라미터 탐색
study.optimize(objective, n_trials=10) # 10회 동안 하이퍼파라미터 탐색

# 하이퍼파라미터 탐색 결과 출력/plot
study.best_params # 최적의 하이퍼파라미터 출력
study.trials_dataframe() # 하이퍼파라미터 탐색 결과 출력
optuna.visualization.matplotlib.plot_param_importances(study) # 하이퍼파라미터 중요도 plot
optuna.visualization.matplotlib.plot_optimization_history(study) # 하이퍼파라미터 탐색 히스토리 plot
optuna.visualization.matplotlib.plot_slice(study) # 각 하이퍼파라미터와 val_auc와의 관계 plot
</code></pre><h2 id="2023-05-16">2023-05-16</h2>
<h3 id="ensemble-learning">Ensemble Learning</h3>
<ol>
<li><p>Ensemble Learning?
여러 개의 약한 성능의 모델을 결합하여 강한 성능의 모델을 만드는 기법. 과적합을 줄이고 성능을 개선하는 효과가 있다.</p>
</li>
<li><p>Ensemble 기법</p>
</li>
<li><p>1 Bagging
Bootstrap Aggregation의 약자로, 각 모델이 데이터셋에서 중복을 허용하여 선별된 샘플 훈련 데이터를 이용해 학습하는 방식을 말한다. 대표적인 예시가 Random Forest.</p>
</li>
<li><p>2 Pasting
Bagging과 유사하나 중복을 허용하지 않고 샘플링 한다.</p>
</li>
<li><p>3 Voting
여러 알고리즘을 투표를 이용해 결정한다. Bagging은 동일한 알고리즘에 다른 학습셋을 이용한 결과를 조합하는 것이고, Voting은 다른 알고리즘에 동일한 학습셋을 이용한 결과를 조합하는 것이다. Hard Vote와 Soft Vote로 나뉜다.
<img src="https://velog.velcdn.com/images/soo-im/post/2feaf428-930f-44d9-a846-da140473b304/image.png" alt="Bagging_vs_Voting"></p>
</li>
<li><p>3.1 Hard Voting
예측 결과 중 가장 많은 분류기가 예측한 결과를 최종 결과로 사용하는 방식. 소수의 예측 결과는 고려하지 않는다.</p>
</li>
<li><p>3.2 Soft Voting
모든 분류기의 예측 결과를 평균하는 방식. Soft Voting을 많이 사용한다.</p>
</li>
<li><p>4 Boosting
여러 개의 모델이 순차적으로 학습하는 방식. 이전 모델이 틀린 예측에 가중치를 부여해서 다음 모델이 학습한다. 성능이 뛰어나 Ensemble Learning에서 많이 사용한다. 대표적인 예시가 XGBoost, LightGBM, CatBoost. 단 Bagging보다 속도가 느리고 과적합이 발생할 수 있다.</p>
</li>
<li><p>5 Stacking
여러 모델을 이용해서 예측한 뒤 그 예측 결과를 결합해 최종 예측을 만드는 방식. 첫 단계 모델을 베이스 모델이라고 하고, 그 모델의 예측값을 이용해 다시 학습하는 모델을 메타 모델이라고 한다. 성능이 뛰어나지만 과적합이 발생할 수 있고 비용이 과다하다.
<img src="https://velog.velcdn.com/images/soo-im/post/6c0fb00f-4d84-4938-a3eb-d34235fdab77/image.png" alt="Stacking"></p>
</li>
</ol>
<h2 id="2023-05-21">2023-05-21</h2>
<h3 id="딥러닝-핵심-요소">딥러닝 핵심 요소</h3>
<ol>
<li>데이터
모델이 배우는 데이터. 어떤 문제를 푸는지에 따라 타입이 달라진다.</li>
<li>모델
데이터로부터 결과를 반환하는 도구.</li>
<li>Loss Function
데이터와 모델이 정해져 있을 때 모델의 성능을 측정하는 값. Regression Task에는 MSE, Classification Task에는 CE... 등이 대표적이다.</li>
<li>알고리즘
Loss Function을 최소화하는 방식. 일반적으로 Loss Function의 1차 미분값을 사용한다. (아마 gradient 얘기인듯...?)</li>
</ol>
<h3 id="neural-networks--multi-layer-perceptron">Neural Networks &amp; Multi-Layer Perceptron</h3>
<ol>
<li>Neural Networks
비선형적인 연산을 수행해서 목적으로 하는 함수에 근사시키는(function approximators) 도구.</li>
<li>Linear Neural Networks
<img src="https://velog.velcdn.com/images/soo-im/post/5a2067ef-cc8c-4547-8be8-a2c1e4170e2c/image.png" alt="Neural_Networks">
가장 간단한 선형회귀 모형을 생각하자. Loss Function은 MSE이고, 우리가 찾고 싶은 파라미터는 Loss Function을 최소화하는 $w$와 $b$이다.
<img src="https://velog.velcdn.com/images/soo-im/post/fc94e3d7-69f2-41ef-a0e2-e5ae3392a377/image.png" alt="Linear_Neural_Function">
따라서 Loss Function을 최소화하기 위한 $w$을 찾으려면 위와 같이 편미분하여 $w$을 구하면 된다. 같은 방법으로 $b$도 구할 수 있다.
이 방식을 gradient descent라고 부른다.</li>
<li>Multi-Layer Perceptron
Linear transform은 다음과 같이 행렬로 변환이 가능하다.
$$
y=W^{T}x
$$
만약 우리가 Linear transform을 여러 번 수행하면 $y=W_1^{T}W_2^{T}x$로 표현이 될 것이다. 하지만 이것은 $W_1^{T}W_2^{T}$은 사실상 한 번의 Linear transform과 동일하다.
따라서 여러 단의 transform을 수행하려면 Nonlinear transform이 포함되어야 한다.
$$
y=W_1^{T}\rho(W_2^{T}x)
$$
여기에서 $\rho$가 Nonlinear transform, Activation Function이다. 이 함수로 인해 모델이 출력할 수 있는 결과의 범위는 더 넓어진다. 이렇게 구성된 Network를 Multi-Layer Perceptron라고 부른다.
아래는 대표적인 Activation Function $\rho$의 예시이다. 
<img src="https://velog.velcdn.com/images/soo-im/post/a63690af-2da9-4d6a-bed8-20ec9abb65f6/image.png" alt="Activation_Function"></li>
</ol>
<h2 id="2023-05-22">2023-05-22</h2>
<h3 id="최적화-용어">최적화 용어</h3>
<ol>
<li>Generalization
학습을 반복할수록 학습셋 에러는 줄어들지만 테스트셋 에러는 어느 시점부터 증가할 수 있다. Generalization Gap은 이 둘 간의 차이를 의미한다. 테스트셋에서도 성능이 우수한 경우 Generalization Performance가 우수하다고 표현한다.
<img src="https://velog.velcdn.com/images/soo-im/post/183648e0-b003-47fd-bdf2-f7c9e41f9cc7/image.png" alt="Generalization_Gap"></li>
<li>Bias &amp; Variance</li>
</ol>
<ul>
<li>Variance: Input으로 인한 Output이 얼마나 분산이 되어있는지?</li>
<li>Bias: 목적으로 하는 Output과 Input으로 인한 Output이 얼마나 차이가 나는지?</li>
</ul>
<ol start="3">
<li>Bootstrapping
학습셋에서 여러 개의 subset을 만들고 여러 개의 모델을 만드는 기법.</li>
<li>Bagging vs. Boosting</li>
</ol>
<ul>
<li>Bagging(Bootstrapping Aggregation): Bootstrapping을 통해 여러 모델을 학습하고, 여러 모델에서 나온 결과를 집계해서 최종 결과를 내는 방식. (앞에서는 Bagging vs. Voting을 했었는데 좀 헷갈리는구만...)</li>
<li>Boosting: 여러 개의 모델(weak learner)이 순차적으로 학습하여 성능이 좋은 하나의 모델(strong learner)을 구현하는 방식. 자세한 설명은 앞에서 적었기 때문에 생략!
<img src="https://velog.velcdn.com/images/soo-im/post/7a361c07-5ef2-4337-a039-8ff20b188ae5/image.png" alt="Bagging_Boosting"></li>
</ul>
<h3 id="gradient-descent">Gradient Descent</h3>
<p>이전 Linear Regression 강의에서 본 것처럼 Loss Function을 최소화하는 파라미터를 편미분을 이용해 학습하는 방식. 아래 그림에서 Local Minimum을 만족하는 해 $w$를 찾는다.
<img src="https://velog.velcdn.com/images/soo-im/post/f208184f-08c9-44ff-a57e-4d1125309c09/image.png" alt="Gradient_Descent"></p>
<ol>
<li>Batch Gradient Descent
한 번에 모든 데이터를 사용해 gradient를 계산한다. </li>
<li>Stochastic Gradient Descent
데이터 중 하나의 subset만 이용해 gradient를 계산한다. (병렬 계산에서 주로 쓰인다.)</li>
<li>Mini-batch Gradient Descent
BGD와 SGD의 절충안으로 여러 subset을 이용해 gradient를 계산한다.</li>
<li>Gradient Descent Methods</li>
<li>1 Gradient Descent
<img src="https://velog.velcdn.com/images/soo-im/post/d2ce9ff6-9ed3-41fa-8e44-d780276f865e/image.png" alt="Gradient_Descent">
$W_{t}$가 Neural Network의 weight(파라미터)라고 할 때, gradient descent는 gradient $g$를 learning rate $\eta$만큼 곱해서 빼주어 파라미터를 조정한다. 즉 Loss Function을 최소화하는 방향으로 파라미터를 이동시키는 것이다.
문제는 $\eta$를 결정하는 것이 어렵다는 것이다. 너무 크면 최적값을 찾지 못할 수도 있고, 너무 작으면 수렴하지 않을 수 있다.
이러한 단점을 보완하기 위한 여러 가지 방법을 아래에서 다룬다. </li>
<li>2 Momentum
<img src="https://velog.velcdn.com/images/soo-im/post/b47d7b65-4df4-46cf-8656-f4d871be79c3/image.png" alt="Momentum">
문자 그대로 &#39;관성&#39;을 이용한 방식. 이전 gradient 방향이 정해지면 다음 gradient 방향도 영향을 받는다.
gradient $g_{t}$만 이용하는 것이 아니라 이전 gradient $g_{t-1}$를 일부 가지고 있는 $\beta a_{t}$를 같이 이용해서 (accumulated) gradient descent한다.
gradient가 매 step마다 발산해도 어느 정도 방향성을 유지시켜준다는 장점이 있다.</li>
<li>3 Nesterov Accelerated Gradient
<img src="https://velog.velcdn.com/images/soo-im/post/edec8922-ecf6-4765-a106-477402f4bcb9/image.png" alt="NAG_Momeuntum">
<img src="https://velog.velcdn.com/images/soo-im/post/58204550-a5f7-44db-a41a-7d6d19c036f2/image.png" alt="NAG_Momeuntum">
Momentum과 유사하게 accumulated gradient descent하는 방식이지만 lookahead gradient를 상용한다는 점이 다르다.
Momentum은 실제로 descent를 한 다음에 그 지점에서의 gradient를 계산하여 $a_{t}$를 계산하지만, NAG는 실제로 이동하는 대신 그 예상 도착 지점의 gradient를 구해서 그것을 보정한만큼 $a_{t}$를 계산한다.
Momeuntum보다 빠르게 수렴한다는 장점이 있다.</li>
<li>4 Adagrad
<img src="https://velog.velcdn.com/images/soo-im/post/ab9017d7-a6fc-4e99-a6f2-822c1542b926/image.png" alt="Adagrad">
각 파라미터가 얼마나 변동했는지 저장한 다음, 지금까지 많이 변한 파라미터는 적게 변화시키고 적게 변한 파라미터는 많이 변화시키는 방법이다.
위 식에서 $G_{t}$가 이전 gradient의 제곱 합이다. 즉 이전에 많이 변동했을수록 gradient descent가 줄어드는 방식이다.
결국 $G_{t}$는 커질수밖에 없기 때문에, 시간이 지날수록 학습이 느려지는 단점을 가진다.</li>
<li>5 Adadelta
<img src="https://velog.velcdn.com/images/soo-im/post/c81bacce-84ac-440e-9b12-3029f71ea865/image.png" alt="Adadelta">
Adagrad의 $G_{t}$가 무한히 커지는 것을 방지하는 방법이다. 과거의 gradient를 모두 합산하는 방법 대신 gradient 이동평균을 사용해 $G_{t}$를 계산한다. 즉 최근의 gradient가 더 많이 반영되고, 오래된 gradient의 비중은 지수적으로 감소함으로써 Adagrad의 단점을 보완할 수 있다.
또다른 특징은 learning rate $\eta$가 없다는 점이다.</li>
<li>5 Adam
<img src="https://velog.velcdn.com/images/soo-im/post/c5576aa3-ac94-431d-a91a-42262be56118/image.png" alt="Adam">
momentm과 이전 gradient의 제곱 합을 모두 혼합한 방법이다. 일반적으로 많이 사용하는 방식이다.</li>
</ol>
<h2 id="2023-05-23">2023-05-23</h2>
<h3 id="regularization">Regularization</h3>
<p>학습을 방해함으로써 과적합을 방지하는 기법.</p>
<ol>
<li>Early Stopping
<img src="https://velog.velcdn.com/images/soo-im/post/b14e6377-5e94-4d36-b5b4-44f0febb9d65/image.png" alt="">
validation error가 커지는 시점에서 학습을 멈추는 방법.</li>
<li>Parameter Norm Penalty
파라미터가 너무 커지지 않게 조절하는 방법으로, 전체 파라미터의 제곱 합이 일정 크기를 넘지 않도록 한다.</li>
<li>Data Augmentation
<img src="https://velog.velcdn.com/images/soo-im/post/ba8e4f33-970a-482b-b8e0-9175183c9cc2/image.png" alt="">
(어라 네이버 강의인데) 데이터는 다다익선이기 때문에 데이터를 변형해서 학습셋으로 추가하는 방법.</li>
<li>Noise Robustness
<img src="https://velog.velcdn.com/images/soo-im/post/613ce76d-b8a9-4d31-83b0-4ee0ff947d76/image.png" alt="">
학습셋에 노이즈를 추가하는 방법. Data Augmentation과 다른 점은 노이즈를 입력 데이터에만 넣는 것이 아니라 파라미터에 넣기도 한다.</li>
<li>Label Smoothing
<img src="https://velog.velcdn.com/images/soo-im/post/e187bd6d-5e36-4ae6-ac01-21c545792526/image.png" alt="">
학습데이터 두 개를 뽑아서 섞는 것. 분류 문제에서 많이 쓰이는 방법.</li>
<li>Dropout
<img src="https://velog.velcdn.com/images/soo-im/post/c8c1fb3e-8c55-4c47-91b8-b214a3331c89/image.png" alt="">
임의의 뉴런을 0으로 바꾸는 것. 각각의 뉴런의 robust feature을 얻을 수 있다.</li>
<li>Batch Normalization
특정 레이어의 파라미터의 통계치를 정규화하는 것. 예를 들어 한 레이어가 천 개의 파라미터를 가지는데, 이 파라미터를 정규화하는 것이다(평균 빼고 표준편차 나누고). </li>
</ol>
<h2 id="2023-05-25-복습">2023-05-25 (복습)</h2>
<h3 id="non-linear-activation-function">Non-linear Activation Function</h3>
<ol>
<li>선형 변환: $W^{T}x=y$</li>
<li>affine 변환: $W^{T}x+b=y$</li>
<li>Non-linear Activation Function: $\sigma(W^{T}x+b)=y$</li>
</ol>
<p>→ Non-linear Activation Function이 딥러닝의 하나의 레이어가 된다. 
레이어를 거치면 Non-linear term을 거치면서 X 공간이 찌그러진다. 공간을 적절히 찌그러트려서 (classification의 경우) 선형 함수로도 X를 분류할 수 있도록 만드는 것이 딥러닝이다.</p>
<h3 id="gradient-descent-1">Gradient Descent</h3>
<p>loss function을 줄이기 위한 $w$를 찾는 Gradient Descent는 아래 그림으로 설명할 수 있다.
<img src="https://velog.velcdn.com/images/soo-im/post/4b7702b8-c061-417e-a124-529351153cf6/image.png" alt=""></p>
<h2 id="2023-05-27">2023-05-27</h2>
<h3 id="convolution">Convolution</h3>
<p>CNN의 Convolution은 signal processing에서 사용하는 도구를 말한다. 두 가지 신호의 시간 지연된 합을 구하는 것을 의미하고, 노이즈 제거나 주파수 변환하는 등의 작업에 사용한다.
이미지에서의 Convolution은 입력 이미지를 &#39;필터&#39;에 곱하는 것을 말한다.
<img src="https://velog.velcdn.com/images/soo-im/post/59a50bd6-c910-4773-b832-9d2909438215/image.png" alt="">
그림에서 3x3 필터를 7x7 이미지에 곱함으로써 5x5의 convolution된 이미지를 얻을 수 있다.
필터의 모양에 따라서 output은 달라질 수 있다. 예를 들어 3x3 필터 내 값이 모두 1/9이라고 생각해보자. 그러면 하나의 output 셀은 아홉 개의 칸의 평균값이 들어가 있다. 이 필터를 사용하면 이미지는 blur 처리가 되어 나올 것이다.
이런 식으로 필터를 조정하면서 이미지를 흐리게 하거나, 외곽선을 강조하는 등의 output을 얻을 수 있다.</p>
<h3 id="rgb-image-convolution">RGB Image Convolution</h3>
<p>RGB는 R/G/B 3가지 채널을 가진다. 이 경우 필터도 3가지 채널을 가지게 하면 하나의 채널 output을 얻을 수 있다.
<img src="https://velog.velcdn.com/images/soo-im/post/354c8ab5-0083-4e24-9bf4-f4663e1ca05b/image.png" alt="">
여러 개의 채널을 가지는 output(feature map)은 어떻게 얻을 수 있을까? 필터를 여러 번 적용하면 된다. 필터의 채널 수는 input과 동일하고, 그 필터를 몇 개를 사용하느냐에 따라 output의 채널 수가 결정된다.
아래의 경우 convolution에 필요한 parameter 개수는 5x5x3x4개이다.
<img src="https://velog.velcdn.com/images/soo-im/post/e9f04a56-0aaf-4937-a3db-0eb72a0ea4ad/image.png" alt="">
non-linear activation을 적용하는 경우도 있다.
<img src="https://velog.velcdn.com/images/soo-im/post/4d7efd15-5563-4fdf-ba10-f248a3c17275/image.png" alt=""></p>
<h3 id="cnn">CNN</h3>
<p>CNN은 이미지의 특징을 추출한 convolution layer, 이미지의 사이즈와 특징을 축소하는 pooling layer, 이미지 분류에 최종적으로 사용하는 fully connected layer로 구성되어 있다.
<img src="https://velog.velcdn.com/images/soo-im/post/229cff2d-0ad4-4a81-a0de-03b7797bdbd5/image.png" alt="">
이 때 fully connected layer의 채널이 굉장히 크기 때문에 이 과정에서 parameter 개수가 엄청나게 늘어난다. 그래서 최근에는 fully connected layer의 크기를 줄이려는 노력을 하고 있다.</p>
<blockquote>
<p><strong>그럼  convolution layer에서 분류하면 안돼?</strong>
convolution layer 대신 fully connected layer을 사용하는 이유는 과적합을 방지할 수 있기 때문이다. convolution은 특징만을 강조하기 때문에 이 layer을 사용해서 분류를 하면 노이즈에 취약해진다. fully connected layer을 사용하면 이미지의 특징과 이미지의 전체적인 구조를 고려할 수 있어 과적합을 방지할 수 있다.</p>
</blockquote>
<h3 id="stride">stride</h3>
<p>stride는 필터가 이미지 상에서 한 번 이동할 때 얼마나 이동하는지를 의미한다. moving average의 window를 생각하면 된다.
<img src="https://velog.velcdn.com/images/soo-im/post/16f33151-859e-438d-87db-f0b12c1b05ec/image.png" alt="">
input이 7이고, 필터가 3이라고 가정하자. stride=1일 때에는 한 칸씩 움직이기 때문에 output이 5가 나온다. 반면 2일 때에는 두 칸을 이동해서 output이 3이 된다.</p>
<h3 id="padding">padding</h3>
<p>필터를 적용하면 불가피하게 이미지의 가장자리 정보를 잃게 된다. 이 때 정보 손실을 막기 위해 맨 끝에 가상의 정보를 추가하는 것을 padding이라고 한다. 이렇게 하면 output의 dimension이 input과 동일하게 유지된다.
<img src="https://velog.velcdn.com/images/soo-im/post/4ffe6e10-ecab-45a6-960e-24ee5eb4e9c1/image.png" alt=""></p>
<h2 id="2023-05-28">2023-05-28</h2>
<h3 id="modern-cnn">Modern CNN</h3>
<p>CNN 발전단계에서 얻은 교훈들(갈수록 layer의 개수는 많아지고 parameter 개수는 줄어듬)</p>
<ol>
<li>동일한 size의 output을 얻을 때, parameter 개수 관점에서 5x5 필터를 한 번 사용하는 것보다 3x3 필터를 두 번 사용하는 것이 이득이다(5x5&gt;3x3x2).
<img src="https://velog.velcdn.com/images/soo-im/post/1a934c2b-95cf-4e71-b1a3-c3cacd2b6cfd/image.png" alt=""></li>
<li>동일한 size의 output을 얻을 때, 1x1 convolution을 이용해 채널 개수를 줄이면 parameter 개수를 줄이는 데 유리하다(3x3x128x128&gt;1x1x128x32+3x3x32x128).
<img src="https://velog.velcdn.com/images/soo-im/post/dcc31f0c-b960-4213-ad0d-e5b1993b6299/image.png" alt=""></li>
</ol>
<h3 id="semantic-segmentation">Semantic Segmentation</h3>
<ol>
<li>semantic segmentation란?
이미지를 통째로 강아지/고양이로 분류하는 것이 아니라, 이미지 중에서 특정 부분이 강아지이고/특정 부분이 사람이고...를 분류하는 문제. 자율주행 등에서 많이 사용한다.</li>
<li>Fully Convolutional Network
<img src="https://velog.velcdn.com/images/soo-im/post/fbb8f5d2-a056-4d7e-980c-a4b74de7e68e/image.png" alt="">
기존 CNN에서 fully connected layer을 제거하는 것. 좌측은 입력 이미지의 특징을 1차원 배열로 변환해서(flat) fully connected layer(dense)를 만든다. 우측은 1차원으로 변환하지 않고 convolution을 이용해서 만든다. 사실상 parameter의 개수는 차이가 없다.
<img src="https://velog.velcdn.com/images/soo-im/post/a80e6c6e-ad69-420a-b2dd-d52c83cc0eee/image.png" alt="">
왜 굳이 fully convolution layer을 만드는걸까? convolution layer는 입력 이미지와 동일한 크기의 feature map이다. 이렇게 하면 heatmap처럼 입력 이미지에서 어느 픽셀에 분류 대상이 있는지 강조가 된다.</li>
</ol>
<h2 id="2023-05-29">2023-05-29</h2>
<h3 id="sequential-model">Sequential Model</h3>
<p>sequential data는 대화, 동작 등 연속된 데이터를 말한다. 데이터의 길이가 정해지지 않았기 때문에 CNN과 같은 방법을 사용할 수 없다.</p>
<ol>
<li>Autoregressive model
다음에 오는 데이터를 예측한다고 하면 가장 대표적인 방법이 Autoregressive model이다. 이전의 n개 step 데이터를 고려하는 방법이다. n=1로 두는 것을 Markov model이라고 한다.</li>
<li>Latent autoregressive model
과거의 정보를 요약한 hidden state를 두고 여기에만 의존하여 다음 step을 예측하는 모델이다.</li>
</ol>
<h3 id="recurrent-neural-network">Recurrent Neural Network</h3>
<p><img src="https://velog.velcdn.com/images/soo-im/post/b31d535b-0a06-4d75-9231-49bb625de109/image.png" alt="">
이전 step의 정보를 이어받아서 학습하는 구조이다. RNN의 가장 큰 단점은 short-term dependencies를 가진다는 점이다. step을 반복할수록 먼 과거의 정보는 희석된다. 즉 장기기억이 어렵다.</p>
<h3 id="long-short-term-memory">Long Short Term Memory</h3>
<p>기존 RNN의 short-term dependency 문제를 해결하기 위해 나온 모델. 기존 모델은 이전 step의 hidden state($h_{t}$)이 다음 step 계산에 쓰이지만, 이 모델에서는 $h_{t}$와 함께 모델 output으로 출력되지 않는 &#39;cell state&#39;를 함께 사용한다. &#39;cell state&#39;는 이전 time step의 내용을 요약한 정보이다.
<img src="https://velog.velcdn.com/images/soo-im/post/447fd62e-5960-41e1-9c98-71be46598ef9/image.png" alt="">
LSTM에는 gate라는 개념이 있는데, 각각에 대해서 알아보자.</p>
<ol>
<li>Forget gate
어느 정보를 버릴지 결정한다. 이 gate에 들어가는 것은 현재 step의 input $X_{t}$와 이전 step의 output $h_{t}$이다. activation function을 통해 둘을 조합하여 이전 step 정보 중 어떤 것을 버릴지 결정한다.
<img src="https://velog.velcdn.com/images/soo-im/post/242094fd-3649-4146-be2e-754b8ec9df3c/image.png" alt=""></li>
<li>Input gate
어느 정보를 cell state로 저장할지 결정한다. 마찬가지로 현재 step의 input $X_{t}$와 이전 step의 output $h_{t}$이 gate에 들어간다.
<img src="https://velog.velcdn.com/images/soo-im/post/51fa1403-a76b-4dbc-a7d0-744cf3c2c03d/image.png" alt=""></li>
<li>Output gate
input gate를 통과한 정보를 이용해 cell state를 업데이트한다. 그리고 업데이트된 cell state에서 어떤 것을 출력할지 output gate에서 결정한다. 
<img src="https://velog.velcdn.com/images/soo-im/post/f9339670-8c3a-491d-bc08-d4045b259745/image.png" alt=""></li>
</ol>
<h3 id="gated-recurrent-unit">Gated Recurrent Unit</h3>
<p>LSTM과 달리 gate가 2개 이고, cell state를 별도로 생성하지 않고 hidden state를 곧바로 다음 cell에서 사용한다. LSTM보다 적은 parameter을 사용한다.
<img src="https://velog.velcdn.com/images/soo-im/post/f5cc8fa0-76c7-4a11-908e-27b417cb705e/image.png" alt=""></p>
<h2 id="2023-05-30">2023-05-30</h2>
<h3 id="transformer">Transformer</h3>
<p>&#39;attention&#39;이라는 개념을 사용하는 모델. sequnetial data 뿐 아니라 이미지 분류 등 광범위하게 쓰인다.</p>
<p>RNN과 다르게 이전 step을 정보를 받아 다음 step을 계산하는 방식이 아니다. 그래서 &#39;I am a student&#39;라는 입력을 받으면 RNN은 &#39;I&#39;, &#39;am&#39;, &#39;a&#39;, &#39;student&#39;를 하나씩 step으로 처리하지만 Transformer는 &#39;I am a student&#39;를 통째로 처리한다.
<img src="https://velog.velcdn.com/images/soo-im/post/d0f4c8e2-3a9b-476a-b70b-c75a93b5a53d/image.png" alt="">
번역하는 문제를 생각하면 input을 다른 언어의 output으로 만드는 모델이 필요하다.
input을 처리하는 encoder와 output을 연산하는 decoder가 서로 parameter는 공유하지 않고 각각 stack 되어있는 구조로 되어있다.
<img src="https://velog.velcdn.com/images/soo-im/post/a9b126a7-0679-4ff9-9b1e-55694ffb7aee/image.png" alt="">
이 때 encoder 내부에는 self-attention이 있다. self-attention은 연속된 데이터 각각을 vector로 변환하는데, 이 때 데이터 하나만 사용하는 것이 아니라 뒤에 나오는 데이터를 함께 사용한다. 즉 &#39;I&#39;를 벡터로 변환할 때 &#39;am&#39;, &#39;a&#39;, &#39;student&#39;도 고려한다는 것이다.
<img src="https://velog.velcdn.com/images/soo-im/post/9f8be40a-ce1e-4130-bbfb-13ef42c50c77/image.png" alt="">
이것이 중요한 이유는 &#39;Soo is tired because she slept late last night.&#39;이라는 문장이 올 때 &#39;she&#39;가 앞의 문장 중 어떤 단어와 연관이 깊은지 self-attention을 통해 학습하기 때문이다.</p>
<p>(여기부터는 진짜 무슨 소리인가 싶은...)
단어를 벡터로 변환한 것을 $X$라고 하면 self-attention 내부에서 각각 적절한 행렬을 곱해 queries $Q$, keys $K$, values $V$라는 벡터를 만든다. 그리고 이 벡터 간의 연산을 이용해 각 단어(벡터)가 서로 어느 정도의 중요도를 가지는지 점수 $Z$를 연산한다.
<img src="https://velog.velcdn.com/images/soo-im/post/3a7b037c-98cd-403f-9df9-15f4e146802e/image.png" alt="">
<img src="https://velog.velcdn.com/images/soo-im/post/82e580b3-8f73-4750-a2f3-23bf704b1fd8/image.png" alt=""></p>
<p>이러한 특성 때문에 input이 정해지면 output도 고정되어 있는 CNN 등과 다르게 Transformer는 input 옆에 어떤 input이 함께 들어오느냐에 따라 output이 달라질 수 있다.
이렇게 데이터 간의 상관성을 연산하는 방식 때문에 n개의 단어가 들어왔을 때 RNN은 연산을 n번 하면 되지만, Transformer는 n의 제곱 번을 연산해야 한다. </p>
<p>Transformer는 처음에 자연어 번역에 주로 쓰였지만 요즘은 이미지 처리 등에도 사용하고 있다.</p>
<h2 id="2023-06-01복습">2023-06-01(복습)</h2>
<ol>
<li>CNN</li>
</ol>
<ul>
<li>Convolution: 필터를 sliding window로 이동하면서 이미지의 특징을 추출한다.</li>
<li>Pooling: AVG Pooling, MAX Pooling 등을 이용해서 정보 손실을 최소화하면서 이미지의 사이즈를 줄인다.</li>
</ul>
<p>-&gt; 듣다보니 Convolution이랑 Pooling이 약간 헷갈리는데(둘 다 사각형으로 필터거는 것처럼 보여서), 이 블로그를 보고 좀 더 내 방식대로 정리를 하면</p>
<ul>
<li>Convolution: &#39;합성곱&#39;이라는 말 그대로 이미지를 특정 행렬과 곱하는 것. 그리고 sliding window로 stride마다 움직여가면서 곱한다.</li>
<li>Pooling: 사이즈를 줄이기 위해 이미지의 구간을 적당히 나눈 다음 그 구간마다 최대값/평균값/최소값 등 하나의 값만 꺼내서 그 이미지 구간을 대표하게 만드는 것. Convolution처럼 곱하거나, sliding window로 이동하는 것이 아니다.</li>
</ul>
<ol start="2">
<li>RNN</li>
</ol>
<ul>
<li>이전 학습에서 연산한 hidden 값을 다음 step의 input으로 넣는 방식</li>
</ul>
<ol start="3">
<li>LSTM</li>
</ol>
<ul>
<li>RNN의 hidden에 장기기억을 추가하는 개념(RNN의 hidden은 시간이 지날수록 back prop으로 초기 기억이 급격히 사라짐)</li>
<li>모든 기억을 장기 저장할 수 없기 때문에 적절히 삭제/추가해서 장기기억을 선별한다.</li>
<li>그 장기기억을 hidden에 고려해서 다음 step에 사용한다.</li>
</ul>
<ol start="4">
<li>Attention
...이번에도 이해를 못했다... 토요일에 다시 들어보자 😇</li>
</ol>
<h2 id="2023-06-20">2023-06-20</h2>
<h3 id="양자컴퓨팅">양자컴퓨팅</h3>
<p>모두의연구소 오픈 강연을 듣고 왔다! 시작 5분만에 이해가 안 되었지만 ^.^ ... 챗지피티와 함께 정리~</p>
<ol>
<li>양자컴퓨터
양자역학 기반의 알고리즘을 가지는 컴퓨팅을 말한다. 전자기학 기반의 Bit와 다르게 Qubit을 기본 단위로 가진다. Qubit은 0, 1 대신 &#39;중첩&#39;된 값을 가질 수 있다. 양자역학과 마찬가지로 측정을 하는 순간 Qubit의 값은 하나로 결정이 된다.</li>
</ol>
<blockquote>
<p><strong>&#39;중첩&#39;된 값이 정확히 뭐야? 여러 개의 값을 하나의 메모리에 넣는다는 거야?</strong>
여기서 &#39;중첩&#39;은 고전적인 의미에서 여러 개의 값을 저장한다는 의미가 아니다. 파동함수를 이용해 Qubit의 state에 확률 진폭 함수를 할당한다. 그리고 측정을 하는 순간 확률 진폭에 의해 확률이 결정되어 하나의 possible state로 &#39;붕괴collapse&#39;되고 Qubit는 고전적인 의미의 Bit 값을 가지게 된다.
(여기서 더 파고들어도 이해는 안 되니 이 선에서 정리하자)</p>
</blockquote>
<blockquote>
<p><strong>만약 이미지 분류 문제를 양자컴퓨터로 푼다면?</strong></p>
</blockquote>
<ol>
<li><p>Qubit 수 결정: 이미지의 각 픽셀의 값(0~255)을 Qubit state에 매핑해야 한다. 필요한 Qubit의 수는 픽셀 값의 범위보다 크거나 같은 2의 거듭제곱값을 통해 결정한다. 이 경우 2^8=256이므로 8개의 Qubit가 필요하다.</p>
</li>
<li><p>Qubit 초기화: 상태 |0⟩로 초기화된 8개 Qubit 세트가 있다.</p>
</li>
<li><p>Quantum Encoding: 0~255 사이의 픽셀 값을 Qubit의 quantum state에 매핑한다. 예를 들어 픽셀 값 128을 이진 표현 |10000000⟩로 인코딩한다.</p>
</li>
<li><p>Quantum Processing: 이미지가 Qubit로 인코딩 된 후 quantum 알고리즘과 quantum gates를 이용하여 quantum state를 계산할 수 있다(하나하나가 인코딩된 게 어떻게 중첩이 된다는건지 왜 빠르다는건지 알아보려고 했으나 모르겠다... 감이 안온다...).</p>
</li>
<li><p>문제 풀이: Quantum Processing이 완료되면 Qubit로부터 classical information(아마도 Bit)을 얻기 위해 측정measurement을 수행한다. 측정한 결과를 이용해 분류 문제를 푼다.</p>
</li>
<li><p>왜 빠를까?
고전컴퓨터에서는 Bit로 기본 논리 게이트를 처리하면서 알고리즘을 구현한다. (똑바로 이해한 게 맞나) 문제가 복잡해지면 알고리즘의 복잡도가 올라가고 연산 시간이 오래 걸린다.
양자컴퓨터는 Qubit을 사용해서 고전컴퓨터의 논리 게이트를 더 단순하게 표현할 수 있기 때문에 연산 시간이 더 적게 걸린다.
고전컴퓨터보다 시간복잡도 면에서 성능이 좋다고 알려진 알고리즘은 (1) 소인수 분해, (2) 검색, (3) 선형연산($Ax=b$)이다.</p>
</li>
<li><p>정말 빠를까?
&#39;양자컴퓨터를 사용하면 고전컴퓨터가 xxx년 걸리는 문제를 xx초만에!&#39;라는 문구로 유명하지만 실제 상황과는 약간의 괴리가 있다고 한다. 일부 알고리즘에서 성능이 좋다고 수학적으로 증명이 되었지만 exponential한 수준이 아니라 polynomial한 수준이라고 알려져 있고, 고전컴퓨터에서도 어느 정도 따라잡을 수 있어 둘 간의 격차가 매우 크다고 보기는 어렵다.
그래서 최근에는 이론적인 복잡도 연구보다도 실증적인 문제(예: 실제로 실험을 해야만 하는 sampling 같은)에 집중하는 추세라고 한다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[12월 TIL]]></title>
            <link>https://velog.io/@soo-im/12%EC%9B%94-TIL</link>
            <guid>https://velog.io/@soo-im/12%EC%9B%94-TIL</guid>
            <pubDate>Tue, 13 Dec 2022 15:15:26 GMT</pubDate>
            <description><![CDATA[<p>테스트로 야근하는 나날... 언젠가 끝나겠지? 🥺 매일은 어려워도 회사에서 쓴 거라도 적어보자
(저번 달에 못다한 ARIMA랑 wrapper도 잊지말자...)</p>
<h2 id="2022-12-13">2022-12-13</h2>
<h3 id="python-multiple-returns-in-map-for-dataframe">Python multiple returns in map for DataFrame</h3>
<p>나의 상황: 여러 개의 값을 반환하는 함수를 pd.DataFrame 의 특정 열에 <code>map</code>으로 실행해서, 그 결과 값을 각각 열로 추가하고 싶다.
(해결법은 맨 밑에!)</p>
<h4 id="온라인에-나온-해결법">온라인에 나온 해결법:</h4>
<ol>
<li>여러 개의 값을 반환하는 함수에서 반환값을 각각 받는 법 자체는 많다. 아래는 대표적인 예시 두 가지.<pre><code class="language-python"># 1번: 반환값 개수대로 변수 선언하기
def my_func():
 ...
 return &quot;hello&quot;, &quot;world&quot;
r1, r2 = my_func()
# print(r1) -&gt; &quot;hello&quot;
# print(r2) -&gt;  &quot;world&quot;</code></pre>
<pre><code class="language-python"># 2번: 반환값을 list로 묶어 반환하기
def my_func():
 ...
 return [&quot;hello&quot;, &quot;world&quot;]
r1 = my_func()
# print(r1) -&gt; [&quot;hello&quot;, &quot;world&quot;]</code></pre>
</li>
<li><code>map</code>을 이용하는 경우에는 반환값이 map object로 나오기 때문에 약간의 가공이 필요하다. 참고한 <a href="https://www.daleseo.com/python-map/">링크1</a>(map object), <a href="https://stackoverflow.com/questions/7015307/mapfunction-sequence-where-function-returns-two-values">링크2</a>(multiple returns with map).</li>
</ol>
<pre><code class="language-python">def function(x):
    return x, x+1

sequence = range(5)

print(&quot;map object&quot;)
print(map(function, sequence))

print(&quot;\nelements in map object&quot;)
for i in map(function, sequence): print(i)

print(&quot;\nlist&quot;)
print(list(map(function, sequence)))
</code></pre>
<pre><code class="language-css">/*출력 결과*/

map object
&lt;map object at 0x7f504ad91370&gt;

/*map은 map object를 반환하기 때문에 각 element를 직접 출력해야 내부의 값을 볼 수 있다.*/
elements in map object
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)

/*map object를 list나 tuple로 변환하면 다음과 같이 나온다.*/
list
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]</code></pre>
<ol start="3">
<li>위에서 첫번째 반환값과 두 번째 반환값을 각각 묶고 싶다면 내장 함수인 <code>zip</code>을 사용하면 된다. <code>zip</code>은 길이가 같은 여러 개의 iterable 변수를 index 기준으로 묶어서 출력해주는 함수이다. <pre><code class="language-python">print(&quot;\nzip&quot;)
a = zip(*map(function, sequence))
for i in a: print(i)</code></pre>
<pre><code class="language-css">/*map object 내부의 (0,1) (1,2)..을 index 기준으로 묶어서 출력한다.*/
zip(0, 1, 2, 3, 4)
(1, 2, 3, 4, 5)</code></pre>
</li>
</ol>
<h4 id="나의-해결법">나의 해결법:</h4>
<p><code>df[&#39;col1&#39;].map(lambda x: my_func(x))</code>와 같이 특정 DataFrame에 실행해서 그 결과를 df[&#39;col2&#39;], df[&#39;col3&#39;]로 추가하는 방법은 아래와 같다.</p>
<pre><code class="language-python"># multiple return을 list로 묶어서 반환
def my_func():
    ...
    return [r1, r2]

returns = df[&#39;col1&#39;].map(lambda x: my_func(x))
returns_df = pd.DataFrame(returns.to_list(), columns=[&#39;col2&#39;, &#39;col3&#39;])

df[&#39;col2&#39;] = returns_df[&#39;col2&#39;]</code></pre>
<p>(쓰고 나니 뭔가 최선의 방법은 아닌 것 같지만...)</p>
<p><code>returns</code>는 map object가 아니라, 각 행별로 실행한 반환값 list를(예:<code>[0,1]</code>) 각 index마다 가지는 pd.Series가 된다.
나는 각 index 내부의 list를 DataFrame으로 변환하고 싶기 때문에, pd.Series를 <code>to_list()</code>를 이용해 먼저 nested list로 바꾸어주었다(<code>[[0,1],[1,2],...]</code>) 그리고 nested list에 <code>pd.DataFrame</code>을 사용해서 복수 개의 return 값을 각각의 열로 가지는 DataFrame을 만들 수 있다.
<img src="https://velog.velcdn.com/images/soo-im/post/a2577339-756f-41a1-9604-76f82744fde3/image.png" alt=""></p>
<h2 id="2022-12-15">2022-12-15</h2>
<h3 id="왜-decorator-안에-wrapper을-정의하나-2">왜 decorator 안에 wrapper을 정의하나? (2)</h3>
<p>지난 달 <a href="https://velog.io/@soo-im/11%EC%9B%94-TIL">게시글</a>에서 가졌던 의문 이어서~</p>
<p>아래는 wrapper을 써야 하는 이유를 보여주는 코드이다. 두 번째의 &quot;wrapper가 없는 decorator&quot; 코드를 보고 &#39;그러면 아예 return 자체를 <code>result</code>로 하면 되는 것 아니야?&#39; 라고 생각했다.</p>
<pre><code class="language-python"># wrapper가 있는 decorator(원본)
def timer(inner_func):
    def wrapper_func(*args, **kwargs):
        start_time = time.time()
        result = inner_func(*args, **kwargs)
        end_time = time.time()
        print(f&quot;Takes {end_time-start_time} sec&quot;)
        return result
    return wrapper_func

# wrapper가 없는 decorator
def timer(inner_func):
    start_time = time.time()
    result = inner_func(*args, **kwargs)
    end_time = time.time()
    print(f&quot;Takes {end_time-start_time} sec&quot;)
    return None</code></pre>
<pre><code class="language-python"># 그럼 이렇게 하면? 이라는 의문이 드러난 코드
def decorator_func(inner_func):
    # do something
    result = inner_func(*args, **kwargs)
    # do something
    return result</code></pre>
<p>결론을 말하면 마지막은 물론이고 &quot;wrapper가 없는 decorator&quot; 코드도 동작하지 않는다. 코드 실행 결과는 아래와 같다.</p>
<pre><code>Traceback (most recent call last):
  File &quot;&lt;string&gt;&quot;, line 11, in &lt;module&gt;
File &quot;&lt;string&gt;&quot;, line 5, in timer
NameError: name &#39;args&#39; is not defined</code></pre><p><code>inner_func</code>에 넘겨준 인자 args가 정의되지 않았다고 나온다.</p>
<p>음... 이유를 계속 추측해 보려고 하는데, 완벽히 이해가 가지는 않는다. <code>timer(inner_func)</code>이기 때문에, <code>inner_func</code>에 넘겨줄 args가 함수 내부에서 정의되지 않았다는 건 이해가 간다. 그런데 그걸 <code>wrapper_func</code>의 인자로 정의한 것 만으로도 해결이 된다는게 좀 의문이다.
<code>wrapper_func</code>에서 임의의 arg를 받아온다고 가정하면 그게 곧바로 <code>inner_func</code>으로 전달되는 형식인데. <code>wrapper_func</code>은 어떻게 <code>my_value = inner_func(*args)</code> 이런 식으로 호출한 <code>inner_func</code>의 인자를 직접 받아오는 건지 이해가 잘 안 간다.</p>
<p>이런저런 자료를 많이 찾아보았지만 정확히 이 의문에 대한 답은 찾을 수가 없어서... &#39;<code>inner_func</code>에 넘겨야 할 args가 정의되지 않았기 때문에 wrapper가 존재해야 한다&#39;로만 이해하고 넘어가야겠다. 🙁</p>
<h2 id="2022-12-18">2022-12-18</h2>
<h3 id="arima-forecasting">ARIMA Forecasting</h3>
<p><a href="https://velog.io/@soo-im/10%EC%9B%94-TIL">10월 강의</a>는 ARIMA forecasting 파트에서 예측값을 plot만 하는 방법만 제시했지만</p>
<pre><code class="language-python">from statsmodels.tsa.arima.model import ARIMA
from statsmodels.graphics.tsaplots import plot_predict

model = ARIMA(data, order=[1, 0, 0]) # 1차 AR
resesult = modelfit()

fig, ax = plt.subplots()
plot_predict(resesult, start=1000, end=1010, ax=ax) # 모델로 예측한 데이터 플롯</code></pre>
<p>이번 강의는 예측값과 오차범위를 pd.DataFrame으로 얻는 방법을 소개한다.</p>
<h4 id="in-sample-prediction">In-sample prediction</h4>
<p>fitting에 사용한 데이터 sample(여기서는 <code>data</code>) 내부에서 예측하는 방법이다. 2022-01-01~2022-12-31의 index를 가진 <code>data</code>에 ARIMA 모델을 fitting을 했다고 가정하자. 그 모델을 이용해 다시 2022-12-01부터 2022-12-31까지 마지막 30일간을 예측하려고 한다. 2022-12-01의 값은 <code>data</code>의 11-31을 이용해 예측하고, 12-02의 값은 <code>data</code>의 12-01 값을 이용해 예측하고... 한 step 이전의 데이터 sample <code>data</code>를 이용해 예측하는 방식을 In-sample prediction이라고 한다.</p>
<pre><code class="language-python">from statsmodels.tsa.arima.model import ARIMA

# fitting 단계까지는 위와 동일
model = ARIMA(data, order=[1, 0, 0])
resesult = model.fit()

# 이전 단계의 data sample 이용해 예측
forecast = results.get_prediction(start=-30)</code></pre>
<p><code>get_prediction</code>의 <code>start</code> 인자는 데이터 sample의 index 시작점을 가리킨다. <code>start=&#39;2022-12-01&#39;</code>로 두어도 동일한 결과를 얻을 수 있다.</p>
<p>예측 결과 object <code>forecast</code>는 예측의 중앙값과 오차항($\epsilon_t$)으로 인한 confidence range를 가지고 있다. 각각은 <code>.predicted_mean</code>과 <code>conf_int()</code>를 이용해 얻을 수 있다.
기존 데이터와 in-sample 예측값, 그리고 예측의 오차범위를 얻고 plot하는 방법은 아래와 같다.</p>
<pre><code class="language-python"># 예측 중앙값
mean_forecast = forecast.predicted_mean
# 예측 오차범위
confidence_intervals = forecast.conf_int()
lower_limits = confidence_intervals.loc[:,&#39;lower close&#39;]
upper_limits = confidence_intervals.loc[:,&#39;upper close&#39;]

# data와 예측값 plot
plt.plot(data.index, data, label=&#39;observed&#39;)
plt.plot(mean_forcast.index, mean_forcast.values)
plt.fill_between(lower_limits.index, lower_limits, upper_limits)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/soo-im/post/ed773f10-939a-471e-8cbe-3901db6d9bc4/image.png" alt="ARIMA_in-sample_prediction_with_confidence_interval"></p>
<h4 id="dynamic-prediction">Dynamic prediction</h4>
<p>In-sample과 달리 데이터 sample <code>data</code>가 아니라, 예측한 값에 이어서 다음 단계를 예측하는 방식을 Dynamic prediction이라고 한다. 예측값에 계속해서 오차($\epsilon_t$)가 더해지기 때문에 단계를 거듭할수록 오차 범위도 점점 커진다.</p>
<p>예측 방법은 위의 코드에서 <code>dynamic</code> 인자만 추가하면 된다.</p>
<pre><code class="language-python"># 이전 단계의 prediction 이용해 예측
forecast = results.get_prediction(start=-30, dynamic=True)</code></pre>
<p><img src="https://velog.velcdn.com/images/soo-im/post/a464cb5f-c1ac-403d-9322-0782ba174cf6/image.png" alt="ARIMA_dynamic_prediction_with_confidence_interval"></p>
<h2 id="2022-12-21">2022-12-21</h2>
<h3 id="arima">ARIMA</h3>
<p>ARIMA는 비정상성(non-stationarity)을 가지는 시계열에 대한 ARMA 예측을 돕는 패키지이다.</p>
<p>비정상성을 가지는 시계열 $X_t$을 ARMA 예측한다고 가정해보자. ARMA는 정상성을 전제로 하기 때문에, $X_t$를 그대로 사용하는 대신 n차 차분(differencing)을 취해야 한다. 
차분을 취한 시계열 $X_{t}&#39;$ 에 대해 ARMA 예측을 하면 그 결과는 $X_t$가 아닌 $X_{t}&#39;$의 예측이다. 이것을 이용해 $X_t$의 예측값을 구하려면  $X_{t}&#39;$를 시간에 따라 적분하면 된다.</p>
<p>요약하면 비정상성을 가진 시계열 $X_t$를 ARMA 예측하는 단계는 다음과 같다.</p>
<ol>
<li>$X_t$를 차분한다.</li>
<li>차분한 $X_{t}&#39;$을 이용해 ARMA 예측을 한다.</li>
<li>$X_{t}&#39;$의 예측값을 시간에 따라 적분하여 $X_t$의의 예측값을 구한다.</li>
</ol>
<p>그리고 이걸 한 번에 수행하는 모델이 있으니 바로 ARIMA(Autoregressive Integrated Moving Average) 모델이다. Integrated가 위에서 말한 차분을 의미한다.</p>
<h3 id="arima-forecasting-1">ARIMA Forecasting</h3>
<p>앞에서 사용한 ARIMA 패키지를 그대로 사용하고, <code>order = (p, d, q)</code>의 <code>d</code>에 원하는 차분 차수를 입력하면 된다(이전 강의에서는 ARMA 모델을 사용하느라 <code>d=0</code>으로 두었었다).</p>
<pre><code class="language-python">from statsmodels.tsa.arima.model import ARIMA

model = ARIMA(data, order=(2,1,1))
result = model.fit()
forecast = result.get_forecast(steps=10)
mean_forecast = forecast.predicted_mean</code></pre>
<blockquote>
<p><code>get_prediction</code> vs <code>get_forecast</code>
전자는 샘플 데이터 내외에서 모두 예측 가능하고, 후자는 샘플 데이터 외부에서만 예측이 가능하다. 즉 전자의 특수한 경우가 후자이다.</p>
</blockquote>
<p>이 때 <code>d</code>는 차분한 시계열이 정상성을 가지는 시점의 차분 차수로 결정하면 된다. 정상성 테스트는 앞에서 다룬 augmented Dicky-Fuller test를 사용하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[11월 TIL]]></title>
            <link>https://velog.io/@soo-im/11%EC%9B%94-TIL</link>
            <guid>https://velog.io/@soo-im/11%EC%9B%94-TIL</guid>
            <pubDate>Tue, 01 Nov 2022 15:26:01 GMT</pubDate>
            <description><![CDATA[<h2 id="2022-11-01">2022-11-01</h2>
<h3 id="scikit-learn-기초---fit--predict">scikit-learn 기초 - fit &amp; predict</h3>
<p>이전 단계에서 예측하려는 특성(features)을 <code>y</code>, features를 예측할 때 사용하려는 특성(samples)을 <code>X</code>로 정의했다. 이 때 <code>y</code>를 모의하는 <code>X</code> 모델을 fitting 하는 방법은 아래와 같다. (이 강의에서는 데이터를 분류하는 모델 중 하나인 <code>LinearSVC</code>를 이용해 꽃을 분류하는 모델을 만든다.)</p>
<pre><code class="language-python">from sklearn.svm import LinearSVC

X = data[[&#39;petal length (cm)&#39;, &#39;petal width (cm)&#39;]] # 꽃의 특성(꽃잎 길이, 너비)
y = data[[&#39;target&#39;]] # 꽃의 종류

# Fit the model
model = LinearSVC()
model.fit(X, y)</code></pre>
<blockquote>
<p>Why use double square brackets?
<code>X</code>, <code>y</code>를 할당할 때 두 개의 대괄호를 쓰는 이유는 <code>X</code>, <code>y</code>의 데이터 타입이 pd.DataFrame이어야 하기 때문이다. 단일 대괄호를 쓰면 pd.Series가 되기 때문에 바깥쪽에 한 번 더 대괄호를 씌워 pd.DataFrame으로 만든다.</p>
</blockquote>
<p>fitting한 모델을 이용해 새로운 samples <code>X_predict</code>의 features <code>y_predict</code>를 예측하는 방법은 아래와 같다.</p>
<pre><code class="language-python">X_predict = predict_data[[&#39;petal length (cm)&#39;, &#39;petal width (cm)&#39;]]
y_predict = model.predict(X_predict)</code></pre>
<p>이렇게 하면 <code>y_predict</code>에는 <code>X_predict</code>로 분류한 꽃의 종류가 array로 반환된다.</p>
<h2 id="2022-11-03">2022-11-03</h2>
<h3 id="오디오-데이터-처리">오디오 데이터 처리</h3>
<p>오디오 데이터(예: wav)는 <code>librosa</code> 패키지로 가공할 수 있다. 아래 코드는 <code>y</code>에 오디오 값(진폭)를 numpy array로 반환하고, <code>sr</code>에 초당 샘플수(아마도 주파수)를 반환한다.</p>
<pre><code class="language-python">import librosa as lr

audio, sfreq = lr.load(audio_files)

print(f&#39;Shape of samples: {audio.shape}&#39;)
print(f&#39;Number of samples: {audio[0]}&#39;)
print(f&#39;Frequency: {sfreq} Hz&#39;)
print(f&#39;Length of file: {audio.shape[0]/sfreq} sec&#39;)</code></pre>
<p>위 코드의 결과는 아래와 같다. 파일에는 총 174,980개의 오디오 진폭 값이 있고, 초당 22,050의 값이 있으며 파일의 전체 길이는 7.9초이다. <code>audio</code>의 채널이 여러 개인 경우에는 <code>audio.shape</code>의 값이 <code>(첫 번째 채널의 값, 두 번째 채널의 값, ...)</code> 이러한 형식으로 나온다.</p>
<pre><code class="language-python">Shape of samples: (174980,)
Number of samples: -0.006969843525439501
Frequency: 22050 Hz
Length of file: 7.935600907029478 sec</code></pre>
<p>오디오 진폭의 시계열을 그리려면 위 정보를 이용해 시간축 <code>time</code> array를 만들어 주면 된다.</p>
<pre><code class="language-python"># (위에 이어서)
# 위에서 구한 Length of time 까지의 배열을 만들어 시간축을 만든다. 채널이 하나이므로 audio.shape[0] 대신 len(audio)를 써도 된다.
time = np.arange(0, len(audio)) / sfreq 

fig, ax = plt.subplots()
ax.plot(time, audio)
ax.set(xlabel=&#39;Time (s)&#39;, ylabel=&#39;Sound Amplitude&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/soo-im/post/2db8c924-eac4-48d8-90d2-a19f1145d94f/image.png" alt="Audio_plot_with_librosa_package"></p>
<h2 id="2022-11-05">2022-11-05</h2>
<h3 id="subplots-with-for-loop">subplots with <code>for</code> loop</h3>
<p>보통 여러 개의 plot을 만들 때에는 <code>fig, axs = plt.subplots(2, 2)</code>와 같이 subplot을 생성한다. 만약 <code>data_set</code>이라는 np.array 안에 plot을 만드려는 np.array가 있다고 가정하다(즉 <code>data_set = [[a, b, c...], [A, B, C...], ...]</code>)) 이 때 각 ax에 loop를 이용해서 그림을 그리는 방법은 아래와 같다.</p>
<pre><code class="language-python">fig, axs = plt.subplots(3, 2)

for each_data, ax in zip(data_set, axs.T.ravel()):
    ax.plot(x_axis, each_data)</code></pre>
<p>이 때 <code>axs.T.ravel()</code>은 <code>axs</code>에 loop를 사용할 수 있도록 subplot 행렬을 1차원 array로 변환해준다.
이해를 돕기 위해서 <code>axs</code>, <code>axs.T</code>, <code>axs.T.ravel()</code>의 모양을 확인하면 다음과 같다.</p>
<pre><code class="language-python">&#39;&#39;&#39;
axs
[[0 1]
 [2 3]
 [4 5]]
&#39;&#39;&#39;
[[&lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt;]
 [&lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt;]
 [&lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt;]]
&#39;&#39;&#39;
axs.T
[[0 2 4]
 [1 3 5]]
&#39;&#39;&#39;
[[&lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt;]
 [&lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt;]]
 &#39;&#39;&#39;
 axs.T.ravel()
 [0 2 4 1 3 5]
 &#39;&#39;&#39;
[&lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt;
 &lt;AxesSubplot:&gt; &lt;AxesSubplot:&gt;]</code></pre>
<p>여기에서는 0 → 2 → 4 → 1 → ... 즉 좌측 subplot을 먼저 채우고 우측 subplot을 그리기 위해서 이렇게 한 것이다. 용도에 따라서 적절히 reshape한 다음 <code>ravle()</code>하면 된다.</p>
<h3 id="classifying-a-times-series">Classifying a times series</h3>
<p>noise가 많은 시계열을 머신러닝에 사용할 때에는 시계열 원본보다 시계열의 특성(진폭의 평균, 최대, 최소 등)을 이용하는 것이 유리할 수도 있다.</p>
<p>예를 들어 복수 개의 시계열 set인 <code>data_set</code>을 가지고 있다면 다음과 같이 moving average로 각 시계열의 노이즈를 줄인 뒤에 각 시계열의 평균, 표준편차, 최대값 등을 예측에 사용할 수 있다.</p>
<pre><code class="language-python">&#39;&#39;&#39;
data_set

                  1          2          3      4          5         6
time                                                                                                               
0.000e+00 -9.952e-04  2.808e-04  2.953e-03  0.005  4.332e-04  1.316e-03
4.535e-04 -3.381e-03  3.808e-04  3.034e-03  0.010  5.541e-04 -1.535e-04
...              ...        ...        ...    ...        ...        ...      
3.998e+00 -8.946e-05 -5.931e-03  2.474e-03  0.008  3.157e-04 -6.354e-03
3.998e+00 -1.119e-04 -4.839e-03  4.467e-03  0.008 -2.686e-04 -1.067e-02

&#39;&#39;&#39;
data_smooth = data_set.rolling(window = 10)
data_mean = np.mean(data_set, axis=1)
data_stds = np.std(data_set, axis=1)
data_max = np.max(data_set, axis=1)

X = np.column_stack([means, stds, maxs])
y = labels.reshape(-1, 1)</code></pre>
<h3 id="단순-교차-검증">단순 교차 검증</h3>
<p>참고: <a href="https://homeproject.tistory.com/6">블로그</a></p>
<p>일반적으로 머신러닝 성능을 측정할 때 학습셋과 예측셋(테스트셋)을 <code>X_train</code>, <code>X_test</code>, <code>y_train</code>, <code>y_test</code>로 나누어 확인한다.
다만 반복적으로 <code>X_train</code>을 이용해 학습을 시킬 경우 모델이 학습셋에 과적합될 위험이 있다. 이를 해결하기 위해 나온 방법이 &#39;교차 검증&#39;이다.</p>
<p>학습셋 전체를 사용하는 대신 학습셋을 임의의 <code>k</code>개로 나누어 &#39;폴드&#39;를 만든다. 이 중 하나의 폴드를 예측셋으로 사용하고 나머지를 학습셋으로 사용하면서 총 <code>k</code> 개의 모델을 생성한다. 이 때 각 모델의 정확도를 측정할 때 사용하는 매소드가 <code>cross_val_score</code>이다.</p>
<p><code>cross_val_score(model, sample, feature)</code>의 형식으로 사용하고 몇 개의 폴드를 만들 것인지 결정하는 <code>cv</code>를 선택 인자로 받는다. 예시는 다음과 같다.</p>
<pre><code class="language-python">from sklearn.model_selection import cross_val_score
model = LinearSVC()
scores = cross_val_score(model, X, y, cv=3) 

print(scores)
# [0.66666667 0.75 0.83333333 0.83333333 0.5]</code></pre>
<h2 id="2022-11-06">2022-11-06</h2>
<h3 id="spectogram-short-time-fourier-transform">Spectogram (Short Time Fourier Transform)</h3>
<p>출처: <a href="https://sanghyu.tistory.com/37">블로그</a></p>
<p>Spectogram은 (오디오) 시계열을 window 사이즈로 지나가면서 각 구간을 Foureir 변환 한 것을 말한다. 시계열 전체를 하지 않고 window 사이즈만큼 하는 이유는 오디오 신호가 긴 구간에서는 상대적으로 non-stationary, 짧은 구간에서는 stationary하다고 보기 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/f4381ffb-488e-4c00-ade9-ebe600480828/image.png" alt="Spectogram">
출처: DataCamp</p>
<p>여기서 가로축은 시간(window 구간), 세로축은 Fourier 변환 주파수, z축은 Fourier 변환 진폭을 의미한다. 즉 짙은 색으로 갈수록 해당 구간에서 해당 주파수를 가지는 sin/cos 파의 진폭이 크다는 의미이다.</p>
<p><code>stft</code>는 Spectogram을 반환하는 매소드이다. 그 결과는 <code>specshow</code>를 통해 가시화할 수 있다.</p>
<pre><code class="language-python">from librosa.core import stft, amplitude_to_db
from librosa.display import specshow
import matplotlib.pyplot as plt

HOP_LENGTH = 2**4
SIZE_WINDOW = 2**7
audio_spec = stft(audio, hop_length=HOP_LENGTH, n_fft=SIZE_WINDOW)

# 스펙토그램을 dB 스케일로 변환
spec_db = amplitude_to_db(audio_spec)

fig, axs = plt.subplots(2, 1, figsize=(10, 10), sharex=True)
axs[0].plot(time, audio)
specshow(spec_db, sr=sfreq, x_axis=&#39;time&#39;, y_axis=&#39;hz&#39;, hop_length=HOP_LENGTH, ax=axs[1])</code></pre>
<p><img src="https://velog.velcdn.com/images/soo-im/post/24af8c52-db68-485f-8ef7-bf819b6bd5c2/image.png" alt="Spectogram_Plot"></p>
<h3 id="machine-learning-with-spectral-features">Machine Learning with spectral features</h3>
<p>이전에는 오디오의 평균값 등을 LinearSVC 모델의 sample로 사용했다. 이제 앞에서 배운 Spectrogram과 spectral features를 사용해서 sample 특성을 추가할 수 있다.</p>
<pre><code class="language-python"># (이전 코드에 이어서)
bandwidths = []
centroids = []

# Spectral Bandwith, Spectral Centroid 추출
for spec in spectrograms:
    this_mean_bandwidth = np.mean(lr.feature.spectral_bandwidth(S=spec))
    this_mean_centroid = np.mean(lr.feature.spectral_centroid(S=spec))
    # Collect the values
    bandwidths.append(this_mean_bandwidth)  
    centroids.append(this_mean_centroid)

X = np.column_stack([means, stds, maxs, tempo_mean, tempo_max, tempo_std, bandwidths, centroids])
y = labels.reshape(-1, 1)

from sklearn.model_selection import cross_val_score
model = LinearSVC()
percent_score = cross_val_score(model, X, y, cv=5)
print(np.mean(percent_score))</code></pre>
<h2 id="2022-11-12">2022-11-12</h2>
<p>하... 코로나 막차 타는 중 😷 지독하구만!</p>
<h3 id="pddataframe-scatter-plot">pd.DataFrame Scatter plot</h3>
<p>index에 따라서 산점도의 색이 바뀌는 scatter plot을 그리려면 인수 <code>c</code>에 index array를 주면 된다. </p>
<pre><code class="language-python">df.plot.scatter(&#39;col1&#39;, &#39;col2&#39;, c=df.index, cmap=plt.cm.viridis)</code></pre>
<h3 id="interpolation-type">interpolation type</h3>
<p><code>scipy.interpolate(data_array)</code>는 <code>kind</code> 인수를 받는다. <code>kind</code>는 어떤 방식으로 interpolate 할 것인지를 정한다. 자세한 종류는 <a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html">문서</a> 참조.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/80b98bf5-44e7-4b14-a9d0-076c9c96872e/image.png" alt="interpolate_type"></p>
<p>zero, slinear, quadratic, cubic은 각자 0<del>3차 Spline interpolation을 가리킨다. Spline interpolation이란 고차항의 Polynomial interpolation을 사용하는 대신 데이터를 특정 개수의 구간으로 분해해서 각 구간을 0</del>3차 다항식에 맞추는 방식을 말한다. 이해를 잘 못 하겠지만... 모든 데이터를 하나의 고차 다항식에 맞추면 일종의 과적합과 유사한 문제가 발생하기 때문에, 이 문제를 해소하기 위한 interpolation 방법이라는 정도까지만 이해했다(출처: <a href="https://qt3b1s62da6s.tistory.com/415">블로그</a>).</p>
<h2 id="2022-11-13">2022-11-13</h2>
<h3 id="cross-validating-time-series-data">Cross-Validating time series data</h3>
<p><code>ShuffleSplit</code> 교차검증은 시계열 데이터에는 적합하지 않다. 시계열은 문자 그대로 시간에 상관이 있는데, 아래처럼 무작위로 나눈 chunk(fold)는 시간 정보를 모두 삭제하기 때문이다. 이 두 가지 방법은 각 데이터셋이 독립적으로 분포할 때에 적합한 방법이다. <code>KFold</code> 교차검증의 경우 한 fold 내에서 데이터의 시계 정보가 어느 정도 남아있으나 미래와 과거 fold가 뒤섞일 수 있으므로 좋은 방법은 아니다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/e808d647-0189-499d-8636-3637f78c85d8/image.png" alt="ShuffleSplit_CV">
<img src="https://velog.velcdn.com/images/soo-im/post/b6ce5299-34b2-45c8-b1a1-7e4deeda45fb/image.png" alt="KFold_CV">
위: KFold, 아래: ShuffleSplit
출처: DataCamp</p>
<p>시계열을 <code>ShuffleSplit</code> 으로 교차검증할 경우, 아래와 같이 데이터 포인트의 시간에 따른 순서가 모두 사라지고 섞인 상태로 교차검증을 수행한다.</p>
<pre><code class="language-python"># Import ShuffleSplit and create the cross-validation object
from sklearn.model_selection import ShuffleSplit
cv = ShuffleSplit(n_splits=10, random_state=1)

# Iterate through CV splits
results = []
for tr, tt in cv.split(X, y):
    # Fit the model on training data
    model.fit(X[tr], y[tr])

    # Generate predictions on the test data, score the predictions, and collect
    prediction = model.predict(X[tt])
    score = r2_score(y[tt], prediction)
    results.append((prediction, score, tt))

# Custom function to quickly visualize predictions
visualize_predictions(results)</code></pre>
<p><img src="https://velog.velcdn.com/images/soo-im/post/ccd443bd-1f92-429b-837b-0955afb1a14c/image.png" alt="ShuffleSplit_on_TimeSeries"></p>
<p>시계열을 예측할 때에는 &#39;과거&#39;의 정보를 사용해 &#39;미래&#39;의 정보를 예측해야 한다. 이 조건을 만족하는 교차검증이 <code>TimeSeriesSplit</code> 이다. (팜유 예측할 때 썼던 거군) 이 교차검증은 아래와 같은 iteration을 돌면서 과거(파란색) 데이터를 이용해 미래(빨간색)을 예측하고 그 성능을 반환한다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/a6271772-3639-4b3b-a878-7f610c08ef69/image.png" alt="TimeSeriesSplit_CV">
출처: DataCamp</p>
<h3 id="stationarity-and-model-stability">Stationarity and Model Stability</h3>
<p>대부분의 모델에는 input과 output의 관계가 static하다는 가정이 깔려있다. 만약 데이터가 stationary하지 않다면 시간에 따라 둘의 관계가 static하지 않을 수 있다. 이를 확인하기 위한 방법 중 하나가 교차검증이다. 교차검증을 통해 나온 모델의 계수(coeff)가 교차 세트마다 크게 달라진다면 데이터가 non-stationary하다고 유추할 수 있다.
<code>TimeSeriesSplit</code>을 이용해 <code>cross_val_score</code>을 구한 것을 교차 세트에 따라 plot한 결과가 아래와 같다면, score가 크게 변하는 구간을 통해 데이터가 non-stationary함을 유추할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/549f2b5f-b56f-4834-b855-116490c87ef1/image.png" alt="Cross_Validation_with_nonStationary_Timeseries"></p>
<h2 id="2022-11-14">2022-11-14</h2>
<h3 id="bootstrapping">Bootstrapping</h3>
<p>출처 1: <a href="https://angeloyeo.github.io/2021/07/19/jackknife_and_bootstrap.html">블로그</a>
출처 2: <a href="https://www.youtube.com/watch?v=Xz0x-8-cgaQ">유튜브</a></p>
<p>Bootstrap은 늪에 빠진 사람이 자기 자신의 부츠 끈(bootstrap)을 끌어잡아 올렸다는(뉴턴역학을 어떻게 한 건지 알 수 없는) 이야기에서 나왔다고 한다. 중요한 점은 &#39;자기 자신&#39;의 끈이다. 하나의 샘플이 존재할 때, 중복을 허용해서 똑같은 개수의 데이터를 뽑아 새로운 샘플을 만들어낸다. 이걸 여러 번 반복해서 자기 자신으로부터 나온 여러 개의 샘플을 뽑아 estimator(추정량)의 분포를 확인하는 작업을 Bootstrap이라고 한다. </p>
<blockquote>
<p>여기서 estimator은 평균, 중위값... 등의 표본을 이용해서 계산하는 일종의 통계치라고 보면 된다</p>
</blockquote>
<h3 id="이걸-왜-하나">이걸 왜 하나</h3>
<p>표준 오차가 알려지지 않은 estimator의 표준 오차를 구하기 위함이다. 샘플의 estimator을 구하면 우리는 그 estimator가 모집단의 estimator와 다르다는 사실을 알고 있다. 모집단으로부터 어떤 샘플을 꺼내느냐에 따라 estimator의 값은 조금씩 변할 것이다. 그래서 우리는 샘플의 estimator가 가지는 값의 범위와 표준 오차를 파악한다. 다만 특정 estimator의 표준 오차는 잘 알려져 있지만(예: 평균) 표준 오차가 알려지지 않은 estimator도 있다(임의로 만든 통계치도 포함해서). 이러한 estimator의 표준 오차를 알아내기 위해, Bootstrap으로 여러 개의 샘플을 만들어 낸 후 그 샘플들로부터 estimator의 분포와 표준 오차를 구하는 것이다.</p>
<h2 id="2022-11-16">2022-11-16</h2>
<p>이제 스터디는 끝났고, 지금부터는 뭘 공부할지 다시 정하면 된다!
일단은 ARIMA 강의를 들어봐야지~ (이 강의가 다른 강의에 비해 설명이 좀 더 구체적인 듯!)</p>
<h3 id="stationarity-정의-자세히">Stationarity 정의 (자세히)</h3>
<p>정상성을 가지려면 아래 세 가지 조건을 만족해야 한다.</p>
<ol>
<li>추세가 없어야 한다(Trend is zero).</li>
<li>데이터의 분산이 시간에 따라 변하지 않아야 한다(Variance is constant).</li>
<li>자기상관이 시간에 따라 변하지 않아야 한다(Autocorrelation is constant).</li>
</ol>
<h3 id="stationarity-test-with-dicky-fuller-test-자세히">Stationarity Test with Dicky-Fuller Test (자세히)</h3>
<p>귀무 가설: 시계열이 (추세 관점에서) non-stationary 하다.</p>
<pre><code class="language-python">from statsmodels.tsa.stattools import adfuller

results = adfuller(time_series_df[&#39;data_column&#39;])
print(results)

# (-1.34, 0.60, 23, 1235, {&#39;1%&#39;: -3.435, &#39;5%&#39;: -2.913, &#39;10%&#39;: -2.568}, 10782.87)</code></pre>
<ol>
<li><code>returns</code>의 첫 번째 값(test statistic)이 음수일수록 시계열이 stationary할 가능성이 높다. (3번과 결합하면 그 가능성의 정도를 파악할 수 있다)</li>
<li><code>returns</code>의 두 번째 값은 p-value이다. p-value가 threshold 미만이면 귀무 가설을 잘못 기각하였을 가능성이 낮다. 즉 귀무 가설을 기각하여 시계열이 stationary하다는 대립 가설을 채택할 수 있다.</li>
<li><code>returns</code>의 마지막 딕셔너리는 p-value에 해당하는 임계값을 보여준다. 1번 값을 임계값과 비교할 수 있다. </li>
</ol>
<p>단, 앞서 귀무 가설에 기재한대로 Dicky-Fuller test는 &#39;추세&#39;에 관하여서만 정상성을 확인한다. 예를 들어 아래의 데이터는 실제로는 non-stationary이지만, Dicky-Fuller test는 추세만을 확인하므로 귀무 가설을 기각한다(시계열이 stationary하다고 판단한다).
<img src="https://velog.velcdn.com/images/soo-im/post/e5e37452-7564-4ecf-9b71-388c46947280/image.png" alt="non-stationary_time_series">
출처: DataCamp</p>
<h2 id="2022-11-21">2022-11-21</h2>
<h3 id="arma-model-자세히">ARMA Model (자세히)</h3>
<p>앞서 본 AR Model과 MA Model을 합친 것이 ARMA Model이다. (이론적인 설명은 더 나오지는 않는다...) 다시 한 번 복습하면 ARMA Model 수식은 다음과 같다.</p>
<p>$X_{t}=\Sigma\phi_{t-p} X_{t-p}+\Sigma\theta_{t-q}\epsilon_{t-q}+\epsilon_{t}$</p>
<p>여기서 $p$가 AR Model 차수, $q$가 MA Model 차수이다.</p>
<p>지정한 AR, MA parameter을 이용해 ARMA Model을 생성하는 코드는 아래와 같다.
이 때 AR, MA parameter은 차수에 따라 list를 받으며 <code>[1, -ar1, -ar2, ...]</code>, <code>[1, ma1, ma2, ...]</code> 와 같이 설정한다. 맨 처음 1은 0차항의 parameter이기 때문에 AR 혹은 MA 차수가 없다 하더라도 반드시 1로 설정해야 한다. (그래야 $X_{t}$가 존재하니까) AR parameter은 음수로 받는 것도 주의한다.</p>
<pre><code class="language-python">from statsmodels.tsa.arima_process import arma_generate_sample

# MA(1), MA 1차 parameter가 -0.7인 경우
ar_coefs = [1]
ma_coefs = [1, -0.7]

# nsamples; 정수인 경우 1-D의 해당 길이의 시계열 생성, 튜플인 경우 해당 길이의 차원 시계열 생성
# scale; noise(epsilon)의 표준편차
y = arma_generate_sample(ar_coefs, ma_coefs, nsample=100, scale=0.5)

plt.plot(y)
plt.show()</code></pre>
<h2 id="2022-11-22">2022-11-22</h2>
<p>어쩐지 맘에 드는 일자로구만</p>
<h3 id="armax-model-fitting">ARMAX Model Fitting</h3>
<p>이전에 ARIMA 패키지를 이용해서 데이터를 AR(I)MA Model에 fitting하는 방법을 배웠다. 여기에서는 $X_{t}$의 ARMA에 외부인자 $y_{t}$를 추가해서 ARMAX fitting하는 방법을 다룬다.
끝에 붙는 &#39;X&#39;는 외부인자를 가리킨다. 즉 ARMAX는 ARMA에 linear regression을 합친 것과 같다.</p>
<p>$X_{t}=\alpha_{t}y_{t}+\Sigma\phi_{t-p} X_{t-p}+\Sigma\theta_{t-q}\epsilon_{t-q}+\epsilon_{t}$</p>
<p>ARIMA 패키지를 이용해 ARMAX Model에 fitting하는 코드는 다음과 같다. <code>exog</code> parameter에 외부인자 데이터를 주어 ARMAX에 fitting할 수 있다.</p>
<pre><code class="language-python"># ARMA(2,1) 사용하는 경우
model = ARIMA(my_df[&#39;data&#39;], order=(2,0,1), exog=my_df[&#39;external_data&#39;])

results = model.fit()
print(results.summary())</code></pre>
<h2 id="2022-11-23">2022-11-23</h2>
<p>DataCamp가 갑자기 멈춰서 🙄 오늘 처음 써 본 python decorator 정리!</p>
<h3 id="decorator">decorator</h3>
<p>내가 이해한 decorator의 기능은 f(g(x))에서 f(x)이다. 즉 함수 자체를 인자로 받는 함수를 만들어야 할 때 쓰는 것.
f(g(x))를 한 번만 쓸거라면 굳이 decorator을 쓸 필요가 없을 수도 있지만, f(g(x)) 말고도 f(h(x)), f(l(x))...와 같이 여러 함수를 인자로 넘기는 경우라면 decorator을 쓰는 게 편리하다.</p>
<p>decorator의 가장 좋은 예시가 &#39;각 함수를 실행할 때마다 소요되는 시간을 알려주는 타이머&#39;이다. 보통은 함수를 시작할 때 <code>start_time = time.time()</code>로 시작시각을 기록하고, 함수가 끝날 때 <code>time.time() - start_time</code>과 같이 종료시각과 시작시각의 차이를 보여주는 방식으로 소요 시간을 측정한다.
그런데 모든 함수에서 소요 시간을 측정해야 한다면 저 구문을 매번 넣어주는 방식이 비효율적이다. 이 때 사용하는 것이 decorator이다.</p>
<p>decorator을 class로 만드는 방법도 있고, 함수로 만드는 방법도 있는데 나는 후자의 방법을 선택했다(관리의 용이성을 고려하면 전자가 더 나을 수도 있다. <a href="https://stackoverflow.com/questions/10294014/python-decorator-best-practice-using-a-class-vs-a-function">상황에 맞게</a> 방법을 택하면 될 듯).</p>
<p>시간을 측정하는 함수인 <code>timer</code>를 다음과 같이 정의한다.</p>
<pre><code class="language-python">import time

def timer(inner_func):
    def wrapper_func(*args, **kwargs):
        start_time = time.time()
        result = inner_func(*args, **kwargs)
        end_time = time.time()
        print(f&quot;Takes {end_time-start_time} sec&quot;)
        return result
    return wrapper_func</code></pre>
<p>여기서 <code>timer</code>은 함수 자체(<code>inner_func</code>)를 인자로 받는다. <code>timer</code> 내부의 <code>wrapper_func</code>은 위에서 f(x)의 역할을 하는데, f(x)의 내부에서 <code>result = inner_func()</code>을 이용해 g(x)를 실행한다. 즉 <code>start_time</code>을 기록하고, <code>result</code>로 <code>inner_func</code>을 실행한다. 실행이 끝나면 다시 <code>end_time</code>을 기록해서 실행 시간을 출력하는 방식이다.
이 함수를 decorator로 실제 함수에 적용하는 방식은 아래와 같다.</p>
<pre><code class="language-python">@timer
def my_func():
    print(&quot;Hello world&quot;)
    return None</code></pre>
<p>이렇게 하면 <code>timer</code>의 인자로 <code>my_func</code>이 들어가면서 위의 절차대로 실행 시간을 출력한다.</p>
<p>다만 굳이 <code>timer</code> 안에 <code>wrapper_func</code>을 추가로 정의하는 이유는 아직 완벽히 이해를 못했다. 찾아보니 <a href="https://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%81%B4%EB%A1%9C%EC%A0%80-closure/">Closer</a>에 대해서도 좀 더 알아봐야 할 것 같다. 내일 읽어봐야겠다.</p>
<h2 id="2022-11-24">2022-11-24</h2>
<h3 id="왜-decorator-안에-wrapper을-정의하나">왜 decorator 안에 wrapper을 정의하나?</h3>
<p>나랑 같은 의문을 가진 <a href="https://stackoverflow.com/questions/45335580/why-do-we-need-wrapper-function-in-decorators">게시글</a>이 하나 있었다. 왜 decorator 안에 굳이 wrapper 함수를 추가하는지 모르겠다는 질문. 어제 본 <code>timer</code>을 예시로 들면 다음과 같다.</p>
<pre><code class="language-python"># wrapper가 있는 decorator(원본)
def timer(inner_func):
    def wrapper_func(*args, **kwargs):
        start_time = time.time()
        result = inner_func(*args, **kwargs)
        end_time = time.time()
        print(f&quot;Takes {end_time-start_time} sec&quot;)
        return result
    return wrapper_func

# wrapper가 없는 decorator
def timer(inner_func):
    start_time = time.time()
    result = inner_func(*args, **kwargs)
    end_time = time.time()
    print(f&quot;Takes {end_time-start_time} sec&quot;)
    return None</code></pre>
<p>위든 아래이든 <code>result</code>에서 <code>inner_func</code>을 불러오는 동작은 똑같은데 왜 굳이 <code>wrapper_func</code> 구간이 필요한지 이해가 가지 않았다.</p>
<p>여기에 대한 답변은 다음과 같다.
decorator는 결국 <code>timer(inner_func)</code>을 실행하는 것이다.<code>inner_func</code>이 특정 값을 return하는 경우를 가정해보자.</p>
<pre><code class="language-python">@timer
def my_func():
    return &quot;Hello&quot;

my_value = my_func()
# my_value = timer(my_func)</code></pre>
<p>여기서 <code>my_value = my_func()</code>은 결국 <code>timer(my_func)</code>과 같다. 이 때 <code>timer</code> 안에 wrapper 함수가 없으면 <code>timer</code>는 None을 반환하기 때문에(위의 <code>timer</code> 함수 정의 참고), <code>timer(my_func)</code>은 내부의 값이 무엇이 되었든 항상 None을 반환한다. 즉 <code>my_value</code>는 &quot;Hello&quot;가 아니라 None이 된다.
이 때문에 decorator 안에 wrapper 함수를 정의한다고 설명한다.</p>
<blockquote>
<p><strong>갑자기 든 의문</strong>
그러면 이렇게 정의하면 return 값을 &quot;Hello&quot;로 받을 수 있지 않나? 결국 wrapper을 사용했을 때에도 wrapper에서 <code>result</code>를 반환하고 다시 decorator가 <code>wrapper_func</code>을 반환하는 거라면... 테스트를 해봐야겠다. </p>
</blockquote>
<pre><code class="language-python">def decorator_func(inner_func):
    # do something
    result = inner_func(*args, **kwargs)
    # do something
    return result</code></pre>
<p><code>result</code>랑 <code>wrapper_func</code>이랑 다른 것이라면 wrapper을 쓰는 이유를 납득할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[10월 TIL]]></title>
            <link>https://velog.io/@soo-im/10%EC%9B%94-TIL</link>
            <guid>https://velog.io/@soo-im/10%EC%9B%94-TIL</guid>
            <pubDate>Sun, 16 Oct 2022 15:09:47 GMT</pubDate>
            <description><![CDATA[<p>자꾸 미루어서 일단 만들었다. 내일부터 꼭 채우자 😎 꼭... 블록체인도 마저 채워넣구...</p>
<p>넣을 것 + linar regression R² 과 correlation coefficient의 관계
<a href="https://stats.stackexchange.com/questions/83347/relationship-between-r2-and-correlation-coefficient">https://stats.stackexchange.com/questions/83347/relationship-between-r2-and-correlation-coefficient</a></p>
<h2 id="2022-10-17">2022-10-17</h2>
<h3 id="r²--correlation-coeff²">R² = correlation coeff²?</h3>
<p>(통계는 왜 매번 새로울까...)</p>
<p>linear regression을 수행하면 나오는 결정계수 R²은 correlation coefficient의 제곱과 같다는 설명을 보았다. 이걸 보고 든 생각은 (1) 왜 linear regression 성능 따지는 데 correlation이 튀어나오지? (2) 그러면 왜 처음부터 correlation coeff라고 안 하는거지? 였다.
좀 더 찾아보니 <a href="https://datalabbit.tistory.com/m/54">둘이 같다는 글</a>도 있고, <a href="https://rython.tistory.com/m/17">다르다는 글</a>도 있어서 하나씩 읽어보려고 한다.</p>
<p>우선 오늘은 R²의 의미부터!</p>
<h3 id="r²-의미">R² 의미</h3>
<p>(출처: <a href="https://datalabbit.tistory.com/m/51">블로그 글</a>)
linear regression으로 구한 $\hat{y}$ 가 있다고 할 때, $y = \hat{y} + e$ 로 표현할 수 있다. 이 때 좌변을 $y_i-\bar{y}$ 의 형식으로 맞추면 좌변은 $y$의 편차가 된다. 우변도 좌변에 맞게 처리를 거치면 $\hat{y_i}-\bar{y}$, $y_i-\hat{y_i}$가 된다. 첫 번째 항은 추정치로 인해 발생한 편차이고, 두 번째 항은 오류항으로 인해 발생한 편차이다.
양변에 제곱을 취하면 좌변은 &quot;${y}$의 변동(SST, Total Sum of Square)&quot;이고 우변은 &quot;회귀식으로 설명 가능한 변동(SSR, Regression Sum of Square)&quot;과 &quot;회귀식으로 설명 불가능한 변동(SSE, Error Sum of Squares)&quot;의 합이다. 
즉 $SST = SSR + SSE$ 로 정리할 수 있다.</p>
<p>&quot;회귀식이 얼마나 실제 데이터를 잘 모사하는지&quot;를 표현하는 설명계수 R²은 다음과 같다.
$R^2 = SSR / SST$
$SSE$, 즉 회귀선으로 인한 오차가 하나도 없다면 $SSR = SST$ 이니까 R²은 1이다. 반대의 경우 0이 된다.</p>
<p>선형 회귀에서 R²=1 이라면 correlation coeff도 1이 될 가능성이 높다는 것은 어렴풋이 추정할 수 있다. R²과 correlation coeff가 동일한 것인지는 다음에 이어서 확인한다!</p>
<h2 id="2022-10-18">2022-10-18</h2>
<h3 id="pycharm-setting-intepreter-with-conda-env">Pycharm setting Intepreter with conda env</h3>
<p>(파이참에서 기존에 세팅한 인터프리터를 못 찾아서 나중을 위해 적어둠)</p>
<ol>
<li>Intepreter settings-show all intepreter로 들어가면 기존에 세팅한 인터프리터가 나온다.</li>
<li>아예 새로 추가해야 하는데 Add Interpreter가 가상환경 인터프리터를 인식하지 못하면 Add Intepreter-Conda Env 메뉴에서 Interprete=<code>C:\Users\계정명\anaconda3\envs\프로젝트명\ python.exe</code>, Conta excutable=<code>C:\Users\계정명\anaconda3\Scripts\conda.exe</code> (디폴트 값 그대로)를 그대로 넣으면 된다.<h3 id="class로-전역변수-제거">class로 전역변수 제거</h3>
원래 이런 모양의 코드였는데<pre><code class="language-python">var1 = 1
var2 = func2()
</code></pre>
</li>
</ol>
<p>def func1():
    var3 = var1 + ...
    #...</p>
<p>def func2():
    func1()
    #...</p>
<pre><code>별도의 main 없이 돌아가는 이 코드를 외부에서 실행해야 해서 부득이하게 전부 하나의 main 함수 안으로 밀어넣어야 했다. 이렇게 했더니 전역변수로 세팅한 `var1`이 함수에 들어가지 않는 문제가 발생...

```python
class something(object):
    def __init__(self):
        self.var1 = 1
        self.var2 = self.func2()

    def func1(self):
        var3 = self.var1 + ...
        #...

    def func2(self):
        self.func1()
        #...</code></pre><p>전역변수 대신 클래스 변수를 쓰는 형태로 바꾸었다! <code>__init__</code> 내에서 메서드 <code>func2</code>를 불러올 수 있다는 것도 새로 알았다. (순서랑 상관있을 줄...) </p>
<p>(그런데 클래스 밖에서 정의한 함수도 메서드에서 불러오는 게 가능한 것 같은데... 실험해봐야겠다.)</p>
<h2 id="2022-10-19">2022-10-19</h2>
<h3 id="선형회귀-결정계수--상관계수²-성립조건">선형회귀 결정계수 = 상관계수² 성립조건</h3>
<p><a href="https://datalabbit.tistory.com/m/51">출처 1</a> ($SST = SSR + SSE$ 유도)
<a href="https://datalabbit.tistory.com/m/54">출처 2</a> (최소자승법 선형회귀 결정계수 = 상관계수² 증명)
<a href="https://rython.tistory.com/m/17">출처 3</a> (등식 성립 조건)</p>
<p>선형회귀 결정계수가 상관계수의 제곱과 같다는 등식은 <strong>최소자승법으로 구한 선형회귀일 때</strong> 성립한다.</p>
<p>이틀 전에 본 $SST = SSR + SSE$ 로 결정계수를 정의하면 결정계수와 상관계수의 제곱이 같은 것이 맞다. 다만 이 수식 자체가 최소자승법을 가정한 것이다.</p>
<p>위 수식을 유도하는 과정에서 아래 식이 나온다.
$\sum_{i} (y_i-\bar{y})^2 = \sum_{i} (\hat{y_i}-\bar{y})^2+\sum_{i} (y_i-\hat{y_i})^2+2\sum_{i} (\hat{y_i}-\bar{y})(y_i-\hat{y_i})$</p>
<p>이 때 좌변이 SST, 우변의 첫 번째 항이 SSR, 두 번째 항이 SSE이다. 즉 $SST = SSR + SSE$ 이 나오려면 우변의 마지막 항이 0이 되어야 한다. 이 때 최소자승법을 가정하면 마지막 항이 0이 되고 위의 등식이 성립한다.</p>
<p>따라서 항상 선형회귀의 결정계수가 상관계수의 제곱이라고 말할 수는 없다. (대부분 최소자승법을 사용하기 때문에 이렇게 표현하는 것 같다)</p>
<h2 id="2022-10-20">2022-10-20</h2>
<h3 id="acf-confidence-interval--standard-error">ACF confidence interval ~ Standard Error</h3>
<p>강의에서 통계학보다 파이썬 사용법 위주로 알려주어서 아쉽다 ㅠㅜ</p>
<p>ACF에서 신뢰구간 구하는 식인 $1/\sqrt{n}$ 이 어떻게 나온건지 궁금해졌다. Standard Error ($\sigma/\sqrt{n}$) 에서 표준편차 $\sigma=1$ 로 두고 나온 것처럼 보이는데(<a href="https://kr.mathworks.com/help/signal/ug/confidence-intervals-for-sample-autocorrelation.html">매틀랩</a>도 White Noise에서 ACF 신뢰구간이라고 설명함) 음... Standard Error가 뭔지 알아야 ACF 신뢰구간도 이해가 가겠다.</p>
<h3 id="standard-error">Standard Error</h3>
<p><a href="https://www.investopedia.com/terms/s/standard-error.asp">출처 1</a> SE 정의
<a href="https://web.archive.org/web/20201112015621/http://faculty.wwu.edu/kriegj/Econ375/Why%20do%20we%20divide%20by%20N.pdf">출처 2</a> 왜 $\sqrt{n}$으로 나누나요? (아직 못읽음)</p>
<p>모집단에서 추출한 표본집단의 통계치(예: 평균)의 표준편차. 예를 들어 모집단에서 표본집단 100개를 만들고 각각의 평균값 100개를 구했을 때, 100개의 평균값의 표준편차를 Standard Error라고 부른다.</p>
<p>모집단의 표준편차가 크면 클수록 표본집단의 통계치의 표준편차도 커지기 때문에, SE는 모집단의 표준편차 $\sigma$ 에 비례한다. 그리고 모집단의 사이즈가 클수록 통계치가 실제의 통계치와 유사해지기 때문에 모집단의 샘플 사이즈 $n$ 에 반비례한다.</p>
<p>White Noise의 ACF 신뢰구간이 SE랑 동일하다는 얘기가 되는데 특정 증명(?)에서 그렇다고만 나오고 더 설명이 나온 건 없어보인다. 예전에 같은 걸로 찾아볼 때에도 없어서 그냥 받아들이고 넘어갔던 것 같다...</p>
<h2 id="2022-10-21">2022-10-21</h2>
<h3 id="stationarity-정상성">Stationarity 정상성</h3>
<ul>
<li>Strong stationarity: 데이터의 분포가 시간에 의존하지 않는다.</li>
<li>Weak stationarity: 데이터의 평균, 분산, 자기상관이 시간에 의존하지 않는다.</li>
</ul>
<p>정상성을 보는 이유는 예측 모델의 parameter를 구할 때 데이터가 stationary하면 추정하는 parameter의 개수가 줄어들기 때문이다.
trend 혹은 seasonality가 강한 데이터는 평균값 등이 시간에 의존하므로(trend: 평균이 시간이 지날수록 커짐, seasonality: 평균이 특정 주기마다 커지거나 작아짐) nonstationary하다.</p>
<p>nonstationary한 데이터를 stationary하게 만드는 대표적인 방법이 $X_{t}-X_{t-1}$과 같이 차분을 구하는 것이다. 주기가 m의 간격으로 나타나는 데이터라면 $X_{t}-X_{t-m}$으로 차분을 할 수 있다. 파이썬 df에서는 <code>df.diff(m)</code>을 사용한다.
(석사 때 seasonality 제거 방법으로 m의 주기로 평균값을 구해서 그 값을 m번째 값마다 빼는 방식을 썼는데 이런 방식도 있었군 😮 신기)</p>
<h2 id="2022-10-22">2022-10-22</h2>
<h3 id="ar-model-정의">AR Model 정의</h3>
<p>$X_{t} = \mu + \phi_{t-m} X_{t-m} + \epsilon_{t}$ </p>
<p>여기서 $\mu$ 는 평균, $\epsilon_{t}$ 은 노이즈, $\phi$ 는 AR parameter를 의미한다. 위 수식을 만족하는 모델이 Auto-Regressive Model이다. m에 따라 m차 AR 모델이라고 부른다.</p>
<h3 id="ar1-model-성질">AR(1) Model 성질</h3>
<ul>
<li>$\phi=1$ 이면 Random  Walk 모델과 같다.</li>
<li>$-1&lt;\phi&lt;1$ 이면 정상성을 가진다.</li>
<li>$\phi&lt;0$ 이면 mean reversion이다(금융 용어). 즉 $X_{t}$ 가 $\mu$ 로 회귀하려는 성질을 가진다. </li>
<li>$\phi&gt;0$ 이면 momentum이다(금융 용어). 즉 $X_{t}$ 가 이전 time step의 값에 관성을 가진다.
<img src="https://velog.velcdn.com/images/soo-im/post/cf4675b5-26a4-41c2-9c87-42c124ddb2c7/image.png" alt="Positive and Negative AR paramter"></li>
<li>$\phi&lt;0$ 이면 Autocorrelation Function이 time lag에 따라 correlation이 교차로 발생한다. 이전 time step과 음의 상관을 가지기 때문에 당연한 결과.
<img src="https://velog.velcdn.com/images/soo-im/post/bc2d8ae5-03d5-4f8b-9715-5a178f10ec44/image.png" alt="Autocorrelation of AR Model">
출처: DataCamp</li>
</ul>
<h3 id="ar-model-차수-정하기">AR Model 차수 정하기</h3>
<p>AR 모델의 차수를 정할 때 아래 두 가지 정보를 사용할 수 있다.</p>
<ol>
<li>Partial Autocorrelation</li>
<li>Information Criteria</li>
</ol>
<p><strong>첫째, Partial Autocorrelation</strong>
Partial Autocorrelation은 일정 lag를 가진 데이터 간의 자기상관을 구할 때, 그 사이의 (1~lag-1) 데이터의 효과를 제거하는 방식이다.
그리고 이 Partial Autocorrelation coefficient는 정의상 AR Model의 parameter $\phi$ 와 일치한다.</p>
<p>$X_{t} = \mu + \phi_{1} X_{t-1} +  \phi_{2} X_{t-2}+...+\phi_{m} X_{t-m}+ \epsilon_{t}$</p>
<p>그래서 Partial Autocorrelation coefficient가 유의한 최대 time lag를 AR 차수로 보면 된다. </p>
<p><strong>둘째, Information Criteria</strong>
모델의 parameter(여기서는 AR Model의 차수)는 많을수록 데이터에 잘 맞지만 동시에 과적합의 위험도 가져온다. 모델이 과적합되었는지 판단하는 방법이 Information Criteria 이다.</p>
<p>parameter의 수에 따라 패널티를 부과하여 모델을 만들어 모델이 적합하게 fit 되었는지 판단하는 방식이다. 이 값이 낮을수록 적합하게 fit 되었다고 본다. 가장 많이 쓰이는 Information Criteria는 (1) AIC (Akaike), BIC (Bayesian) 이다.</p>
<p>데이터를 AR Model에 fitting하면 여러 차수의 AR Model을 생성하는데, 이 때 가장 낮은 AIC와 BIC를 가지는 차수를 AR Model의 차수로 선택한다. 아래 그림에서는 AR(3)가 가장 적합함을 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/320ae09a-f73b-4a8a-af0e-b44a4e47120f/image.png" alt="Choose AR Model parameter with BIC">
출처: DataCamp</p>
<p>아래와 같이 테스트로 만든 모델의 AIC를 출력해서 비교할 수도 있다.</p>
<pre><code class="language-python">from statsmodels.tsa.arima.model import ARIMA

# Fit the data to an AR(1) model and print AIC:
mod_ar1 = ARIMA(my_data, order=(1, 0, 0))
res_ar1 = mod_ar1.fit()
print(&quot;The AIC for an AR(1) is: &quot;, res_ar1.aic)

# Fit the data to an AR(2) model and print AIC:
mod_ar2 = ARIMA(my_data, order=(2, 0, 0))
res_ar2 = mod_ar2.fit()
print(&quot;The AIC for an AR(2) is: &quot;, res_ar2.aic)</code></pre>
<p>아래는 위 코드의 결과이다. AR(2)가 더 적합하다.</p>
<pre><code>&lt;script.py&gt; output:
    The AIC for an AR(1) is:  510.534689873311
    The AIC for an AR(2) is:  501.9274123409139</code></pre><h2 id="2022-10-24">2022-10-24</h2>
<p>밤이 늦어 오늘은 복습만 😪</p>
<h3 id="random-walk-정의">Random Walk 정의</h3>
<p>$X_{t} = \mu + X_{t-1} + \epsilon_{t}$ </p>
<p>$\mu =0$ 이면 random walk, $\mu \not=0$ 이면 drifted random walk 라고 한다. </p>
<p>스터디 시간에 random walk와 drifted random walk의 차이점으로 시간에 따른 분산의 변화(전자는 증가/후자는 유지), 평균값의 변화(전자는 유지/후자는 증가)가 언급되었는데 왜 그렇게 되는지는 아직 확실하지가 않다.</p>
<h2 id="2022-10-25">2022-10-25</h2>
<h3 id="ma-model">MA Model</h3>
<p>$X_{t} = \mu + \epsilon_{t} + \theta_{t-m}\epsilon_{t-m}$ </p>
<p>AR과 마찬가지로 $\mu$ 는 평균, $\epsilon_{t}$ 은 노이즈이고, $\theta$는 MA parameter이다. m에 따라 m차 MA 모델이라고 부른다.</p>
<h3 id="ma1-model-성질">MA(1) Model 성질</h3>
<ul>
<li>$\theta=0$ 이면 White noise와 같다.</li>
<li>모든 $\theta$ 에 대해 정상성을 가진다.</li>
<li>AR과 마찬가지로 $\theta&lt;0$ 이면 mean reversion, $\theta&gt;0$ 이면 momentum이다.</li>
<li>$AutoCorr(X_{t},X_{t-1})=\theta/(1+\theta^2)$
이 식의 증명은 이 <a href="https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=dhkdwnddml&amp;logNo=220229422049">블로그</a> 참조. </li>
</ul>
<h2 id="2022-10-26">2022-10-26</h2>
<h3 id="arima">ARIMA</h3>
<p><code>statsmodels</code> 패키지의 <code>ARIMA</code>는 AR, MA, 둘을 혼합한 ARMA, 그 외(ARIMA, SARIMA) 모델을 fitting할 수 있는 클래스이다.</p>
<p>ARMA 클래스가 받는 파라미터인 <code>order = (p, d, q)</code>는 세 개의 값을 가진 튜플이다. 여기서 <code>p</code>는 AR의 차수, <code>d</code>는 차분의 차수, <code>q</code>는 MA의 차수이다. 그 외에도 여러 파라미터가 있지만 강의에서는 이 파라미터만 다룬다.</p>
<h3 id="ar--ma-estimation-with-arima">AR &amp; MA estimation with ARIMA</h3>
<p>시계열 <code>data</code>에 ARMA 모델을 fitting 하는 방법은 다음과 같다.</p>
<pre><code class="language-python">from statsmodels.tsa.arima.model import ARIMA

mod = ARIMA(data, order=[1, 0, 1]) # 1차 ARMA, 차분 없음
# mod = ARIMA(data, order=[1, 0, 0]) # 1차 AR, 차분 없음
# mod = ARIMA(data, order=[0, 0, 1]) # 1차 MA, 차분 없음
res = mod.fit()
print(res.summary())</code></pre>
<p>ARMA의 fit summary 출력 결과는 다음과 같다.
<code>const coef</code>는 $\mu$, <code>ar.L1 coef</code>은 1차 AR parameter $\phi$, 
<code>ma.L1 coef</code>는 $\theta$ 이다.</p>
<pre><code class="language-python">                               SARIMAX Results                                
==============================================================================
Dep. Variable:                      y   No. Observations:                 1000
Model:                 ARIMA(1, 0, 1)   Log Likelihood               -1420.196
Date:                Wed, 26 Oct 2022   AIC                           2848.391
Time:                        16:45:53   BIC                           2868.022
Sample:                             0   HQIC                          2855.852
                               - 1000                                         
Covariance Type:                  opg                                         
==============================================================================
                 coef    std err          z      P&gt;|z|      [0.025      0.975]
------------------------------------------------------------------------------
const         -0.0038      0.003     -1.196      0.232      -0.010       0.002
ar.L1          0.0277      0.037      0.756      0.449      -0.044       0.099
ma.L1         -0.9025      0.015    -60.721      0.000      -0.932      -0.873
sigma2         1.0009      0.045     22.237      0.000       0.913       1.089
===================================================================================
Ljung-Box (L1) (Q):                   0.00   Jarque-Bera (JB):                 0.13
Prob(Q):                              0.99   Prob(JB):                         0.94
Heteroskedasticity (H):               1.00   Skew:                            -0.03
Prob(H) (two-sided):                  1.00   Kurtosis:                         2.98
===================================================================================</code></pre>
<h3 id="ar--ma-forecasting-with-arima">AR &amp; MA forecasting with ARIMA</h3>
<p>fitting한 모델로 이후 시간의 예측값을 얻으려면 <code>plot_predict</code>를 사용해야 한다.
<code>ARIMA</code> 클래스를 이용해 fitting을 마친 후, 그 모델을 <code>plot_predict</code>에 전달해 예측한다. 파라미터 <code>start</code>와 <code>end</code>는 예측 시작과 종료 time step이다.</p>
<p>MA 모델의 경우 차수만큼의 time step이 지난 다음에는 이후 값이 동일하다. 그 이유는 MA 식에서 차수 다음 스텝의 값을 예측할 때 $\epsilon_{t}$에 White noise의 기대값 $E_{\epsilon_{t}}$ 를 대입하기 때문이다. $E_{\epsilon_{t}}=0$ 이므로 $\hat{X_{t}}=\mu$ 가 된다.
...고 <a href="https://stats.stackexchange.com/questions/333092/why-i-get-the-same-predict-value-in-arima-model">스택</a>에서 설명 하는데 이해가 잘 안 된다. 이건 스터디 방에 물어봐야겠다...</p>
<p>$X_{t} = \mu + \epsilon_{t} + \theta_{t-m}\epsilon_{t-m}$</p>
<pre><code class="language-python">from statsmodels.tsa.arima.model import ARIMA
from statsmodels.graphics.tsaplots import plot_predict

mod = ARIMA(data, order=[1, 0, 0]) # 1차 AR
res = mod.fit()

fig, ax = plt.subplots()
data.loc[950:].plot(ax=ax) # 기존 데이터 플롯
plot_predict(res, start=1000, end=1010, ax=ax) # 모델로 예측한 데이터 플롯
plt.title(&#39;Data and Forcasted Data with AR(1) Model&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/soo-im/post/e889e496-6da1-4ce1-86a5-8064e0ee9194/image.png" alt="AR(1) Forecasting"></p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/2014d06e-b92e-4d44-b232-e93a94c6670d/image.png" alt="MA(1) Forecasting"></p>
<h2 id="2022-10-30">2022-10-30</h2>
<h3 id="cointegration-models">Cointegration Models</h3>
<p>서로 다른 Random Walk 시리즈 $P_{t}$ 와 $Q_{t}$ 의 선형 조합 $P_{t}-cQ_{t}$ 는 Random Walk가 아닐 수 있다.
따라서 Random Walk인 시리즈는 예측할 수 없지만, 이들의 선형 조합은 예측 가능하다. 이 때 $P_{t}$ 와 $Q_{t}$ 를 Cointegration(공적분)이라고 부른다.</p>
<p>(이게 무슨 소리인가 싶어 본 예시)</p>
<ol>
<li>천연가스와 난방용 기름 가격은 각각 Random Walk이지만, 둘의 선형 조합은 평균값을 돌아가려는 성질(mean reverting)을 가진다.</li>
<li>대체제에서 이런 성질을 많이 보이지만 두 시리즈가 경쟁 관계라고 해서 반드시 Cointegration인 것은 아니다.</li>
</ol>
<h3 id="test-for-cointegration-models">Test for Cointegration Models</h3>
<ol>
<li>$P_{t}$ 를 $Q_{t}$ 에 선형회귀하여 선형회귀 계수 $c$ 를 구한다.</li>
<li>$P_{t}-cQ_{t}$ 에 Random Walk 테스트인 Augmented Dickey-Fuller 테스트를 수행한다.</li>
<li>2의 결과가 Random Walk가 아니라면 $P_{t}$ 와 $Q_{t}$ 는 Cointegration이다.</li>
</ol>
<p>Cointegration 테스트는 아래 코드로 수행할 수 있다.</p>
<pre><code class="language-python">from statsmodels.tsa.stattools import coint
coint(P,Q)</code></pre>
<h2 id="2022-10-31">2022-10-31</h2>
<p>머신러닝 파트로 넘어왔다! (이래도 되는가) 다음 주는 내가 발표이니까 좀 더 열심히 보자 🤗</p>
<h3 id="machine-learning-절차">Machine Learning 절차</h3>
<ol>
<li>데이터 특징 추출(Feature extraction)</li>
<li>적합한 모델 피팅(Model fitting)</li>
<li>예측 및 모델 검증(Prediction and validation)</li>
</ol>
<h3 id="scikit-learn-기초---sampels-features"><code>scikit-learn</code> 기초 - sampels, features</h3>
<ul>
<li><code>scikit-learn</code> 내 메서드는 대부분 <code>(samples, features)</code>의 형식을 가진 데이터를 받는다.</li>
<li><code>samples</code>는 표본 각각, <code>features</code>는 표본으로부터 예측하려는 특성을 의미한다. (<code>samples</code>가 데이터(행)의 개수, <code>features</code>가 데이터 특성(열)의 개수라는 설명을 보았는데... 내가 생각한 것이랑 약간 달라서 이건 계속 강의 들어야 파악이 될 것 같다)</li>
<li>형식을 맞추기 위해 <code>df.T</code> (전치행렬) 혹은 <code>df.reshape(-1, 1).shape</code> 등을 사용한다.</li>
</ul>
<blockquote>
<p>예
아래와 같은 데이터프레임 <code>data</code>가 있다고 가정하자.
<img src="https://velog.velcdn.com/images/soo-im/post/51418fa5-87d4-4c1f-9fba-5340f73a7e13/image.png" alt="">
이 때 예측하려는 특성(features)가 &#39;target&#39;이고, 특성을 모의할 수 있는 데이터(samples)를 &#39;petal length (cm)&#39;라고 할 때 아래와 같이 정의한다.
이렇게 하면 <code>X</code>와 <code>y</code>는 각각 1D array가 된다.</p>
</blockquote>
<pre><code class="language-python">X = data[[&#39;petal length (cm)&#39;]]
y = data[[&#39;target&#39;]]</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[8월 TIL]]></title>
            <link>https://velog.io/@soo-im/8%EC%9B%94-TIL</link>
            <guid>https://velog.io/@soo-im/8%EC%9B%94-TIL</guid>
            <pubDate>Tue, 02 Aug 2022 13:33:27 GMT</pubDate>
            <description><![CDATA[<h2 id="2022-08-02">2022-08-02</h2>
<h3 id="1-블록체인-vs-기존-db심화">1. 블록체인 vs. 기존 DB(심화)</h3>
<table>
  <thead>
    <tr>
      <th>구분</th>
      <th>기존 DB</th>
      <th>블록체인</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>시스템 구성</td>
      <td>중앙집중 시스템</td>
      <td>분산 시스템</td>
    </tr>    
    <tr>
      <td>데이터 유형</td>
      <td>단발성 데이터</td>
      <td>시계열 데이터</td>
    </tr>
    <tr>
      <td>데이터 저장 형태</td>
      <td>중앙 집중식 저장</td>
      <td>모든 노드가 동일 데이터 저장</td>
    </tr>
    <tr>
      <td>data consistency</td>
      <td>Real time</td>
      <td>Soft Status(near-Real time)</td>
    </tr>
    <tr>
      <td>삭제 가능 여부</td>
      <td>가능</td>
      <td>원칙적 불가</td>
    </tr>
    <tr>
      <td>무결성 보장 주체</td>
      <td>별도 어플리케이션</td>
      <td>블록체인 플랫폼(전자서명, 해시)</td>
    </tr>
    <tr>
      <td>데이터 검증 방식</td>
      <td>별도 어플리케이션</td>
      <td>합의 알고리즘, 스마트 컨트랙트</td>
    </tr>
    <tr>
      <td>데이터 위변조 용이성</td>
      <td>용이</td>
      <td>어려움</td>
    </tr>
    <tr>
      <td>인증</td>
      <td>DCL(Data Control Language); 권한설정 명령어</td>
      <td>PKI 기반 인증</td>
    </tr>
  </tbody>
</table>

<h3 id="2-블록체인-개인정보-보호-이슈">2. 블록체인 개인정보 보호 이슈</h3>
<p><strong>1.1 OECD 개인정보보호 8원칙</strong></p>
<ol>
<li>수집제한의 원칙<ul>
<li>개인정보 수집에 제한을 두어야 함</li>
<li>적법하고 공정한 수단으로 개인정보를 수집해야 함</li>
<li>개인정보주체의 인지 혹은 동의를 얻어 수집해야 함</li>
</ul>
</li>
<li>정보내용의 원칙<ul>
<li>개인정보는 그 이용목적에 따라야 함</li>
<li>이용목적상 필요한 범위 내에서 개인정보의 정확성, 완정성, 최신성을 확보해야 함</li>
</ul>
</li>
<li>목적명확화 원칙<ul>
<li>수집 이전 혹은 수집 당시에 목적을 명시해야 함</li>
<li>명시된 목적에 적합하게 개인정보를 이용해야 함</li>
</ul>
</li>
<li>이용제한의 원칙<ul>
<li>정보주체의 동의가 있거나 법규정이 있는 경우를 제외하고 목적 외 이용 및 공개를 금지함</li>
</ul>
</li>
<li>안정성확보의 원칙<ul>
<li>분실, 부당한 접근, 파괴, 사용, 수정, 공개 위협에 대해서 합리적인 안전보장 조치에 의해 보호되어야 함</li>
<li>개인정보의 침해, 누설, 도용 등을 방지하기 위한 물리적, 조직적, 기술적 안전조치를 확보해야 함</li>
</ul>
</li>
<li>공개의 원칙<ul>
<li>개인정보 처리 및 보호를 위한 정책을 공개해야 함</li>
<li>개인정보 관리자의 신원 및 연락처, 개인정보의 존재 사실, 이용 목적 등에 접근이 용이해야 함</li>
</ul>
</li>
<li>개인참가의 원칙<ul>
<li>데이터 관리자는 자기 데이터 유무를 확인할 수 있어야 함</li>
<li>정보주체의 개인정보 열람, 정정, 삭제 청구권을 보장해야 함</li>
<li>정보주체가 합리적 시간과 방법으로 개인정보에 접근하는 것을 보장해야 함</li>
</ul>
</li>
<li>책임의 원칙<ul>
<li>개인정보 관리자에게 원칙준수의무 및 책임을 부과해야 함</li>
</ul>
</li>
</ol>
<blockquote>
<p><strong>해시값은 괜찮지않나?</strong>
개인정보의 해시값도 개인정보로 간주한다. 해시값을 처리할 때도 개인정보보호 원칙을 준수해야 한다.</p>
</blockquote>
<p><strong>1.2 보안 이슈</strong></p>
<ul>
<li>현재 우리나라 개인정보보호법은 중앙 집중화된 환경에 맞추어져 있다. 블록체인의 특성 중 법을 위반하는 특성이 많다.</li>
<li>개인정보 침해: 개인정보보호법은 인증받은 주체만 정보를 수집하고 관리할 수 있다. 그러나 블록체인에서는 모든 노드 참여자가 개인정보를 볼 수 있다.</li>
<li>잊힐 권리 보장 불가: 삭제 기능이 없어 잊힐 권리를 보장할 수 없다.</li>
<li>스마트 컨트랙트: 개인정보보호법은 정보주체에 대한 자동화된 의사결정을 금지한다. 그러나 스마트 컨트랙트는 자동화된 의사결정 수행이므로 이에 상충할 수 있다.</li>
</ul>
<p><strong>1.3 대응 방안</strong></p>
<ol>
<li>기밀거래는 별도 채널을 구성하여 거래정보 유출을 최소화한다.</li>
<li>거래정보 보관 기간에 대한 규정을 수립하고, 기간이 경과하면 무결성에 필요한 정보만 남기고 세부 정보는 삭제한다.</li>
<li>인증서 등을 이용해 참여자의 거래정보 접근을 통제한다.</li>
<li>거래정보를 암호화하여 참여자의 거래정보 접근을 통제한다.</li>
<li>개인정보를 저장할 때 비식별화하여 저장한다.</li>
<li>블록체인에 직접 개인정보를 저장하는 대신 암호화된 오프라인 DB에 저장하고, 블록체인에는 데이터 해시값을 보관한다(Off-Chain).</li>
</ol>
<h2 id="2022-08-03">2022-08-03</h2>
<h3 id="1-블록체인-개인정보-보호-이슈이어서">1. 블록체인 개인정보 보호 이슈(이어서)</h3>
<p><strong>1.1 블록체인의 개인정보처리자 지정</strong>
Private은 일반적으로 시스템 운영자를 개인정보처리자로 지정한다. 그러나 Public은 노드 참여자가 모두 개인정보를 처리하기 때문에, 트랜잭션을 발생시킨 모든 정보주체와 노드를 개인정보처리자로 지정할 필요가 있다.</p>
<p><strong>1.2 개인정보 삭제 대안</strong></p>
<ol>
<li>Off-Chain
블록 밖의 DB에 개인정보를 저장해서 필요시 삭제하는 방식이다. 다만 일반적인 DB가 가지는 정보 조작 등의 위협은 남아있다.</li>
<li>Blacklisting
개인정보를 암호화해서 블록체인에 저장한 후, 키를 파기하여 개인정보를 복호화할 수 없게 만드는 방식이다(상용 중인 방식은 아님). 실질적인 삭제라고 보기는 어렵다.</li>
</ol>
<h3 id="2-신원증명기술">2. 신원증명기술</h3>
<p><strong>2.1 신원증명 기술의 변화</strong></p>
<ol>
<li>중앙집중형: 모든 서비스마다 아이디/패스워드를 생성하는 방식(예전에 웹페이지마다 아이디/패스워드 만들었듯이). 각 site가 신원정보를 관리한다.</li>
<li>연합형: 네이버, 카카오 로그인과 같이 특정 site에서 인증을 하고 이를 다른 site에서 사용하는 방식. 여전히 특정 site가 신원정보를 관리한다.</li>
<li>분산형: 외부가 인증정보를 관리하지 않고 사용자가 직접 관리하는 방식. 전자지갑 등으로 관리한다.</li>
</ol>
<p><strong>2.2 신원증명 정책의 변화</strong>
웹 사용이 증가하면서 온라인 상의 신원증명 수요가 늘어났다. 초기에는 공인인증서로만 신원정보를 관리할 수 있었는데, 2020년 12월 공인인증서를 이용한 독점적인 인증 정책을 폐지하였다. &#39;공인&#39;인증서에서 &#39;공인&#39;이라는 단어가 빠지고 &#39;인증서&#39;로 이름이 바뀌었다. 이후 다양한 민간 전자서명수단이 나오면서 현재에 이르렀다.</p>
<p><strong>2.3 DID(분산신원증명)</strong>
온라인에서 사용하는 신분증. 정보주체가 자신의 신원정보를 관리/통제하는 디지털 신원관리 체계를 말한다.
이름, 주민등록번호, 주소, 사진... 등 사용자의 정보를 Claim이라 부르고, 이의 집합(주민등록증)을 Credential이라고 부른다.</p>
<p><strong>2.3 DID 발급 및 검증 체계</strong></p>
<ul>
<li>Issuer: Credential을 발급하는 자(예: 공공기관)</li>
<li>Verifier: Credential을 검증하는 자(예: 신분증명을 요구하는 자)</li>
<li>Holder: Credential의 소유자(개인)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/soo-im/post/cb0d36f8-fda2-462d-831f-1d9f417c2abf/image.jpg" alt="DID_process"></p>
<ol>
<li>Issuer, Holder가 각각 공개키와 개인키를 발급하고 공개키를 Verifiable Data Registry에 저장한다.</li>
<li>Issuer가 개인키로 서명을 한 Credential을 Holder에게 발급한다.</li>
<li>Holder가 Credential을 전자지갑에 보관한다.</li>
<li>Holder가 개인키로 서명을 한 Credential을 Verifier에게 보낸다.</li>
<li>Verifier가 Issuer(정상적으로 발급이 된 Credential인지)와 Holder(Holder의 Credential이 맞는지)의 전자서명을 검증한다.</li>
</ol>
<h2 id="2022-08-04">2022-08-04</h2>
<h3 id="1-defi탈중앙화-금융-시스템">1. DeFi(탈중앙화 금융 시스템)</h3>
<p><strong>1.1 DeFi</strong>
블록체인 기술을 이용하여 다양한 금융서비스를 제공하는 탈중앙화된 분산금융 또는 분산 재정.
시스템이 블록체인 상에서 구축되고, 금융상품을 암호화폐 기반으로 지원하고, 오픈소스로 구축된다는 특징을 가진다.</p>
<p><strong>1.2 핀테크, CeFi, DeFi 비교</strong></p>
<ul>
<li>CeFi(중앙화금융): 기존 금융과 DeFi의 중간 지점으로, 중앙화된 암호화폐 금융서비스. 빗썸, 업비트, 바이낸스, 코인베이스 등과 같은 암호화폐 거래소가 대표적인 예이다.</li>
</ul>
<table>
  <thead>
    <tr>
      <th>구분</th>
      <th>핀테크</th>
      <th>CeFi</th>
      <th>DeFi</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>거래수단</td>
      <td>법정화폐</td>
      <td>가상자산</td>
      <td>가상자산</td>
    </tr>    
    <tr>
      <td>규제</td>
      <td>전자금융거래법</td>
      <td>특정금융정보거래법</td>
      <td>-</td>
    </tr>
    <tr>
      <td>관리 주체</td>
      <td>중앙화</td>
      <td>서비스 제공자</td>
      <td>탈중앙화</td>
    </tr>
    <tr>
      <td>거래장부</td>
      <td>단일</td>
      <td>단일/분산</td>
      <td>분산</td>
    </tr>
    <tr>
      <td>데이터 접근</td>
      <td>허가받은 사용자</td>
      <td>등록 사용자</td>
      <td>모든 참여자</td>
    </tr>
    <tr>
      <td>데이터 저장</td>
      <td>중앙화</td>
      <td>중앙화</td>
      <td>노드 참여자</td>
    </tr>
    <tr>
      <td>익명성</td>
      <td>실명거래</td>
      <td>익명거래</td>
      <td>익명거래</td>
    </tr>
    <tr>
      <td>투명성주</td>
      <td>불투명</td>
      <td>불투명</td>
      <td>투명</td>
    </tr>
  </tbody>
</table>

<blockquote>
<p><strong>핀테크 vs. 테크핀</strong>
핀테크는 금융기관이 디지털 서비스를 제공하는 것, 테크핀은 테크업체가 금융서비스를 제공하는 것을 말한다.</p>
</blockquote>
<h3 id="2-cbdccentral-bank-digital-currencies">2. CBDC(Central Bank Digital Currencies)</h3>
<p><strong>2.1 CBDC</strong>
중앙은행이 전자적 형태로 발행하는 새로운 화폐. 원칙적으로는 신용리스크가 없고 익명성이 보장되며 이자지급이 불가능하다. 다만 발행 주체의 정책에 따라 이 특징이 적용되지 않을 수 있다.
기존 전자지급 수단은 전자 결제가 이루어질 때 은행과 은행 간의 시스템 내부에서 자금이 이동한다. 반면 CBDC는 은행 외부의 블록체인 시스템에서 자금이 이동한다.</p>
<p><strong>2.2 CBDC vs. 비트코인</strong></p>
<table>
  <thead>
    <tr>
      <th>구분</th>
      <th>CBDC</th>
      <th>비트코인</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>발행 주체</td>
      <td>중앙은행</td>
      <td>민간(탈중앙화)</td>
    </tr>    
    <tr>
      <td>발행 규모</td>
      <td>중앙은행 재량</td>
      <td>고정(2100만 개)</td>
    </tr>
    <tr>
      <td>화폐 단위</td>
      <td>법정 화폐 단위</td>
      <td>BTC</td>
    </tr>
    <tr>
      <td>화폐 가치</td>
      <td>액면 고정</td>
      <td>수급 따라 변동</td>
    </tr>
    <tr>
      <td>제반 기술</td>
      <td>블록체인</td>
      <td>블록체인</td>
    </tr>
    <tr>
      <td>감독 방식</td>
      <td>중앙은행 등 특정 기관이 관리감독</td>
      <td>관리감독 없음</td>
    </tr>
    <tr>
      <td>자금세탁 가능성</td>
      <td>모든 거래 기록 남아 불가능</td>
      <td>익명성으로 인해 자금세탁 가능</td>
    </tr>
  </tbody>
</table>

<h2 id="2022-08-10">2022-08-10</h2>
<h3 id="1-nft-한계">1. NFT 한계</h3>
<p><strong>1.1 NFT 러그풀(사기)</strong>
흔히 말하는 &#39;먹튀&#39;. 국내의 대표적인 사건이 &#39;캣슬&#39; NFT 사기. 범인은 &#39;캣슬&#39;이라는 이미지를 1만개 민팅하고, 이 NFT를 보유할 경우 가상화폐를 준다고 허위광고를 내었다. NFT를 판매한 후 가상화폐를 지급하지 않고 잠적한 사건이다.</p>
<p><strong>1.2 해킹</strong>
계정 탈취 등 다양한 수법이 있다. 해커가 NFT 서비스 이용자에게 가짜 계약서를 보내 서명을 탈취하고, 그 서명으로 NFT 거래를 위조해 이용자의 NFT를 탈취하는 방법이 대표적이다.</p>
<p><strong>1.3 저작권</strong>
원작자/저작권자와 합의하지 않은 작품을 민팅하거나, 원본 파일을 도용해서 민팅하는 사례가 많다(페이크 민팅). 판례상 원작자나 저작권자가 항상 승소한다는 보장은 없다(디지털 저작물을 제2차 창작물로 보는 사례가 있음).</p>
<p><strong>1.4 자전거래(Wash Trading)</strong>
NFT 가격에 버블을 형성하는 행위. NFT 소유주가 복수 개의 지갑으로 NFT 매매를 반복해서 버블을 형성한다. 기존 증권/선물 거래에서는 자전거래가 금지되어 있지만 NFT 시장은 아직 단속 범위가 아니다.</p>
<h3 id="2-redeemable-nft-irlin-real-life">2. Redeemable NFT, IRL(In Real Life)</h3>
<p><strong>2.1 Redeemable NFT, IRL</strong>
현물과 교환 가능한 NFT. NFT를 소각하면 현물과 교환해주는 등의 방식으로 실물 가치와 교환하는 NFT를 말한다.</p>
<p><strong>2.2 와인 시장</strong>
현재 와인 시장에 Redeemable NFT 프로젝트가 잘 적용되고 있다. 국내 주류법상 개인이 와인을 투자 목적으로 거래하는 것은 불가능하다. &#39;아웃스탠딩&#39;이나 &#39;뱅크오브와인&#39;이라는 프로젝트는 와인과 동등한 가치를 가지는 NFT를 발행해서 일종의 와인 교환권을 발급한다. 이 NFT를 이용해서 소유자는 와인을 거래할 수 있다.</p>
<p><strong>2.3 패션 시장</strong>
MCM은 메타제트라는 Redeemable NFT 플랫폼을 만들어 의류/신발 NFT를 발행한다. 그 실물은 MCM에서 별도로 보관하고 NFT 소유자는 NFT를 실물과 교환할 수 있다.</p>
<h3 id="3-nft-마켓플레이스2차-시장">3. NFT 마켓플레이스(2차 시장)</h3>
<p><strong>3.1 NFT 마켓플레이스</strong>
NFT 프로젝트의 주요 수입은 1차 판매(민팅 이후 최초의 판매)보다 마켓플레이스(예: 오픈씨)에서 이루어지는 2차 판매에서 발생한다. 모든 거래가 온체인에 기록이 되기 때문에 거래를 할 때마다 수수료가 발생하기 때문이다.</p>
<p><strong>3.2 NFT 마켓플레이스 특징</strong></p>
<ol>
<li>낮은 신뢰성
판매자에 대한 신뢰성 확보가 어려워 러그풀, 페이크 민팅의 위험이 있다.</li>
<li>높은 거래 수수료
오픈씨는 거래당 2.5%의 수수료를 징수한다.</li>
<li>오픈씨로 인한 중앙화
현재 오픈씨가 NFT 마켓플레이스를 독과점하고 있다.</li>
<li>낮은 유동성
고가의 NFT는 현금화하기 어렵다.</li>
<li>P2P 교환 불가
같은 가격의 NFT끼리 직접 교환할 수는 없다.</li>
</ol>
<h2 id="2022-08-24">2022-08-24</h2>
<h3 id="블록체인-제안시-작성-사안">블록체인 제안시 작성 사안</h3>
<ol>
<li><p>제안 개요</p>
<ul>
<li>기존 시장의 문제점이 무엇인지(논문, 기사, 통계 등)</li>
<li>기존 시장에 블록체인을 도입하려는 움직임이 있는지</li>
</ul>
</li>
<li><p>제안 범위</p>
<ul>
<li>서비스 개념도</li>
<li>기능 나열</li>
</ul>
</li>
<li><p>제안의 특징 및 장점</p>
<ul>
<li>문제점을 어떻게 해결할 수 있는지</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[7월 TIL]]></title>
            <link>https://velog.io/@soo-im/7%EC%9B%94-TIL-42vl3mlq</link>
            <guid>https://velog.io/@soo-im/7%EC%9B%94-TIL-42vl3mlq</guid>
            <pubDate>Wed, 06 Jul 2022 13:41:01 GMT</pubDate>
            <description><![CDATA[<h2 id="2022-07-04">2022-07-04</h2>
<h3 id="1-블록체인-사업에서-중요한-내용">1. 블록체인 사업에서 중요한 내용</h3>
<ul>
<li>(기본)BM에 대한 이해</li>
<li>네트워크 참여자 간의 신뢰 관계(위계없이 동일한 권리 가지므로)</li>
<li>블록체인의 도입 배경인 보안에 대한 이해</li>
<li><strong>&quot;블록체인을 도입해야 하는 이유&quot;</strong></li>
</ul>
<p>→ 블록체인이 항상 만능은 아님! BM 특성상 굳이 도입할 필요가 없는 경우도 있음(예: 단일 사용자의 정보 저장).</p>
<h3 id="2-비즈니스-모델의-구분">2. 비즈니스 모델의 구분</h3>
<ul>
<li>문제 해결형; 문제의 진단과 해결 솔루션을 제공(컨설팅, 광고, 법률...)</li>
<li>가치 창조형; 미완의 제품/원재료에 가치를 부여하고 제품을 제공(제조, 소매...)</li>
<li>네트워크형(플랫폼); 사람들이 물건을 주고받는 네트워크 제공(통신사, 보험사...)</li>
</ul>
<p>→ 블록체인은 문제 해결형+네트워크형에 적용되는 경우가 많음.
→ 다만 블록체인은 실시간성, 대용량 트랜잭션 등을 요구하는 서비스에는 부적합한 경우가 많음.</p>
<h3 id="3-블록체인의-용도">3. 블록체인의 용도</h3>
<ul>
<li>계약 관리, 본인 인증... 등 신뢰 이슈에서 나온 해법</li>
</ul>
<p>→ 중개자 없이 개인간의 <strong>거래, 가치, 자산 등을 이동시키는 교환 네트워크</strong>
→ <strong>서로 신뢰할 수 없는 환경에서 신뢰를 보장하는 기술</strong></p>
<h3 id="4-매우-간단한-블록체인-개념">4. (매우) 간단한 블록체인 개념</h3>
<p>각 블록은 최초의 블록(제네시스 블록) 이후에 생긴 모든 거래 내역을 가지고 있다. 새로운 블록은 이전 블록의 내용이 변경되지 않았음을 보증하는 체인을 가지고 있다. 이 체인을 이용하여 이전 블록과 연결한다. 연결이 완료되면 실제 거래가 이루어진다.
이 때 네트워크 내에서 시간이 동기화 되어야 하므로 블록이 발견된 날짜(타임스탬프)도 함께 가지고 있다.</p>
<h2 id="2022-07-05">2022-07-05</h2>
<h3 id="1-블록체인의-신뢰성-보장">1. 블록체인의 신뢰성 보장</h3>
<p>블록체인 네트워크는 아래 세 가지를 반드시 검증해야 한다.</p>
<ul>
<li>무결성; 위변조 방지</li>
<li>부인(否認)방지 </li>
<li>사용자 인증<blockquote>
<p><strong>인증 VS. 부인방지</strong></p>
</blockquote>
</li>
<li>인증*은 당사자의 자격을 검증하는 것이고</li>
<li>부인방지*는 거래(데이터 송수신)에 대해 당사자가 누구인지 추적하는 것이다.</li>
</ul>
<p>이를 위한 암호화 알고리즘을 알아본다.</p>
<h3 id="2-보안의-3요소cia">2. 보안의 3요소(CIA)</h3>
<ul>
<li>기밀성 Confidentiality; 인가된 사람만 복호화(암호문을 평문으로 변환) 가능 ↔ 유출, 노출</li>
<li>무결성 Integrity; 인가된 사람만 인가된 방법으로 정보 변경이 가능 ↔ 위변조</li>
<li>가용성 Availability; 필요한 시점에 정보에 접근 가능 ↔ 서비스 지연(예: DDOS)</li>
</ul>
<p>블록체인은 <em>무결성</em>을 이용해 거래의 신뢰성을 높인다.</p>
<h3 id="3-암호화-알고리즘-분류">3. 암호화 알고리즘 분류</h3>
<p>복호화 가능 여부에 따라 단방향과 양방향으로 나눈다.</p>
<ul>
<li>단방향; 암호화만 가능(예: 해시함수)</li>
<li>양방향; 암호화, 복호화 모두 가능</li>
</ul>
<h3 id="4-해시함수">4. 해시함수</h3>
<p><strong>4.1 해시함수 특성</strong>
데이터를 복호화할 수 없게 암호화하는 단방향 함수. 임의 길이의 메시지를 고정된 길이의 출력값(Message Digest, MD)로 변환한다. 데이터 변경 여부를 확인할 수 있으므로 <em>무결성</em>을 제공한다.
해시함수(h)는 아래의 특성을 가져야 한다.</p>
<ul>
<li>((압축성; 해시값(h(x))의 길이는 항상 동일해야 한다.)) *&lt;이건 뒤에 나오는 거랑은 좀 결이 다른데... 애매해서 여기 넣음*</li>
<li>역저항성(단방향성); 임의의 h(x)를 만족하는 x를 찾는 것(복호화)이 계산적으로 불가하다.</li>
<li>제2역상 저항성; 주어진 x에 대해 h(x)=h(x&#39;), x≠x&#39;인 x&#39;를 찾는 것이 계산적으로 불가하다.</li>
<li>충돌 저항성; h(x)=h(x&#39;)를 만족하는 임의의 x, x&#39;를 찾는 것이 계산적으로 불가하다.<blockquote>
<p><strong>제2역상 저항성 VS. 충돌 저항성</strong>
제2역상 저항성은 x가 주어진 상태에서 x&#39;를 찾는 문제이고, 충돌 저항성은 복수의 해를 찾는 문제인 듯?</p>
</blockquote>
</li>
</ul>
<p><strong>4.2 해시함수 기법</strong>
그러나 이러한 특성 때문에 동일한 두 개의 해시값이 있다면 두 개의 데이터가 동일하다는 점을 알 수 있다. 이를 해결하기 위해 나온 몇 가지 기법이 있다.</p>
<p>1) 해시 솔트(Hash Salt)
입력한 데이터에 임의의 문자열을 추가한 뒤(예: 계정 생성 시각) 해시함수를 만드는 방법이다.
<img src="https://velog.velcdn.com/images/soo-im/post/32cd843c-0f4a-4729-aae3-378f85f25407/image.jpg" alt="Hash_salt">
2) 키 스트레칭(Key Stretching)
해시 솔트를 붙이는 방식으로도 부족해서(!) 솔트를 붙인 후에 나온 해시값을 임의의 횟수만큼 다시 해시함수에 넣는 방법이다.</p>
<p><strong>4.3 블록체인 내 해시 함수</strong>
블록 헤더를 해시값으로 만들어 일종의 지문을 만든다. 다음 블록이 해당 지문(해시값)을 가지는 것을 검증하여 체인을 연결한다.</p>
<p><strong>4.4 HMAC</strong>
HMAC은 key를 이용하는 해시함수이다. 임의의 key를 붙여 데이터의 무결성과 <em>기밀성</em>을 제공한다.
<code>h(h(messeage + key) + key)</code>
A가 보낸 데이터가 변조되지 않았는지 검증하기 위해서, A는 message(데이터)와 key, 그리고 그 값을 이용한 해시값을 B에게 보낸다. 그러면 B가 해시함수를 이용하여 직접 해시값을 구함으로써 해당 message가 변조되지 않았는지 확인할 수 있다.
key를 통해서 인가된 사람만 복호화할 수 있기 때문에 <em>기밀성</em>을 추가로 가진다.</p>
<h3 id="5-대칭키비대칭키">5. 대칭키/비대칭키</h3>
<p>양방향 알고리즘 중에서 암호화키와 복호화키가 동일한 암호화 기법을 &quot;대칭키&quot;, 다른 기법을 &quot;비대칭키&quot;라고 한다.</p>
<p><strong>5.1 대칭키</strong></p>
<ul>
<li>특징: 암호화 키와 복호화 키가 동일하다. A와 B가 동일한 키를 공유해서 A는 그 키를 이용해 암호화하고, B는 복호화한다.</li>
<li>장점: 암호화 처리가 빠르다.</li>
<li>단점: 암호화-복호화 키가 동일하기 때문에 모든 쌍마다 각각 키를 만들어야 해서(A-B, A-C, B-C...) 키 관리가 불편하다.</li>
</ul>
<p><strong>5.2 비대칭키</strong></p>
<ul>
<li>특징: 암호화 키와 복호화 키가 다르다. 공개키(public key)와 비밀키(private key)를 사용한다. A는 온라인에 공개된 B의 암호화 공개키를 찾아서 암호화한다. 그걸 B에게 보내면 B는 자신만 가지고 있는 비밀키로 복호화해서 A가 보낸 데이터를 확인한다. 즉 누구나 B에게 &#39;공개키&#39;를 이용해서 &#39;암호화&#39;할 수 있고, 그 메시지는 B가 가진 &#39;개인키&#39;로만 &#39;복호화&#39;할 수 있다.</li>
<li>장점: 키 관리가 용이하다.</li>
<li>단점: 암호화 처리가 느리다.</li>
</ul>
<h3 id="6-블록암호">6. 블록암호</h3>
<p><strong>6.1 ECB(Electronic Codebook)</strong>
암호화 속도를 빠르게 하기 위하여 모든 데이터를 일일이 암호화하지 않고, 일정 길이의 블록으로 나누어서 블록별로 암호화하는 방식. 블록을 병렬로 암호화하기 때문에 속도가 빠르다.
그러나 <code>AAAA</code> <code>BBBB</code> <code>AAAA</code>와 같이 세 블록으로 나누어 암호화하면 <code>AAAA</code> 블록의 원문이 같음을 유추할 수 있다(해시에서 본 문제와 유사). 이 문제를 해결하기 위해 나오는 해법이 다음에 나오는 CBC, Counter.</p>
<p><strong>6.2 CBC(Cipher-block Chaining)</strong>
첫 블록을 암호화할 때, 난수를 이용해 값을 변경한(XOR 연산) 다음 그 값을 암호화한다. 그리고 그 암호화 결과를 이용해 다음 블록의 값을 변경하고 그 값을 암호화한다. 이를 무한히 반복한다.
<img src="https://velog.velcdn.com/images/soo-im/post/eefbd969-07b9-4a30-a6f9-d51f3e8ab530/image.jpg" alt="CBC_diagram">
이처럼 이전 블록의 암호화 결과를 이용하기 때문에 Chaining이라고 부른다. 이 방법을 사용하면 똑같은 <code>AAAA</code> 블록이지만 암호화 결과가 다르게 나오는 것을 볼 수 있다. (이 과정을 좀 더 복잡하게 하는 PCBC(Propagating CBC)도 있긴 한데 그건 생략 😵)
하지만 ECB와 다르게 이전 블록의 결과를 기다려야하기 때문에 시간이 오래 걸리는 단점이 있다.</p>
<p><strong>6.3 Counter</strong>
ECB처럼 병렬처리가 되면서 CBC처럼 임의성을 부여하기 위한 기법. 이전 블록의 암호화 결과를 사용하는 대신 임의의 Counter를 부여해서 블록 값을 바꾸고 암호화하는 방식이다. 예를 들어 첫 <code>AAAA</code> 블록에는 0000, <code>BBBB</code> 블록에는 0001, 두 번째 <code>AAAA</code>에는 0010...과 같은 serial number인 Counter을 부여해서 병렬로 암호화하는 방식이다.
(CBC보다 간단해 보이는데 CBC보다 나중에 나온 게 신기...)</p>
<h2 id="2022-07-06">2022-07-06</h2>
<h3 id="1-pkipublic-key-infrastructure">1. PKI(Public Key Infrastructure)</h3>
<p>번역하면 &#39;공개키 기반 구조&#39;. 사용자가 전자거래를 할 때 사용하는 <strong>인증기관이 전자서명한 인증서</strong>를 관리하는 체계를 말한다. 공인인증서가 대표적인 예시이다.
부인방지(전자서명), 인증(전자인증서), 무결성(해시 이용), 기밀성(암호화 이용)을 제공한다.</p>
<p><strong>1.1 PKI 인증서 발급 프로세스</strong>
<img src="https://velog.velcdn.com/images/soo-im/post/f084638d-60e5-48ef-9d50-3936463a296b/image.jpg" alt="PKI_process"></p>
<ol>
<li>사용자가 RA(등록대행기관, 주로 은행/증권사)에 인증서 발급 대행을 요청한다.</li>
<li>RA에서 사용자의 신원을 확인한 후 CA(인증기관)에 사용자의 인증서 발급을 요청한다.</li>
<li>CA가 RA의 요청을 받고 인증서를 생성하고, 사용자에게 인증서를 받을 수 있는 키를 제공한다.</li>
<li>사용자가 그 키를 CA에게 주어서 초기 인증한다.</li>
<li>CA가 공개키를 LDAP에, 개인키를 사용자 PC의 레지스트리에 저장한다.<blockquote>
<p><strong>액티브X를 사용한 이유</strong>
PKI는 레지스트리에 저장되어야 브라우저가 바로 찾을 수 있다. 하지만 우리나라 인증서는 &#39;NPKI&#39; 등과 같은 임의의 폴더 안에 저장하기 때문에 브라우저가 찾을 수가 없다. 그래서 인증서를 찾아주는 별도의 프로그램인 액티브X를 사용한 것이다.</p>
</blockquote>
</li>
</ol>
<p><strong>1.2 PKI 인증서 검증 프로세스</strong></p>
<ol>
<li>사용자가 인증서를 검증해서(전자서명) CA에 보낸다.</li>
<li>CA가 CRL(해지되었거나 유효하지 않은 인증서의 목록)을 조회한다.</li>
<li>해당 인증서가 CRL에 있는지 검증한다(폐기 여부 확인).</li>
<li>CA는 해당 인증서가 본인(CA)가 만든 것이 맞는지 검증한다.</li>
<li>또, Root CA는 CA의 인증이 본인(Root CA)이 만든 것이 맞는지 또 검증한다. 4번과 5번을 Chain 검증이라고 부른다(CA와 Root CA 둘 다 자기가 만든 것이 맞는지 검증하기 때문에 Chain이라고 부르는 듯)</li>
<li>Chain 검증을 완료하면 인증서가 검증된다.</li>
</ol>
<p><strong>1.3 OCSP(Online Certificate Status Protocol)</strong>
PKI 인증서 검증에서 사용하는 CRL은 폐기된 인증서의 리스트이다. CRL은 특정 주기마다 업데이트가 되는데, 이 때문에 업데이트 전까지는 폐기 여부를 확인할 수 없다. 예를 들어 매일 자정에 업데이트가 된다면 오후 1시에 폐기를 신청한 나의 인증서는 다음 날이 되어서야 CRL에 들어간다. 이 문제를 해결하기 위해 실시간으로 폐기 인증서를 확인하는 프로토콜이 OCSP이다.</p>
<p><strong>1.4 PKI 한계</strong>
CA의 권한이 과도하게 크고, 저성능 기기(IoT 등)에서 인증을 연산하기 힘들다는 문제가 있다. 그 대안으로 IBE(Identity-Based Encryption) 등이 있다. IBE는 e-mail과 같은 식별이 가능한 공개된 정보(ID)를 이용해서 인증하는 기술이다.</p>
<h3 id="2-전자서명-절차">2. 전자서명 절차</h3>
<ol>
<li>A가 B에게 전자서명한 문서를 보내려고 한다.</li>
<li>A가 원본문서를 해시함수를 이용해 MD로 만든다.</li>
<li>A가 MD를 <strong>A의 개인키</strong>를 이용해 암호화한다. 이를 전자서명이라고 한다.</li>
<li>B가 A의 공개키를 이용해 전자서명을 복호화하여 MD를 받는다.</li>
<li>B가 A의 원본문서를 받고 해시함수를 이용해 MD로 만든다.</li>
<li>4번의 5번의 MD를 비교해서 둘이 같으면 전자서명 검증이 완료된다.</li>
</ol>
<blockquote>
<p><strong>전자서명은 개인키, 암호화는 공개키</strong>
<img src="https://velog.velcdn.com/images/soo-im/post/6e4c5027-9a91-40bb-ac4b-5c56d13e444a/image.jpg" alt="Digital_signature_Encryption_Assymetric_key">
&#39;암호화&#39;한 문서를 보낼 때에는 받는 쪽의 공개키로 암호화한다. 그렇게 하면 받는 사람의 개인키로만 복호화 할 수 있기 때문에, 받는 사람 외에는 원본을 아무도 알 수 없다.
&#39;전자서명&#39;은 전자서명을 하는(보내는) 쪽의 개인키로 암호화한다. 받는 사람말고는 못 읽게 하는 게 아니라, 보내는 사람이 서명한 것이 맞다고 보여주는 것이 전자서명의 목적이기 때문이다. 즉 공개된 키가 아니라 보내는 사람만이 가지는 키로 암호화하는 것이 타당하다. 그 후에는 받는 사람이 보내는 사람의 공개키로 복호화하여 &#39;이게 A가 자신의 개인키로 서명한 것이 맞구나&#39;하고 확인을 할 수 있다.</p>
</blockquote>
<h3 id="3-전자봉투">3. 전자봉투</h3>
<p>PKI는 비대칭키를 사용하기 때문에 암호화 속도가 오래 걸린다. 대칭키처럼 암호화 속도는 빠르게 하면서도 비대칭키처럼 키 관리는 편하게 하는 기법이 전자봉투이다.
<img src="https://velog.velcdn.com/images/soo-im/post/0775564f-efb5-4664-9785-64d9b4792817/image.jpg" alt="Digital_envelope"></p>
<ol>
<li>A가 문서에 전자서명한다(원문을 해시값으로 만들어서 &#39;A의 개인키&#39;로 암호화)</li>
<li>A가 원문, 전자서명한 문서, &#39;A의 공개키&#39;를 묶는다. 이 묶음을 &#39;B와의 대칭키&#39;로 암호화한다. 이를 암호문이라고 부른다.</li>
<li>&#39;B와의 대칭키&#39;를 &#39;B의 공개키&#39;로 암호화한다. 이를 전자봉투라고 부른다.</li>
<li>2(암호문)와 3(전자봉투)을 B에게 보내준다.</li>
<li>B가 4를 받으면 역순으로 풀면 된다.</li>
<li>B가 &#39;B의 개인키&#39;로 3(전자봉투)를 복호화한다. 이제 &#39;A와의 대칭키&#39;가 생겼다.</li>
<li>&#39;A와의 대칭키&#39;를 이용해서 2(암호문)를 복호화한다. 이제 원문, 전자서명한 문서, &#39;A의 공개키&#39;가 생겼다.</li>
<li>남은 단계는 전자서명 검증과 똑같다. &#39;A의 공개키&#39;를 이용해서 복호화한 해시값을 확인하고, 자신이 받은 원문도 해시값으로 만들어 둘을 비교하면 된다.</li>
</ol>
<blockquote>
<p><strong>왜 키로 키를 싸는거야? (3번)</strong>
비대칭키는 속도가 느린 대신 관리가 편하고, 대칭키는 관리가 어려운 대신 속도가 빠르다고 했다. 그래서 대칭키로는 (용량이 큰) 데이터를 암호화한 다음에, 관리가 편한 비대칭키로 (용량이 작은) 대칭키를 암호화하는 것이다.</p>
</blockquote>
<blockquote>
<p><strong>결국 대칭키가 있으니까 관리해야 하잖아? 뭐가 좋다는 거야?(2번)</strong>
A랑 B가 항상 같은 대칭키를 써야한다면 그렇지만, 이제는 대칭키를 매번 바꾸어도 개인키로 암호화해서 그때그때 보내주면 되니까 상관없다! 즉 둘만 사용하는 일회용 자물쇠를 매번 B의 우편함에 꽂아주는 것이다.</p>
</blockquote>
<h2 id="2022-07-08">2022-07-08</h2>
<h3 id="1-블록체인-정의">1. 블록체인 정의</h3>
<p>분산화된 P2P 네트워크에서 모든 거래 내용이 기록, 검증 및 공개되는 데이터 저장 시스템이다.</p>
<h3 id="2-블록체인-동작-메커니즘">2. 블록체인 동작 메커니즘</h3>
<ol>
<li>PKI 기반으로 전자지갑을 생성한다. 개인키를 전자지갑에 저장한다.</li>
<li>A가 B에게 송금을 요청하면서 A의 개인키로 전자서명을 한다.</li>
<li>A의 전자서명된 트랜잭션이 참여자(노드)에게 전송된다. </li>
<li>참여자가 트랜잭션의 적정성을 확인한다.</li>
<li>그 중 한 참여자가 특정 문제를 해결하면 마이닝을 하면 블록이 생성된다.</li>
<li>불특정 다수의 블록체인 네트워크 참여자에게 블록을 전송(Broadcasting)한다.</li>
<li>모든 참여자가 해당 블록의 적정성을 확인하고 합의한다.</li>
<li>승인된 블록이 기존 블록체인에 연결이 된다.</li>
<li>A가 B에게 송금이 완료된다.</li>
</ol>
<h3 id="3-블록의-구성요소">3. 블록의 구성요소</h3>
<ol>
<li><p>헤더
1) 이전 블록의 해시 값 → 기존 블록의 무결성을 보장한다.
2) 타임스탬프; 블록이 생성된 시각
3) 논스; 블록의 해시값을 생성할 때 더해지는 값(솔트 비슷한 건가)</p>
</li>
<li><p>바디
1) 머클 루트(Merkle Root); 머클트리는 일정 시간동안 발생한 트랜잭션(거래 내역)과 그 해시값을 이진 트리 구조로 저장한 것으로, 트랜잭션의 무결성을 보장한다. 머클트리의 루트 부분을 머클 루트라고 부른다.</p>
</li>
</ol>
<h2 id="2022-07-11">2022-07-11</h2>
<h3 id="1-화폐-비교">1. 화폐 비교</h3>
<p><strong>1.1 디지털화폐 vs. 가상화폐 vs. 암호화폐</strong></p>
<table>
  <thead>
    <tr>
      <th>디지털화폐</th>
      <th>가상화폐</th>
      <th>암호화폐</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>법정화폐의 대체물로서 법정화폐을 더 사용하기 쉽게 형태를 바꾼 것이다. 네이버페이, 페이팔, 신용카드 등이 있다.</td>
      <td>특정 실물이 없이 네트워크로 연결된 특정 가상 공동체에서 사용하는 전자적 형태의 화폐이다(다만 디지털화폐와 구분이 명확히 되지 않는 경우도 있다). 싸이월드 도토리, 게임머니 등이 있다.</td>
      <td>중앙 관리자 없이 분산 장부(distributed ledger)에서 공개키 암호화를 통해 전송하고 해시함수로 소유권을 증명하는 디지털 자산이다. </td>
    </tr>
  </tbody>
</table>

<p><strong>1.2 비트코인</strong></p>
<ul>
<li>금융 기관 개입 없이 대금 결제가 가능한 순수한 P2P 버전의 전자화폐이다.</li>
<li>이중지불(같은 화폐를 복제하여 여러 번 결제)을 저지할 수 있는 믿는 제3자가 필요하지 않다.</li>
<li>타임스탬핑으로 거래를 기록한다. 거래의 해시값을 기반으로 한 작업증명이 연쇄적인 체인 형상으로 기록된다.</li>
</ul>
<h3 id="2-분산-원장-비교">2. 분산 원장 비교</h3>
<p><strong>2.1 분산 DB vs. 분산 원장 vs. 블록체인</strong></p>
<table>
  <thead>
    <tr>
      <th>분산DB</th>
      <th>분산 원장</th>
      <th>블록체인</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>논리적으로는 하나의 시스템에 속하지만 물리적으로는 여러 개의 장소에 분산되어 있는 DB. 분산된 노드의 데이터 일관성을 위해 트랜잭션을 관리하는 코디네이터가 있다.</td>
      <td>분산된 노드가 동일한 데이터를 가질 수 있도록 관리하는 합의 기술. 노드간 직접 접속 네트워크가 필요하고 노드 간 복제 데이터에 대한 합의 알고리즘이 수행되어야 한다.</td>
      <td>분산 원장의 한 형태. 변경이 불가능한 레코드 그룹을 암호화하고 연결한다.</td>
    </tr>
  </tbody>
</table>

<h3 id="3-블록체인-역사">3. 블록체인 역사</h3>
<ol>
<li><p>1세대
비트코인으로 인해 블록체인 논의가 본격적으로 시작되었다. 다만 가상통화 외의 응용가능성이 다소 낮다. 퍼블릭 블록체인이 시작되었다.</p>
</li>
<li><p>2세대
스마트 계약을 기반으로 다양한 응용이 가능하다. 이더리움으로 대표된다. 프라이빗 블록체인이 시작되었다.</p>
</li>
<li><p>3세대
범위가 확장되어 거버넌스 등에 적용된다. (아직 명확히 2세대와 3세대를 구분하지는 않는 듯 하다)</p>
</li>
</ol>
<h2 id="2022-07-12">2022-07-12</h2>
<h3 id="1-블록체인-특징">1. 블록체인 특징</h3>
<p><strong>1.1 투명성</strong></p>
<ul>
<li>블록체인 네트워크의 모든 참여자에게 정보가 공유됨</li>
<li>네트워크 참여자의 실시간 거래 모니터링이 가능함(가시성)</li>
<li>거래 가시성으로 인해 투명성, 부인방지가 가능함</li>
</ul>
<p><strong>1.2 보안성</strong></p>
<ul>
<li>참여자가 공동으로 정보를 소유하므로 해킹 위험이 낮음(다만 모든 노드를 해킹할 가능성 등을 배제할 수 없음)</li>
<li>보안성, 무결성, 유효성 위해 해시, PKI, 합의 알고리즘 사용</li>
<li>현재까지의 보안 위협은 주로 블록체인 자체보다 전자지갑 해킹 등에 맞추어져 있음</li>
</ul>
<p><strong>1.3 비용 절감</strong></p>
<ul>
<li>거래 중개 기관이 불필요하므로 수수료 절감(예: 해외송금시 은행/SWIFT 수수료 발생)</li>
<li>중앙 시스템 구축, 유지비용 절감</li>
</ul>
<p><strong>1.4 비가역성, 무결성</strong></p>
<ul>
<li>한 번 저장하면 이전 상태로 돌릴 수 없고(비가역성), 저장된 데이터의 위/변조가 불가(불변성)하므로 무결성 가짐</li>
<li>신뢰의 강도는 네트워크 크기에 비례함</li>
<li>한 곳에 모든 데이터를 보관하지 않으므로 SPOF (Single Point Failure) 없음</li>
</ul>
<h3 id="2-블록체인-분류">2. 블록체인 분류</h3>
<p>네트워크 참여 방식에 따라 블록체인 유형이 public/private/consortium으로 나뉜다.</p>
<p><strong>2.1 Public</strong></p>
<ul>
<li><p>네트워크 참여에 제한이 없다. 누구나 네트워크에 참여하고 읽고 쓸 수 있다.</p>
</li>
<li><p>운영 주체가 없기 때문에 코인을 발행하고 코인에 가치를 부여함으로써 네트워크를 유지시킨다.</p>
</li>
<li><p>대표적인 예시는 비트코인, 이더리움.</p>
</li>
<li><p>장점</p>
<ul>
<li>참여자가 늘어날수록 투명성과 보안성이 커진다.</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>확장성 제한: 블록 하나의 크기가 정해져 있어 많은 양의 데이터를 넣을 수 없다.</li>
<li>제한된 프라이버시: 선택적으로 정보를 공개할 수 없고 모든 노드에 공개해야 한다.</li>
<li>계약 검증 취약: 거래의 신뢰성을 보장하는 제3자가 없어 계약의 정당성과 합법성을 검증할 수 없다.</li>
<li>느린 속도: 블록 시계열을 생성하기 때문에 병렬 처리를 할 수 없어 속도가 느리다.</li>
<li>합의 알고리즘: PoW, PoS 외에도 다양한 합의 알고리즘이 있으나 전원이 동의하는 알고리즘은 없다.</li>
<li>법적/책임 리스크: 사전에 노드 구성원 파악이 되지 않아 책임 소재 문제가 있다.<ul>
<li>양자컴퓨터의 위협: 양자컴퓨터로 인해 해시 기반 암호 체계가 붕괴될 위험이 있다. (해시 해독에 슈퍼컴퓨터로는 1여년이 걸리지만 양자컴퓨터로는 수시간이 걸린다. 공인인증서를 수시간마다 업데이트해야 하는 상황이 벌어진다...)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>2.2 Private</strong></p>
<ul>
<li>사전 합의된 규칙을 준수하는 자만 참여할 수 있다.</li>
<li>관리자가 모든 권한을 가지고 있다.</li>
<li>참여자가 적어 룰 변경 등이 매우 빠르다. 그러나 Public에 비해 투명성과 보안성이 떨어진다.</li>
<li>단일 집단의 프로젝트에 적합하다.</li>
</ul>
<p><strong>2.3 Consortium</strong></p>
<ul>
<li>참여자 각각의 동의를 받아야 참여할 수 있다. Public과 Private의 중간 단계.</li>
<li>소속된 참여자가 권한을 가지고 있다.</li>
<li>Public은 모든 참여자가 블록을 검증하고 Private은 단일 주체가 블록을 생산하는데 반해 Consortium은 소수의 주체가 블록을 검증한다.</li>
<li>한정된 다자간의 공통 트랜잭션을 공유해야 하는 경우 적합하다.</li>
</ul>
<p><strong>2.4 요약</strong></p>
<ul>
<li>투명성과 익명성이 중요한 경우 → Public</li>
<li>안정성과 속도가 중요한 경우 → Private</li>
<li>협의를 통한 이해관계 조정이 필요한 경우 → Consortium</li>
</ul>
<h2 id="2022-07-13">2022-07-13</h2>
<h3 id="1-블록체인-특성">1. 블록체인 특성</h3>
<p><strong>1.1 왜 블록체인을 선택할까?</strong>
단순히 정보를 저장하려는 목적만 있다면 DB를 사용하는 것이 나을 수도 있다. 블록체인 사업을 기획하려면 &#39;블록체인을 사용할 때 어떤 이슈가 해결되는지&#39;를 충분히 검토해야 한다. 아래와 같은 블록체인 특성을 고려하여서 &#39;왜 블록체인을 사용하는지&#39;를 설명해야 한다.</p>
<ol>
<li>공유성
모든 노드가 정보를 공유한다.</li>
<li>투명성
정보를 공유하기 때문에 숨기는 항목이 (거의) 사라진다.</li>
<li>무결성
정보 조작이 어렵다.</li>
<li>신뢰성
무결한 정보를 공유함으로써 노드 사이에 신뢰가 형성된다.</li>
<li>탈중앙성
(1) 중개비용 감소; 제3신뢰중개기관(Trusted Third Party)을 거치지 않아 중개기관에서 발생하는 비용, 시간을 절약할 수 있다. (해외송금할 때 중개수수료를 보낼 필요가 없는 것처럼)
(2) 공유비용 감소; 제3자를 거치지 않고 노드에 공유가 되기 때문에 공유에 드는 비용을 줄일 수 있다. (다만 합의 알고리즘 때문에 공유 전체에 걸리는 시간은 DB보다 오래 걸릴 수밖에 없다. 제3자를 거칠 때보다 시간이 줄어든다는 이야기.)
(3) 개인 권리 강화; 개인정보를 중앙 기관에 맡기지 않고 블록체인으로 개인 이력을 추적할 수 있어서(내 개인정보가 어디에 쓰이는지 파악할 수 있어서) 개인의 권리가 강화되는 효과가 있다.</li>
</ol>
<p><strong>1.2 왜 블록체인을 선택하지 않을까?</strong>
항상 블록체인이 해법은 아니다. 만약 DB로 충분히 해결 가능한 비즈니스라면 굳이 블록체인을 도입할 필요는 없다. 블록체인은 아래와 같은 문제에 취약하다.</p>
<ol>
<li>낮은 효율성
새로운 블록이 추가되려면 모든 블록의 암호화 확인 절차가 필요하다. 이 때문에 처리속도가 느려, 빠른 거래가 필요한 분야에는 적절하지 않다.</li>
<li>병렬화 불가
앞의 블록에 연결하는 &#39;체인&#39; 형태이기 때문에 여러 블록을 병렬처리할 수 없다. 하나가 처리된 후에 다음을 처리하는 직렬처리만 가능하다.</li>
<li>미흡한 기술/소프트웨어
블록체인 관련 프로젝트 개발 수준이 다소 미흡한 면이 있다.</li>
<li>안전 위험
블록체인은 암호화 기술에 의존하고 있는데, 모든 프로젝트의 알고리즘의 안전성이 검증되지는 않았다.</li>
<li>신뢰성
신뢰성을 높이려면 많은 노드가 필요하지만 많은 노드가 들어오면 비용이 증가한다(불확정성 원리처럼 신뢰성를 올리면 효율이 떨어지는...OFFSET 관계)</li>
<li>확장성
블록체인은 많은 양의 트랜잭션을 처리하기 어렵다. 사용자 수와 거래 수가 증가하면 한정된 블록 크기로는 이를 처리하기 어렵다.</li>
</ol>
<h3 id="2-public-보완책">2. Public 보완책</h3>
<p><strong>2.1 세그윗(Segregated Witness)</strong>
블록 사이즈가 커지면 속도가 느려지기 때문에 용량을 크게 늘릴 수는 없다. 그 대안으로 트랜잭션과 디지털 서명을 모두 담는 기존 방식과 달리 트랜잭션만 블록 안에 넣고 디지털 서명은 따로 저장하는 방식을 세그윗이라고 한다.</p>
<p><strong>2.2 라이트닝 네트워크(Lightning Network)</strong>
비트코인은 거래가 발생할 때마다 블록에 기록한다(On-Chain 방식). 그 대안으로 별도의 채널에서 트랜잭션만 주고받다가 일정 주기 후에 최종 정산 값만 저장하는 방식을 라이트닝 네트워크라고 한다.</p>
<p><strong>2.3 작업증명 방식 전환</strong>
현재 이더리움은 작업증명(Proof of Work) 방식을 사용하는데, 이를 지분증명(Proof of Stake) 방식으로 변경할 계획이다.</p>
<ul>
<li>PoW: 컴퓨팅 파워를 이용해서 문제를 해결하는 사람이 채굴</li>
<li>PoS: 암호화폐 지분량에 비례하여 채굴 권한을 부여</li>
<li>DPoS: 암호화폐 지분을 가진 사람 중 특정인을 투표를 통해 선발하여 채굴 권한을 부여</li>
</ul>
<p><strong>2.4 Private Blockchain</strong>
Private도 Public의 대안으로 나온 개념이다. 특정 노드만 참여하기 때문에 높은 신뢰성, 빠른 처리속도, 높은 확장성을 가진다. (거래비용, 개방성, 탈중앙성을 낮추는 대신 속도와 확장성을 올리는 방식)</p>
<h3 id="3-이더리움">3. 이더리움</h3>
<p><strong>3.1 배경</strong></p>
<ul>
<li>비트코인 코드는 활용하기 어려워(예: loop가 안 돌아간다...) 탈중앙화된 어플리케이션을 만들기 위한 스크립팅 언어의 필요성이 생겼다.</li>
<li>2015년 7월 완벽한 튜링 언어(Turing-complete language)가 내장된 블록체인이 배포되었다.</li>
</ul>
<p><strong>3.2 특징</strong></p>
<ul>
<li>스마트 컨트랙트로 어플리케이션을 만들 수 있는 오픈소스 소프트웨어이다.</li>
<li>이를 이용해서 EVM(Etherium Virtual Machine)에서 동작하는 어플리케이션을 DApp이라고 한다.</li>
<li>비트코인과 유사하게 블록 번호, 이전 블록 정보, 해시트리, 트랜잭션 정보 등을 담고 있고 그 외 엉클 블록(uncle block: 첫 번째가 아닌 다음 순서에 마이닝된 블록)과 가스(화폐와 유사) 등의 개념이 추가되었다.</li>
</ul>
<p><strong>3.3 등록 방식 및 GAS</strong></p>
<ol>
<li>거래를 등록하려는 사람이 임의의 GAS량을 책정해서 거래를 등록한다.</li>
<li>채굴이 되면 정산이 이루어지고, 책정된 GAS가 채굴자에게 전송된다. (은행에 내던 수수료를 여기다 주는 느낌...?)</li>
</ol>
<ul>
<li>장점: 거래 등록에 GAS가 소모되기 때문에 무분별한 거래 등록으로 인한 트래픽 과중을 방지한다.</li>
<li>단점: GAS가 없으면 거래할 수 없다.
<img src="https://velog.velcdn.com/images/soo-im/post/8bae7eb7-08ff-42a0-bd68-9a0a5d5866d5/image.png" alt="Ethereum_process"></li>
</ul>
<h2 id="2022-07-14">2022-07-14</h2>
<h3 id="1-이더리움-20">1. 이더리움 2.0</h3>
<p>(현재 개발 중!)</p>
<ul>
<li>지분 증명
작업증명 방식은 블록체인 규모가 커질수록 연산 자원을 많이 필요로 하기 때문에 블록체인 확장을 어렵게 한다. 따라서 일정 조건의 이더리움을 보유한 노드에게 검증을 맡기는 지분 증명 방식으로 전환한다.</li>
<li>샤딩
샤드는 DB의 데이터 중 일부를 분할하여 관리하는 것을 말한다. (엑셀 파일 행이 너무 많으면 일부 행을 추출해서 다른 파일로 저장하듯이) 이더리움의 샤딩은 각 노드에 모든 정보를 저장하지 않고 일부 정보만 관리하는 방식이다. 이를 통해 트랜잭션 처리량과 이더리움 전체 용량이 증가할 수 있다.</li>
<li>비콘 체인
병렬처리를 하려면 노드 간의 상태를 동기화시켜주어야 한다. 비콘 체인은 병렬로 운영되는 모든 샤드 체인의 동기화를 지원한다.</li>
</ul>
<blockquote>
<p>시계열로 저장해야 하는데 어떻게 병렬처리를 하지?</p>
</blockquote>
<h3 id="2-하이퍼레저-hyperledger">2. 하이퍼레저 Hyperledger</h3>
<p><strong>2.1 채널 기능</strong>
<img src="https://velog.velcdn.com/images/soo-im/post/3793901f-96df-4f41-aed7-809d33d9fda5/image.png" alt="Hyperledger_chanel"></p>
<p>하이퍼레저는 한 블록체인 네트워크 안에 여러 개의 &#39;채널&#39;을 만들 수 있다. A, B, C, D라는 네 사람이 블록체인 네트워크를 만들었다고 가정해보자. 그 와중에 A, C는 자기들끼리만 블록체인을 보고 싶다고 하자. 이더리움이었다면 A, C만 참여하는 블록체인 네트워크를 다시 만들어야 한다. 하지만 하이퍼레저를 이용한 네트워크에서는 &#39;채널 1&#39;을 하나 만들어 &#39;채널 1&#39;만 볼 수 있는 &#39;블록체인&#39;을 따로 만들 수 있다. 하나의 네트워크 안에서 여러 개의 채널과 블록체인을 만들고 참여 권한을 부여할 수 있다는 점이 하이퍼레저의 큰 특징이다.</p>
<p><strong>2.2 합의 알고리즘</strong>
<img src="https://velog.velcdn.com/images/soo-im/post/6eb42b02-a3c8-4ca4-9f97-4f288ca633da/image.png" alt="Hyperledger_process"></p>
<ol>
<li>클라이언트가 트랜잭션(거래)을 요청한다.</li>
<li>Endorser(보증) 참여자에게 해당 트랜잭션이 적합한지 체인코드를 이용해 시뮬레이션한다. 적합하다고 판단하면 시뮬레이션 결과 파일에 전자서명을 해서 클라이언트에게 보내준다.</li>
<li>클라이언트는 Endorser로부터 거래가 적합하다는 인증을 받으면 Orderer(순서) 서비스에게 블록 생성을 요청한다.</li>
<li>Orderer는 클라이언트들로부터 받은 거래의 타임스탬핑을 보고 거래의 순서를 정렬한다.</li>
<li>순서 정렬이 끝나면 Orderer가 블록을 생성해서 Committer 참여자에게 보낸다.</li>
<li>Committer 참여자들이 블록을 한 번 더 확인한 후 블록을 확정해서 모든 노드에 전송한다.</li>
</ol>
<h2 id="2022-07-15">2022-07-15</h2>
<h3 id="1-하이퍼레저-hyperledger-이어서">1. 하이퍼레저 Hyperledger (이어서)</h3>
<p><strong>1.1 Private Data</strong>
비즈니스 정보나 개인정보를 다룰 때에는 공유의 목적을 달성하면 그 데이터(private 데이터)를 지울 필요가 있다. 하이퍼레저는 개별 채널을 만들지 않고도 private 데이터 컬렉션을 생성하는 기능을 제공한다. private 데이터 삭제를 요청하면 노드 전체에서 삭제가 된다.</p>
<p><strong>1.2 Raft (합의 알고리즘)</strong>
앞에서 Orderer가 거래의 순서를 정렬한 후 블록을 생성한다고 설명했다. 이 과정에서 Orderer가 여러 노드일 수도 있는데, 이 때 블록을 생성하는 노드를 Leader라고 부른다. 특정 주기마다 Orderer 중에서 Leader을 투표로 선출하고 그 Leader가 블록을 만드는 방식을 Raft 합의 알고리즘이라 부른다.</p>
<p><strong>1.3 하이퍼레저가 적합한 프로젝트</strong></p>
<ul>
<li>토큰이 필요없는 경우</li>
<li>기밀성 정보와 공유 정보가 동시에 필요한 경우(channel)</li>
<li>비가역성 데이터와 삭제해야 할 데이터(private)가 있는 경우</li>
</ul>
<h3 id="2-chains">2. Chains</h3>
<ul>
<li>Onchain
블록체인 네트워크에서 일어나는 트랜잭션을 OnChain이라 부른다. 지금까지 살펴본 모든 거래를 OnChain이라고 보면 된다.</li>
<li>OffChain
반대로 블록체인 네트워크 외부에서 발생하는 트랜잭션을 OffChain이라 부른다. 기존에 사용하는 중앙화 방식을 말한다. 중앙 기관에서 데이터를 관리하고 그 정보를 블록체인 네트워크와 연결한다.</li>
<li>InterChain
서로 다른 블록체인을 연결하는 네트워크를 말한다.</li>
<li>SideChain
블록체인 메인체인의 하부 체인을 말한다. 사이드체인은 자체적인 합의 알고리즘을 가지지만 메인체인이 없으면 동작하지 않는다.</li>
</ul>
<h3 id="3-블록체인-오라클-문제">3. 블록체인 오라클 문제</h3>
<p><strong>3.1 블록체인 오라클 문제</strong>
블록체인 외부의 데이터(예: 웹 API에서 받은 정보)를 블록체인 내부로 가져오는 도구를 블록체인 오라클(중개자)이라고 부른다.
이 경우, 외부 데이터가 잘못되었거나 오라클이 정보를 조작할 가능성을 배제할 수 없다. 블록체인 자체가 신뢰성을 보장한다 하더라도 입력되는 데이터 자체를 신뢰할 수 없는 문제가 발생한다. 이를 블록체인 오라클 문제라고 부른다. 블록체인 서비스를 기획할 때에는 위 문제를 방지하는 데이터 검증 과정을 반드시 고려해야 한다.</p>
<p><strong>3.2 블록체인 오라클 문제 해결 방안</strong></p>
<ul>
<li>투표
지분증명 방식 등을 기반으로 투표를 해서 데이터 신뢰성을 판단한다.</li>
<li>중앙값
왜곡된 데이터를 피하기 위해 데이터 셋 중 MEDIAN을 선택한다. 데이터를 정렬할 수 없는 경우라면 사용하기 어렵다.</li>
<li>중간자
신뢰할 수 있는 데이터 제공자(중간자)를 두는 방법이다. 다만 중간자 조직은 새로운 형태의 중앙이 될 가능성이 있기 때문에 탈중앙화 관점에서는 적절하지 않을 수 있다.</li>
</ul>
<h2 id="2022-07-19">2022-07-19</h2>
<h3 id="1-합의-알고리즘">1. 합의 알고리즘</h3>
<p><strong>1.1 정의</strong></p>
<p><strong>1.2 선택 규칙</strong>
합의 알고리즘은 &#39;블록을 누가 생성할 것인지&#39;, &#39;만약 블록이 여러 개가 동시에 생겨서 체인이 갈라졌으면(체인포크) 어디에 뒷 블록을 붙일 것인지&#39;를 결정해야 한다.</p>
<ol>
<li>블록선택 규칙
누구에게 블록생성 권한을 줄 것인지 정하는 규칙이 &#39;블록선택 규칙&#39;이다. (예: PoS, PoW)</li>
<li>체인선택 규칙
여러 사람이 블록을 생성하다보면 체인이 한 가닥이 아니고 여러 갈래로 나뉠 수 있다(체인포크). 이렇게 되면 다음 블록을 어느 갈래에 붙여야할 지 정해야 한다. 이 때 어느 갈래를 선택할지 결정하는 규칙이 &#39;체인선택 규칙&#39;이다. (예: 비트코인-Longest chain, 가장 긴 체인을 선택, 이더리움-Ghost, 트랜잭션이 가장 많은 체인을 선택)</li>
</ol>
<p><strong>1.3 경쟁방식 합의 알고리즘</strong></p>
<ul>
<li>특징
동시에 여러 노드가 합의에 참여하고 경쟁을 통해 특정 조건을 만족하는 하나의 합의만을 인정한다. 하나만 인정함으로써 체인의 통일성을 유지한다. </li>
<li>장점
모든 노드가 증명에 참여할 필요가 없고, 악의적 비참여가 문제가 되지 않는다.</li>
<li>단점
여러 노드가 블록생성에 참여하기 때문에 블록이 동시에 여러 개가 생성되는 체인포크가 발생할 수 있다. 체인선택 규칙에 의해 선택하지 못한 체인은 작업 내용이 무효화되기 때문에 리소스 낭비가 발생한다.</li>
<li>예시: DPoS, PoS 등</li>
</ul>
<p><strong>1.4 비경쟁방식 합의 알고리즘</strong></p>
<ul>
<li>특징
한 번에 하나의 합의만을 진행한다. 정해진 노드만이 블록을 생성하기 때문에 포크가 생기지 않아 체인선택 규칙이 그다지 중요하지 않다. </li>
<li>장점
빠르게 합의가 가능하고 리소스를 낭비하지 않는다.</li>
<li>단점
블록 생성 노드를 정하기 때문에 악의적인 투표가 발생할 수 있고, 탈중앙화 효과가 다소 떨어진다.</li>
<li>예시: PBFT 등</li>
</ul>
<h3 id="2-powproof-of-works">2. PoW(Proof of Works)</h3>
<p><strong>2.1 특징</strong></p>
<ul>
<li>어려운 문제를 빨리 푼 노드에게 블록 생성 권한을 준다.</li>
<li>과반수 이상의 동의를 얻은 블록을 체인에 연결한다.</li>
<li>거래내용을 조작하기 위해서는 과반수 이상의 참여자보다 빠르게 배포하거나, 노드의 절반 이상을 차지해야 한다.</li>
<li>블록이 생기기 전까지 이중지불(하나의 화폐로 여러 번 결제)이 발생할 수 있다. 이를 방지하기 위해 UTXO 형식으로 거래한다. UTXO는 일종의 돈봉투같은 것인데, 거래할 때 특정 금액을 담을 수 있는 돈봉투(UTXO)를 보내는 방식이다. UTXO의 이동은 트랜잭션과 무관하게 바로 일어나기 때문에 존재하지 않는 화폐로 지불하는 이중지불을 방지한다.<blockquote>
<p><strong>UTXO(Unspect Transaction Outputs)</strong>
코인은 지갑에 저장되는 것이 아니라 UXTO에 저장되어 있고, UTXO가 소유자의 지갑 주소로 연결되는 방식이다. 거래를 하면 지불자는 본인 지갑에 연결된 UXTO 만큼 거래를 할 수 있다. 예를 들어 5코인/3코인/2코인 UTXO가 내 지갑에 연결되어 있으면 나는 10코인을 소유한 것과 같다. 만약 내가 11코인을 지불하려고 한다면 해당 UTXO가 없기 때문에 지불이 일어나지 않는다.
<a href="https://steemit.com/kr/@brownbears/utxo">구체적인 설명</a>은 여기 잘 나와 있다.</p>
</blockquote>
</li>
</ul>
<p><strong>2.2 장점</strong></p>
<ul>
<li>보안성이 높다.</li>
</ul>
<p><strong>2.3 단점</strong></p>
<ul>
<li>체인포크가 발생할 수 있다.</li>
<li>전력 등 리소스 낭비가 크다.</li>
<li>합의가 오래 걸린다.</li>
</ul>
<h3 id="3-posproof-of-stake">3. PoS(Proof of Stake)</h3>
<p><strong>3.1 특징</strong></p>
<ul>
<li>노드가 보유한 암호화폐 지분에 비례하여 블록 생성 권한을 준다.</li>
<li>거래내용을 조작하기 위해서는 과반 이상의 암호화폐를 가져야 한다.</li>
</ul>
<p><strong>3.2 장점</strong></p>
<ul>
<li>리소스 낭비가 적다.</li>
<li>지분을 담보로 잡고 있으므로 블록 생산자가 네트워크에 부정적인 영향을 미칠 확률이 적다.</li>
</ul>
<p><strong>3.3 단점</strong></p>
<ul>
<li>체인포크가 발생할 수 있다.</li>
<li>PoW에 비해 보안성 검증이 되지 않았다.</li>
<li>초반 지분을 기준으로 하기 때문에 지분이 높은 노드가 권력을 독점할 수 있다.</li>
</ul>
<h3 id="4-dposdelegated-proof-of-stake">4. DPoS(Delegated Proof of Stake)</h3>
<p><strong>4.1 특징</strong></p>
<ul>
<li>지분을 가진 노드(증인)의 표를 받은 노드에게 블록 생성 권한을 준다. 마이너와 마이너에게 투표한 노드들에게 블록 생성 보상이 주어진다.</li>
</ul>
<p><strong>4.2 장점</strong></p>
<ul>
<li>PoS에 비해 빠르게 처리가 가능하다.</li>
<li>PoW에 비해 비용이 낮다.</li>
<li>증인이 투표에 참여할 인센티브가 분명하다.</li>
</ul>
<p><strong>4.3 단점</strong></p>
<ul>
<li>증인 간 담합 위험이 있다.</li>
<li>공개된 소수의 증인에 대한 디도스 공격 위험이 있다.</li>
</ul>
<h3 id="5-기타-합의-알고리즘">5. 기타 합의 알고리즘</h3>
<p><strong>5.1 PoA(Proof of Authority)</strong></p>
<ul>
<li>지분을 지불해서 Authority를 증명한 노드에게 블록 생성 권한을 준다. (Authority를 증명한 노드들 중에서 가장 &#39;평판&#39;이 좋은 노드가 선발된다. 그치만 &#39;평판&#39;이 정확히 뭔지는 잘 모르겠다...)</li>
<li>하나의 노드가 블록을 생성하는 중앙집중 형태이다.</li>
</ul>
<p><strong>5.2 RAFT</strong>
(자세한 설명은 Hyperledger 내용에 있음)</p>
<ul>
<li>복수의 Orderer에게 노드가 투표를 해서 블록을 생성할 Leader을 선출한다.</li>
</ul>
<h3 id="5-스마트-컨트랙트">5. 스마트 컨트랙트</h3>
<p><strong>5.1 스마트 컨트랙트</strong></p>
<ul>
<li>특정 계약을 스스로 수립, 검증, 이행하기 위한 컴퓨터 프로그램</li>
<li>블록체인 외부에서 일어나는 일을 블록체인 내부로 끌어오기 위해 사용자가 실행하는 프로그램. 스마트 컨트랙트 실행 결과는 블록체인 네트워크에 저장되어 노드에게 공유된다.</li>
<li>계약 당사자가 계약 조건과 계약 내용을 미리 프로그래밍한다. 이후 별도의 계약 중재자 없이도 계약 조건이 달성되면 계약 내용이 자동으로 실행된다.</li>
<li>스마트 컨트랙트는 블록체인의 모든 노드에게 배포된다.</li>
<li>블록체인은 스마트 컨트랙트를 구현한 최초의 사례이다.</li>
</ul>
<p><strong>5.2 유형</strong></p>
<ol>
<li>코드내재 계약형
계약 조건이 스마트 컨트랙트에 들어가 있어 계약까지 자동으로 실행되는 경우</li>
<li>코드외재 계약형
계약 자체는 외부에서 이루어지고, 그 계약의 결과가 블록체인 네트워크에 저장되는 경우 (법적 책임을 따져야하는 경우 사용함)</li>
</ol>
<blockquote>
<p><strong>변화 관리</strong>
기존 사업모델을 스마트 컨트랙트로 이관하려면 결국 이해관계자 간의 합의가 필요하다. 합의하고 사업 모델을 변경하는 과정에서 불편이 발생할 수밖에 없고, 기획자는 이해관계자 간의 의견을 잘 조율하여야 한다. 이를 &#39;변화 관리&#39;라고 부른다.</p>
</blockquote>
<h2 id="2022-07-20">2022-07-20</h2>
<h3 id="1-스마트-컨트랙트이어서">1. 스마트 컨트랙트(이어서)</h3>
<p><strong>1.1 블록체인과 스마트 컨트랙트의 관계</strong>
<img src="https://velog.velcdn.com/images/soo-im/post/895590e2-ff11-4bc7-9c0f-d3601aa67706/image.jpg" alt="BlockChain_and_SmartContract"></p>
<p>블록체인의 각 노드는 이전까지의 거래내역이 담긴 블록체인과, 현재 잔고(?) 상태를 보여주는 World State, 스마트 컨트랙트를 가지고 있다. 스마트 컨트랙트를 실행하면 그 상태는 World State에 저장되고 거래 결과는 블록체인에 저장된다.</p>
<blockquote>
<p><strong>World State</strong>
이전 거래 내역이 아니라 &#39;현재&#39;의 잔고만 보여준다. 블록체인이 거래 내역이 나오는 통장이라면, World State는 잔고만 나오는 ATM 화면이다. 위에서 말한 비트코인의 UTXO의 기능은 World State의 기능과 대응하는 면이 있다.</p>
</blockquote>
<p><strong>1.2 기존 계약 vs. 스마트 컨트랙트</strong></p>
<table>
  <thead>
    <tr>
      <th>절차</th>
      <th>기존 계약</th>
      <th>스마트 컨트랙트</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>두 당사자가 계약을 희망한다.</td>
      <td>두 당사자가 계약을 희망한다.</td>
    </tr>
    <tr>
      <td>2</td>
      <td>협상을 위해 두 당사자가 모두 변호사를 고용한다.</td>
      <td>두 당사자가 모든 계약조건에 동의한다.</td>
    </tr>
    <tr>
      <td>3</td>
      <td>두 당사자가 모든 계약조건에 동의한다.</td>
      <td>계약조건을 컴퓨터가 인식 가능한 코드로 작성한다.</td>
    </tr>
    <tr>
      <td>4</td>
      <td>계약조건이 이행되지 않으면 변호사 등이 계약이행을 강제한다.</td>
      <td>분산원장체계에 의해 계약조건이 시행된다.</td>
    </tr>
  </tbody>
</table>


<p><strong>1.3 스마트 컨트랙트 이슈</strong>
현실 세계의 계약을 스마트 컨트랙트로 변환하려면 아래 이슈를 고민해야 한다.</p>
<ol>
<li>모든 노드가 계약의 내용을 볼 수 있어 프라이버시/개인정보 유출 위험 있음</li>
<li>계약의 &#39;성립&#39;과 &#39;이행&#39;을 구분하는 현재 민법과 개념이 대치됨</li>
<li>문제 발생시 책임 소재가 불분명함</li>
</ol>
<h3 id="2-dappdecentralized-application">2. DApp(Decentralized Application)</h3>
<p><strong>2.1 DApp 구조</strong>
탈중앙화된 블록체인을 이용해서 서비스를 제공하는 어플리케이션. DApp은 스마트 컨트랙트를 호출해서 스마트 컨트랙트를 실행한다.</p>
<p><strong>2.2 전통적인 앱 vs. DApp</strong></p>
<p><img src="https://velog.velcdn.com/images/soo-im/post/e577858c-f3c4-449d-9409-f9569b6a79a1/image.jpg" alt="App_vs_DApp"></p>
<table>
  <thead>
    <tr>
      <th>특징</th>
      <th>전통적인 앱</th>
      <th>DApp</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>구동방식</td>
      <td>프론트↔API↔데이터베이스</td>
      <td>프론트↔스마트 컨트랙트↔블록체인</td>
    </tr>
    <tr>
      <td>가용성</td>
      <td>서버가 다운되면 정지</td>
      <td>모든 노드가 다운되지 않는 한 정지 X</td>
    </tr>
    <tr>
      <td>정보 투명성</td>
      <td>데이터베이스 접근 권한 필요</td>
      <td>모든 노드에게 공개</td>
    </tr>
    <tr>
      <td>비용</td>
      <td>일반적으로 사용 자체는 무료</td>
      <td>일반적으로 코인 필요</td>
    </tr>
    <tr>
      <td>거래 속도</td>
      <td>상대적으로 빠름</td>
      <td>상대적으로 느림</td>
    </tr>
  </tbody>
</table>

<p><strong>2.3 DApp 유형</strong>
모든 정보를 블록체인(OnChain)에 저장하는 대신 전통적인 데이터베이스(OffChain)를 함께 사용하는 경우도 있다.</p>
<ol>
<li>Contract Only
스마트 컨트랙트만으로 구성된 DApp</li>
<li>Save Data Only
OffChain과 함께 사용하고, OncChain은 데이터 저장용으로만 사용하는 DApp</li>
<li>Hybrid
OffChain과 OnChain이 하나의 서비스를 만드는 DApp</li>
</ol>
<h2 id="2022-07-21">2022-07-21</h2>
<h3 id="1-digital-wallet">1. Digital Wallet</h3>
<p><strong>1.1 Digital Wallet</strong>
디지털자산을 주고받거나 거래하기 위한 개인의 &#39;주소&#39;. 이용자가 블록체인 네트워크 상에서 디지털자산 지갑을 개설하면 공개키와 개인키를 부여받는다.
디지털자산 지갑은 키와 암호화폐 주소를 관리해서 암호화폐 전송과 보관을 할 수 있게 만든다.</p>
<p><strong>1.2 Digital Wallet 종류</strong>
지갑의 종류는 물리적 형태, 온라인 접속 여부에 따라 구분한다.</p>
<table>
  <thead>
    <tr>
      <th>종류</th>
      <th>Hot Wallet</th>
      <th>Cold Wallet</th>
      <th>Cloud Wallet</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>개념</td>
      <td>소프트웨어 지갑</td>
      <td>물리적 지갑(보안USB 등)</td>
      <td>클라우드 내 소프트웨어 지갑</td>
    </tr>    
    <tr>
      <td>물리적 실체</td>
      <td>없음</td>
      <td>있음</td>
      <td>없음</td>
    </tr>
    <tr>
      <td>네트워크 연결</td>
      <td>상시 연결</td>
      <td>상시 연결 X</td>
      <td>상시 연결을 "보장"</td>
    </tr>
    <tr>
      <td>해킹 위험</td>
      <td>높음</td>
      <td>낮음</td>
      <td>높음</td>
    </tr>
  </tbody>
</table>

<blockquote>
<p><strong>Hot Wallet vs. Cloud Wallet</strong>
둘 다 온라인에 연결된 소프트웨어 지갑이다. 다만 Hot Wallet은 디바이스를 종료하거나 로컬 스토리지에서 삭제하면 키에 접근할 수 없지만, Cloud Wallet은 그와 무관하게 상시 네트워크에 연결 되어있어 언제든지 접근할 수 있다.</p>
</blockquote>
<h3 id="2-nftnon-fungible-token">2. NFT(Non-Fungible Token)</h3>
<p><strong>2.1 NFT</strong>
디지털 자산의 고유한 가치와 소유권을 기록하기 위한 블록체인 기반 &#39;토큰&#39;. 현재 NFT는 대부분 이더리움을 기반으로 한다.</p>
<blockquote>
<p><strong>코인 vs. 토큰</strong></p>
</blockquote>
<table>
  <thead>
    <tr>
      <th>종류</th>
      <th>코인</th>
      <th>토큰</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>개념</td>
      <td>물리적인 화폐와 유사한 디지털 화폐</td>
      <td>특정 프로젝트에서 발행한 디지털 자산</td>
    </tr>    
    <tr>
      <td>블록체인 네트워크</td>
      <td>특정 블록체인 네트워크 내에서 유통</td>
      <td>여러 블록체인 네트워크에서 유통 가능</td>
    </tr>
    <tr>
      <td>용도</td>
      <td>결제</td>
      <td>디지털 동의, 소유권 이전 등</td>
    </tr>
    <tr>
      <td>예</td>
      <td>이더리움, 리플</td>
      <td>BON, DAO 토큰</td>
    </tr>
  </tbody>
</table>

<p><strong>2.2 교환/대체 불가</strong></p>
<ul>
<li>특정 &#39;자산&#39; 그 자체를 다루기 때문에 교환/대체가 불가능하다. (내가 그린 기린 그림을 네가 그린 구름 그림으로 대체할 수 없다는 것과 유사한 느낌)</li>
<li>블록체인을 이용해 디지털 자산에 고유한 값을 부여한 인증서가 있어 대체가 불가능하다.</li>
<li>자산 그 자체가 아니라 &#39;소유권&#39;을 교환한다.</li>
</ul>
<p><strong>2.3 NFT Life Cycle</strong></p>
<ol>
<li>탈중앙화 저장소에(IPFS 등) 원본 파일을 등록한고 NFT를 발행한다(Minting).</li>
<li>마켓플레이스에서 소유권 판매/구매가 일어난다.</li>
<li>소유권 보유자는 마켓플레이스에서 재판매 할 수 있다.</li>
</ol>
<blockquote>
<p><strong>왜 블록체인에 파일을 올리지 않고?</strong>
블록체인은 대용량의 파일을 올리기에 적절하지 않다. 그래서 파일은 탈중앙화된 저장소에 업로드하고, 업로드된 파일의 주소를 (무결성 검증을 한 후) 블록체인에 저장한다.</p>
</blockquote>
<h2 id="2022-07-22">2022-07-22</h2>
<h3 id="1-web30">1. Web3.0</h3>
<p><strong>1.1 Web1.0</strong>
정보 공유가 주목적인 HTML로만 이루어진 단방향 웹. 생산자만 콘텐츠 업로드가 가능하고 사용자는 불가능한 초기 웹 형태.</p>
<p><strong>1.2 Web2.0</strong>
개방, 공유, 협력을 가치로 하는 양방향 웹. 다른 사용자의 콘텐츠에 참여할 수 있다.
그러나 웹서비스 소유자가 사용자의 동의 없이 운영 정책을 변경하고, 데이터를 제3자에게 제공하여 프라이버시를 침해하고, 서비스 수익은 사용자의 참여로부터 나옴에도 불구하고 소유자가 독점한다는 문제가 있다.</p>
<p><strong>1.3 시맨틱 웹(Semantic Web)</strong>
Web1.0, Web2.0에서는 사용자가 정보를 일일이 검색해야 한다. 그 대안으로 구조화된 데이터를 정의하고, 구축(Ontology)하고, 구축된 데이터를 연결(Link Data)하여 지능적인 자료처리를 하는 웹을 목표로 한다. 특정 정보를 검색하면 사용자가 원하는 정보를 찾아 클릭하며 찾아다니지 않아도 자동으로 연결된 데이터를 보여준다.</p>
<p><strong>1.4 탈중앙화 웹(Decentralized Web)</strong>
플랫폼 기업(웹서비스 소유자)가 서비스를 통제하는 중앙집중형 웹에 대항하는 개념. 사용자가 데이터나 프라이버시를 완벽하게 제어할 수 있는 웹을 목표로 한다.</p>
<p><strong>1.5 Web3.0</strong>
시맨틱 웹과 탈중앙화 웹을 포괄하는 개념.
인공지능과 블록체인을 기반으로 &#39;맞춤형 정보&#39;를 제공하는 &#39;탈중앙화&#39; 인터넷 환경을 말한다. 플랫폼 기업이 독점하고 있는 중앙집중화된 인터넷 환경을 개선하기 위해 탈중앙화를 실현하고 사용자가 데이터를 소유할 수 있는 웹을 목표로 한다.</p>
<h3 id="2-업무-블록체인-적용-사례">2. 업무 블록체인 적용 사례</h3>
<p><strong>2.1 공급망 관리(SCM)</strong></p>
<ul>
<li>Suppliers; 부품 공장에서 각 부품의 정보를 (블록체인에) 기록한다.</li>
<li>Producer; 생산에 사용한 부품 내역과 생산 정보를 기록한다.</li>
<li>Distributor; 제품의 운송 정보를 기록한다.</li>
<li>Retailer/Store; 제품의 판매 정보를 기록한다.</li>
<li>Customer; 구매한 제품의 부품, 생산, 운송, 판매 정보를 확인한다.</li>
</ul>
<p><strong>2.2 의료</strong></p>
<ul>
<li>IoT 의료기기가 환자의 건강 정보를 기록한다.</li>
<li>병원이 환자의 질병과 처방을 기록한다.</li>
<li>약국이 환자의 약 수령을 기록한다.</li>
<li>보험사가 환자의 건강 정보, 질병, 처방, 수령 내역을 확인한다.</li>
</ul>
<h2 id="2022-07-25">2022-07-25</h2>
<h3 id="1-망-구성">1. 망 구성</h3>
<p><img src="https://velog.velcdn.com/images/soo-im/post/5dd24f78-c9eb-4bf6-ab7b-65f457bcf741/image.jpg" alt="Network"></p>
<p><strong>1.1 망 구성</strong></p>
<ul>
<li>내부망: 조직 내에서 인터넷이 아닌 내부 네트워크를 통해 PC끼리 통신하는 망을 말한다.</li>
<li>DMZ: 외부에 서비스를 제공할 때 내부망을 보호하기 위해 내부 네트워크와 분리한 망을 말한다.</li>
<li>인바운드 Inbound: 서버가 사용자로부터 메시지를 받는(in) 것을 말한다. 그 중 유효한 메시지만 필터링하는 과정을 InGress라고 한다.</li>
<li>아웃바운드 Outbound; 서버가 사용자에게 메시지를 보내는(out) 것을 말한다. 마찬가지로 이를 필터링하는 과정을 EGress라고 한다.</li>
</ul>
<p><strong>1.2 망 구성 방안</strong>
만약 블록체인을 설치해야 한다면 어디에 해야 할까?
내부망이라고 생각할 수도 있지만, 내부망에 드나드는 인바운드와 아웃바운드는 보안상 철저히 통제되어야 한다. 허가된 포트(port)를 통해서만 정보가 오가기 때문에 노드가 늘어날수록 포트를 그만큼 열어주어야 한다. 프라이빗도 아니고 퍼블릭 블록체인이라면 더더욱 내부망에 두어서는 안된다.</p>
<blockquote>
<p><strong>참여자 성격에 따른 망 구성 방안</strong>
참여자가 ICT 운영 여력이 있다면 DMZ에 노드를 설치하고 포트를 사용할 수 있다. 다만 운영 여력이 없다면 클라우드에 설치하는 것이 더 좋은 방법이다.</p>
</blockquote>
<h2 id="2022-07-26">2022-07-26</h2>
<h3 id="1-클라우드-컴퓨팅">1. 클라우드 컴퓨팅</h3>
<p><strong>1.1 클라우드 컴퓨팅이란</strong>
가상화된 IT자원(SW, 스토리지, 서버 등)을 서비스로 제공하고, 서비스 부하에 따라 실시간 확장성을 지원하여 사용한 만큼 비용을 청구하는 컴퓨팅 기술. AWS, GCP 외에도 드롭박스, 구글 워크스페이스 등도 포함하는 개념이다.</p>
<blockquote>
<p><strong>G-Cloud</strong>
우리나라 공공기관, 공기업은 자체적으로 구성한 클라우드 혹은 G-Cloud 인증을 받은 민간 클라우드만(예: 네이버클라우드, KT클라우드 등) 사용할 수 있다. </p>
</blockquote>
<p><strong>1.2 클라우드 컴퓨팅 유형</strong>
<img src="https://velog.velcdn.com/images/soo-im/post/eb70ae45-38ad-495d-aaf1-fcb9e1c11edd/image.jpg" alt="Cloud_Computing_Service_type"></p>
<p>서비스 제공 범위에 따라 IaaS(Infra as a Service), PaaS(Platform as a Service), SaaS(Software as a Service)로 분류한다.
IaaS</p>
<p>또한 서비스 제공주체에 따라 퍼블릭과 프라이빗으로 분류할 수 있다. 퍼블릭은 AWS, GCP와 같이 외부에서 제공하는 클라우드를 말하고, 프라이빗은 내부 자원을 모아서 자체적으로 구성하는 클라우드를 말한다.</p>
<p><a href="https://experience.dropbox.com/ko-kr/resources/what-is-the-cloud">이 글</a>과 <a href="https://wnsgml972.github.io/network/2018/08/14/network_cloud-computing/">이 글</a>에 유형 설명이 잘 나와있다.</p>
<blockquote>
<p><strong>멀티 클라우드</strong>
여러 개의 퍼블릭 클라우드를 사용해 하나의 서비스를 운영하는 방식이다. 예를 들어 AWS, GCP 두 개를 이용해서 서비스를 운영하면 둘 중 하나가 다운되더라도 서비스가 동작할 수 있다.</p>
</blockquote>
<blockquote>
<p><strong>퍼블릭 클라우드</strong>
퍼블릭 클라우드와 프라이빗 클라우드를 혼합해서 하나의 서비슬르 운영하는 방식이다. 중요한 활동은 프라이빗에서, 덜 중요한 활동은 퍼블릭에서 수행한다.</p>
</blockquote>
<p><strong>1.3 블록체인 클라우드 서비스(BaaS)</strong>
클라우드 컴퓨팅 솔루션을 이용해 사용자가 블록체인 앱, 스마트 컨트랙트 등을 빌드하고 사용할 수 있도록 하는 서비스를 말한다. PaaS 범위에 속한다.</p>
<p><strong>1.4 클라우드 한계</strong></p>
<ol>
<li>클라우드 기능에 서비스를 맞추어야 한다.</li>
<li>성능이 일관적이지 않고, 광고보다 성능이 낮아질 수 있다.</li>
<li>직접 서버를 관리하는 것보다 비용이 더 들어갈 수 있다.</li>
<li>플랫폼 특성상 클라우드 서비스를 제공하는 벤더의 권한이 더 커질 수 있다.</li>
</ol>
<h2 id="2022-07-29">2022-07-29</h2>
<h3 id="1-블록체인-연계-기술">1. 블록체인 연계 기술</h3>
<p><strong>1.1 QR코드</strong>
매트릭스 형식의 2차원 바코드. 2차원이기 때문에 기존의 1차원 바코드보다 많은 정보를 넣을 수 있다.</p>
<blockquote>
<p><strong>QR코드 엄청 많이 생성하는데 자기들끼리 안겹치나?</strong>
최근에 코로나 인증 때문에 QR코드를 빈번하게 생성하면서 이런 궁금증이 생겼다. 확인해보니 QR코드 생성기는 일종의 해시함수와 비슷해서, 원문이 동일하면 그 QR코드도 동일하다고 한다.</p>
</blockquote>
<p><strong>2. 블루투스</strong>
근거리의 전자기기를 무선으로 연결하여 실시간으로 쌍방향 통신이 가능하게 만드는 근거리 무선 데이터 통신 규격. 별도의 면허 없이 사용할 수 있는 ISM(Industry, Science, Medical) 밴드의 주파수를 사용한다.</p>
<p><strong>3. 미라이봇넷</strong>
디지털카메라나 라우터 등의 IoT 디바이스를 좀비(악성코드가 심겨 DDos 공격을 하는 디바이스)로 만들어 DDos 공격을 수행하는 악성코드. pc보다 상대적으로 보안이 취약한 IoT를 이용하는 것이다.</p>
<blockquote>
<p><strong>미라이?</strong>
미라이가 일본어로 그 미라이(미래)다. 제작자가 좋아하는 <a href="http://wiki.hash.kr/index.php/%EB%AF%B8%EB%9D%BC%EC%9D%B4_%EB%B4%87%EB%84%B7">애니 주인공 이름</a>이었단다....... 뭔가 의미가 있을 줄 알았는데...</p>
</blockquote>
<h3 id="2-ipfsinter-planetary-file-system">2. IPFS(Inter Planetary File System)</h3>
<p><strong>2.1 IPFS</strong>
네트워크 참여자가 전체 데이터의 일부를 저장하는 P2P 기반의 분산 파일 시스템. 토렌트(...)와 비슷한 시스템이다.</p>
<p><strong>2.2 블록체인 vs. IPFS</strong>
컨셉은 블록체인과 비슷하지만 아래와 같은 차이가 있다.</p>
<ul>
<li>대용량 데이터를 저장할 수 있다.</li>
<li>삭제가 가능하다.</li>
</ul>
<p><strong>2.3 블록체인과 IPFS 연계</strong>
<img src="https://velog.velcdn.com/images/soo-im/post/7f101e4b-57bc-4167-8d70-1b70fab28295/image.jpg" alt="BlockChain_and_IPFS"></p>
<ol>
<li>유저A가 파일을 IPFS에 저장한다.</li>
<li>IPFS에 저장된 파일의 경로를 해시값으로 반환한다.</li>
<li>블록체인에 해시값을 저장하여 블록을 생성한다.</li>
<li>유저B가 해당 파일의 해시값을 이용해 파일을 요청한다.</li>
<li>IPFS에서 파일을 찾아 유저 B에게 반환한다.</li>
</ol>
<h3 id="2-인공지능">2. 인공지능</h3>
<p><strong>2.1 연합학습 Federated Learning</strong>
인공지능 모델 개발에는 대용량 데이터와 높은 컴퓨팅 파워가 든다. 그래서 일반적으로 중앙 서버에 데이터를 저장해서 그 데이터로 모델을 만든다. 하지만 개인정보 등은 서버에 저장하기 어렵다. 그 대안으로 나온 개념이 연합학습이다. (구글에서 처음 제시했는데, 아직 상용화된 기술은 아니라고 한다.)</p>
<ol>
<li>개인 디바이스에 학습 모델 생성 코드를 배포한다.</li>
<li>디바이스에서 개인정보와 모델 생성 코드를 사용해 개인화된 학습 모델을 생성한다.</li>
<li>학습이 완료된 모델 정보를 중앙 서버로 보낸다.</li>
<li>중앙 서버는 개인정보를 저장하지 않으면서도 개인정보를 학습한 모델 정보를 받을 수 있다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[6월 TIL]]></title>
            <link>https://velog.io/@soo-im/6%EC%9B%94-TIL</link>
            <guid>https://velog.io/@soo-im/6%EC%9B%94-TIL</guid>
            <pubDate>Thu, 02 Jun 2022 14:28:04 GMT</pubDate>
            <description><![CDATA[<h2 id="2022-06-02">2022-06-02</h2>
<h3 id="fk로-연결된-모델-필드-읽기">FK로 연결된 모델 필드 읽기</h3>
<p><code>ForeignKey</code>로 다른 테이블의 모델(레코드)을 연결하면 그 모델의 필드도 사용할 수 있다. 예를 들어 <code>User</code> 모델이 name, age 필드를 가지고 이것이 <code>Post</code> 모델에 연결된다고 가정하자.</p>
<pre><code class="language-python">class Post():
    title = models.TextFiled()
    user = models.ForeignKey(&quot;users.User&quot;, on_delete = models.CASCADE)

    def __str__(self):
        return self.user.name</code></pre>
<p>이렇게 <code>Post</code> 모델에서 연결된 <code>User</code> 모델의 name 필드를 사용할 수 있다. 연결된 모델에서 또 연결된 모델로 접근할 수도 있다. 만약 <code>User</code> 모델이 <code>Classroom</code>이라는 모델과 연결되어 있으면 <code>self.user.classroom.number</code> 이런 식으로 <code>Classroom</code> 모델의 필드에도 접근 가능하다.</p>
<h2 id="2022-06-06">2022-06-06</h2>
<h3 id="앱-등록-모델-설정-어드민-패널-등록-정리">앱 등록, 모델 설정, 어드민 패널 등록 정리</h3>
<h3 id="step-1-앱-등록">STEP 1. 앱 등록</h3>
<ol>
<li>특정 기능을 하는 앱을 만든다. 터미널에서 <code>django-admin startapp 앱 이름</code></li>
<li>앱을 만들면 앱 폴더 내에 <code>apps.py</code>라는 파일이 들어있다. 그 안에 <code>class (앱)Config</code>가 있다. 이 클래스는 <code>django.apps</code>로부터 앱의 configuration과 검사 기능을 저장하는 레지스트리를 불러오는 역할을 한다. (<a href="https://velog.io/write?id=9e194305-75b0-46da-9762-0153e49630a7">문서</a>를 되는대로 번역한 것이라 잘못된 설명일 수도 있음...)</li>
<li>프로젝트에서 내가 만든 앱을 사용하려면 config 폴더의 <code>settings.py</code>에 앱 configuration을 추가해야 한다. <code>INSTALLED APPS = [...]</code>에 2번에서 본 configuration을 추가한다. 
예를 들어 앱 이름이 user라면 <code>INSTALEED APPS = [..., ..., users.apps.UsersConfig,]</code> 와 같이 입력한다.<h3 id="step-2-모델-스키마-설정">STEP 2. 모델 스키마 설정</h3>
</li>
<li>앱 폴더의 <code>model.py</code>에서 모델의 필드 등을 설정한다. 보통은 만드려는 모델 클레스를 생성하고 그 안에 필드를 입력하는 방식이다. 예를 들면 다음과 같다.<pre><code class="language-python">class User(models.Model):    # 일반적으로 django.db의 models 내 Model 클래스를 상속한다.
 name = models.CharField()</code></pre>
</li>
<li>앱의 모델, 즉 DB 스키마가 바뀌었으니 그것을 프로젝트 DB에 반영해주어야 한다. 터미널에서 <code>python manage.py makemigrations (앱이름)</code>으로 변경된 앱의 모델 클래스 스키마를 반영한 migration을 만들어준다.<blockquote>
<p>(앱이름)은 특정 앱의 DB 스키마만 변경하고 싶을 때 입력하고, 여러 앱을 한번에 적용하려면 (앱이름)은 빼고 하면 된다.</p>
</blockquote>
</li>
<li>5번에서 만든 migration을 실제 프로젝트 DB에 적용한다. 터미널에서 <code>python manage.py migrate</code><h3 id="step-3-어드민-패널에-모델-등록">STEP 3. 어드민 패널에 모델 등록</h3>
</li>
<li>만약 앱의 모델 클래스를 어드민 패널에서 관리하고 싶다면 앱 폴더의 <code>admin.py</code>에서 모델을 등록해야 한다. 모델 클래스 등록을 위해 <code>models.py</code>를 불러온다. <code>from . import models</code></li>
<li>여기서부터 내가 듣는 강의와 <a href="https://docs.djangoproject.com/ko/4.0/intro/tutorial02/#activating-models">공식 튜토리얼</a>에서 모델 클래스를 등록하는 방법이 약간 다른데, 일단 두 가지 다 소개해본다.</li>
<li>강의에서 사용하는 방법: <code>models.py</code> 내에 생성한 모델 클래스를 등록한다. 만약 <code>user</code>라는 클래스가 있다면 다음과 같다.<pre><code class="language-python">from . import models
</code></pre>
</li>
</ol>
<p>@admin.register(models.user)
class UserAdmin(admin.UserAdmin):    # 위와 두 줄 이상 띄어쓰면 안 된다.
    pass</p>
<pre><code>10. 튜토리얼에서 사용하는 방법: `models.py` 내에 생성한 모델 클래스를 등록한다. 위와 같은 `user` 클래스를 예시로 들면 다음과 같다.
```python
from . import models

admin.site.register(models.user)</code></pre><h2 id="2022-06-07">2022-06-07</h2>
<h3 id="어드민-패널-편집보이는-필드-필터링-검색">어드민 패널 편집(보이는 필드, 필터링, 검색)</h3>
<p>어드민 패널에서는 모델 클래스의 <code>__str__</code> 혹은 모델 클래스명이 필드로 나온다. 예를 들어 <code>Room</code> 모델 클래스에서 아무런 <code>__str__</code>을 등록하지 않았다면 Room 어드민 패널은 이렇게 보인다.
<img src="https://velog.velcdn.com/images/soo-im/post/2ec922ea-0e4e-4e20-be63-6af84207b0a8/image.png" alt=""></p>
<p>패널에서 보이는 정보를 수정하려면 그 앱의 <code>admin.py</code>의 해당 모델 클래스에서 다음과 같이 편집한다.</p>
<ol>
<li>패널에서 다른 필드를 보여주려면 <code>list_display</code>에 원하는 필드명을 튜플 혹은 리스트로 준다.</li>
<li>패널에서 필드를 기준으로 필터링하려면 <code>list_filter</code>에 필드명을 튜플 혹은 리스트로 준다.</li>
<li>패널에서 필드를 기준으로 검색하려면 <code>search_fileds</code>에 필드명을 튜플 혹은 리스트로 준다.</li>
</ol>
<p>예를 들어 <code>Room</code> 모델 클래스의 어드민 패널을 변경하려면 다음과 같이 <code>admin.py</code>를 편집한다.</p>
<pre><code class="language-python">@admin.register(models.Room)
class RoomAdmin(admin.ModelAdmin):
    &quot;&quot;&quot;Room Admin Definition&quot;&quot;&quot;

    list_display = (
        &quot;name&quot;,
        &quot;country&quot;,
        &quot;city&quot;,
    )

    list_filter = (
        &quot;city&quot;,
        &quot;country&quot;,
    )

    search_fields = (
        &quot;=city&quot;,
        &quot;^host__username&quot;, # FK 필드로 검색
    )</code></pre>
<p>이렇게 하면 어드민 패널은 이렇게 보인다.
<img src="https://velog.velcdn.com/images/soo-im/post/0c443a84-443b-46b4-80ac-f5cdc57e0bf3/image.png" alt=""></p>
<blockquote>
<p><strong><code>search_fileds</code> 옵션</strong>
<code>search_fields</code>에 입력하는 필드명 앞에 <code>=</code>, <code>^</code>를 붙여 검색 옵션을 줄 수 있다.
<code>=</code>: 검색 문자열과 정확히 일치해야 한다.
<code>^</code>: 검색 문자열로 시작해야 한다.
아무 옵션도 주지 않으면 substring으로 검색한다. 모든 옵션에서 대소문자는 구분하지 않는다.</p>
</blockquote>
<blockquote>
<p><strong><code>search_fileds</code>에서 FK 필드로 검색</strong>
모델 내에서 FK의 필드를 사용할 때: <code>self.FK모델명.FK필드명</code> (예: <code>self.user.name</code>)
<code>search_fields</code>에서 FK의 필드를 사용할 때: <code>FK모델명__FK필드명</code> (예: <code>user__name</code>)</p>
</blockquote>
<h2 id="2022-06-13">2022-06-13</h2>
<h3 id="어드민-패널-편집mn-관계-필드-선택-필드-숨기기">어드민 패널 편집(M:N 관계 필드 선택, 필드 숨기기)</h3>
<ol>
<li><p><code>filter_horizontal</code>로 
Many to Many인 필드는 어드민 패널에서 편집하기가 불편하다(여러 모델을 Shift 키를 누른 상태로 클릭해야 다중선택이 된다). 모델 연결을 편리하게 해주는 옵션이 <code>filter_horizontal</code>이다.
다음과 같이 <code>admin.py</code>를 설정하면 Many to Many 관계인 모델을 아래와 같은 편리한 GUI로 편집할 수 있다.</p>
<pre><code class="language-python">@admin.register(models.Room)
class RoomAdmin(admin.ModelAdmin):
 filter_horizontal = (
     &quot;amenities&quot;,
     &quot;facilities&quot;,
     &quot;house_rules&quot;,
 )</code></pre>
<p><img src="https://velog.velcdn.com/images/soo-im/post/a877a2ec-13c8-4e1b-b647-c4ca8af379bd/image.png" alt=""></p>
</li>
<li><p><code>collapse</code>로 여러 필드 숨기기
필드 중 일부를 숨기려면 <code>fieldsets</code>의 <code>classes</code>에 <code>collapse</code>를 넣으면 된다. <code>filedsets</code>은 어드민 패널에서 보이는 필드를 그룹화할 때(<code>fields</code>) 주로 사용하는데, <code>classes</code>에 스타일 클래스를 줘서 그룹의 스타일을 바꿀 수 있다. 아래처럼 작성하면 해당 그룹이 숨겨져서 나온다.</p>
<pre><code class="language-python">@admin.register(models.Room)
class RoomAdmin(admin.ModelAdmin):
fieldsets = (
     (
     (
         &quot;More About Space&quot;,
         {
             &quot;classes&quot;: (&quot;collapse&quot;,),    # 그룹의 스타일
             &quot;fields&quot;: (    # 그룹화할 필드
                 &quot;amenities&quot;,
                 &quot;facilities&quot;,
                 &quot;house_rules&quot;,
             ),
         },
     ),
     (&quot;Last Details&quot;, {&quot;fields&quot;: (&quot;host&quot;,)}),
 )</code></pre>
<blockquote>
<p><strong><code>classes</code> 리스트</strong>
<a href="https://docs.djangoproject.com/en/4.0/ref/contrib/admin/">문서</a>에 따르면 <code>classes</code>에는 CSS 클래스를 입력할 수 있다. <code>collapse</code>와 <code>wide</code>는 기본적으로 주어지는 옵션이고, 그 외 본인이 만든 CSS 클래스도 사용할 수 있다.</p>
</blockquote>
</li>
</ol>
<h2 id="2022-06-29">2022-06-29</h2>
<h3 id="admin-actions">Admin Actions</h3>
<p>위에서 <code>list_display</code>에 원하는 필드 리스트를 넣어 어드민에서 해당 필드를 바로 볼 수 있다고 했다. 다만 여기에는 Many to Many 관계인 필드를 넣을 수 없다.</p>
<blockquote>
<p>잊었을까 다시 보는 ForeignKey vs ManytoMany 
ForeignKey 관계인 경우 외부 테이블에서 하나의 값만 가져오지만, ManytoMany에서는 여러 개의 값을 가져올 수 있다.</p>
</blockquote>
<p>Many to Many 필드에 여러 개의 값이 있다면 그걸 전부 보여줄 수는 없으니 COUNT처럼 적당히 집계를 해서 보여주어야 한다.
집계 함수는 <code>admin.py</code>의 원하는 클래스 내에서 만들면 된다. 어드민 내 함수는 <code>self</code>, <code>obj</code>라는 파라미터를 자동으로 가지는데 각각 클래스, 모델(DB 내 데이터)을 가리킨다.</p>
<pre><code class="language-python">    def count_amenities(self, obj):
        print(obj)
        print(obj.amenities.all())
        return None</code></pre>
<p>위 함수를 넣고 어드민 패널을 실행하면 현재 DB에 있는 모델(<code>obj</code>) 각각의 <code>__str__</code> 과 모델의 amenities 필드 값을 출력한다.</p>
<pre><code class="language-python">Room No.2
&lt;QuerySet [&lt;Amenity: Shower&gt;]&gt;
Room No.1
&lt;QuerySet []&gt;</code></pre>
<h3 id="queryset">QuerySet?</h3>
<p>QuerySet은 마치 SQL을 이용한 것처럼 DB의 모델 필드 값을 리스트로 반환한다. 이게 가능한 이유는 장고가 <code>models.py</code>내 클래스를 SQL 없이 모델을 조회할 수 있게 QuerySet API를 제공하기 때문이다. 
위에서 사용한 <code>all()</code> 외에도 <code>count()</code>, <code>filter()</code> 등을 이용해 모델을 집계할 수 있다. 위의 경우에는 <code>return obj.amenities.count()</code>를 하면 우리가 원하는대로 개수가 나온다.</p>
<p>위의 예제는 Many to Many로 연결된 여러 개의 값을 조회하는 방법이었다. 거꾸로 말하면 ForeignKey로 역참조된 경우에도 역으로 어떤 값에 연결되었는지 조회하는 것도 가능하다.</p>
<p>A에 B 필드를 FK로 연결할 경우, A는 무조건 하나의 B를 가지지만 B는 여러 A를 가질 수 있다(1:N). 다만 A의 어드민에서는 각 모델이 어떤 B 모델을 가지는지 볼 수 있지만, B의 어드민에서 각 모델이 어떤 A 모델을 가지는지는 확인할 수 없다. 여기서 위의 M:N 관계처럼 QerySet을 사용할 수 있다.</p>
<p>유저(B)가 방(A)에 FK로 연결된 경우를 가정해보자. Soo라는 유저(B의 모델)가 어떤 방을 가지고 있는지 확인하는 방법은 아래와 같다.</p>
<pre><code class="language-python">soo = User.objects.get(username=&quot;Soo&quot;)
soo.room_set.all()</code></pre>
<p>이처럼 <code>A_set</code>의 형식으로 어떤 A 모델에 연결되었는지 조회할 수 있다.</p>
<h2 id="2022-06-30">2022-06-30</h2>
<p>서울ICT 이노베이션 블록체인 기획강의 수강신청 성공했다!! 이제 8월까지... 월화수목금 TIL은 블록체인으로 대체되었다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[4월-5월 TIL]]></title>
            <link>https://velog.io/@soo-im/4%EC%9B%94-5%EC%9B%94-TIL-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@soo-im/4%EC%9B%94-5%EC%9B%94-TIL-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Sat, 02 Apr 2022 16:10:37 GMT</pubDate>
            <description><![CDATA[<h2 id="2022-04-04">2022-04-04</h2>
<p><img src="https://media.vlpt.us/images/soo-im/post/e8a06a42-4a7d-42b9-8ab6-4ae0e3617cd6/image.png" alt="">
MA(q) 모델에서 ACF(t&gt;q)가 0이 되는 <a href="https://www.youtube.com/watch?v=_tgB-ri9-8c">이유</a></p>
<p>왜 ACF가 MA를 설명하는지 아무리 찾아도 모르겠다... ACF(k)가 lag=k에서 $y_{t}$랑 $y_{t-k}$의 직간접 상관을 의미하는 거랑 MA가 $c$에서 이전 오차를 반영하는 거랑 무슨 상관인건데... ACF가 차분한 다음에 계산한 것도 아니잖아? SARIMAX까지 갈 길이 멀다</p>
<h2 id="2022-04-06">2022-04-06</h2>
<p>Django 플젝에서 secret code 파일을 따로 만들어 gitignore에 넣으려다가 여러 문제가 생겨서... 처음부터 플젝 다시 만들었다 😂 나중에 문제 생기느니 지금 해야지</p>
<h3 id="pipenv---three-에러-해결">pipenv --three 에러 해결</h3>
<p><code>pipenv --three</code>로 다시 버블 만드려는데 아래 에러가 나오면 다음 스텝으로 해결한다.</p>
<pre><code>pipenv system is intended to be used for pre-existing Pipfile installation, not installation of specific packages.</code></pre><ol>
<li><code>pipenv --uninstall all</code> pipenv 관련 패키지 모두 삭제</li>
<li>다시 <code>pipenv --three</code> 시도</li>
<li>된다면 축하하기</li>
<li>그래도 안된다면 <code>pipenv --unsinstall all</code>을 다시 실행하고</li>
<li><code>pip uninstall virtualenv</code>, <code>pip uninstall virtualenv-clone</code> 가상환경 세팅 지우고</li>
<li><code>pip uninstall pipenv</code> pipenv 제거하고</li>
<li><code>pip install pipenv</code> pipenv 다시 설치</li>
</ol>
<p>pipenv 작업을 했던 폴더를 그냥 삭제한 후 똑같은 이름의 폴더를 다시 만들어서 <code>pipenv --three</code>를 하니까 저런 에러가 있었다. 실험을 해보지 못해서 같은 이름의 폴더가 문제였던 건지는 확인하지 못했지만(그냥 pipenv로 이것저것 하다 생긴 오류였을지도) 우선 저렇게 해결했다!</p>
<h2 id="2022-04-07">2022-04-07</h2>
<h3 id="pipfile이-실행-플젝에-생성되게-하는-법">Pipfile이 실행 플젝에 생성되게 하는 법</h3>
<p>어제 에러를 다 고친 줄 알고 기뻐했는데... pipenv를 다시 설치하고 <code>pipenv --three</code>로 Python 버전을 세팅하자 Pipfile이 다른 폴더에 생겼다😱</p>
<pre><code>Virtualenv location: C:\Users\username\.virtualenvs\username-hFtWgrld</code></pre><p>플젝 폴더가 아니라 저 폴더 안에 .virtualenvs, Pipfile, Pipfile.lock이 생겼다. 그래서 <code>pipenv shell</code>을 실행하면 자동으로 저 폴더로 이동해버렸다.
찾아보니 Pipfile이 다른 폴더에 생기는 게 오류는 아니라고 한다(.virtualenvs 안에 내 프로젝트명이 생기는 걸로 봐서 플젝에서 작동 안 하는 건 아닌 것 같다). </p>
<p>그래도 나는 플젝 폴더 안에 생기는 게 마음이 편할 것 같아서 해결책을 찾았다. 방법은 간단하다.</p>
<pre><code>set PIPENV_VENV_IN_PROJECT=&quot;enabled&quot; # window 기준
export PIPENV_VENV_IN_PROJECT=&quot;enabled&quot; # mac 기준</code></pre><p>이렇게 설정해주고 다시 <code>pipenv --three</code>를 하면 Pipfile이 플젝 폴더 안에 들어가있다!</p>
<h2 id="2022-04-12">2022-04-12</h2>
<h3 id="django-migration">Django migration</h3>
<p>데이터 유형이 바뀌면 migration을 생성하고 그에 맞추어 DB를 업데이트하는 과정을 말한다. (데이터 유형 ≃ 데이터 제약 조건으로 봐도 되는 듯)
<code>models.py</code> 안에 데이터 유형을 입력한 후 <code>python manage.py makemigrations</code>을 실행하면 <code>migration</code>이 생성된다. 그리고 <code>python manage.py migrate</code>를 실행하면 <code>migration</code>에 맞추어 데이터 유형이 DB에 적용된다.</p>
<h2 id="2022-04-15">2022-04-15</h2>
<h3 id="framework-vs-library">framework vs. library</h3>
<p>framework가 library보다 훨씬 강한 규칙을 가진다. Django(framework)에서는 모든 파일/폴더/데이터 명칭 등등의 규칙을 원래 상태 그대로 따라야 한다. 대신 새로운 것을 만드는 것은 괜찮다.</p>
<h3 id="앱-db-필드modelspy-수정하기">앱 DB 필드(models.py) 수정하기</h3>
<ol>
<li><code>users</code> 앱을 생성한다.
<code>django-admin startapp users</code></li>
<li>해당 앱 디렉토리의 <code>models.py</code>에서 원하는 형식으로 DB 필드를 편집한다. 필드 유형은 <a href="https://docs.djangoproject.com/en/4.0/ref/forms/fields/">Django 문서</a>에서 선택한다. (<code>modles.py</code>에 입력한 코드를 ORM(Object Relational Mapper)가 SQL문으로 바꿔서 데이터베이스를 생성한다.)
그리고 새 필드를 추가할 경우 기존 레코드의 값을 처리하려면 <code>default=채우려는 기본값</code> 혹은 <code>null=True</code> 등의 옵션을 준다. 후자는 기존 레코드의 새 필드 값을 null로 두는 것이다. <pre><code class="language-python">from ast import Pass
from django.db import models
from django.contrib.auth.models import AbstractUser
</code></pre>
</li>
</ol>
<h1 id="abstractuser-클래스를-상속하여-user-클래스를-만든다">AbstractUser 클래스를 상속하여 User 클래스를 만든다.</h1>
<p>class User(AbstractUser):
    # 원하는 필드를 적절히 수정한다.
    bio = models.TextField(default=&quot;&quot;)</p>
<pre><code>3. 최상위 디렉토리의 `settings.py`에서 앱을 추가한다. 기본 세팅은 `INSTALLED_APPS = [...]` 형식으로 되어있는데, 기본 세팅과 내가 만든 앱을 구분하고 싶으면 이런 식으로 써도 된다.
```python
DJANGO_APSS = [
    # 기존 INSTALLED_APPS에 들어있던 APP 리스트 넣기
]

PROJECT_APPS = [
    # 내가 만든 APP 리스트 넣기
    &#39;users.apps.UsersConfig&#39;,
]

INSTALLED_APPS = DJANGO_APSS + PROJECT_APPS</code></pre><ol start="4">
<li>최상위 디렉토리의 <code>settings.py</code>에서 USER을 덮어씌운다. <a href="https://docs.djangoproject.com/en/4.0/topics/auth/customizing/">Django 문서</a>는 유저를 커스터마이징할 때 위 파일에서 <code>AUTH_USER_MODEL=</code>을 이용해 덮어씌우라고 설명한다.
우리의 경우에는 <code>users</code> 앱의 <code>models.py</code>에서 <code>User</code>라는 새로운 클래스를 만들었으니까 아래와 같이 설정한다.<pre><code class="language-python">AUTH_USER_MODEL = &#39;users.User&#39;</code></pre>
</li>
<li>데이터 유형을 바꾸었으니 기존의 DB 설정을 지우고, migrate도 다시 해줘야 한다. 먼저 최상위 디렉토리의 <code>db.sqlite3</code>를 지운다.</li>
<li>그 다음 다시 migrate을 한다.
<code>python manage.py makemigrations</code>를 실행해서 migration 파일(<code>users\migrations\0001_initial.py</code>)을 만들고,
<code>python manage.py migrate</code>을 실행해서 migrate을 한다. 그러면 다시 <code>db.sqlite3</code>가 생성된다.</li>
<li>이제 <code>python manage.py runserver</code>하면 내가 만든 앱 DB에 맞게 서버가 작동한다!</li>
</ol>
<h2 id="2022-04-17">2022-04-17</h2>
<h3 id="docstring">docstring</h3>
<p>가끔 파이썬에서 함수나 클래스를 새로 선언하면 자동으로 <code>&quot;&quot;&quot; &quot;&quot;&quot;</code>가 나오는 경우가 있다. 이걸 docstring이라고 하는데 그 안에 들어있는 문자열(보통은 설명문)을 별개의 객체로 처리해준다. 모듈 import 등을 할 때 설명을 편리하게 확인할 수 있다. 자세한 내용은 <a href="https://wikidocs.net/16050">이 글</a> 참고.
<img src="https://velog.velcdn.com/images/soo-im/post/79204c34-2429-4f3d-9ae6-c917768adce8/image.png" alt=""></p>
<h3 id="null--blank">null &amp; blank</h3>
<p>위에서 특정 필드의 값에 null을 허용하려면 <code>null=True</code>로 두면 된다고 했다. 하지만 admin 패널에서 입력폼(form)에 아무것도 넣지 않은 채 저장하려면 해당 필드를 입력하라는 경고가 나온다. 이는 DB 유효성 검사와 폼의 유효성 검사가 별개이기 때문이다. <code>null=True</code>는 입력폼 내 필수 입력필드가 빈 칸으로 제출하는 걸 허용하지 않는다. 이 경우에는 따로 <code>blank=True</code>를 주어야 한다.</p>
<blockquote>
<p><strong>CharField, TextField는 null=True를 쓰지 말자!</strong>
장고는 두 문자 필드에 대해서 빈 값(null)을 &quot;&quot;로 두기 때문에 <code>null=True</code> 옵션을 줄 필요가 없다. 만약 위 옵션을 주면 빈 값을 처리하는 방법이 &quot;&quot;와 null 두 가지가 되기 때문에 에러가 날 수 있다. 출처: <a href="https://stackoverflow.com/questions/8609192/what-is-the-difference-between-null-true-and-blank-true-in-django">스택오퍼플로우 답변</a></p>
</blockquote>
<h2 id="2022-04-23">2022-04-23</h2>
<h3 id="register-model-into-adminpy">Register model into admin.py</h3>
<p>모델을 admin에서 관리하게 만들 수 있다. 위에서 만든 <code>users</code>의 모델을 admin에 등록하는 과정은 다음과 같다.</p>
<ol>
<li><code>users</code> 앱 내의 <code>admin.py</code> 파일을 편집한다.</li>
<li>아래와 같이 우리가 만든 모델 클래스를 등록한다.<pre><code class="language-python">from . import models
</code></pre>
</li>
</ol>
<p>@admin.register(models.User) # &lt;- decorator
class CustomUserAdmin(admin.ModelAdmin):
    pass</p>
<pre><code>&gt; 한 번에 여러 모델을 등록할 수도 있다.
```python
@admin.register(models.RoomType, models.Facility, models.Amenity, models.HouseRule)
class ItemAdmin(admin.ModelAdmin):
    pass</code></pre><ol start="3">
<li><p>첫 줄을 decorator라고 부르는데, &quot;admin 패널에 <code>models.User</code> 클래스를 등록하라&quot;는 뜻이다. </p>
</li>
<li><p>아랫줄은 우리가 admin 패널에서 사용할 클래스를 선언하는 것이다. 이 때 상속받는 <code>admin.ModelAdmin</code>은 <code>admin.py</code>에 들어있는 클래스로, 원래의 admin 패널 설정이 들어가있다.</p>
</li>
<li><p>decorator는 무조건 클래스는 한 줄 위에 있어야 한다. </p>
</li>
<li><p>클래스에서 우리가 <code>useres</code> 모델 중 어느 필드를 노출할지/필터를 걸지/수정 가능하게 할 지... 등등을 설정할 수 있다. 가능한 옵션은 <a href="https://docs.djangoproject.com/en/4.0/ref/contrib/admin/">공식 문서</a>에서 확인 가능.</p>
</li>
<li><p>예를 들어 아래와 같이 보여줄(<code>list_display</code>) 필드 혹은 필터를 걸(<code>list_filter</code>) 필드를 설정할 수 있다.</p>
<pre><code class="language-python">@admin.register(models.User)
class CustomUserAdmin(admin.ModelAdmin):
 &quot;&quot;&quot;
 Custom User Admin
 &quot;&quot;&quot;

 list_display = (&quot;username&quot;, &quot;gender&quot;, &quot;language&quot;, &quot;currency&quot;, &quot;superhost&quot;)
 list_filter = (&quot;superhost&quot;, &quot;language&quot;, &quot;currency&quot;)</code></pre>
</li>
</ol>
<h2 id="2022-05-16">2022-05-16</h2>
<h3 id="반복되는-필드-선언">반복되는 필드 선언</h3>
<p>상속을 이용해서 여러 앱에서 공통으로 사용하는 필드를 한 번에 선언할 수 있다. <code>created</code> 필드가 &#39;게시글&#39;, &#39;댓글&#39; 앱 모두에 사용하는 경우를 예시로 들어보자.</p>
<ol>
<li><p><code>created</code> 필드를 선언하기 위한 임의의 앱 core을 생성한다. (<code>django-admin startapp core</code>)</p>
</li>
<li><p>core 앱의 <code>models.py</code>에 아래와 같이 필드를 선언한다.</p>
<pre><code class="language-python">class TimeStampedModel(models.Model):
 &quot;&quot;&quot;Time Stamped Model&quot;&quot;&quot;

 created = models.DateTimeField()</code></pre>
</li>
<li><p>하지만 우리는 core 앱의 <code>models.py</code>를 따로 데이터베이스로 만들고 싶지 않다. 위에서 만든 필드에 아래와 같이 선언하면 된다.</p>
<pre><code class="language-python">class TimeStampedModel(models.Model):
 &quot;&quot;&quot;Time Stamped Model&quot;&quot;&quot;

 created = models.DateTimeField()

 class Meta:
     abstarct = True    # 데이터베이스를 만들지 않는 옵션</code></pre>
</li>
<li><p>이제 생성한 모델을 <code>config</code>&gt;<code>settings.py</code>에 등록한다.</p>
<pre><code class="language-python">PROJECT_APPS = [
 &quot;core.apps.CoreConfig&quot;,
 ...]</code></pre>
</li>
<li><p>이제 core 앱에서 선언한 필드를 다른 앱의 <code>models.py</code>에서 상속해서 사용할 수 있다.</p>
<pre><code class="language-python">from django.db import models
from core import models as core_models    # core 앱 내 모델 상속
</code></pre>
</li>
</ol>
<h1 id="create-your-models-here">Create your models here.</h1>
<p>class Room(core_models.TimeStampedModel):
    &quot;&quot;&quot;Room Model Definition&quot;&quot;&quot;</p>
<pre><code>pass</code></pre><pre><code>
## 2022-05-19
### `__str__`
객체의 내용을 문자열로 가공해서 보여줄 때 사용하는 함수. [이 글](https://recordnb.tistory.com/47)에 설명이 잘 나와있다. 
예를 들어 `Human` 클래스와 `Soo` 인스턴스를 아래처럼 만들었다고 가정해보자.

```python
&gt;&gt;&gt; class Human():
        def __init__(self, name):
            self.name = name

&gt;&gt;&gt; Soo = Human(&#39;Soo&#39;)</code></pre><p>여기서 <code>print(Soo)</code>를 하면 <code>&lt;__main__.Human object at 0x00000...&gt;</code> 이런 식으로 메모리 주소가 나온다. 메모리 주소 대신 다른 정보를 문자열 형태로 전달하려면 <code>Human</code> 클래스 내부에서 <code>__str__</code> 함수를 사용하면 된다.</p>
<pre><code class="language-python">class Human():
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return self.name</code></pre>
<p>이제 <code>print(Soo)</code>를 하면 <code>Soo</code>가 출력된다!</p>
<blockquote>
<p><code>def __str__(self): return &#39;something&#39;</code>을 하면 어떤 인스턴스가 생성되더라도 무조건 <code>something</code> 문자열만 반환한다. 이럴 일은 없겠지만 반드시 인스턴스의 정보를 사용할 필요는 없다는 걸 보여주기 위해 든 예시!</p>
</blockquote>
<h3 id="relation-with-other-models">Relation with Other Models</h3>
<p>장고 내 테이블(DB)는 다른 테이블과 relation을 가질 수 있다. 일대다는 <code>ForeignKey</code>, 다대다는 <code>ManyToManyField</code>로 설정한다. 예를 들어 User 테이블(1)-Room 테이블(N), Room type 테이블(N)-Room 테이블(M)의 관계가 있다고 가정하자.
이 때 Room의 <code>models.py</code>에서 아래와 같이 관계를 설정할 수 있다.</p>
<pre><code class="language-python">from users import models as user_models    # 연결하려는 테이블 가져오기
from roomtype import models as roomtype_models

class Room(core_models.TimeStampedModel):
    host = models.ForeignKey(user_models.User, on_delete=models.CASCADE)
    room_type = models.ManyToManyField(roomtype_models, blank=True)</code></pre>
<p>이렇게 하면 Room 테이블의 <code>host</code> 필드는 User 테이블 내의 모델(레코드)을 FK로 가진다.
<code>ForeignKey</code>에서 사용하는 파라미터는... 이어서 알아보자</p>
<h2 id="2022-05-21">2022-05-21</h2>
<h3 id="foreignkey-파라미터"><code>ForeignKey</code> 파라미터</h3>
<p>한 테이블에서 모델이 사라졌을 때, 그 테이블의 모델을 <code>ForeignKey</code>(일대다 관계)로 가지는 다른 테이블에서 어떻게 처리할지 설정하는 파라미터이다.</p>
<ol>
<li><code>CASCADE</code>
위의 예시인<code>models.CASCADE</code>는 폭포수가 떨어지듯이(cascade) 모델이 사라지는 효과가 다른 곳에도 적용된다는 의미이다. 즉 User 테이블의 특정 모델을 삭제하면, 그 모델에 연결된 Room 테이블의 모델도 함께 삭제된다.</li>
<li><code>PROTECT</code>
<code>models.PROTECT</code>는 User의 모델이 다른 테이블의 FK로 들어가있다면 그 User 모델을 곧바로 삭제할 수 없게 제한한다. 다른 테이블의 FK를 삭제한 후에야 User 모델을 지울 수 있다.</li>
<li><code>SET_NULL&#39;, &#39;SET_DEFAULT</code>
<code>models.SET_NULL</code>은 User 모델을 지우면 다른 테이블의 FK를 NULL 처리 한다. <code>models.SET_DEFAULT</code>는 NULL 대신 FK를 따로 설정한 default 값으로 처리한다.</li>
</ol>
<h2 id="2022-05-30">2022-05-30</h2>
<h3 id=""></h3>
<p>admin 패널에서 모델의 값을 수정하려면(Users 모델에 user을 추가했듯이) 다음과 같이 <code>admin.py</code>에 모델을 추가해야 한다.</p>
<pre><code class="language-python">@admin.register(models.Model1, models.Model2)    # , 를 이용해서 한 번에 여러 모델을 등록할 수도 있다.
class RoomAdmin(admin.ModelAdmin):</code></pre>
<p>이렇게 등록하면 admin 패널에서 모델 명칭은 &#39;모델명&#39;+&#39;s&#39;와 같이 나온다. 예를 들어 위의 경우에는 &#39;Model1s&#39;, &#39;Model2s&#39;로 나온다.
만약 다른 명칭을 원한다면 <code>class Meta</code>에서 <code>verbose_name_plural</code>을 설정해야 한다(앞에서 <code>abstract = True</code>을 썼던 그 <code>Meta</code>이다).</p>
<pre><code class="language-python">class Model1():
     class Meta:
         verbose_name_plural = &quot;First Models&quot;</code></pre>
<p>&#39;s&#39;를 붙이는 것은 유지하되 다른 명칭으로 쓰고 싶으면 <code>verbose_name</code>을 설정한다. 예를 들어 RoomType 모델은 &#39;Room types&#39;로 나오는데, 이걸 &#39;Room Types&#39;으로 바꾸고 싶으면 아래와 같이 쓴다.</p>
<pre><code class="language-python">class RoomType():
    class Meta:
        verbose_name = &quot;Room Type&quot;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2월-3월 TIL]]></title>
            <link>https://velog.io/@soo-im/2%EC%9B%94-TIL</link>
            <guid>https://velog.io/@soo-im/2%EC%9B%94-TIL</guid>
            <pubDate>Mon, 07 Feb 2022 14:15:07 GMT</pubDate>
            <description><![CDATA[<h2 id="2022-02-06">2022-02-06</h2>
<ol>
<li>추가/삭제 버튼 디자인을 <a href="https://getcssscan.com/css-buttons-examples">이 사이트</a>를 보고 바꿨다. CSS는 설명이 직관적이라 좋다 :9 </li>
<li>결과 텍스트 디자인이랑 내용을 살짝 수정했다.</li>
</ol>
<h2 id="2022-02-07">2022-02-07</h2>
<p>맨 위의 텍스트랑 입력 블록을 모두 <code>grid</code>로 정렬하려고 했는데 이렇게 하려면 입력 블록 안의 모든 요소를 다 별도의 div로 만들어야 한다. 요소를 div로 다 떼어놓으면 블록 한 단위의 의미가 사라져서 득보다 실이 클 것 같아 <code>grid</code>는 포기하고 <code>flex</code>를 선택했다. <a href="https://studiomeal.com/archives/197">이 블로그</a>를 보고 두 속성의 차이를 배웠다!</p>
<p>드디어 JS 미니 프로젝트 종료! 막판에 조금 늘어졌지만 끝낸 내 자신 장하다 🤩
<img src="https://images.velog.io/images/soo-im/post/7965414d-05de-44d0-b88f-649027837a15/tracker7.gif" alt=""></p>
<h2 id="2022-02-15">2022-02-15</h2>
<p>노코드 모임 + 노션 웨비나(중 일부)를 듣고왔다. 새로 알게 된 걸 적어보면</p>
<ol>
<li>마솦 오피스 계정으로 <a href="https://powerplatform.microsoft.com/ko-kr/">Power Platform</a>을 사용할 수 있다(하지만 Power BI는 따로 결제해야 함).</li>
<li>webflow에서 <a href="https://www.memberstack.com/">Memberstack</a>을 로그인 플러그인으로 많이 사용한다.</li>
<li>노션 플러그인 1) <a href="https://joey.team/">Joey</a>: 설문조사 결과를 노션 데이터베이스로 전달 가능</li>
<li>노션 플러그인 2) <a href="https://lottiefiles.com/featured">Lottifiles</a>: gif를 노션에 임베드할 수 있다. 원하는 이미지의 <code>oEmbed URL</code>을 노션에 <code>복붙</code>&gt;<code>Create Embed</code> 하면 된다! 제일 자주 쓸 듯.</li>
</ol>
<h2 id="2022-02-22-🥜">2022-02-22 🥜</h2>
<h3 id="seasonal_decompose가-trend-seasonality를-제거하는-간략한-원리"><code>seasonal_decompose</code>가 trend, seasonality를 제거하는 간략한 원리</h3>
<p><code>statsmodels.tsa.seasonal</code>의 <code>seasonal_decompose</code> 매서드를 사용하면 시계열의 계절성, 트렌드, 잔차항을 분리하여 볼 수 있다.</p>
<pre><code class="language-python">from statsmodels.tsa.seasonal import seasonal_decompose
import pandas as pd

# data를 불러온다.
# data df의 index는 DATETIME 형식이어야 한다. 아닌 경우 파라미터를 따로 지정해야 한다.

decompose_result = seasonal_decompose(data, model=&quot;multiplicative&quot;)

trend = decompose_result.trend
seasonal = decompose_result.seasonal
residual = decompose_result.resid

decompose_result.plot();</code></pre>
<p>plot 결과는 아래와 같다. 맨 위가 원본 데이터,  그 아래가 트렌드, 계절성, 잔차항이다.</p>
<p><img src="https://images.velog.io/images/soo-im/post/c7d13307-6119-41f8-b7f8-78980c99c2e7/image.png" alt=""></p>
<p>어떻게 각각을 분리하는 것인지 궁금해져서 몇몇 문서를 찾아보았다. 주로 참고한 자료는 <a href="https://www.statsmodels.org/stable/_modules/statsmodels/tsa/seasonal.html#seasonal_decompose">statsmodels.tsa.seasonal 문서</a>, <a href="https://www.statsmodels.org/dev/_modules/statsmodels/tsa/filters/filtertools.html#convolution_filter">statsmodels.tsa.filters.filtertools 문서</a>, <a href="https://bookdown.org/rdpeng/timeseriesbook/filtering-time-series.html">Filtering Time Series 관련 게시글</a> 이다.
아주 정확하지는 않지만 자료를 읽고 내가 이해한 방향대로 정리해보았다.</p>
<ol>
<li><p><code>seasonal_decompose</code>는 &quot;additive&quot;, &quot;multiplicative&quot; 두 가지 모델을 가지고 있다. 데이터를 trend, seasonality, residual로 분리한다고 가정했을 때 그것들의 합(add)으로 되어있는지, 곱(multipl)으로 되어있는지 선택하는 것이다. 공식 문서에서는 간단히 아래의 식으로 설명한다.</p>
<pre><code>The additive model is Y[t] = T[t] + S[t] + e[t]

The multiplicative model is Y[t] = T[t] * S[t] * e[t]</code></pre></li>
<li><p>multiplicative 모델을 가정했을 때, 어떻게 각각의 항을 구하는 것일까? 메서드는 가장 먼저 <code>trend</code>부터 구한다. <code>trend = convolution_filter(x, filt)</code> 특정 주파수 이하의 노이즈는 적당히 필터링(filt)해서 구하는 방식이다.
왼쪽의 데이터를 여러 개의 주기 함수의 합으로 분해한다고 가정해보자. 거대한 상승 흐름은 거대한 주기 함수로 대략 표현할 수 있을 것이고, 아주 세밀한 진동은 작은 주기의 함수로 표현할 수 있다. 이것들을 합치면 원래의 왼쪽 데이터가 나온다.
<code>convolution_filter</code>는 그 중에서 아주 세밀한 진동은 무시하고 거대한 주기, 즉 거대한 트렌드만을 보겠다는 의미가 된다.
필터링 기준(filt)는 파라미터를 주지 않으면 원본 데이터의 DATETIME 정보를 보고 메서드가 직접 결정한다. 예를 들어 monthly 데이터는 1개월 미만은 노이즈로 처리해서 제거한다.
<img src="https://images.velog.io/images/soo-im/post/8670c785-2b20-4856-a210-ca5a45c5dadf/image.png" alt=""></p>
</li>
<li><p>이제 원본 데이터에서 <code>trend</code>를 제거한다. <code>trend</code>를 제거한 데이터에서 계절별(예: monthly의 경우 12회)로 평균을 구하면 계절의 평균값을 구할 수 있다.
예를 들어 2000년 1월부터 2020년 12월까지 데이터가 있다면 2000년 1월, 2001년 1월, ... 2020년 1월의 평균을 구해 &#39;평균적인&#39; 1월 값을 구한다. 같은 방법을 반복해 &#39;평균적인&#39; 1월부터 12월까지의 값이 나오면 이것이 계절의 평균값이다. 이제 이걸 21회 반복시키면 데이터의 <code>seasonality</code>(혹은 <code>period</code>)가 나온다.
<img src="https://images.velog.io/images/soo-im/post/5c25dbd7-5b15-4f02-b584-c45b96a800cf/image.png" alt=""></p>
</li>
<li><p>마지막으로 <code>trend</code>와 <code>period</code>를 모두 원본 데이터에서 제거하면 <code>residual</code>만 남는다.</p>
</li>
</ol>
<h2 id="2022-02-23">2022-02-23</h2>
<h3 id="random-forest-regression-for-autocorrelated-time-series">Random Forest Regression for (AutoCorrelated) Time Series</h3>
<p>RF 모형은 외삽을 위한 모형이 아니기 때문에, 관측하지 못한 값을 예측할 수는 없다. 무한히 먼 미래의 값을 끊임없이 예측하려면 linear regression이 더 나을 수 있다.</p>
<p>하지만 독립변수(X)가 자기상관이 존재하는 상황에서 Xt, Xt-1, Xt-2...를 이용한 multiple linear regression은 적절하지 않다. 이 방법은 독립변수간의 공산성이 없음을 가정하는데 X가 자기상관이 있다면 당연히 Xt, Xt-1, Xt-2...의 공산성은 0이 아니다.</p>
<p>이런 문제에서는 RF가 더 자유롭다. RF는 nonlinear 모형이기 때문에, linearity 가정에서 발생하는 자기상관이 예측을 방해할 여지가 낮다.</p>
<blockquote>
<p><strong>그런데 왜 독립변수끼리 공산성이 없어야 하나? (내맘대로 정리)</strong>
$Y = m_{1}X_{1} + m_{2}X_{2} + ... + m_{n}X_{n}+e$ 에서 $m_{1}, m_{2}...$는 각각 다른 독립변수를 고정한 상태에서 해당 변수만 이동하였을 때의 선형회귀 계수이다.
키 $X_{1}$, 독후감 개수 $X_{2}$을 이용해 국어성적 $Y$를 구한다면 &quot;독후감 개수가 같은 경우 키에 따라 얼마나($m_{1}$) 국어성적이 바뀌는가?&quot;와 같은 방식으로 구하는 것이다.
그런데 $X_{1}$과 $X_{2}$가 이미 서로 연관이 높은 상태라면 사실상 $X_{1}$을 고정한 채로 $X_{2}$의 효과만 고려할 수는 없다. $X_{1}$이 도서관 방문 횟수라면, 도서관 방문 횟수가 바뀌는 시점에 이미 독후감 개수가 영향을 받고 있을 것이다. 따라서 multiple linear regression에서는 독립변수 공산성이 없어야만 계수가 의미가 있다.
<img src="https://images.velog.io/images/soo-im/post/5825651d-a05d-424f-b890-a0f33d7d4c4f/image.png" alt=""></p>
</blockquote>
<h2 id="2022-02-24">2022-02-24</h2>
<h3 id="정규식">정규식</h3>
<p>매번 제대로 이해 못하고 따라치는 게 갑갑해서 책을 빌려왔다! <a href="https://wikidocs.net/4308">점프투파이썬</a>!</p>
<ol>
<li>문자 클래스 <code>[ ]</code>
<code>[ ]</code> 사이의 문자와 매칭한다는 의미이다. <code>[abc]</code>와 문자열 appple, banana는 a 혹은 b가 들어있으니 매칭되고, regex는 안 된다.
<code>-</code>는 문자 사이의 연속된 범위를 나타내고 <code>^</code>은 매칭되지 않음을 의미한다. <code>[a-c]</code>는 <code>[abc]</code>와 같고, <code>[a-zA-Z]</code>는 모든 알파벳을 의미한다. <code>[^0-9]</code>는 숫자가 아닌 문자를 의미한다.
알파벳, 숫자 전체를 가리키는 정규식은 <code>[a-zA-Z0-9_]</code> 또는 <code>[\w]</code>이고, 이들을 제외하는 정규식은 <code>[^a-zA-Z0-9_]</code> 혹은 <code>[\W]</code>이다(참고로 마지막의 <code>_</code>는 특별한 의미를 가지는 것이 아니고 말그대로 문자열에 _가 있는지 확인하는 것이다).</li>
<li>Dot <code>.</code>
모든 문자를 가리키는 와일드카드. <code>a.b</code>는 aab, a5b와 매칭된다. 다만 앞에서 본 <code>[_]</code>와 마찬가지로 <code>a[.]b</code>는 . 그 자체가 들어가있는지 확인하는 것이다. 그래서 <code>a[.]b</code>는 오로지 a.b 만 매칭된다.</li>
<li>반복 <code>*</code>, <code>+</code>
<code>*</code> 앞의 문자가 0번 이상 반복된다는 의미이다. <code>hi*</code>는 h, hi, hiiiii 모두와 매칭된다. <code>+</code>도 비슷하지만 한 번 이상 반복된다는 뜻이다.</li>
<li>반복 <code>{m,n}</code>
반복 횟수를 m부터 n까지 제한한다는 의미이다. m, n 중 하나만 쓰는 것도 가능하다. <code>ca{1,2}t</code>는 cat, caat과 매치되고 <code>ca{2}t</code>는 caat, <code>ca{,2}t</code>은 ct, cat, catt와 매치된다. 참고로 <code>{,1}</code>은 <code>?</code>로도 표현할 수 있다.</li>
</ol>
<h2 id="2022-03-03">2022-03-03</h2>
<h3 id="timeseriessplit-for-time-series-in-ml">TimeSeriesSplit for Time Series in ML</h3>
<p>이전에 쓴 것처럼(02/23) 자기상관이 있는 시계열을 (다른 데이터로부터) 예측하려면 Linear Regression보다 Random Forest를 쓰는 것이 적절하다. 다만 주의할 점은 시계열 자료의 학습셋/테스트셋 분리는 <code>train_test_split</code>이 아닌 <code>TimeSeriesSplit</code>을 사용해야 한다.
<a href="https://stackoverflow.com/questions/70243933/what-is-difference-between-train-test-splitshuffle-false-and-timeseriesspli/70245293">이 글</a>에 설명이 정말정말 잘 되어있는데, 간단히 요약하면 다음과 같다.</p>
<ol>
<li>시계열 &#39;예측&#39;은 당연히 과거의 데이터만 사용해야 한다.</li>
<li>1월 5일을 예측하려면 1월 4일까지만 학습셋으로 사용해야 한다. 1월 6일 데이터를 이용해서 1월 5일을 &#39;예측&#39;하는 상황은 현실에서 있을 수 없다.</li>
<li>문제는 <code>train_test_split</code>을 사용하면 위의 상황이 가능해진다. <code>shuffle = False</code> 옵션으로 뒤죽박죽 섞이는 것을 방지하더라도, 1월 1일, 2일, 4일, 6일이 학습셋으로 들어간다면 5일의 예측 성능은 매우 좋아질 것이다.</li>
<li>이 현상을 <a href="https://machinelearningmastery.com/data-leakage-machine-learning/">data leakage</a> 라고 부른다. 학습셋에 현실에서 존재할 수 없는 데이터 혹은 테스트셋이 일부 포함된 경우를 말한다(테스트셋 데이터가 학습셋으로 유출되었다는 뜻에서 &#39;leakage&#39;라고 표현한다). 이는 과도한 성능 향상을 가져온다.</li>
<li>이런 문제를 방지하려면 예측 시점보다 과거의 데이터만 학습셋으로 만들어주는 <code>TimeSeriesSplit</code>을 사용하면 된다. 아래 그림 출처는 <a href="https://scikit-learn.org/stable/auto_examples/model_selection/plot_cv_indices.html#sphx-glr-auto-examples-model-selection-plot-cv-indices-py">여기</a>.
<img src="https://images.velog.io/images/soo-im/post/c672e308-a9cd-4e80-9cfc-0dffa48e81d7/image.png" alt=""></li>
</ol>
<blockquote>
<p><strong>Overfitting vs. Data Leakage</strong>
<a href="https://medium.com/analytics-vidhya/overfitting-vs-data-leakage-in-machine-learning-ec59baa603e1">이 글</a>에 잘 설명되어 있다.
Overfitting은 &#39;학습셋&#39;에 과도하게 최적화되어서, &#39;테스트셋&#39;을 넣었을 때 제대로 예측하지 못하는 현상을 말한다.
Data Leakage는 &#39;학습셋&#39;이 &#39;테스트셋&#39;을 일부 포함하고 있어서, &#39;테스트셋&#39;을 넣었을 때 과도하게 예측 성능이 높아지는 현상을 말한다.</p>
</blockquote>
<h2 id="2022-03-08">2022-03-08</h2>
<h3 id="seasonal_decompose결과의-trend-residual-nan처리"><code>seasonal_decompose</code>결과의 trend, residual NaN처리</h3>
<p><code>statsmodels.tsa.seasonal</code>의 <code>seasonal_decompose</code>를 기본 옵션으로 사용하면 trend와 residual의 앞-뒤 일부가 NaN으로 나오는 것을 볼 수 있다. 아래 그림에서 seasonal은 전체 데이터 타임을 커버하지만 residual은 비어있는 걸 볼 수 있다.
<img src="https://images.velog.io/images/soo-im/post/64a542a1-c824-49e5-934e-5a5e880eed7d/image.png" alt="">
그 이유는 <a href="https://github.com/statsmodels/statsmodels/issues/3872">깃허브 이슈</a>에서 볼 수 있었다. seasonal을 구하려면 최소한 한 주기는 있어야 하기 때문에 맨 앞과 뒤의 데이터 중 반 주기는 알 수가 없다. seasonal이 무엇인지 알 수 없으니 trend, residual도 구할 수 없는 것이다.
NaN 대신 임의로 채우려면 <code>extrapolate_trend</code> 옵션을 사용하면 된다. <a href="https://www.statsmodels.org/devel/generated/statsmodels.tsa.seasonal.seasonal_decompose.html">공식 문서</a>에 따르면 &quot;freq&quot;로 설정할 경우 가장 가까운 포인트, &quot;0이상의 정수&quot;로 설정하면 그 개수만큼의 포인트를 이용해 linear least-squares extrapolated 한다. 아래는 &quot;freq&quot;, 정수로 외삽한 예시.
<img src="https://images.velog.io/images/soo-im/post/66d6f083-dd9e-4689-959d-f1492bd0c3fb/image.png" alt=""></p>
<blockquote>
<p><strong>왜 seasonal은 처음/마지막이 NaN이 아닌가?</strong>
trend, residual의 앞뒤 반 주기는 날린다는 점에서 &quot;그럼 seasonal도 비어야지 왜 seasonal은 있나?&quot;는 질문이 나올 수 있다. 이건 2월 22일 기록으로 답할 수 있다. seasonal은 하나의 주기를 찾아두고 그걸 데이터 길이만큼 곱한 것이라서 전체 데이터 길이를 커버할 수 있다.</p>
</blockquote>
<h2 id="2022-03-10">2022-03-10</h2>
<h3 id="seasonality계절성-vs-cyclicity주기성">Seasonality(계절성) vs. Cyclicity(주기성)</h3>
<p>Seasonal(Periodic)와 Cyclic은 다른 개념이다!! (기후학에서 seasonal cycle이라고 쓰는데 애초에 둘이 다른 거라 저렇게 썼던 모양이다🙄) 심지어 Periodic이랑 Cyclic은 보통 둘 다 &#39;주기성&#39;이라고 해석해서 더 헷갈린다...
둘을 구분하는 게 중요한 이유는 <a href="https://otexts.com/fppkr/stationarity.html">stationarity(정상성)</a>을 판단하는 중요한 척도이기 때문이다.</p>
<blockquote>
<p><strong>시계열이 정상적(stationarity)이다</strong>
= 시계열의 특징이 관측 시점과 무관하다
= 시계열의 특징이 시점에 따라 달라지지 않는다
= 일부를 보았을 때 그것을 언제 측정한 것인지 파악할 수 없다</p>
</blockquote>
<p>이 조건을 만족하려면 시점에 따라 값의 평균값이 변하거나(trend) 분산이 변하는 등 측정값의 통계값이 변하면 안 된다. 그리고 언제 측정한 것인지 추정이 가능한 계절성도 존재하면 안된다. 즉 정상 시계열은 계절성(seasonality)과 추세(trend)를 가지면 안된다. 
반면 주기성(cyclicity)은 정상성에 영향을 주지 않는다. 주기성만으로는 그 데이터가 어느 시점에 측정된 것인지 알 수 없기 때문이다.</p>
<p>그러면 어떤 것이 계절성이고 어떤 것이 주기성일까?
<a href="https://robjhyndman.com/hyndsight/cyclicts/">이 글</a>에서는 아래와 같이 설명한다.
<strong>계절성</strong>은 문자 그대로 계절에 영향을 받는(일/월/연 변화) 패턴으로, 항상 <em>고정된 주기_를 가진다. 연 평균 기온은 무조건 1년 주기로 진동을 하기 때문에 계절성을 가진다.
<strong>주기성</strong>은 _고정된 주기가 없는</em> 패턴이다. 경제 사이클은 그것이 위아래로 진동한다는 것은 알지만 사이클이 끝나기 전까지는 그 사이클의 주기를 알 수도 없고, 매번 주기가 다르다.</p>
<p>아래 예시는 미국의 신축 단독 주택 월별 판매량(1973-1995)이다(그림 원본 <a href="https://robjhyndman.com/hyndsight/cyclicts/">출처</a>).
1년의 고정된 주기로 진동하는 계절성(파란색)과 주기를 정확히 파악할 수 없는 주기성(노란색)이 함께 보인다.
<img src="https://images.velog.io/images/soo-im/post/edb74be4-ffb3-4bc5-b070-369056e7ff53/image.png" alt=""></p>
<h2 id="2022-03-21">2022-03-21</h2>
<h3 id="moving-average-model-사례">Moving Average Model 사례</h3>
<p>Moving Average Model은 Auto Regressive Model보다 덜 직관적이다. 왜 뜬금없이 에러가 튀어나온거지?</p>
<blockquote>
<p>$y_{t} = c + \varepsilon_t + \theta_{1}\varepsilon_{t-1} + \theta_{2}\varepsilon_{t-2} + \dots + \theta_{q}\varepsilon_{t-q}$</p>
</blockquote>
<p>수식을 풀어서보면
대략적인 값($c$)으로 예측을 해본다. 예측한 값($\hat{y_{t}}$)이 실제 값($y_{t}$)이랑 얼마나 차이($\varepsilon_{t}$)가 나는지 계산해본다. 그리고 다음 값을 예측할 때는 대략적인 값($c$)에 그 전에 발생한 오차($\varepsilon_{t-q}$)를 써서 적당히($\theta_{t-q}$) 보정해준다.</p>
<p>그래도 이게 무슨 소리인가 싶다. 여기 <a href="https://www.youtube.com/watch?v=voryLhxiPzE">유튜브 영상</a>은 주식같은 얘기 없이 아주 직관적으로 잘 설명해준다.</p>
<ol>
<li>내가 <del>가엾은</del> 대학원생이라고 생각해보자. 나는 세미나 조교라 주에 한 번 열리는 학과 세미나에서 과자를 준비해야 한다. 학과 사람이 40명이니 40개($c$) 정도 챙겨가면 된다.</li>
<li>첫 주에는 딱 40개($c$)를 챙겨갔다. 그런데 교수님이 보시고는 &quot;이렇게 모자라게 준비하면 어떡하나? 넉넉히 10개($\varepsilon_{t}$)는 더 챙겨와야지!&quot; 하고 혼내신다. 내가 센스가 없었지. 오병이어를 해서라도 40개($\hat{y_{t}}$) 살 돈으로 50개($y_{t}$)를 준비해야 했는데. </li>
<li>한 주가 지나고 나는 고민한다. 당연히 40($c$)개 챙겨가면 혼날거다. 하지만 세미나 준비 비용이 넉넉하지는 않아서 저번 주에 혼난 개수($\varepsilon_{t-1}$)의 절반($\theta_{t-1}$)만 더 챙기기로 했다. 10/2 = 5니까 45개($\hat{y_{t}} = c+\theta_{1}\varepsilon_{t-1}$)를 사갔다.</li>
<li>&quot;왜 과자가 남아도나? 학과 예산으로 자네 과자 사먹는건가? 학생들도 38명($y_{t}$) 뿐인데!&quot; 10개를 더 샀으면 횡령죄로 잡혀갈 뻔 했다. 남는 과자 7개($\varepsilon_{t}$)는 애들이나 나눠줬다.</li>
<li>넉넉하게도 딱 맞게도 준비할 수가 없다. 생각을 포기하고 지난 주에 모자란 양($\varepsilon_{t-1}$)의 절반($\theta_{t-1}$)만 반영해 40개($c$)에서 3.5개($\theta_{1}\varepsilon_{t-1}$) 모자라게 챙겼다. 그런데 놀랍게도 이번 주에는 교수님이 오늘의 과자는 딱 맞다고 하셨다! 기준은 알 수 없지만 이번 주 오차($\varepsilon_{t}$)는 0이다!
...</li>
</ol>
<p>이런 식으로 평균 값의 범위($c$, 위에서 40개)에서 지난 번의 오차($\varepsilon_{t-q}$)를 적당히($\theta_{t-q}$) 반영하는 방식이 Moving Average Model이다. 여기서 Average는 $c$와 같은 의미로, 어쨌거나 저 범위 내에서 값이 변동한다는 의미이다.</p>
<h2 id="2022-03-23">2022-03-23</h2>
<h3 id="pip-vs-pipenv">pip vs. pipenv</h3>
<p>(노마드코더 에어비앤비 클론 강의 시작!)</p>
<p>패키지를 설치할 때 <code>pip install packagename</code> 으로 설치하는 경우가 많지만 그다지 추천하는 방법은 아니다. <code>pip</code>은 패키지를 global에 설치하기 때문이다. 어떤 프로젝트는 패키지 버전 A를 써야하고 다른 프로젝트는 버전 B를 써야 한다면 <code>pip</code>으로는 불가능하다. global에 설치하는 거니까 둘 중 하나만 가능하다.
대신 <code>pipenv</code>를 사용하면 프로젝트별로 패키지를 따로 설치할 수 있다. 프로젝트별로 서로를 침해하지 않는 일종의 거품(버블)같은 가상환경을 만들어서 그 안에서 패키지를 설치하면 된다.</p>
<ol>
<li>프로젝트 디렉토리에서 <code>pipenv --three</code> 명령어를 입력한다. 파이썬 버전3(<code>--three</code>는 파이썬 버전을 의미)로 가상환경이 만들어진다.</li>
<li>가상환경이 만들어졌으면 해당 디렉토리에 Pipfile이 생성된다.</li>
<li>패키지를 가상환경 &#39;안&#39;에 설치하기 위해 해당 가상환경 안으로 이동해야한다. <code>pipenv shell</code>로 가상환경 내부로 들어간다.</li>
<li><code>pipenv install packagename</code>으로 원하는 패키지를 설치한다.</li>
<li>설치가 완료되면 Pipfile에서 내가 설치한 패키지를 확인할 수 있다.
<img src="https://images.velog.io/images/soo-im/post/274bcc8f-b34c-4d15-bdb7-eb429eff098f/image.png" alt=""></li>
</ol>
<h2 id="2022-03-24">2022-03-24</h2>
<h3 id="vscode로-django-개발-준비">VSCode로 Django 개발 준비</h3>
<ol>
<li>.gitgnore은 깃헙에 올리고 싶지 않은 파일의 목록을 적어둔 리스트이다. Python gitgnore을 예시로 보면 <code>*.log</code>나 <code>local_settings.py</code>와 같은 파일은 깃헙에 올리지 않는다는 의미이다. 
```<h1 id="django-stuff">Django stuff:</h1>
</li>
</ol>
<p>*.log
local_settings.py</p>
<pre><code>2. (pipenv) Django 프로젝트 세팅하기
1) 폴더를 생성한다(깃헙도 원하면 세팅).
2) 폴더 경로에서 `pipenv shell` 명령어로 가상환경 안으로 들어간다.
3) `django-admin startproject config` 명령어로 config 폴더를 만든다.
4) config 폴더 내부에 config와 기타 파일이 있는데 이것들을 config 폴더 바깥으로 꺼낸다. 이제 비어버린 원래의 config 폴더는 삭제한다.
5) Ctrl-Shift-P에서 &quot;Python: Select Interpreter&quot; 옵션으로 들어가 현재 디렉토리 내 `pipenv`를 선택한다.

3. VSCode 파이썬 개발환경 세팅하기
1) Extension에서 python을 설치한다(실제 python이 아니고 확장 프로그램임).
2) 저장하면서 자동으로 python pep(파이썬 코드 작성 규칙) 스타일에 맞춰주는 것을 formatter라고 부른다. formatter에는 여러 종류가 있는데 만약 `black`을 사용한다면 .vscode 디렉토리 내 settings.json 파일에 아래를 추가한다.
`
&quot;python.formatting.provider&quot;: &quot;black&quot;
`
3) 에러가 날 법한 부분을 미리 찾아주는 것을 linter라고 부른다. `flake8`을 쓸 경우 마찬가지로 settings.json에 아래를 입력한다.
`
&quot;python.linting.flake8Enabled&quot;: true,
&quot;python.linting.enabled&quot;: true
`
아니면 Ctrl-Shift-P의 &quot;Python: Select Linter&quot; 옵션에서 flake8을 선택한다.
4) 위 과정에서 설치가 안 되어있을 경우 우측 하단에 설치 메시지가 나오니 OK 눌러 설치하면 된다. 수동으로 설치할 경우에는 `&amp; pipenv install black --dev --pre` 와 같이 옵션을 붙여 pipenv 내부에 설치하면 된다.

## 2022-03-30
###  ACF vs. PCAF

1. ACF(Auto-Correlation Function)
시계열에서 두 시점 간의 상관을 구하는 것이다. 자기 자신Auto)과 상관(Correlation)을 구한다고 해서 ACF라고 부른다. 예를 들어 오늘의 기온은 어제의 기온과 연관이 높으므로 lag = 1day에서 자기상관이 존재하고 ACF 값이 높을 것이다.

2. PACF(Partial ACF)
ACF를 &#39;Partial&#39;로 적용한 것이다. ACF가 전미분이라면 PCAF는 편미분에 대응할 수 있다. 오로지 내가 궁금한 것들만 ACF를 수행하는 것이다. 오늘의 기온은 어제의 기온과 연관이 있고, 어제의 기온은 그저께의 기온과 연관이 있다. 그렇다면 오늘의 기온과 어제의 기온의 상관을 구해도 그저께의 기온이 영향을 주었을 것이다. 다른 시점(그제)의 영향을 제외하고 오로지 두 시점(오늘, 어제)간의 상관을 구하는 것이 PACF이다.

### ACF-&gt;MA, PCAF-&gt;AR

1. PACF -&gt; AR model
오늘의 기온은 어제의 기온과 유의미한 PCAF를 가지지만 그제와 그 이전의 기온과는 유의미한 PCAF를 가지지 않는다고 가정해보자. 그렇다면 어제의 기온만 가지고도 오늘의 기온을 예측할 수 있을 것이다. 이것이 Auto-Regressive 모델이다. 현재로부터 하나의 시점(어제)만 사용하면 1차 AR 모델, 둘의 시점(어제, 그제)을 사용하면 2차 AR 모델,... 과 같이 부른다.

2. ACF -&gt; MA model
아... ACF 차수가 왜 MA로 연결되는지는 아직 이해가 잘 안 간다. [이 영상](https://www.youtube.com/watch?v=5Q5p6eVM7zM)에서 설명 잘 듣고 있었는데 이 부분에서 막혔다 ㅠㅠ 좀 더 찾아보자...</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[게더타운 행사 만들기-예약, 게스트 목록]]></title>
            <link>https://velog.io/@soo-im/%EA%B2%8C%EB%8D%94%ED%83%80%EC%9A%B4-%ED%96%89%EC%82%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%98%88%EC%95%BD-%EC%B1%84%ED%8C%85-%EC%A0%80%EC%9E%A5-%EB%93%B1-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EA%B8%B0%EB%8A%A5%EB%93%A4</link>
            <guid>https://velog.io/@soo-im/%EA%B2%8C%EB%8D%94%ED%83%80%EC%9A%B4-%ED%96%89%EC%82%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%98%88%EC%95%BD-%EC%B1%84%ED%8C%85-%EC%A0%80%EC%9E%A5-%EB%93%B1-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EA%B8%B0%EB%8A%A5%EB%93%A4</guid>
            <pubDate>Sun, 02 Jan 2022 12:56:26 GMT</pubDate>
            <description><![CDATA[<p>게더타운으로 행사를 할 때에는 맵도 중요하지만 게스트를 관리하는 것도 중요한 업무이다. 이번 글에서는 행사를 관리할 때 유용한 기능인 예약과 게스트 목록을 소개한다.</p>
<h1 id="유료-예약">유료 예약</h1>
<h3 id="더-들어오려면-돈을-내시오">더 들어오려면 돈을 내시오</h3>
<p>작성일(2021.12.11) 기준으로 25명이 넘는 인원이 모일 경우 유료 예약을 해야 한다. 예약하지 않은 상태에서 많은 인원이 몰리면 서버 문제로 행사가 갑자기 종료될 수도 있다고 하니 예약해 두는 것이 안전하겠다(출처: <a href="https://support.gather.town/help/reservations-subscriptions">게더타운 FAQ</a>).</p>
<p>예약은 <code>Space Dashboard</code> &gt; <code>Reservation</code> 탭에서 할 수 있고 2시간($2/인), 하루($3/인), 월간 구독($7/인) 세 가지 옵션이 있다. 2시간을 초과하면 그냥 하루치로 결제해야 한다. 바로 결제하거나 인보이스를 받은 후에 결제할 수도 있다. 최소 행사 30분 전에는 결제가 완료되어야 안정적으로 사용할 수 있다.
<img src="https://images.velog.io/images/soo-im/post/c907ea36-4b2d-4931-9cde-684b68d3b463/image.png" alt=""></p>
<blockquote>
<p><strong>예약한 인원보다 몇 명 더 들어오면 어떡하죠?</strong>
라는 질문을 보냈더니 예약 인원에 따라 약간의 버퍼를 제공하기 때문에(예: 30명 예약시 33명까지 버퍼 제공) <strong>한두 명 더 들어온다고 서버가 터지거나 하진 않는다</strong>고 했다. 하지만 버퍼 비율을 따로 명시한 것은 아니라서 중요한 행사라면 좀 더 넉넉히 잡는 것이 안전하겠다.</p>
</blockquote>
<blockquote>
<p><strong>덜 들어오면요?</strong>
예약 인원보다 덜 들어왔다고 환불을 해주지는 않는다. <a href="https://support.gather.town/help/reservations-subscriptions">FAQ</a>에 따르면 마치 레스토랑을 예약하는 것과 같아서, 공간을 예약하고 나면 이미 그만큼 좌석이 쓰였기 때문에 사람이 덜 왔다고 해서 환불되진 않는다고 한다. <del>근데 레스토랑은 결국 온 사람 수만큼 요금 받지 않나...?</del></p>
</blockquote>
<h1 id="게스트-리스트">게스트 리스트</h1>
<h3 id="사칭-중복-닉네임-멈춰">사칭, 중복 닉네임 멈춰!</h3>
<p>외부인이 들어오는 것을 막을 때는 비밀번호를 설정하면 된다. <code>Space Dashboard</code> &gt; <code>Space Access</code> &gt; <code>Space Password</code> 에서 간단히 설정할 수 있다.
그런데 외부인을 막을 뿐만 아니라 <strong>들어올 사람의 이름까지 지정하는</strong> 기능도 있다. &#39;게스트 리스트&#39;이다. 사칭을 방지해야 한다거나 하는 상황이라면 이 기능이 유용하게 쓰일 수 있다.</p>
<h3 id="기능">기능</h3>
<p>게스트 리스트는 <strong>리스트에 올라온 사람만 입장할 수 있게 하고</strong>, <strong>이름과 소속 명칭을 호스트가 직접 지정</strong>할 수 있게 해준다. 아래 게스트 리스트 예시를 보면 설명이 빠르다. 설정하는 방법은 다음 챕터에서 설명하겠다. (사진 출처: Gather Town)
<img src="https://images.velog.io/images/soo-im/post/3f288321-1fcf-49fd-ab78-a5af99633303/image.png" alt=""></p>
<ol>
<li>email(필수)
게스트의 이메일. 이 때 <strong>반드시 본인의 이메일도 입력해야 한다.</strong> 리스트에 안 적으면 본인이 호스트여도 못 들어간다 🙄;;</li>
<li>name(선택)
게스트의 이름을 강제로 지정할 수 있다. 비워두면 게스트는 자신이 원하는대로 설정할 수 있다.</li>
<li>role(선택)
실제로 특정 기능을 부여하는 것은 아니다. 그냥 참고용으로 사용한다.</li>
<li>affiliation(선택)
이 칸을 입력하면 해당 게스트의 정보 카드를 볼 때 아래와 같이 이름 밑에 소속 정보가 나온다. (사진 출처: Gather Town)
<img src="https://images.velog.io/images/soo-im/post/e721f51f-04e3-40a5-b526-4e9591ae2846/image.png" alt=""></li>
</ol>
<h3 id="설정-방법">설정 방법</h3>
<ol>
<li>엑셀 첫 행에 email, name을 입력하고 아래에 게스트 이메일과 지정 이름(선택)을 입력한다. 본인의 이메일도 적어야 한다는 점과, 이름을 지정하지 않더라도 반드시 name 열을 만들어야 하는 점을 유의한다.
<img src="https://images.velog.io/images/soo-im/post/75ed5e4a-050b-4db7-b490-a0f12e0d403f/image.png" alt=""></li>
<li>csv로 저장한 후 파일을 닫는다.</li>
<li>csv 파일을 워드패드로 열어(<code>우클릭-연결프로그램-워드패드</code>) 맨 마지막에 들어간 빈 칸을 삭제한 후 저장한다.
(귀찮지만 이걸 안 하면 에러를 맛보게 될 것이다)
<img src="https://images.velog.io/images/soo-im/post/72840f82-3124-48e1-8652-648afac8170e/image.png" alt=""></li>
<li>게더타운에서 <code>Space Dashboard</code>-<code>Space Access</code>로 들어간다.
<img src="https://images.velog.io/images/soo-im/post/6a4ad858-f03e-4f36-921a-6f44abc4b2b9/image.png" alt=""></li>
<li><code>Upload .csv</code>를 눌러 방금 만든 csv 파일을 넣는다.</li>
<li>아래에 게스트 목록이 나왔다면 성공이다.
<img src="https://images.velog.io/images/soo-im/post/9d403b9c-7cb5-4f15-811c-b61e21cb07ea/image.png" alt=""></li>
</ol>
<blockquote>
<p><strong>csv 넣었는데 에러 나는데요?</strong>
아래와 같은 에러가 나왔다면 두 가지 가능성이 있다.
<img src="https://images.velog.io/images/soo-im/post/b13fcde9-521a-4c43-ae37-50b938361ece/image.png" alt="">
첫째는 email 열만 만들고 name 열을 안 만들었을 때.
두번째는 3번을 따라 csv 파일에서 맨 마지막 빈 칸을 삭제하지 않았을 때.
경우에 맞게 수정한 후에 업로드하면 된다. 참고로 한 번 에러나면 해당 페이지가 먹통이 되는 경우가 있어 새로고침 한 후에 다시 업로드하는 것을 추천한다.</p>
</blockquote>
<p>덧붙여서, 만약 게스트 목록에 몇 명을 추가하고 싶다면 <code>Add more .csv</code>를 하면 된다. 그냥 <code>Upload .csv</code>하면 아예 게스트 목록이 교체되는 거라서 헷갈리면 안 된다!</p>
<h1 id="기타-기능">기타 기능</h1>
<p>채팅 저장, 유튜브 스트리밍 등등의 팁이 몇 가지 더 있는데 연재가 자꾸 늦어지는 바람에 이번 글은 여기까지! 다음 팁은 다음 글에서 다뤄보겠다 😅 (이번 달 내에는 꼭...!)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1월 TIL 기록]]></title>
            <link>https://velog.io/@soo-im/1%EC%9B%94-TIL-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@soo-im/1%EC%9B%94-TIL-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Sat, 01 Jan 2022 17:07:02 GMT</pubDate>
            <description><![CDATA[<h3 id="2022-01-01">2022-01-01</h3>
<p>1월에는 Git &amp; 데이터 분석 위주로! (Node.js는 언제 하지...?)</p>
<p><strong>GitHub</strong> 레포지토리를 로컬에 연동</p>
<ol>
<li>Git Bash
<code>$ git clone &lt;repo url&gt;</code> </li>
<li>소스트리
좀 더 직관적이다!
<img src="https://images.velog.io/images/soo-im/post/5bf33861-0d6c-4dab-a158-cc7fea51c7b2/image.png" alt=""><blockquote>
<p><strong>깃허브 계정 전환</strong>
소스트리에 여러 계정을 입력하고 계정을 바꿀 수 있다.&#39;설정 초기화(원래는 Defualt Setting 이라는 듯)&#39;를 누르면 해당 계정으로 기본 계정이 바뀐다. 
<img src="https://images.velog.io/images/soo-im/post/c460639f-c4dc-4d9f-84aa-c1f11024d1a3/image.png" alt=""></p>
</blockquote>
</li>
</ol>
<h3 id="2022-01-02">2022-01-02</h3>
<p><strong>GitHub</strong> (소스트리 버전)</p>
<ol>
<li>용어 정리
 b. <code>add</code> 깃헙에 올리기를 원하는 파일을 묶는 과정. &#39;스테이지에 올린다&#39;고 표현한다. <code>add</code>를 한 후에 <code>commit</code>을 할 수 있다.
 a. <code>commit</code> 특정 버전을 저장하는 행위.
 c. <code>push</code> 커밋을 하면 로컬에 저장된다. 커밋을 깃헙에 업로드하는 것을 <code>push</code>라고 부른다.
 d. <code>add</code>-<code>commit</code>-<code>push</code> 과정으로 깃헙에 커밋한다.
 <img src="https://images.velog.io/images/soo-im/post/567ca4a4-6fd7-45df-9d8d-554dbefead91/image.png" alt=""></li>
<li>커밋 메시지 작성 요령
 a. 하나의 커밋은 하나의 작업만을 담아야 한다.
 b. 요약 설명 + 구체 설명을 작성한다.</li>
<li>소스트리는 마지막 커밋으로 되돌리는 작업을 &#39;코드뭉치 버리기&#39;라는 기능으로 제공한다.
<img src="https://images.velog.io/images/soo-im/post/ae6274ab-d686-4c4b-96a4-212f9b52a771/image.png" alt=""></li>
</ol>
<h3 id="2022-01-03">2022-01-03</h3>
<p><strong>GitHub</strong> (소스트리 버전)</p>
<ol>
<li>브랜치 생성 및 체크아웃
<img src="https://images.velog.io/images/soo-im/post/75aeee66-767e-45a6-aa68-c65daed4713f/image.png" alt="">
???? 왜 갑자기 인프런 접속이 안 되지?? 🙄;; 병합은 내일 이어서...</li>
</ol>
<h3 id="2022-01-05">2022-01-05</h3>
<p><strong>GitHub</strong> 병합 <code>merge</code> </p>
<ol>
<li><strong>Fast Forward merge</strong>
(1) 합치려는 브랜치(<code>version2</code>)가 헤드 브랜치(<code>main</code>)로부터 갈라져 나왔고, (2) 합치려는 브랜치가 갱신되는 동안 헤드 브랜치에는 갱신이 없을 때 Fast Forward merge가 된다.</li>
</ol>
<p><img src="https://images.velog.io/images/soo-im/post/255ae0fe-4fa5-4d9c-89dc-8ef6b0c32905/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/soo-im/post/b96e36d6-5d7b-4de9-8512-cf79b7388802/image.png" alt=""></p>
<ol start="2">
<li><strong>Merge Conflict</strong>
Fast Forward와 다르게 합치려는 브랜치 외의 다른 브랜치에서도 변경이 발생했을 때 생기는 경우이다. 여러 가지 기능을 동시에 작업할 때 주로 생긴다.
이럴 때 에디터에서 직접 변경한 후에 다시 커밋하면 된다.(<code>git .add</code> 후 <code>git commit</code>). </li>
</ol>
<p><img src="https://images.velog.io/images/soo-im/post/582dbfbb-b2e2-4b5d-a295-f4400d99e190/image.png" alt="">
<img src="https://images.velog.io/images/soo-im/post/fc3bfd91-f1d5-4987-8042-a854ee55cf2c/image.png" alt=""></p>
<h3 id="2022-01-06">2022-01-06</h3>
<p><strong>GitHub</strong></p>
<ol>
<li><p><code>pull</code>
깃헙에 저장된 커밋이 헤드 브랜치보다 최신인 경우(로컬 파일보다 최신 버전의 커밋이 깃헙에 올라가 있을 때) 최신 커밋을 끌어오는 명령어. </p>
</li>
<li><p>특정 커밋 시점으로 돌아가기(소스트리)
<code>우클릭</code>-<code>이 커밋까지 현재 브랜치를 초기화</code></p>
</li>
<li><p>충돌 해결(소스트리)
헤드 브랜치(&#39;내것&#39;)나 병합될 브랜치(&#39;저장소&#39;) 중 하나만 남길 거라면 굳이 에디터를 열지 않아도 된다.<code>우클릭</code>-<code>충돌 해결</code>을 이용해 원하는 브랜치만 남긴 후 커밋하면 된다.
<img src="https://images.velog.io/images/soo-im/post/669f2ce2-9f13-417b-9897-1d58bc98d649/image.png" alt=""></p>
</li>
</ol>
<h3 id="2022-01-0709">2022-01-07~09</h3>
<p>팜유랑 엘니뇨 상관 보려고했는데 시장 가격만 나오고 선물 데이터가 안 나와서 잠시 스탑... :/ sigh 유명한 농산물은 엘니뇨 영향권이 아니라서 사용할 수가 없는데 고민이로다 적도에서만 나는 것 좀 없을까</p>
<h3 id="2022-01-12">2022-01-12</h3>
<p>FIS에 2010년 11월 이후 팜유 선물은 나와있어서 아쉽지만 일단 이걸로 해야겠다! 일단 오늘은 스크랩까지 완료~</p>
<h3 id="2022-01-18">2022-01-18</h3>
<p>엘니뇨로 팜유 선물을 Random Forest Regression으로 예측했다. 자기상관도 심한 애들을 저걸로 한 이유는 회사에서 피드백 받아보려고...() 이해하지 못하고 따라친 수준이니 피드백 전까지 좀 침착하게 공부해보자.</p>
<h3 id="2022-01-20">2022-01-20</h3>
<p>START 버튼 hover 애니메이션(?)을 CSS로 추가했다! <a href="https://wsss.tistory.com/148">이 사이트</a>에 다양한 CSS 예제가 나와있어서 보고 따라했다(SCSS라는 것도 알게됐다). <a href="https://codepen.io/himalayasingh/pen/QZKqOX">요 사이트</a>에도 예제가 많다.</p>
<p>START 버튼 CSS에는 hover 애니메이션 때문에 <code>transition</code> 옵션이 들어가있는데, 이것 때문에 <code>visibility: hidden;</code> 동작이 같이 지연되는 문제가 있었다. 그래서 시간 박스가 먼저 사라지고 START 버튼이 나중에 사라져서 보기가 영 안 좋았다.
시간 박스에도 똑같은 옵션을 주면 될 것 같아서 임의로 <code>transition</code> CSS 클래스를 하나 더 만들어서 시간 박스에 넣어주니 해결되었다!
<img src="https://images.velog.io/images/soo-im/post/76c88e00-64ff-460f-b528-60020fec22ab/tracker6.gif" alt=""></p>
<h3 id="2022-01-22">2022-01-22</h3>
<ol>
<li>웹에서 직접 폰트를 import해서 새 폰트를 적용했다! <a href="https://ansohxxn.github.io/blog/font/#1%EF%B8%8F%E2%83%A3-%EB%AC%B4%EB%A3%8C-%EC%9B%B9-%ED%8F%B0%ED%8A%B8-%EC%82%AC%EC%9D%B4%ED%8A%B8%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%A0-%ED%8F%B0%ED%8A%B8%EB%A5%BC-%EA%B3%A8%EB%9D%BC%EB%B3%B4%EC%9E%90">블로그</a>에 설명이 잘 되어 있다.</li>
<li><code>visibility</code> 속성은 binary라서(보인다/안보인다) <code>transition</code> 옵션을 써도 서서히 변하는 효과가 적용되지 않는다. <code>opacity</code> 옵션을 이용해서 버튼이 서서히 사라지게 만들었다(출처: <a href="https://stackoverflow.com/questions/27900053/css-transition-with-visibility-not-working">스택오버플로</a>).</li>
</ol>
<h3 id="2022-01-24">2022-01-24</h3>
<p>information 아이콘에 딱 맞는 <a href="https://stackoverflow.com/questions/33878539/is-there-an-html-entity-for-an-info-icon/33878646">예시</a>가 있어 제목 옆에 추가했다! 시작할 때 안내가 나오고 시작버튼을 누르면 사라진다.</p>
<h3 id="2022-01-25">2022-01-25</h3>
<ol>
<li>스크롤바 디자인을 <a href="https://felix-escape.tistory.com/623">수정</a></li>
<li><code>input</code> text 박스에 글을 입력할 때 outline이 보이지 않도록 <a href="https://codepen.io/ainalem/pen/qLjpLm">수정</a></li>
<li>배경을 투명하게 바꾸는 대신 내부 그림자가 보이도록 수정</li>
</ol>
<h3 id="2022-01-26">2022-01-26</h3>
<ol>
<li>스크롤바가 너무 가장자리에 붙어있어 <code>border-right</code>로 <a href="https://stackoverflow.com/questions/67978443/how-to-add-left-and-right-padding-to-webkit-scrollbar-thumb">조절</a>하고 스크롤 배경색을 삭제</li>
<li>CSS 속성에서 <code>webkit</code>, <code>moz</code>, <code>ms</code>가 뭔가 찾아봤더니 브라우저별로 <a href="https://stackoverflow.com/questions/18083056/what-are-moz-and-webkit">구분</a>한 것이라고 한다. 저런 접두사가 붙지 않는 버전이 최종 업뎃되는 경우가 있기 때문에, 접두사를 붙인 걸 먼저 작성하고 접두사를 안 붙인 걸 맨 마지막에 덧붙인다고 한다.</li>
</ol>
<h3 id="2022-01-28">2022-01-28</h3>
<p>앗... 팜유 선물 다른 것 썼더니 correlation이랑 예측 성능이 확 떨어진다 😱 보아하니 특정 일에 선물값이 치솟던데 확인해봐야겠다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[12월 TIL 기록]]></title>
            <link>https://velog.io/@soo-im/12%EC%9B%94-TIL-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@soo-im/12%EC%9B%94-TIL-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Wed, 01 Dec 2021 14:58:13 GMT</pubDate>
            <description><![CDATA[<h3 id="2021-12-01">2021-12-01</h3>
<p>딥브레인AI 풀스택 과정 코딩테스트 완료~ 만점이다!</p>
<h3 id="2021-12-0304">2021-12-03~04</h3>
<p>통계치 나오는 기능까지 만들었다! 이제 가장 기본적인 기능은 끝!!!
<img src="https://images.velog.io/images/soo-im/post/98506126-7588-4469-9d76-34c13670a95b/tracker4.gif" alt=""></p>
<h3 id="2021-12-06">2021-12-06</h3>
<p>추가하고 싶은 기능 목록!</p>
<ol>
<li>&#39;추가&#39;를 누르면 맨 밑이 아니라 누른 블록 바로 아래에 추가 + startTime도 누른 블록 기준으로 세팅</li>
<li>블록 삭제 기능</li>
<li>시작 시간 순서대로 정렬해주는 기능</li>
<li>00:00 형식을 00.00h 형식으로 바꾸는 옵션</li>
<li>입력 값 저장 기능</li>
</ol>
<p>** 1. <code>.insertAfter()</code>?**
<code>myParentNode.insertBefore(newChild, oldChild)</code>는 <code>myParentNode</code>가 가지고 있는 기존 자식노드 <code>oldChild</code> (기준) 앞에 새로운 자식노드 <code>newChild</code> 를 추가하는 함수이다.
하지만 그 반대로 기준 자식노드 뒤에 추가하는 함수는 따로 정의되어 있지 않아서 보통 다음과 같은 스니펫을 사용한다.</p>
<pre><code class="language-js">Object.prototype.insertAfter = function (newNode) {     
    if (!!this.nextSibling) {
      this.parentNode.insertBefore(newNode, this.nextSibling);
    } else {
      this.parentNode.appendChild(newNode);
    }
  };</code></pre>
<p><strong>2. <code>Node</code> vs. <code>Element</code></strong>
나의 경우에는 위의 스니펫에서 <code>parentNode</code> 대신 <code>parentElement</code>를 사용했다. 둘의 차이점은 노드와 요소(element)의 차이일텐데, 정확히 무엇이 다른걸까?
<a href="https://stackoverflow.com/questions/9979172/difference-between-node-object-and-element-object">설명</a>에 따르면 <em>노드 ⊃ 요소</em> 라고 보면 된다. 노드는 <code>docuement</code>, <code>document.body</code>, <code>&lt;p&gt;</code> 등 DOM 요소, html 태그 등등을 포함하는 개념이다. 반면 요소는 특정 유형의 노드로 html 태그를 이용해 지정 가능한 것을 말한다. 그러면 어떻게 노드와 요소를 구분해서 호출할 수 있을까?
<a href="https://velog.io/@yejineee/DOM%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-DOM-Node%EC%99%80-Element%EC%9D%98-%EC%B0%A8%EC%9D%B4">이 블로그</a>의 맨 아래 파트 예시를 보면 요소는 태그 안에 들어가 있는 것을, 노드는 전체를 다 가져온다고 이해하면 될 것 같다.</p>
<h3 id="2021-12-08">2021-12-08</h3>
<ol>
<li><p>블록 시작 시각 수정
항상 맨 마지막 블록에만 시간이 입력되게 만들어서 시작 시각을 맨 마지막 블록의 종료 시각으로 설정했는데, 어제 중간에도 추가되게 만들면서 &#39;추가를 누른 블록의 종료 시각&#39;으로 설정하게 만들어야 했다.
처음에는 <code>this</code>가 함수를 불러온 요소 그 자체를 가리킨다고 착각해서;; <code>this.previousElement</code>를 시도하는 등의 헛발질을 하다가... (<code>this</code>는 파이썬의 <code>self</code>랑 유사한 것 같음)
버튼 클릭 함수로부터 <code>e</code>를 받아와서 간단히 해결했다! 바뀐 코드는 이렇다↓</p>
<pre><code class="language-js">// 전: 맨 마지막 블록의 시간을 가져옴
const latestTime = allBlocks[allBlocks.length-1].querySelector(&quot;.endTime&quot;).value;
// 후: 클릭 이벤트가 일어난 블록의 시간을 가져옴
const latestTime = e.target.previousSibling.previousSibling.value;</code></pre>
</li>
<li><p>프로토타입?
<code>.insertAfter</code> 프로토타입 함수를 만든 후 집계 버튼을 누르면 &quot;insertAfter NaN:NaN&quot;라는 예상하지 못한 게 나온다. 정작 html을 확인해보면 저런 요소도 존재하지도 않는데 왜 나오는지 아직 잘 모르겠다. 그냥 함수랑 프로토타입 함수의 차이 때문에 그런 것 같은데... 🙄 내일 검색 좀 해봐야겠다</p>
</li>
</ol>
<h3 id="2021-12-09">2021-12-09</h3>
<ol>
<li>프로토타입</li>
</ol>
<p>파이썬 클래스와 유사한 개념이라고 이해했다! <a href="https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/Object_prototypes">MDN 설명</a>에서 아래와 같은 생성자 함수(≃클래스 생성자 함수)를 선언하고 그걸 이용해 인스턴스를 만들었다.</p>
<pre><code class="language-js">function Person(first, last, age, gender, interests) {
  // 속성과 메소드 정의
  this.first = first;
  this.last = last;
//...
}

var person1 = new Person(&#39;Bob&#39;, &#39;Smith&#39;, 32, &#39;male&#39;, [&#39;music&#39;, &#39;skiing&#39;]);</code></pre>
<p>그러면 <code>person1</code> 인스턴스에는 <code>first</code>, <code>last</code>,...와 같이 생성자 함수에서 정의한 속성도 들어있다. 이 때 <code>Person()</code>은 <code>person1</code>의 프로토타입 객체(≃상위 클래스)이다. 그리고 <code>person1</code>에는 <code>Person()</code>의 속성 뿐 아니라 <code>Person()</code>의 프로토타입 객체인 <code>Object</code>의 속성인 <code>vaueOf</code>와 같은 속성도 들어있다. 
파이썬에서 인스턴스에서 메소드를 호출하면 상위 클래스의 메소드를 확인하고, 없으면 상위 클래스의 상위 클래스의 메소드를 확인하는 것과 똑같은 방식이다.
<img src="https://images.velog.io/images/soo-im/post/6b119400-42fc-4e4a-bcd9-17033c02d47d/image.png" alt="">
출처: MDN</p>
<p>하지만 <code>person1</code>이 <code>Object</code>의 모든 속성을 상속받은 것은 아니다. 프로토타입 객체가 인스턴스에게 상속해줄 속성은 따로 지정할 수 있는데, 그런 속성을 프로토타입 속성이라고 부른다. 즉 <code>Object.prototype.valueOf()</code>로 메소드가 정의되었기 때문에 <code>Person()</code>과 <code>person1</code>이 상속받을 수 있는 것이다.</p>
<blockquote>
<p>&quot;보통 <code>this</code>가 현재 객체의 프로토타입 객체를 가리킬 것이라 오해하지만 그렇지 않죠. (프로토타입 객체는 <code>__proto__</code> 속성으로 접근 가능한 내장 객체인 것 기억 하시나요?). 대신에 <code>prototype</code> 속성은 상속 시키려는 멤버들이 정의된 객체를 가리킵니다.&quot;</p>
</blockquote>
<p>뭔가 이해했다고 생각했는데 이 글을 보니 다시 혼란에 빠졌다... OOP는... 참 묘하다... 시간을 좀 더 들여야겠다 🤨 아직 Python OOP도 완벽히 이해를 못한 것 같다</p>
<ol start="2">
<li>그래서 왜 집계 버튼 오류가 생기는가
<img src="https://images.velog.io/images/soo-im/post/28db2b2f-72dd-4522-821d-b83b311e07a4/image.png" alt=""></li>
</ol>
<p>디버그를 돌려봐도 마지막에 <code>writeTrack(trackObj)</code> 함수에 전달하는 객체에는 <code>insertAfter</code>가 없는데... 왜 저 함수가 실행되고 나면 없던 게 생기는 건지 나 참 ㅠㅠ
지금은 도저히 알 수 없어서 그냥 마지막 <code>&lt;ul&gt;</code> 을 제거하는 식으로 작업했다. 좀 더 연습하다보면 이유를 알게 되겠지...?</p>
<ol start="3">
<li>총 시간 추가
<img src="https://images.velog.io/images/soo-im/post/2151398e-6c3e-4536-916f-3b9a82b94fc6/image.png" alt="">
얘도 이러네...ㅎ 도대체 프로토타입 함수는 왜 다 끌려나오는걸까 부른 적도 없는데</li>
</ol>
<h3 id="2021-12-12">2021-12-12</h3>
<p>저놈의 프로토타입... 해결책이 안 보여서 JS 객체지향 인강을 좀 들어보기로 🙃 (코드잇과 생활코딩 섞어서 들음)</p>
<p><strong>Prototype-based Programming</strong>
자바스크립트는 다른 언어와는 다른 방법으로 객체 지향에 접근한다. 자바스크립트에서 객체 지향 프로그래밍을 논의할 때 Prototype-based Programming이라고 부른다.</p>
<ol>
<li>Factory Function
(이 부분은 생활코딩에서는 따로 설명하지 않았고, 다른 자료에서도 자주 보지 못한 내용이라 그냥 참고만 하고 넘어가면 되겠다.)
아래와 같이 객체를 생성하는 함수를 &#39;Factory Function&#39;이라고 부른다. 공장처럼 객체를 찍어낸다는 의미에서 이렇게 부른다.
여기에서 <code>this</code>는 생성된 객체(정확히는 인스턴스인듯?) 그 자체를 가리킨다.<pre><code class="language-js">function createUser (name, age) {
const user = {
 //name: name,
 //age: age,
 name,
 age,    // 프로퍼티와 파라미터가 동일한 경우에는 이렇게 생략해서 써도 된다(모던 JS).
 helloTo(others) {
   console.log(`${this.name} says hello to ${others.name}`);
 },
};
return user;
}
</code></pre>
</li>
</ol>
<p>const user1 = createUser(&#39;Soo&#39;,27);</p>
<pre><code>
2. Constructor Function
자바스크립트에서 함수는 로직의 재사용 기능뿐 아니라 객체 생성자의 역할도 한다. 함수를 호출할 때 `new`를 붙이면 해당 함수는 새로운 객체를 초기화하한다. 이 함수를 생성자 함수라고 부른다.
파이썬에서는 생성자는 클래스 내부에 존재하고 클래스의 인스턴스를 만들어내었다. 하지만 자바스크립트에서는 생성자가 어디에도 소속되어 있지 않고 `new`와 결합하여 객체를 생성한다.
관습적으로 함수명을 대문자로 시작하고, 예상치 못한 객체 반환을 막기 위해`return` 값을 지정하지 않는다.
```js
function User (name, age) {
  this.name = name;
  this.age = age;
  this.helloTo = function (others) {
    console.log(${this.name} says hello to ${others.name}`);
    };
}

const user1 = new User(&#39;Soo&#39;, 27);</code></pre><h3 id="2021-12-13">2021-12-13</h3>
<p><strong>Prototype-based Programming</strong></p>
<ol>
<li><p>class
ES6부터 <code>class</code>를 사용할 수 있다. 이전에 배운 파이썬 OOP와 유사해 보인다.</p>
<pre><code class="language-js">class User {
constructor (name, age) {    // 파이썬으로 치면 __init()__ 인 듯?
 this.name = name;
 this.age = age;
}

helloTo(others) {
 console.log(`${this.name} says hello to ${others.name}`);
}
}
</code></pre>
</li>
</ol>
<p>const user1 = new User(&#39;Soo&#39;, 27);</p>
<pre><code>
2. Global Object
**모든 객체는 전역 객체의 프로퍼티이다.**
`myFunc()` 함수를 정의한 후에 `myFunc()`을 호출해도 되지만 `window.myFunc()`을 호출해도 결과는 동일하다. 이는 `myFunc()`이 `window` 객체의 메소드라는 의미이다. 즉 우리는 `function myFunc()` 과 같이 함수를 정의할 때 `window`라는 전역 객체의 내부에 메소드로 정의한 것이다. `window`는 편의를 위해 생략된 것이다.
그래서 모든 객체가 전역 객체(`window`)의 프로퍼티라고 하는 것이다.
&gt; **참고**
전역 객체의 명칭은 호스트 환경에 따라 달라질 수 있다. node.js에서 전역 객체는 `window`가 아니라 `global`이다.

### 2021-12-14
**Prototype-based Programming**

1. `this`
함수 안에서 사용하는 특수한 변수. 어떤 함수에서 실행했는지에 따라 값이 달라진다.

1) 함수에서 `this`는 전역 객체`window`를 의미한다.
```js
// 출처: 생활코딩
function func(){
    if(window === this){
        document.write(&quot;window === this&quot;);
    }
}
func(); // 실행 결과는 &quot;window === this&quot;</code></pre><p>2) 메소드에서 <code>this</code>는 메소드를 가지고 있는 객체를 가리킨다. 이것을 통해 1)의 결과도 이해할 수 있다. <code>func()</code>은 전역 객체 <code>window</code>의 메소드이기 때문이다.</p>
<pre><code class="language-js">var o = {
    func : function(){
        if(o === this){
            document.write(&quot;o === this&quot;);}
    }
}</code></pre>
<h3 id="2021-12-17">2021-12-17</h3>
<p><strong>Prototype-based Programming</strong></p>
<ol>
<li><p>함수 리터럴
아래의 두 코드는 동일한 기능을 한다.</p>
<pre><code class="language-js">function sum(x, y) { return x+y; }    // 함수 리터럴
var sum2 = new Function(&#39;x&#39;, &#39;y&#39;, &#39;return x+y;&#39;);</code></pre>
<p><code>sum2</code>와 같이 생성자 함수를 통해 함수를 정의할 수도 있고, <code>sum</code>과 같이 함수를 정의할 수도 있다. <code>sum</code>과 같이 정의하는 것을 &#39;함수 리터럴&#39;이라고 부른다. 이는 <code>const a = { ... };</code>를 객체 리터럴 이라고 부르는 것과 마찬가지이다.
즉 <code>sum</code>, <code>sum2</code> 함수는 둘 다 객체이다.</p>
</li>
<li><p><code>apply</code>
JS에서는 함수(메소드)가 반드시 특정 객체에만 종속되지는 않는다. 위에서 함수 리터럴로 정의한 함수는 <code>window</code>의 메소드이지만, 다르게 호출할 경우 다른 객체의 메소드가 될 수도 있다. 그 예가 <code>apply</code>이다.</p>
</li>
</ol>
<pre><code class="language-js">// 출처: 생활코딩
var o = {}
function func(){
    switch(this){
        case o:
            document.write(&#39;o&lt;br /&gt;&#39;);
            break;
        case window:
            document.write(&#39;window&lt;br /&gt;&#39;);
            break;          
    }
}
func();    // window 의 객체이므로 this === window
func.apply(o);    // o 의 객체로 호출되었으므로 this === o</code></pre>
<p>(강의에서 <code>apply</code> 자체를 자세히 다루지 않아서 좀 더 찾아보고 있는데 뭔가... 왜 굳이 이렇게 하나 싶게 생긴 <a href="https://beomy.tistory.com/4">예제</a>가 나온다; 함수를 호출할 때 인자를 그냥 주는 거랑 무슨 차이인거지?)</p>
<h3 id="2021-12-20">2021-12-20</h3>
<p><strong>Prototype-based Programming</strong> 상속(하는 법)</p>
<pre><code class="language-js">// 출처: 생활코딩
function Person(name){
    this.name = name;
}

// 사람 객체의 프로퍼티 선언
Person.prototype.name=null;    // 굳이 안 해줘도 되지만 name 프로퍼티가 있음을 보여주기 위함
Person.prototype.introduce = function(){
    return &#39;My name is &#39;+this.name; 
}

function Programmer(name){
    this.name = name;
}

// 사람 객체의 프로퍼티를 프로그래머 객체에 상속시킴
Programmer.prototype = new Person();    // 작동 방식은 다음 장에 설명
Programmer.prototype.coding = function(){
    return &quot;hello world&quot;;
}

var p1 = new Programmer(&#39;Soo&#39;);
document.write(p1.introduce()+&quot;&lt;br /&gt;&quot;);
document.write(p1.coding()+&quot;&lt;br /&gt;&quot;);</code></pre>
<h3 id="2021-12-21">2021-12-21</h3>
<p><strong>Prototype-based Programming</strong> 프로토타입!</p>
<ol>
<li><p>왜 필요할까?
prototype 말 그대로 그 객체의 &#39;원형&#39;이다. 즉 생성될 때부터 기본적으로 가지고 있어야 하는 것을 의미한다.
만약 &#39;원형&#39;이 필요가 없다면 굳이 힘들게 <code>const obj = new myFunc();</code> 과 같이 생성자 함수를 이용해 객체 <code>obj</code>를 만들 필요가 없다. 그냥 <code>const obj = {}</code> 이렇게 객체 리터럴로 선언해도 된다.
하지만 <code>obj</code> 안에 어떤 프로퍼티가 처음부터 담겨있기를 원한다면, 즉 특정 &#39;원형&#39;을 원한다면 prototype를 이용해 그것을 줄 수 있다.</p>
</li>
<li><p>어떻게 상속하나?
prototype은 객체 안의 객체이다. <code>myFunc.prototype</code>에 프로퍼티를 주어 사용할 수 있다. <code>myFunc.prototype.name = &#39;Soo&#39;;</code>와 같이 부여하면 <code>const obj = new myFunc();</code>으로 <code>obj</code> 객체를 만들었을 때 그 객체 안에 <code>{name : &#39;Soo&#39;}</code>가 들어간다. 전달하는 과정은 아래와 같다.</p>
<pre><code class="language-js">function Person(){}
Person.prototype.hi = &#39;hi&#39;;
</code></pre>
</li>
</ol>
<p>function Programmer(){}
Programmer.prototpye = new Person();</p>
<p>const soo = new Programmer();
console.log(soo.hi);    // &#39;hi&#39;</p>
<pre><code>네 번째 라인에서 `new Person();`은 `{hi : &#39;hi&#39;}`를 `Programmer.prototype`에 전달해준다. 그래서 `Programmer.prototype`은 `Person.prototpye`과 같은 프로퍼티를 가지는 것이다. 이런 식으로 prototype을 통해 객체 간의 프로퍼티를 전달하는 것을 prototype chain이라고 부른다.

3. prototype chain 추가
반드시 생성자 함수(객체)를 통해서만 프로퍼티를 전달할 필요는 없다. 아래처럼 임의의 객체 `someone`을 만들고 그 프로퍼티 `{hi : &#39;hello world&#39;}`를 전달하는 것도 가능하다. 
```js
function Person(){}
Person.prototype.hi = &#39;hi&#39;;

const someone = new Person();
someone.hi = &#39;hello world&#39;;

function Programmer(){}
Programmer.prototpye = someone;

const soo = new Programmer();
console.log(soo.hi);    // &#39;hello world&#39;</code></pre><blockquote>
<p><strong>왜 <code>Programmer.prototpye = Person.prototype</code>이라고 하지 않나?</strong>
이렇게 해도 결과는 동일하게 나온다. 하지만 <code>Person.prototype</code>을 전달하면 문자 그대로 <code>Person</code>의 prototpye 그 자체가 전달되어 버린다. 그래서 <code>Programmer</code>에 프로퍼티를 추가할 때 <code>Person</code>에도 똑같이 추가가 된다.
이렇게 부모 객체에 영향을 주는 것을 막기 위해서 <code>Person</code>의 복사본을 만들어 prototype에 주는 것이다.</p>
</blockquote>
<h3 id="2021-12-22">2021-12-22</h3>
<p><strong>Prototype-based Programming</strong> Object 확장</p>
<ol>
<li>Object 확장의 위험성
드디어 <code>insertAfter</code>가 출력되는 이유를 찾았다!!!
내가 한 것처럼 Object에 변수 <code>neddle</code>을 찾는 함수 <code>contain</code>을 프로토타입에 추가해 확장했다고 가정해보자.<pre><code class="language-js">//코드 출처: 생활코딩
Object.prototype.contain = function(neddle) {
 for(const name in this){
     if(this[name] === neddle){
         return true;
     }
 }
 return false;
}</code></pre>
이렇게 확장 한 뒤에 임의의 객체 내부의 프로퍼티를 출력하면 우리가 추가한 메소드까지 출력된다.<pre><code class="language-js">const o = {&#39;name&#39;:&#39;Soo&#39;, &#39;hello&#39;:&#39;world&#39;}
for(const name in o){
 console.log(name);  
}
</code></pre>
</li>
</ol>
<p>// 출력 결과
// name
// hello
// contain &lt;-- 우리가 추가한 메소드까지 출력된다! 모든 객체에 contain이 상속되었기 때문에 이렇게 나온다.</p>
<pre><code>따라서 넓은 범위에 영향을 주는 Object와 같은 객체를 확장하는 것은 위험하다.

2. `hasOwnProperty`
확장한 Object 프로퍼티까지 출력하는 것을 방지하는 방법이 있다. 상속받은 것인지, 혹은 자기 자신만의 프로퍼티인지 체크해서 상속받은 Object 프로퍼티를 건너뛰는 방식이다.
```js
for(const name in o){
    if(o.hasOwnProperty(name))    // o 안의 프로퍼티 name이 o 만의 것인지 체크한다. 상속받은 contain은 false이기 때문에 출력되지 않는다.
        console.log(name);  
}</code></pre><p>와아악 해결했다 이제 안나온다 🤗</p>
<h3 id="2021-12-23">2021-12-23</h3>
<p>깃 브랜치 잘못 합쳐서 날아갔다... ^^ 이젠 깃 공부다</p>
<h3 id="2021-12-27">2021-12-27</h3>
<p><strong>Python</strong> 
여러 값을 반환하는 함수를 <code>apply</code>(혹은 <code>map</code>)해서 한 번에 여러 열을 채우려고 했다. 예를 들어 &#39;a&#39;열의 값을 <code>map</code>해서 df의 &#39;c&#39;, &#39;d&#39;열을 한 번에 채우는 상황이다.</p>
<pre><code class="language-python">def my_function(x):
    a = x + 1
       b = x + 2
        return a, b

df = pd.DataFrame(np.array([[1, 2], [11, 12]]), columns=[&#39;a&#39;, &#39;b&#39;])</code></pre>
<p>하지만 <code>df[&#39;a&#39;].map(lambda x: my_function(x))</code>의 반환값은 <code>[2, 3], [12, 13]</code> 의 object이기 때문에 이걸 단번에 &#39;c&#39;, &#39;d&#39;열로 넘길 수는 없다. 이걸 object-&gt;array-&gt;pd로 바꾸어서 넣어야 하나 고민하고 있는데 <a href="https://stackoverflow.com/questions/23586510/return-multiple-columns-from-pandas-apply">이 글</a>을 보고 쉽게 해결했다.</p>
<pre><code class="language-python">def my function(s):
    s[&#39;c&#39;] = s[&#39;a&#39;] + 1
        s[&#39;d&#39;] = s[&#39;a&#39;] + 2
        return s

df = df.apply(my_function, axis = 1)        </code></pre>
<p>함수에 단일 값 <code>x</code> 대신 pd.Series를 넘겨주고 함수가 pd.DataFrame을 반환하는 방식이다. 이번에는 <code>apply</code>로 끝냈지만 <code>map</code>으로 하는 방법도 있지 않을까 궁금하다.</p>
<h3 id="2021-12-28">2021-12-28</h3>
<p>아!!!! 드디어!!!! Object 프로토타입 함수 문제 해결했다!!!!! 오래걸렸다 정말 ㅠㅠㅠ 이제 꼭 구현해야 하는 &#39;Start!&#39; 버튼 hidden이랑 삭제 만들어보자! 그리고 빈 칸은 읽어오지 않는 것까지 구현해야겠다</p>
<h3 id="2021-12-29">2021-12-29</h3>
<p>시작 hidden &amp; 삭제 버튼 &amp; 모든 block 삭제되었을 때 시작 다시 나타나는 것까지 구현 완료!! 이제 더 이상 근무시간은 기록하지 않아도 된다고 해서... 살짝 당황스럽지만... 혼자서라도 쓰면 되니까~ 😄 (아까운데 어디 뿌릴 곳 없을까)
<img src="https://images.velog.io/images/soo-im/post/d3c099f0-2817-480f-94b4-bd95848f8e59/tracker5.gif" alt=""></p>
<h3 id="2021-12-31">2021-12-31</h3>
<p>1년 동안 CS(알고리즘, 자료구조)기초, SQL, HTML&amp;CSS, JS, OOP 기초를 공부했다. <em>데이터 분석 직무로 가고 싶다→근데 분석만 하면 안 되고 데이터 추출도 알아야 한다고?→데이터 추출하려면 웹을 알아야 한다고?</em> 하는 사고를 거쳐 어쩌다보니 JS를 제일 많이 공부했다. 원래 목표는 백엔드였는데 JS에 빠져들어서 미니 웹 어플리케이션을 만드는 것으로 한 해를 마무리했다 🤗
JS를 예상 외로 오래 공부했지만 실제로 동작하는 무언가를 만드는 것은 정말 재미있는 경험이었다! 내년에는 개발과 분석 사이의 줄타기를 좀 더 잘 하며 공부해야겠다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[게더타운 행사 만들기-맵 만들기(2)]]></title>
            <link>https://velog.io/@soo-im/%EA%B2%8C%EB%8D%94%ED%83%80%EC%9A%B4-%ED%96%89%EC%82%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EB%A7%B5-%EB%A7%8C%EB%93%A4%EA%B8%B02</link>
            <guid>https://velog.io/@soo-im/%EA%B2%8C%EB%8D%94%ED%83%80%EC%9A%B4-%ED%96%89%EC%82%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EB%A7%B5-%EB%A7%8C%EB%93%A4%EA%B8%B02</guid>
            <pubDate>Fri, 12 Nov 2021 15:41:29 GMT</pubDate>
            <description><![CDATA[<h1 id="이어서-3-맵-편집-포토샵">(이어서) 3. 맵 편집-포토샵</h1>
<blockquote>
<p>쉬움 ★☆☆
가성비 ★★☆
자유도 ★★★</p>
</blockquote>
<p>템플릿처럼 예쁘면서도 Mapmaker처럼 내 마음대로 맵을 만드려면 픽셀 아트가 가능한 툴(포토샵, <a href="https://pixlr.com/">Pixlr</a>, Tiled 등)을 쓰는 것이 좋다. 이 글에서는 포토샵과 Pixlr(온라인에서 사용하는 무료 편집 툴)을 썼다.</p>
<h2 id="1-리소스-다운로드">1) 리소스 다운로드</h2>
<p>픽셀 맵을 만드는 것이 익숙하지 않다면 게더타운에서 제공하는 리소스를 사용하는 것이 좋다. 게더타운 깃허브에서 맵, 오브젝트, 아바타 및 미공개 리소스까지 모두 볼 수 있다. <a href="https://github.com/gathertown/mapmaking">mapmaking 리포</a>를 zip 파일로 다운받으면 된다.
<img src="https://images.velog.io/images/soo-im/post/4fd6b15c-4786-46a1-8bd9-19b61525b24c/2-resources.png" alt="">
<em>\maps\templates</em> 에서 위와 같은 템플릿 png 파일을 찾을 수 있다. <code>Tiled</code>와 같은 픽셀 툴을 잘 다루는 사람이라면 tmx 파일을 사용할 수도 있다.
그 외 <em>\assets</em>, <em>\objects</em> 등의 경로에서 업로드 되지 않은 오브젝트 이미지도 다운받을 수 있으니 적극 활용하자. (고양이도 있다🐈)</p>
<h2 id="2-맵-구성">2) 맵 구성</h2>
<p>리소스로 맵을 만들 때 미리 알아둘 것이 두 가지 있다.</p>
<h3 id="첫째-맵의-크기는-무조건-32-배수-픽셀">첫째, 맵의 크기는 무조건 32 배수 픽셀</h3>
<p>게더타운의 칸 하나는 32 픽셀이기 때문에 배경을 그릴 때는 무조건 32 배수 픽셀로 만들어야한다. 모든 공간을 꽉 채울 필요는 없고 비워놓아도 상관없다(비워놓으면 그냥 까맣게 보인다).
포토샵에서 좌측 상단의 <code>Image</code>-<code>Canvas Size</code>로 들어가 32 배수 픽셀로 빈 캔버스를 만든 후 그 위에 원하는 그림을 올려두면 된다. 상세한 예시는 <strong>3)</strong>에서 보여준다.</p>
<h3 id="둘째-foreground--background">둘째, Foreground &amp; Background</h3>
<p>게더타운의 맵은 포그라운드와 백그라운드 두 가지로 나누어져있다. Mapmaker로 들어가서 좌측 상단의 <code>≡</code>-<code>Background &amp; Foreground</code>를 클릭하면 포그라운드, 백그라운드 각각을 업로드하거나 다운로드 받을 수 있다. 이 두 개는 모두 배경에 쓰이는 그림(png)이다.</p>
<blockquote>
<p><strong>참고</strong>
여기에서 <code>Download</code>를 선택하면 <strong>1)</strong> 에서 다운받은 리소스와 동일한 그림 파일을 얻을 수 있다.</p>
</blockquote>
<p><img src="https://images.velog.io/images/soo-im/post/17151e6c-8d4b-4aca-9c99-80e7a2da3391/image.png" alt="">
그렇다면 무엇이 포그라운드이고 백그라운드일까? 두 개의 차이는 <strong>&quot;아바타보다 앞에 있는가, 뒤에 있는가?&quot;</strong> 이다.
나무를 예로 들어보자. 백그라운드 이미지에 나무가 있거나, 나무 Object를 설치하면 아바타가 그걸 밟고 지나가버린다. 하지만 포그라운드 이미지에 나무를 넣으면 나무가 아바타를 가리면서 훨씬 현실적으로 표현할 수 있다. 
<img src="https://images.velog.io/images/soo-im/post/c26553a7-3800-488a-b440-98bde0c80db5/2-bgVSfg.png" alt=""></p>
<p>템플릿에 들어가서 <code>Download Foreground</code>를 누르면 어떻게 포그라운드를 만드는지 볼 수 있다. 캐릭터보다 앞에 와야하는 (예: 키 큰 나무, 테이블) 물체 이미지를 포그라운드에 넣으면 된다.
<img src="https://images.velog.io/images/soo-im/post/55764318-7d87-4f7d-a4f7-075507388240/2-bgfgConcept.png" alt=""></p>
<h2 id="3-맵-그리기">3) 맵 그리기</h2>
<p>오래 걸렸다. 이제 정말 그리기만 하면 된다!
이번 글에서는 포토샵을 기준으로 설명하지만, Tiled나 다른 픽셀 아트 도구를 사용할 수 있다면 그 방법도 추천한다.</p>
<h3 id="첫째-방-이어붙이기">첫째, 방 이어붙이기</h3>
<p>템플릿의 일부 방만 쓰거나 여러 템플릿의 방을 합칠 수 있다. <strong>32 배수(중요!) 픽셀</strong>의 캔버스를 만들고 리소스 폴더의 배경 png에서 원하는 구간만 복사해서 캔버스에 붙여넣으면 된다.
<img src="https://images.velog.io/images/soo-im/post/14effd27-e0e8-43db-beb6-a22b65b132a8/2-%ED%95%A9%EC%B9%98%EA%B8%B0.gif" alt="">1. 템플릿 하나를 불러온다.
2. 템플릿을 이어붙일 수 있게 캔버스 사이즈를 조절한다. (혹은 1~2 대신 처음부터 빈 캠버스를 만든다.)
3. 새로운 탬플릿을 불러와 이어붙인다.</p>
<h3 id="둘째-원하는-방-만들어-이어붙이기">둘째, 원하는 방 만들어 이어붙이기</h3>
<p>템플릿을 이어붙이다 보면 원하는 모양의 방이 없어 애매할 때가 있다. 그럴 때는 Mapmaker와 포토샵을 함께 이용하면 퍼즐처럼 들어맞는 방을 만들 수 있다.
<img src="https://images.velog.io/images/soo-im/post/39b19211-182e-410d-bbf8-9e8bdc34f270/2-NewWall2.gif" alt="">1. 게더타운에서 빈 공간을 하나 만들고 <code>≡</code>-<code>Background &amp; Foreground</code>-<code>Upload Background</code>를 눌러 직접 만든 배경 png를 올린다. 
2. 배경 png에서 가장자리를 따라 아무 오브젝트를 배치한다.
3. 오브젝트로 가장자리를 다 땄으면 <code>Walls&amp;Floors</code>로 들어간다.
4. 가장자리에 맞추어 벽과 바닥을 그리고 <code>≡</code>-<code>Background &amp; Foreground</code>-<code>Download Background</code>를 눌러 그린 바닥 png를 저장한다.
5. 이제 위에서 설명한 것처럼 포토샵으로 두 방을 이어붙이면 된다.</p>
<h3 id="셋째-fg로-빛-효과-만들기">셋째, FG로 빛 효과 만들기</h3>
<p>Foreground가 캐릭터를 가린다는 점을 이용해서 빛 효과를 주거나 문을 현실감 있게 만들 수 있다. 먼저 빛 효과부터 살펴보자.
<img src="https://images.velog.io/images/soo-im/post/b3bcbbed-6087-4496-a62b-5351b014c33f/image.png" alt="">
&#39;Park-Night time&#39; 템플릿의 Foreground를 다운받으면 빛 효과를 잘 준 것을 볼 수 있다. 반투명하고 어두운 색으로 화면을 칠한 다음에 조명이 있는 곳에는 밝은 색을 넣어 빛이 나는 것처럼 표현했다.
포토샵으로 Background를 만든 다음 레이어를 추가해 어둡거나 밝은 곳에 맞게 색을 칠하면 된다. 아래 예시는 공원과 루프탑에 조명 효과를 넣은 것이다.
<img src="https://images.velog.io/images/soo-im/post/2d1bac5d-b095-4c40-bda5-84698fdac036/FG%EC%A0%81%EC%9A%A9.gif" alt=""></p>
<h3 id="넷째-fg로-문-만들기">넷째, FG로 문 만들기</h3>
<p>직접 배경을 그릴 때에는 문도 신경써야 한다. Background에만 문을 그리면 그냥 바닥의 그림일 뿐이라, 앞에서 나무를 밟는 것처럼 천장을 밟고 넘어가는 것처럼 보인다. 그래서 천장 부분은 캐릭터를 가리도록 아래 사진처럼 Foreground에 천장을 추가해야 한다.
<img src="https://images.velog.io/images/soo-im/post/7dfe10c0-275d-4951-92c0-c6b14edb8f3b/%ED%8F%AC%ED%86%A0%EC%83%B5_FG_%EB%AC%B8%ED%99%95%EB%8C%80.PNG" alt="">
자세히 살펴보면 문 윗쪽이 캐릭터를 가리도록 Foreground가 그려진 것을 볼 수 있다.</p>
<h1 id="맵-제작-끝">맵 제작 끝!</h1>
<p>이제 앞의 설명을 잘 활용하면 원하는 형태로 맵을 만들 수 있다. 리소스를 잘 활용해서 마음에 드는 맵을 만들어보자!
<img src="https://images.velog.io/images/soo-im/post/9df76ffe-98d2-4a80-a9f9-8ad28b833e35/%ED%8F%AC%ED%86%A0%EC%83%B5_%EC%A0%84%EC%B2%B4.PNG" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[11월 TIL 기록]]></title>
            <link>https://velog.io/@soo-im/11%EC%9B%94-TIL-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@soo-im/11%EC%9B%94-TIL-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Mon, 01 Nov 2021 15:27:17 GMT</pubDate>
            <description><![CDATA[<h3 id="2021-11-01">2021-11-01</h3>
<p><strong>JS</strong> 이벤트 버블링
이벤트 핸들러가 실행될 때, 부모가 가지는 동일한 종류의 이벤트 핸들러까지 실행되는 현상이다. window 객체를 만나기 전까지 거품처럼 쭉 올라간다고 해서 &#39;버블링&#39;이라고 한다. 자식(하위) 요소에서 발생한 이벤트 정보를 부모(상위) 요소까지 전달하기 때문에 별도로 설정하지 않아도 항상 발생한다.</p>
<p>예를 들어 <code>&lt;ol id=&#39;list&#39;&gt;</code>과 그 자식 요소인 <code>&lt;li class=&#39;item&#39;&gt;</code>에 각각 아래와 같은 이벤트 핸들러를 넣었다고 가정하자.</p>
<pre><code class="language-js">const list = document.querySelector(&#39;#list&#39;);
const item = list.querySelector(&#39;.item&#39;);

list.addEventListener(&#39;click&#39;, function(e) {
  console.log(&#39;list&#39;);
  //console.log(e.target);    // 이벤트가 발생한 위치(요소) 표시
});
item.addEventListener(&#39;click&#39;, function(e) {
  console.log(&#39;item&#39;);
  //console.log(e.target);    
});</code></pre>
<p>이 코드를 실행하고 <code>item</code>을 클릭하면 콘솔에는 &#39;item&#39;, &#39;list&#39;가 출력된다. 즉 <code>item</code>의 이벤트 함수가 실행되고 이어 부모인 <code>list</code>의 함수까지 실행되는 것이다. 버블링을 멈추려면 <code>e.stopPropagation();</code>을 쓰면 되지만 이렇게 하는 경우는 잘 없다.</p>
<p>그리고 한 가지 중요한 점은 <code>e.target</code>으로 이벤트가 발생한 요소를 추적하면 부모의 이벤트라 하더라도 부모 요소가 아닌 자식 요소가 출력된다는 점이다. 즉 위 코드에서 두 이벤트 모두 <code>&lt;li&gt;</code> 요소를 타겟으로 반환한다. 이렇게 나오는 이유는 버블링이 하위 요소의 정보를 상위로 전달하기 위함임을 생각해보면 납득이 간다.</p>
<blockquote>
<p>만약 이벤트가 발생한 요소가 아니라 함수가 실행된 그 요소를 보고 싶으면 <code>e.currentTarget</code>을 이용하면 된다.</p>
</blockquote>
<h3 id="2021-11-08">2021-11-08</h3>
<p><strong>JS</strong> 이벤트 위임
이벤트 버블링을 이용해서 한 번의 자식 요소의 이벤트를 핸들링하는 것을 이벤트 위임이라고 한다. <a href="https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/">이 블로그</a>에 설명이 잘 되어 있다.
앞서 본 것처럼 자식 요소 <code>item</code>을 클릭하면 해당 요소의 이벤트 함수에 이어 부모 요소 <code>list</code>의 이벤트 함수까지 실행된다. 이걸 이용하면 여러 개의 <code>item</code> 자식 요소마다 이벤트 함수를 넣어줄 필요가 없다.</p>
<p>모든 <code>class = item</code> 요소를 클릭할 때마다 특정 함수를 실행시키려면 일일이 <code>.addEventListener</code>을 할 필요 없이 아래와 같이 작성하면 된다.</p>
<pre><code class="language-js">const list = document.querySelector(&#39;#list&#39;);
const item = list.querySelector(&#39;.item&#39;);

list.addEventListener(&#39;click&#39;, function(e) {
  console.log(&#39;My Function for all items&#39;);
});                   </code></pre>
<p>이렇게 하면 여러 개의 자식 요소 <code>item</code> 중 무엇을 클릭하더라도 자동으로 그 부모 요소인 <code>list</code>로 버블링이 될 것이고, 그것은 <code>function(e)</code>를 실행시킬 것이다. 이렇게 이벤트를 부모 요소에 대신 넣어주는 방식을 이벤트 위임이라고 하고, 이벤트 버블링을 이용하는 것이니만큼 <code>e.stopPropagation();</code>으로 버블링을 막으면 이벤트 위임은 동작하지 않는다.</p>
<blockquote>
<p><strong>이렇게 하면 자식뿐만 아니라 부모를 클릭해도 실행되잖아?</strong>
자식 요소가 아닌 부모 요소를 눌렀을 때 핸들러가 실행되는 것을 방지하려면 정확히 자식 요소를 눌렀는지 확인하는 단계를 추가하면 좋다. 예를 들어 이벤트가 발생한 타겟의 class 리스트에 `.item&#39;이 있을 때에만 부모 이벤트 핸들러가 실행되게 만들면 된다.</p>
</blockquote>
<pre><code class="language-js">list.addEventListener(&#39;click&#39;, function(e) {
  if (e.target.classList.contains(&#39;item&#39;)){
      console.log(&#39;My Function for all items&#39;);
  }
}); </code></pre>
<p><strong>Node.js</strong> REPL(대화형 창)
터미널에서 node를 실행하여 사용자가 입력하는 JS를 실행하는 모드를 말한다. 낯설게 느껴지지만 터미널에서 파이썬을 실행하여 사용자가 대화형으로 코드를 입력하고 실행하는 것과 같은 것이다.
<strong>R</strong>ead <strong>E</strong>valuate <strong>P</strong>rint <strong>L</strong>oop 의 약자로 사용자가 입력한 내용을 &#39;읽고&#39;, 결과값을 &#39;계산하고&#39;, 결과값을 &#39;출력하고&#39;, 이 과정을 &#39;반복한다&#39;는 의미를 가지고 있다.</p>
<h3 id="2021-11-09">2021-11-09</h3>
<p>강의 듣고 만들면 강의 내용을 따라서 치는 수준이라서 퀴즈부터 풀어보기로!</p>
<p><strong>JS</strong> 청기백기 게임 만들기</p>
<blockquote>
<p><strong>조건</strong></p>
</blockquote>
<ol>
<li>마우스 좌/우를 누르면 청/백기에 <code>up</code> 클래스가 추가된다.</li>
<li>마우스 우클릭 시 브라우즈 메뉴가 나오지 않는다.</li>
</ol>
<p>첫 번째 답안:</p>
<pre><code class="language-js">const flagBlue = document.querySelector(&#39;.flag-blue&#39;);
const flagWhite = document.querySelector(&#39;.flag-white&#39;);

function reset() {
  document.querySelector(&#39;.up&#39;).classList.remove(&#39;up&#39;);
}

// 1. 좌/우 클릭시 클래스 추가
function flagUp(e) {
  if (e.button === 0){
    flagBlue.classList.add(&#39;up&#39;);
  }
  else if (e.button === 2){
    flagWhite.classList.add(&#39;up&#39;);
  }
  // 500 밀리초 뒤에 reset함수를 실행(깃발을 내림)
  setTimeout(reset, 500);
}

// 2. 우클릭 방지
document.addEventListener(&#39;contextmenu&#39;, function (event) {
  event.preventDefault();
});

// 테스트
// &#39;mousedown&#39;은 눌렀다 뗄 때 실행되는 &#39;click&#39;과 달리 누른 순간 실행된다. 
document.addEventListener(&#39;mousedown&#39;, flagUp);    </code></pre>
<p>찾아보니 <code>witch</code> 클릭 구분을 더 쉽게 할 수 있나보다. <code>witch</code> 다시 써보자.</p>
<pre><code class="language-js">function flagUp(e) {
  switch (e.button){
    case 0:
      flagBlue.classList.add(&#39;up&#39;);
      break;
    case 2:
      flagWhite.classList.add(&#39;up&#39;);
      break;
  }
  setTimeout(reset, 500);
}</code></pre>
<p>오호 이렇게 해도 된다!
<code>switch</code> 문법을 살펴보니 <code>switch (expr)</code>의 <code>expr</code>이 <code>case X</code>의 <code>X</code>와 정확히 일치하는지(<code>===</code>) 확인한 후 실행된다고 한다. 뭔가 익숙하길래 파이썬에서 사용해봤나? 싶었는데 파이썬에는 <code>switch</code>가 따로 없다고 한다(대신 <code>match</code>가 업뎃 되었다고 함). 그 이유까지 찾아보려고 했는데 <a href="https://stackoverflow.com/questions/46701063/why-doesnt-python-have-switch-case">설명</a>이 이해가 잘 안 가서 그냥 받아들이기로.</p>
<h3 id="2021-11-10">2021-11-10</h3>
<p><strong>JS</strong> 키보드 이벤트</p>
<p><code>keydown</code> vs. <code>keypress</code>
둘 다 키보드를 누르는 순간 발생하는 이벤트이지만 <code>keydown</code>은 모든 키보드를 인식하는 반면 <code>keypress</code>는 기능 키(Shift, Ctrl 등)와 한글을 인식하지 않는다. 그리고 하나의 키를 계속 누르고 있으면 <code>keydown</code>은 계속해서 발생하지만 <code>keypress</code>는 딱 한 번 인식된다. 현재는 <code>keypress</code> 사용을 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/keypress_event">권고하지 않는다</a>.</p>
<p><code>.key</code> vs. <code>.code</code>
키보드 이벤트의 <code>key</code> 프로퍼티는 키보드의 값 그 자체를 출력하는 반면 <code>code</code>는 키보드의 물리적인 위치를 출력한다. 예를 들어 &#39;A&#39;를 누르면 <code>key: A</code>, <code>code: keyA</code>를, 우측 Shift를 누르면 <code>key: Shift</code>, <code>code: RightShift</code>를 출력한다.</p>
<h3 id="2021-11-12">2021-11-12</h3>
<p><strong>JS</strong> 채팅 앱 만들기
&#39;send&#39; 버튼을 클릭하면 메시지가 전송되는 함수 <code>sendMyTexT</code>는 이미 완성되어 있다. 여기에 Enter을 눌러 메시지를 전송하는 기능을 추가하자.</p>
<blockquote>
<p><strong>조건</strong></p>
</blockquote>
<ol>
<li>Enter을 눌러 메시지를 전송하고 <code>textarea</code> 태그는 초기화 한다.</li>
<li>Shift+Enter을 눌러 줄바꿈한다.</li>
<li><code>keypress</code> 타입으로 핸들러를 등록한다.</li>
</ol>
<p>중간 풀이: 아니... <code>keypress</code>로 이벤트 등록하면 <code>e.shiftKey</code>, <code>e.key</code> 모두 안 나오잖아? 이걸로 어떻게 Shift+Enter을 감지하라는거지? Enter로 보내는 함수야 간단한데 2번이 이해가 안 가네.</p>
<pre><code class="language-js">function keyboardSender(e){
  if (e.key === &#39;Enter&#39;){
    e.preventDefault(); // Enter로 인한 줄바꿈 방지
    sendMyText();
  }
}

input.addEventListener(&#39;keypress&#39;, keyboardSender);</code></pre>
<p>해설: 이상하게 <code>console.log(e.shiftKey)</code>해두고 Shift를 눌렀을 때에는 아무 것도 안 나오더니, 막상 해설대로 따라하니 된다. 그리고 Shift+Enter가 줄바꿈이라는 건 직접 설정하지 않아도 된다는 점을 몰라서 더 헤맸다.</p>
<p>내가 시도한 방법:</p>
<pre><code class="language-js">function keyboardSender(e){
  console.log(e.key);
  console.log(e.shiftKey);    // 아무것도 출력하지 않는다.
  if (e.key === &#39;Enter&#39;){
    e.preventDefault(); // Enter로 인한 줄바꿈 방지
    sendMyText();
  }
  else if (e.shiftKey &amp;&amp; e.key === &#39;Enter&#39;){
    alert(&#39;test&#39;);    // 이 함수는 작동하지 않는다. 정확히 같은 타이밍에 누르는 게 불가능해서 그런가?
  }
}</code></pre>
<p>해설:</p>
<pre><code class="language-js">function keyboardSender(e){
// .shiftKey가 누르지 않은 상태라면 Enter로 인식한다는 방식으로 만든 함수.
// 근데 console.log(e.shiftKey)에는 아무것도 출력되지 않는데 이건 좀 이상하다.
  if (e.key === &#39;Enter&#39; &amp;&amp; !e.shiftKey){
    e.preventDefault();
    sendMyText();
  }
}</code></pre>
<h3 id="2021-11-13">2021-11-13</h3>
<p><strong>JS</strong> </p>
<ol>
<li><p><code>&lt;input&gt;</code> 이벤트 
1) 포커스
<code>&lt;input&gt;</code> 태그를 클릭하면 (별도의 CSS를 적용하지 않는 한) 해당 입력칸이 파랗게 변하여 이용자에게 입력을 받을 준비가 되었다는 표시를 한다. 이 순간 <code>focusin</code>이, 태그를 빠져나오면 <code>focusout</code> 이벤트가 발생한다. <code>focus</code>와 <code>blur</code>라는 이벤트도 있는데 이들은 버블링을 하지 않는다.
2) 입력
<code>&lt;input&gt;</code> 태그에 값이 입력되면 <code>input</code> 이벤트가 발생한다.<code>keydown</code>과 유사해 보이지만 입력과 무관한 키(Shift 등) 입력은 이벤트를 발생시키지 않는다. 그리고 <code>focusout</code> 이벤트가 발생하거나 Enter을 누를 때 <code>&lt;input&gt;</code> 태그의 값이 이전과 달라진 경우 <code>change</code> 이벤트가 발생한다.
3) 요약
태그에 입력을 할 때 이벤트의 발생 순서는 아래와 같다. <code>focusin</code>→<code>input</code>→<code>change</code>(값이 바뀌었을 때)→<code>focusout</code></p>
</li>
<li><p>타자연습 만들기
게임을 실행할 때마다 임의의 단어가 <code>&lt;span&gt;</code> 태그로 생성되고, 각 단어는 <code>data-word</code>라는 속성 값으로 해당 단어 텍스트를 가지고 있다. <code>&lt;input&gt;</code>에 단어 텍스트를 입력하면 해당 단어가 사라지게 만들어야 한다.</p>
</li>
</ol>
<blockquote>
<p><strong>조건</strong></p>
</blockquote>
<ol>
<li>입력값과 일치하는 단어를 가진 요소가 있다면 그것을 삭제한다.</li>
<li>이벤트 핸들러가 호출되면 <code>&lt;input&gt;</code> 태그는 초기화되어야 한다.</li>
<li>단어를 삭제한 후에는 남은 단어의 개수를 세는 <code>checker</code> 함수를 실행해야 한다.</li>
</ol>
<p>중간 풀이:
입력값이 들어와야 실행되어야 하니까 이벤트는 <code>change</code>로 잡고, <code>&lt;input&gt;</code>태그의 값은 <code>e.target.value</code>로 잡으면 된다. </p>
<pre><code class="language-js">function removeText(e) {
  const words = document.querySelectorAll(&#39;.word&#39;);
  const inputText = e.target.value;

  e.target.value = &#39;&#39;;  // 태그를 초기화 한다.
}</code></pre>
<p>그러면 이 값이 <code>words</code> 노드 리스트의 특정 태그의 속성과 일치하는지 확인해야 하는데... 단어 아무거나 잡아서 오브젝트를 확인해보면 <code>.dataset.word</code> 이렇게 나온다. 이걸 어떻게 <code>words</code> 리스트 안의 <code>word</code>를 하나하나 꺼내보지? 
다른 코드 보니까 <code>for (let word...)</code> 코드가 있던데 따라해보니 아래 코드가 잘 작동한다.</p>
<pre><code class="language-js">function removeText(e) {
  const words = document.querySelectorAll(&#39;.word&#39;);
  const inputText = e.target.value;

  for (let word of words) {
    if (inputText === word.dataset.word) {
      console.log(&quot;Gotcha!&quot;);
    }
  }
  e.target.value = &#39;&#39;;  // 태그를 초기화 한다.
}</code></pre>
<p>이제 단어를 삭제하고 <code>checker</code> 함수를 추가하면 최종 코드는 아래와 같다.
답안:</p>
<pre><code class="language-js">function removeText(e) {
  const words = document.querySelectorAll(&#39;.word&#39;);
  const inputText = e.target.value;

  for (let word of words) {
    if (inputText === word.dataset.word) {    // 단어의 오브젝트가 입력값과 동일한지 확인한다.
      word.remove()    // 해당 단어를 삭제한다.
    }
  }
  e.target.value = &#39;&#39;;  // 태그를 초기화 한다.
  checker();    // checker 함수를 실행한다.
}

input.addEventListener(&#39;change&#39;, removeText)</code></pre>
<p>모범 답안:</p>
<pre><code class="language-js">function removeWord() {
  const word = document.querySelector(`[data-word=&quot;${input.value}&quot;]`);
  if (word) {
    word.remove();
    checker();
  }

  input.value = &#39;&#39;;
}

input.addEventListener(&#39;change&#39;, removeWord);</code></pre>
<p>나와 다른 점</p>
<ol>
<li><code>e.target.value</code> 대신 간단하게 <code>input.value</code>로 입력값을 찾았다.</li>
<li>나는 매 순간 모든 단어의 리스트 <code>words</code>를 불러오고 그 안의 <code>word</code>를 하나하나 for문으로 돌리며 확인했지만, 이 코드는 <code>querySelector</code>로 애초에 프로퍼티가 입력값을 가지는 단어만 추출했다.</li>
<li>내 코드는 모든 단어가 사라지면 애초에 재시작 되니 <code>null.remove()</code>가 발생할 가능성이 없지만, 이 코드는 <code>querySelector</code>로 아무것도 안 나올 경우를 대비해서 <code>if(word)</code>문을 추가했다.</li>
</ol>
<p><code>querySelector</code>로 처음부터 맞는 단어만 골라낸다는 발상은 하지 못했다. 매번 for문 돌리는 내 코드보다 훨씬 간결하다.</p>
<h3 id="2021-11-15">2021-11-15</h3>
<p><strong>JS</strong> 
11-12에 했던 <strong>채팅 앱 만들기</strong>에서 의문점에 대한 답을 받았다.</p>
<ol>
<li>내 질문:
<code>keypress</code>는 shift를 감지하지 않아서 shift를 눌러도 <code>e.shiftKey</code>가 나오지 않는데, 어떻게 <code>if (e.key === &#39;Enter&#39; &amp;&amp; !e.shiftKey)</code>가 작동할 수 있나?</li>
<li>답변:
shift만 누르면 <code>keypress</code>와 그로 인한<code>e.shiftKey</code>가 모두 발생하지 않지만, shift+Enter을 누르면 Enter로 인해 <code>keypress</code>가 작동하면서 <code>e.shiftKey</code> 값이 발생할 수 있다. 그래서 아래 코드로 Enter와 Shift+Enter을 차례로 누르면 다음과 같이 나온다.<pre><code class="language-js">function keyboardSender(e){
console.log(&quot;e.key: &quot; + e.key);
console.log(&quot;e.shiftKey: &quot; + e.shiftKey);
}</code></pre>
<img src="https://images.velog.io/images/soo-im/post/fea3e60f-ad54-4a52-a7e6-d261d3cf2cb6/image.png" alt=""></li>
</ol>
<p>그래서 shift+Enter을 누를 때 <code>if (e.key === &#39;Enter&#39; &amp;&amp; !e.shiftKey)</code>가 false가 되어 메시지가 보내지지 않고 Shift를 안 누른 채 Enter을 누르면 메시지가 보내지는 것이다.</p>
<p><strong>JS 플젝</strong>
이제 기본 동작은 알았으니 웹에 필요한 fetch, API 등도 알아야 하는데... 이걸 계속 강의로 듣고 있자니 좀이 쑤셔서 ㅠㅠ 다른 플젝을 생각했다. 내가 제일 자주 쓸 것 같은 #TODO 를 JS로 만들어야지(카카오톡 챗봇은 API까지 배워야해서 예상보다 늦게 만들 것 같다).
<img src="https://images.velog.io/images/soo-im/post/365e72cf-82e2-4002-8abe-f3621f97d571/TODO%20APP.png" alt="">
일단 구상은 이렇게! (맘같아선 음악 듣게 유튜브도 넣고 싶은데 우선 이 정도로) TODO 체크하면 완료 리스트로 넘어가고, 넘어간 건 한 번 더 누르면 삭제되도록! </p>
<h3 id="2021-11-16">2021-11-16</h3>
<p>앗 플젝 좋은 거 생각났다!!! 근무기록 쓸 때 필요한 기록지 만들어야지!! (둘 다 있음 좋고) 근데 어째 구상하다보니 좀... 복잡해보인다?
<img src="https://images.velog.io/images/soo-im/post/da669f0e-bfbb-41eb-85ec-23127d561107/LOG%20APP.png" alt=""></p>
<p>오늘은 맨 위 근무시각 입력 칸까지 만들었다! <a href="https://github.com/Soo-Im/TimeTrackerApp">깃허브</a>에 올리는 중~</p>
<h3 id="2021-11-17">2021-11-17</h3>
<p><strong>플젝</strong> (하다가 알게 된 점)</p>
<ol>
<li><p><code>.createElement</code> 로 만든 <code>&lt;input&gt;</code>에 <code>required</code> 속성을 넣는 방법; <code>newinput.required = True;</code> 이런 방법으로 넣을 수 있는 속성을 <a href="https://stackoverflow.com/questions/18770369/how-to-set-html5-required-attribute-in-javascript">reflected property</a> 라고 부른다. 음... 설명이 아직은 이해가 잘 가지 않는다. 참고로 이게 불가능한 속성은 <code>element.setAttribute(&quot;required&quot;, &quot;&quot;);</code>와 같이 속성을 준다.</p>
</li>
<li><p><code>submit</code> 이벤트는 창을 새로고침하기 때문에 <code>e.preventDefault();</code> 를 해줘야 한다.</p>
</li>
</ol>
<p>오늘 만든 것! Start를 누르면 기본 블록(시작/끝 시간, 텍스트 입력 칸)이 나오게 만들었다. 내일은 Start를 hidden으로 만들고 끝 시간 등등을 설정해야겠다.</p>
<p><img src="https://images.velog.io/images/soo-im/post/98b55fa6-2126-41e9-82d0-949322e92ec0/tracker1.gif" alt=""></p>
<h3 id="2021-11-18">2021-11-18</h3>
<p><strong>플젝</strong></p>
<ol>
<li>블록을 추가하는 <code>addBlock</code> 함수를 그 블록 내의 버튼에서 다시 불러내려고 했다. 처음에는 <code>&lt;input type=&quot;submit&quot;&gt;</code>을 사용했는데 분명 <code>addBlock</code> 안에 <code>e.preventDefault()</code>가 있음에도 불구하고 자꾸 새로고침이 돼서 왜지...하고 고민하던 차에 <code>&lt;button&gt;</code> 태그로 바꾸었더니 기대했던 대로 동작한다. 왜 <code>&lt;input&gt;</code>만 동작방지가 안 먹히는지 이유를 모르겠다... 검색해도 두 태그의 기능 자체는 동일하다고 나오는데 흠 🤔
<img src="https://images.velog.io/images/soo-im/post/385dc45c-0b5f-4c64-a136-f83519faca12/tracker2.gif" alt=""></li>
</ol>
<p>오늘은 새 블록을 추가하는 &#39;입력&#39;버튼을 만들고 첫 번째 블록의 시작 시간은 Start와 동일하게 나오도록 설정했다! 이제 그 다음 블럭의 시작 시간은 이전 블럭의 종료 시간을 따르게 만들면 된다. 이전 블럭의 종료 시간은 반드시 채워지도록 설정하고!</p>
<h3 id="2021-11-19">2021-11-19</h3>
<p><strong>플젝</strong></p>
<ol>
<li><p>두 개의 요소가 비슷한 속성을 가지고 있어 한 번에 설정하려고 아래처럼 코드를 썼다.</p>
<pre><code class="language-js"> const startTime = document.createElement(&quot;input&quot;);
 startTime.type = &quot;time&quot;;
 startTime.required = true;
 startTime.step = &quot;900&quot;;

 const endTime = startTime;</code></pre>
<p>그런데 이렇게 하니까 <code>startTime</code>, <code>endTime</code> 요소가 각각 생기는 게 아니라 그냥 <code>startTime</code> 하나만 나오더라! <code>endTime = startTime;</code> 이게 값을 복사하는 게 아니라 주소를 복사하는 건가보다. (왜 항상 헷갈리는지...) <a href="https://developer.mozilla.org/ko/docs/Web/API/Node/cloneNode">노드를 복제</a>하려면 <code>startTime.cloneNode()</code>를 쓰면 된다.
<img src="https://images.velog.io/images/soo-im/post/b13f3fee-62f5-4fd7-898a-a68c5e85fa46/tracker3.gif" alt="">
이제 종료시간은 시작시간에 +30분 되게 설정했고, 다음 블록의 시작시간은 직전 종료시간을 이어오도록 만들었다! 블록 편집이 좀 더 자유로우면 좋을텐데 좀 고민해봐야겠다 ㅎㅎㅎ</p>
</li>
</ol>
<h3 id="2021-11-2122">2021-11-21~22</h3>
<p><strong>플젝</strong> 
21일; &#39;휴식&#39; 체크박스를 체크하면 &#39;휴식&#39;이 입력되고 해제하면 텍스트가 삭제되게 만들었다.
22일; 블록이 일정 길이를 넘어가면 스크롤이 되도록 CSS를 추가했다. 지금까지 입력한 모든 블록의 텍스트를 추출하는 함수를 만들었다. 이제 시계를 추출하고, 집계를 내는 함수를 추가하면 가장 중요한 기능은 마무리된다!</p>
<p><strong>JS</strong></p>
<ol>
<li><p><code>fetch</code> 함수</p>
<pre><code class="language-js">fetch(&#39;https://www.google.com&#39;)
.then((response) =&gt; response.text())
.then((result) =&gt; { console.log(result); });</code></pre>
<p><code>fetch(url)</code> url로 요청을 보내는 함수. 그 응답으로 &#39;프로미스(Promise)&#39; 객체가 돌아오면 <code>.then</code> 구문이 동작한다. 이렇게 특정 조건을 만족할 때까지 기다리는 함수를 &#39;콜백&#39;이라고 부른다. 콜백은 등록한 순서대로 실행되고 이전 콜백의 리턴값을 이후의 콜백이 넘겨받아 사용한다.</p>
</li>
<li><p>url의 구성
<code>scheme</code>(http); 통신 규약(프로토콜)의 이름. HyperText Transfer Protocol의 약어로 HyperText를 주고받기 위한 프로토콜이라는 의미. 해당 프로토콜 규칙에 따라 서버와 클라가 요청-응답을 주고 받음.
<code>host</code>(<a href="http://www.naver.com">www.naver.com</a>); 하나의 서버를 특정하는 주소. 도메인은 좀 더 좁은 의미로 사람이 읽기 편한 형태로 바꾼 주소를 말함. (참고: <a href="http://www.differencebetween.net/technology/difference-between-hostname-and-domain-name/">Difference b/w Host name and Domain name</a>)
<code>path</code>; 서버에서 원하는 데이터를 특정
<code>query</code>(?pg=1); 세부적인 요구사항 특정</p>
</li>
</ol>
<h3 id="2021-11-23">2021-11-23</h3>
<p><strong>플젝</strong></p>
<ol>
<li>업무별로 집계를 하려면 <code>reduce</code> 함수를 써야 한다(<a href="https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-an-array-of-objects">유사한 질문</a>). <code>reduce</code>는 <code>map</code>처럼 배열을 돌면서 값을 누적해서 연산해주는 함수인데 <a href="https://ratseno.tistory.com/25">여기</a>를 먼저 읽고 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce">공식문서</a>를 읽는 게 이해하기 좋다. <code>[{text: &quot;업무1&quot;, time: &quot;1:30&quot;}, {text: &quot;업무2&quot;, time: &quot;1:00&quot;}...]</code> 이런 식으로 배열을 만든 다음에 예시에 나온 대로 분류한 후 time을 합치는 식으로 진행해야겠다.</li>
<li>array 안의 값끼리 연산하려면 <code>map</code>을 사용하면 된다. <pre><code class="language-js">arr1.map((a, i) =&gt; a + arr2[i]);</code></pre>
</li>
<li>nodeList는 array랑 비슷해 보이지만 <code>map</code> 등의 함수를 사용할 수 없다. array 함수를 쓰려면 다음과 같이 변환해주어야 한다. 
<code>const myArr = Array.prototype.slice.call(myNodeList);</code> </li>
</ol>
<h3 id="2021-11-24">2021-11-24</h3>
<p><strong>JS</strong> JSON
JavaScript Object Notation의 약자로 JS 객체 문법(key-value)을 빌린 데이터 형식을 말한다. 다만 JS와 문법이 완전히 동일하지는 않다. 아래는 두 문법의 차이점이다.</p>
<ol>
<li>JSON은 key에 큰따옴표를 사용한다. JS는 key에 따옴표가 없어도 된다.</li>
<li>JSON은 문자열에 큰따옴표를 사용한다. JS는 작은따옴표를 써도 된다.</li>
<li>JSON은 undifined, NaN, Infinity 등을 value로 사용할 수 없다.</li>
<li>JSON은 주석을 포함할 수 없다.</li>
</ol>
<h3 id="2021-11-25">2021-11-25</h3>
<p><strong>플젝</strong>
startTime과 endTime의 시간 차이를 계산하려면 <code>map</code> 함수를 써야한다.
<code>startTime = [&quot;9:30&quot;, &quot;10:00&quot;]</code>, <code>endTime = [&quot;10:00&quot;, &quot;11:00&quot;]</code> 이렇게 나올 때 해야 할 일은 1. 각각을 split해서 <code>[[9, 30], [10, 00]]</code> 이런 형식으로 만들고 2. <code>[n][0]</code>, <code>[n][1]</code> 을 각각 시와 분으로 두어서 Date를 생성한 다음 3. 두 Date 간의 차이를 <code>&quot;0:30&quot;</code> 형식으로 출력해서 빈 array에 push해야 한다.
뭔가 map 함수가 파이썬이랑 비슷한 것 같기도 한데 좀 낯설다... 두 개의 array를 동시에 쓸 때는 <code>let zip = (a1, a2) =&gt; a1.map((x,i) =&gt; [x, a2[i]]);</code> 이렇게 index <code>i</code>로 다른 array를 가져오는 것 같은데 좀만 더 찾아봐야겠다.</p>
<h3 id="2021-11-27">2021-11-27</h3>
<p>음... 콘솔에서 하려니까 불편한데 ipython 같은 건 없나... 일단 테스트한 걸 하나의 함수로 바꿔봐야겠다. </p>
<pre><code class="language-js">A = [&quot;9:00&quot;, &quot;10:00&quot;];
B = [&quot;10:00&quot;, &quot;11:00&quot;];

let C = A.map((x,i) =&gt; [x.split(&quot;:&quot;), B[i].split(&quot;:&quot;)]);
&gt;&gt; [Array(2), Array(2)]
&gt;&gt; [&#39;9&#39;, &#39;30&#39;] [&#39;10&#39;, &#39;00&#39;]
&gt;&gt; [&#39;10&#39;, &#39;00&#39;] [&#39;11&#39;, &#39;00&#39;]

let D = C.map((x,i) =&gt; [C[0][i][0]-C[1][i][0]]);

&gt;&gt; [Array(1), Array(1)]
&gt;&gt; [-1]
&gt;&gt; [-1]</code></pre>
<h3 id="2021-11-28">2021-11-28</h3>
<p><strong>JS</strong></p>
<ol>
<li><p><code>JSON.parse</code>
<code>JSON.parse(myJson)</code>; string 타입의 <code>myJson</code>을 JS 객체로 만들어주는 함수.</p>
</li>
<li><p>메소드
일반적으로 서버에 보내는 요청은 조회, 추가, 수정, 삭제 네 종류로 각각 요청(리퀘스트)의 GET, POST, PUT, DELETE 메소드로 전달된다.
리퀘스트는 헤드와 바디로 구분되는데 어떤 요청을 보낼지 결정하는 메소드는 헤드에, 실제로 담아야 하는 데이터는 바디에 전달된다(데이터를 받을 필요가 없는 GET, DELETE는 바디에 데이터가 따로 들어가지는 않는다).</p>
</li>
</ol>
<p>사진 출처: 코드잇
<img src="https://images.velog.io/images/soo-im/post/cb6c4d3e-6413-4ce1-9737-a4abef4d29f0/image.png" alt=""></p>
<h3 id="2021-11-29">2021-11-29</h3>
<p><strong>플젝</strong> <code>map</code> vs. <code>forEach</code></p>
<p>둘 다 array의 요소를 순차로 도는(?) 메소드인데 아래와 같은 차이가 있다(출처: <a href="https://code.tutsplus.com/tutorials/javascript-map-vs-foreach-when-to-use-each-one--cms-38365">블로그1</a>, <a href="https://frontdev.tistory.com/entry/JS-Map-vs-ForEach">블로그2</a>).</p>
<ol>
<li><code>forEach</code>
아무것도 반환하지 않아 <code>return something</code>을 해도 항상 undifined가 나온다. 콜백 함수로 이미 존재하는 array의 값을 바꿀 수 있다. iterating 하는 배열 자체를 수정할 수 있으므로 mutator 메소드이다. (모르는 단어가 또...)<pre><code class="language-js">let arr = [1, 2, 3, 4, 5];
</code></pre>
</li>
</ol>
<p>// arr 배열의 값에 2씩 곱하기
arr.forEach((num, index) =&gt; {
    return arr[index] = num * 2;
});
// arr을 출력하면 [2, 4, 6, 8, 10]</p>
<pre><code>
2. `map`
함수의 결과로 새로운 배열을 반환한다. 아래와 같이 return 결과에 이어 계속해서 `map`을 수행하는 것도 가능하다.
```js
let numberArray = [1, 2, 3, 4, 5];

// 숫자 배열을 [$1, $2...]와 같이 달러 텍스트 배열로 변환하기
let returnValue = numberArray
  .map((num) =&gt; num * 2)
  .map((num) =&gt; num.toString())
  .map((string) =&gt; &quot;$&quot;+string);</code></pre><ol start="3">
<li>뭐가 더 좋을까?
<code>map</code>은 콜백 함수의 결과를 새로운 배열로 반환하고, <code>forEach</code>는 그렇지 않으므로 용도에 맞게 사용하면 된다.</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>