<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>_choongyul.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 10 May 2023 21:58:27 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>_choongyul.log</title>
            <url>https://images.velog.io/images/_choongyul/profile/fbc92944-6de1-48b3-913d-bfb0ab802041/IMG_9716.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. _choongyul.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/_choongyul" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[피보나치 수열로 알아보는 알고리즘 최적화]]></title>
            <link>https://velog.io/@_choongyul/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98</link>
            <guid>https://velog.io/@_choongyul/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98</guid>
            <pubDate>Wed, 10 May 2023 21:58:27 GMT</pubDate>
            <description><![CDATA[<h2 id="프롤로그">프롤로그</h2>
<p>IT 업계에서 근무하면서 느끼는 건 개발자 수준에 따라 같은 문제를 참 다양한 방식으로 푼다는 것이다. 내가 풀었을 때는 수행시간이 3분인데, 또 내 선임이 손 대면 3초로 끝나는 마법!</p>
<p>왜 그러는 것일까? 그냥 내가 단순 무식하게 빨리 일을 끝내려고 해서 그런건 아니였을까?</p>
<p>피보나치 수열을 다양한 방식을 적용하여 풀어보고 수행시간 변화를 확인해 보자.</p>
<h2 id="피보나치-수열이란">피보나치 수열이란?</h2>
<p>고등학교 1학년 수학시간에 배우는 피보나치 수열은 너무나 유명한 &#39;수&#39;이다.</p>
<p>제 0항을 0으로, 제 1항을 1로 두고, 둘째 번 항부터는 바로 앞의 두 수를 더한 수로 놓는다. 이를 점화식 형태로 표현하면 다음과 같다.</p>
<blockquote>
<p>$$<br>F_{0} = 0,\quad F_{1} = 1$$
$$
F_{n+2} = F_{n+1} + F_{n}$$</p>
</blockquote>
<h2 id="재귀적으로-풀기">재귀적으로 풀기</h2>
<p>재귀적인 풀이는 위의 점화식 정의를 충실히 따른다. 수학적인 반복관계를 그대로 구현하면 된다.</p>
<pre><code class="language-python">def fibonacci(n)
    if n &gt; 2:
        return n
    else
        return fibonacci(n-1) + fibonacci(n-2)</code></pre>
<p>위 방법은 매우 간단 하지만 실용적이지는 않은데, 너무 느리기 때문이다. <code>n=100</code>만 넣어도 원하는 시간에 답을 구할 수 없다. 그 이유는 지속적인 반복 <strong>호출</strong> 때문인데, 재귀 트리를 그려보면 그 이유를 알 수 있다. 그림을 보면 <code>fibonacci(5)</code>를 계산 하기 위해 <code>fibonacci(3)</code>은 두번 호출되었고, <code>fibonacci(2)</code>는 세번 호출된다. 그리고 <code>fibonacci(1)</code>은 5번이나 호출되는 것을 알 수 있다.
<img src="https://velog.velcdn.com/images/_choongyul/post/84199740-f244-4a0a-b3f0-cc1afa78e15c/image.png" alt=""></p>
<p>재귀 트리에서 <strong>하나의 노드 당 2개씩 자식 노드가 증가</strong>하고 있음을 주목해야 한다.
<code>n</code>번 호출하면, $$2^{n}$$ 번씩 호출되는 것이다. 이를 $$Big-O$$ 표기법으로 표시하면 $$O(2^{n})$$이 되고, 이 함수의 수행속도는 <code>n</code> 이 증가 함에 따라 급격히 느려질 것이다.</p>
<p>그럼, <code>n=100</code>일때 몇 번 호출 될까? $$2^{100}$$ 을 계산해 보면 126,7650,6002,2822,9401,4967,0320,5376 된다. 나는 이 수를 어떻게 읽어야 하는지도 모르겠다.</p>
<p><em>우리는 정말 <code>n=100</code> 도 못구하는 풀이를 풀었다고 할 수 있을까?</em></p>
<h2 id="동적-계획법-적용하기">동적 계획법 적용하기</h2>
<p>단순 재귀적 풀이는 반복 호출이 문제였다. 답을 구하기 위해서 했던 계산을 또 하고 또 하고 계속해야 한다면, 전에 했던 계산을 저장해 놓으면 되지 않을까? </p>
<p>동적 계획법은, 
동일한 계산을 반복해야 할 경우 한 번 계산한 결과를 메모리에 저장해 두었다가 꺼내 쓰는 것이다. 그래서 &quot;컴퓨터 과학이 여는 세계&quot;에서 이광근 교수는 동적 계획법을 <strong>기억하며 풀기</strong>로 표기하였다.</p>
<h3 id="하향식으로-접근하기">하향식으로 접근하기</h3>
<p>동적 계획법에는 하향식 접근과 상향식 접근법 두가지가 있는데, 먼저 하향식 접근법을 적용해 보자. 하향식 접근은 위에서 아래로 가는 것이다. $$F_{n}$$을 구하기 위해서 $$F_{n-1}$$ 부터 $$F_{0}$$ 까지 n을 감소 시켜가는 것이 하향식 접근이다.</p>
<pre><code class="language-python">def fibonacci(n)
    if n &gt; 2:
        return n
    else
        return fibonacci(n-1) + fibonacci(n-2)</code></pre>
<p>재귀적 풀이를 보면 <code>fibonacci(n)</code> 을 구하기 위해 <code>fibonacci(n-1)</code>, <code>fibonacci(n-2)</code>를 지속적으로 호출하고 있다. n을 감소시키면서 호출하고 있다. 이는 하향식 접근을 따르는 풀이이고, <strong>반복 호출</strong> 문제를 가지고 있다.</p>
<p>이 문제를 해결하기 위해 <code>fibonacci(i)</code>를 계산 할 때 마다 dictionary 타입 변수 <code>cahce</code>에 저장한다. 그리고 필요할 때 그 값을 꺼내서 사용하도록 한다.</p>
<pre><code class="language-python">def fibonacci(n) -&gt; int:
    cache = {0:0, 1:1}

    def fibonacci_inner(n):
        if n in cache:
            return cache[n]
        else:
            cache[n] = fibonacci_inner(n-1) + fibonacci_inner(n-2)
        return cache[n]

    return fibonacci_inner(n)</code></pre>
<p>동적 계획법을 사용한 풀이의 재귀 트리를 그려보면 아래와 같다.(사각형으로  표시된 부분은 캐시값을 그대로 사용하는 경우이다.)
<img src="https://velog.velcdn.com/images/_choongyul/post/45d965b7-2d9e-4563-b79e-b45e9394bd41/image.png" width="70%">
노드의 개수가 수행시간이 되는데, 각 노드의 자식 노드는 한 개 이므로 N에 대한 총 노드의 개수는 대략 $$2\times n$$가 된다. 따라서 수행 시간을  $$Big-O$$표기 법으로 표현하면 $$O(n)$$이 된다.</p>
<p><code>n=100</code>을 넣어보면 성능이 얼마나 개선되었는지 알수 있는데,
재귀적인 풀이와 다르게 결과 값를 바로 리턴하는 것을 확인 할 수 있다. 수행 시간 $$O(n)$$ 과 $$O(2^{n})$$은 엄청난 차이가 있음을 체감할 수 있다.</p>
<h3 id="상향식으로-접근하기">상향식으로 접근하기</h3>
<p>상향식 접근은 아래에서 위를 향해가는 것이다. $$F_{n}$$을 구하기 위해서 $$F_{0}$$에서 부터 n을 증가시켜 가는 것이다. </p>
<p>피보나치 점화식에서, $$F_{0} = 0,\quad F_{1} = 1$$ 이라 정의한 것을 기억하자. 이 둘을 이용해서 $$F_{2}$$를 계산하고, 차례로 $$F_{3}, \quad F_{1}$$ 등을 이전의 결과를 이용해 계산한다.</p>
<pre><code class="language-python">def fibonacci(n):
    fib_n = 0 # fibonacii(0)
    fib_n_plue_1 = 1 # fibonacii(1)
    fib_n_plue_2 = 0 # fibonacii(n+2)

    if n &lt; 2:
        return n

    for i in range(2,n+1):
        fib_n_plue_2 = fib_n_plue_1 + fib_n
        fib_n = fib_n_plue_1
        fib_n_plue_1 = fib_n_plue_2 
    return fib_n_plue_2</code></pre>
<p>상향식 접근법의 수행 시간 <code>for</code>문이 하나으로 $$O(n)$$이 되고, <code>iterative</code> 한 풀이와 완벽하게 같다.</p>
<h2 id="행렬과-분할-정복법-적용하기">행렬과 분할 정복법 적용하기</h2>
<p>$$<br>F_{0} = 0,\quad F_{1} = 1$$
$$
F_{n+2} = F_{n+1} + F_{n}$$</p>
<p>분할 정복법을 사용하기 위해서, 피보나치 수열의 점화식을 행렬의 곱으로 표현해 보자.</p>
<p>$$
\begin{pmatrix}
F_{n+2} \
F_{n+1}
\end{pmatrix}= 
\begin{pmatrix}
1 &amp; 1 \
1 &amp; 0
\end{pmatrix}
\begin{pmatrix}
F_{n+1} \
F_{n}
\end{pmatrix}$$
행렬의 곱을 풀어보면 점화식과 동일하게 됨을 확인 할 수 있는데, 위의 식은 아래와 같이 표현이 가능하다.
$$
\begin{pmatrix}
F_{n+2} \
F_{n+1}
\end{pmatrix}= 
\begin{pmatrix}
1 &amp; 1 \
1 &amp; 0
\end{pmatrix}
\begin{pmatrix}
1 &amp; 1 \
1 &amp; 0
\end{pmatrix}
\begin{pmatrix}
F_{n} \
F_{n-1}
\end{pmatrix}$$</p>
<p>$$F_{n+1}$$ 와 $$F_{n}$$을 $$F_{1}$$ 와 $$F_{0}$$ 까지 확장시켜보면 아래의 식 형태가 되고
$$
\begin{pmatrix}
F_{n+2} \
F_{n+1}
\end{pmatrix}= 
\begin{pmatrix}
1 &amp; 1 \
1 &amp; 0
\end{pmatrix}^{n}
\begin{pmatrix}
F_{1} \
F_{0}
\end{pmatrix}$$</p>
<p>$$\begin{pmatrix}1 &amp; 1 \1 &amp; 0\end{pmatrix}^{n}$$ 행열의 제곱을 풀어보면 결국 $$a_{1,1}$$ 원소는 $$F_{n}$$과 같음을 확인 할 수 있다. </p>
<p>결국, 행렬의 제곱으로 표현된 피보나치 수열의 관계식을 갖게 된다.
$$\begin{pmatrix}
1 &amp; 1 \
1 &amp; 0
\end{pmatrix}^{n} = \begin{pmatrix}
F_{n} &amp; F_{n-1} \
F_{n-1} &amp; F_{n-2}
\end{pmatrix}
$$</p>
<p>이제 우리는 $$if\quad n&gt;2$$일 때, <code>M=[[1,1],[1,0]]</code> 의 거듭 제곱한 결과의 <code>M[0][0]</code> 을 리턴하기만 하면 된다.</p>
<p>일단 가장 익숙한 <code>interative</code> 한 방법으로 풀면, 반복문 하나 사용되고 이 풀이의 수행시간은 $$O(n)$$이 된다.</p>
<pre><code class="language-python">def fibonacci(n):
    M=[[1,1],[1,0]]
    F = [[1,1],[1,0]]
    if n==0:
        return 0
    if n == 2 or n == 1:
        return 1
    for i in range(n-2):
        x = F[0][0]*M[0][0] + F[0][1]*M[1][0]
        y = F[0][0]*M[0][1] + F[0][1]*M[1][1]
        z = F[1][0]*M[0][0] + F[1][1]*M[1][0]
        w = f[1][0]*M[0][1] + F[1][1]*M[1][1]

        F[0][0] = x
        F[0][1] = y
        F[1][0] = z
        F[1][1] = w

    return result[0][0]</code></pre>
<p>정복법에 의해 $$O(\log{n})$$ 의 수행 시간의 풀이로 최적화 할 수 있는데 방법은 매우 간단하다.</p>
<p>예를 들어, $$2^8$$ 를 풀어보자. $$2^8$$ 을 푸는 방법은 두가지가 있는데,</p>
<p>첫째는,
$$2 \times 2 \times 2 \times 2 \times 2\times 2\times 2\times 2$$ 
이렇게 2를 8번 곱하는 것이다.</p>
<p>두번째는, 지수의 특성을 이용하는 방법으로 $$((2^2)^2)^2$$ 제곱된 값을 제곱하는 것이다. 이런식으로 계산하면 8번 계산 했던걸 3번만 계산하면 된다. 그리고 3은 $$\log_{2}8$$ 값과 동일하다.</p>
<p><strong>즉, 지수계산은 지수의 특성을 이용해서 $$O(\log{n})$$ 수행시간으로 풀수 있다.</strong></p>
<pre><code class="language-python">def matrix_fibonacii(n):
    F = [[1, 1],[1, 0]]
    if n == 0:
        return 0
    else:
        power(n-1, F)
        return F[0][0]

def multi(F, b):

    x = (F[0][0] * b[0][0]) + (F[0][1] * b[1][0]) 
    y = (F[0][0] * b[0][1]) + (F[0][1] * b[1][1])
    z = (F[1][0] * b[0][0]) + (F[1][1] * b[1][0])
    w = (F[1][0] * b[0][1]) + (F[1][1] * b[1][1])

    F[0][0] = x
    F[0][1] = y
    F[1][0] = z
    F[1][1] = w

def power(n, F):

    if( n == 1 or n == 0):
        return
    else:
        power(n//2, F)
        multi(F,F)
        if (n % 2) != 0:
            M = [[1, 1],[1, 0]]
            multi(F,M)</code></pre>
<h2 id="에필로그">에필로그</h2>
<p>재귀적인 방법에서는 어떻게 반복적인 연산을 처리 했는지 확인하는 것이 중요했고,
동적 계획법에서는 어떻게 반복 연산을 제거하려고 했는지가 중요했다.
행렬과 분할 정복법을 적용은 문제를 바라보는 관점의 전환이 중요하지 않았을까?</p>
<p>개발자라면, 최적화 하기위해 고민하고 또 고민해야 한다.</p>
<p>&#39;어떻게 하면 반복하는 것을 줄일 수 있을까?&#39;에 대한 해답을 찾으려고 노력해야 한다. 한번에 완벽하게 끝나는 경우는 없더라. 수많은 테스트와 검증이 필요하더라.</p>
<p>기업들이 코딩 테스트를 하는 건, 능력 있는 개발자를 뽑기위한 방법일까? 능력 없는 개발자를 거르기 위한 방법일까?</p>
<h2 id="참고-사이트">참고 사이트</h2>
<ul>
<li><a href="https://namu.wiki/w/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98%20%EC%88%98%EC%97%B4">나무위키-피보나치 수열</a></li>
<li><a href="https://www.geeksforgeeks.org/program-for-nth-fibonacci-number/">geeksforgeeks-Program for Fibonacci numbers</a></li>
<li><a href="https://star7sss.tistory.com/358">star7sss-[분할정복/DQ]백준 11444 피보나치 수 6 - 파이썬</a></li>
<li><a href="https://velog.io/@falling_star3/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Level2-%ED%96%89%EB%A0%AC%EC%9D%98-%EA%B3%B1%EC%85%88">falling_star3-[프로그래머스 Level2][Python] 행렬의 곱셈</a></li>
<li><a href="https://shoark7.github.io/programming/algorithm/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%84-%ED%95%B4%EA%B2%B0%ED%95%98%EB%8A%94-5%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95">shoark7-피보나치 수열 알고리즘을 해결하는 5가지 방법</a></li>
<li><a href="http://ironcreek.net/syntaxtree/">jsSyntaxTree</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[20230419 TIL | keyowod 'in' in dictionary in python]]></title>
            <link>https://velog.io/@_choongyul/20230419-TIL-keyowod-in-in-dictionary-in-python</link>
            <guid>https://velog.io/@_choongyul/20230419-TIL-keyowod-in-in-dictionary-in-python</guid>
            <pubDate>Tue, 18 Apr 2023 22:11:22 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>19<sup>th</sup> April 2023</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<pre><code class="language-python">m = {0:0, 1:1}

def func(n):
    if n in m:
        # to do something</code></pre>
<p>이런 표현식이 보인다. 무슨 뜻이지?</p>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://wikidocs.net/16043">파이썬 - 기본을 갈고 닦자!-17. dictionary(딕셔너리)</a></li>
</ul>
<h2 id="🎽-learn">🎽 Learn</h2>
<p>We can test if a key is in a dictionary or not using the keyword in. Notice that the membership test is only for the keys and not for the values.</p>
<h3 id="in-keyword">&#39;in&#39; keyword</h3>
<p><code>Dictionary</code>에 <code>key</code>가 있는지 없는지 알기 위해서 <code>in</code> 키워드를 사용한다. <code>value</code>가 아니라 <code>key</code>를 검사하는데 사용하는 것을 주의해야 한다.</p>
<pre><code class="language-python">m = {0:0, 1:1}

def func(n):
    if n in m:
        print(&#39;The key, &#39; + str(n) + &#39;, is in m&#39;)
    else:
        print(&#39;The key, &#39; + str(n) + &#39;, is not in m&#39;)

func(1)
func(10)</code></pre>
<h4 id="output">output</h4>
<pre><code>The key, 1, is in m
The key, 10, is not in m</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[What is Recursion?]]></title>
            <link>https://velog.io/@_choongyul/Recursion-Program</link>
            <guid>https://velog.io/@_choongyul/Recursion-Program</guid>
            <pubDate>Thu, 13 Apr 2023 21:48:32 GMT</pubDate>
            <description><![CDATA[<h2 id="📍-프롤로그">📍 프롤로그</h2>
<p>어떤 문제를 재귀적으로 풀어내는 것. 그게 나에게는 너무 어렵다. 
계속 <code>Iterative</code> 하게 생각하게 된다. <code>Recursive</code> 하게 생각하는 방법으로 바꿔보자. </p>
<h2 id="📍-재귀란-무엇일까">📍 재귀란 무엇일까?</h2>
<p>함수가 자기 자신을 직접적으로(Directly) 또는 간접적(Indirectly)으로 호출할 때 우리는 <code>재귀(Recursion)</code>라고 한다. 그리고 그 함수를 <code>재귀 함수(Recursive Function)</code>라 부른다.</p>
<pre><code class="language-python"># An example of direct recursion
def directRecFun():
    # some code...
    directRecFun()
    # some code...</code></pre>
<pre><code class="language-python"># An example of indirect recursion
def indirectRecFun1():
    # some code...
    indirectRecFun2()
    # some code...

def indirectRecFun2():
    # some code...
    indirectRecFun1()
    # some code...</code></pre>
<h2 id="📍-언제-재귀reclusion을-사용할까">📍 언제 재귀(Reclusion)을 사용할까?</h2>
<p>&quot;N번째 ... 를 계산하라&quot;, &quot;첫 N를 나열하는 코드를 작성하라&quot;, &quot;모든 ...를 나열하라&quot; 등의 문제들은 재귀적으로 풀수 있는 경우가 많다. 그리고 가장 전통적인 문제로 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/12946">하노이탑</a> 문제가 있으며, 삼성 알고리즘 테스트에 단골로 출제되는 <code>깊이우선 탐색(Depth-First Search)</code> 문제들도 재귀적인 방법을 사용해서 푼다.</p>
<h2 id="📍-a-idimplementsa재귀는-어떻게-구현할까">📍 <a id="implements"></a>재귀는 어떻게 구현할까?</h2>
<p>재귀를 구현하기 위한 가장 보편적인 방법은 아래의 단계를 따르는 것이다.</p>
<ul>
<li>Step1: 종료 조건을 정의한다.</li>
<li>Step2: 재귀적으로 호출하는 함수를 정의한다.(문제를 더 작은 하위문제로 나눈다.)</li>
<li>Step3: 재귀가 종료되는지 확인한다.</li>
<li>Step4: 하위 문제의 솔루션을 결합하여 문제를 해결한다.</li>
</ul>
<p>이중 중요한 단계를 뽑자면 <code>Step1</code> 과 <code>Step2</code>라고 할 수 있는데,
종료조건(Step1)이 적절하지 않으면 무한루프에 빠게되고, 문제를 하위문제(Step2)로 나눌수 없으면 재귀가 성립하지 않는다. 그래서 구현할 때는 <code>Step1</code> 과 <code>Step2</code> 단계에 집중해야 한다.</p>
<h2 id="📍-재귀-호출에서-메모리는-어떻게-할당될까">📍 재귀 호출에서 메모리는 어떻게 할당될까?</h2>
<p>모든 함수가 호출되면, 함수 정보(Local variable, return address, function pointer)가 스택에 저장되는데 이는 재귀 함수에서도 동일하게 적용된다. 스택의 특성에 따라 <strong>먼저 호출된 함수는 스택 아래, 나중에 호출된 함수는 스택 위</strong>에 위치한다. <code>Base Case(종료조건)</code> 에 이르면 <code>return value</code>를 <code>Caller(호출한 함수)</code>에게 전달하고 메모리는 해제된다.</p>
<p>예를 통해서 재귀함수가 어떻게 동작하는지 살펴보면,</p>
<pre><code class="language-python">def printFun(n):

    if(n &lt; 1):
        return n
    else:
        print(n, end=&quot; &quot;)
        printFun(n-1)
        print(n, end=&quot; &quot;)
        return


n=3
printFun(3)</code></pre>
<p><strong>output</strong></p>
<pre><code>3 2 1 1 2 3</code></pre><p>&#39;
<img src="https://velog.velcdn.com/images/_choongyul/post/dc7db512-8416-4700-9899-c4650820e1f4/image.png" width="70%"/></p>
<ol>
<li><code>printFunc(3)</code> 이 <code>main()</code>에 의해서 불려진다.
n=3으로 초기화되고, 실행해야 할 구분이 다이어그램에서 보이는 것 처럼 Stack에 Push 된다.</li>
<li><code>printFunc(3)</code>은 첫번째 구문을 실행하여 3을 출력 하고, <code>printFunc(2)</code>를 호출한다.</li>
<li><code>printFunc(2)</code>는
n=2으로 초기화되고, Statement 1부터 4를 Stack에 Push 한다.</li>
<li><code>printFunc(2)</code>은 2를 출력하고, <code>printFunc(1)</code>를 호출한다.</li>
<li><code>printFunc(1)</code>는
n=1으로 초기화되고, Statement 1부터 4를 Stack에 Push 한다.</li>
<li><code>printFunc(1)</code>은 1을 출력하고, <code>printFunc(0)</code>를 호출한다.</li>
<li><code>printFunc(0)</code>은 <code>if</code> 문을지나 <code>printFunc(1)</code>로 리턴된다.</li>
<li><code>printFunc(1)</code> 은 <strong>3번째 Statement 인 <code>Print(n)</code> 구문을 실행하고 <code>printFunc(2)</code>로</strong> 리턴된다.</li>
<li><code>printFunc(2)</code>도 마찬가지로 <code>Print(n)</code> 구문을 실행하여 2를 출력하고 <code>printFunc(3)</code>으로 리턴된다.</li>
<li><code>printFunc(3)</code>에서 <code>Print(n)</code> 구문을 실행하여 3을 출력하면 모든 함수가 종료된다.</li>
</ol>
<p>이제 왜 함수의 실행 결과가 <code>3 2 1 3 2 1</code> 이 아니라 <code>3 2 1 1 2 3</code> 이렇게 출력되는지 이해할 수 있다. 이는 재귀적 호출이 Stack의 특성 사용하기 때문이다.</p>
<blockquote>
<p>재귀를 이해하기 위해서는 Stack의 특성을 이해하는 것이 중요하다. 
유명한 피보나치 수열을 구하는 재귀함수 F(n) = F(n-1) + F(n-2) 의 실행을 생각해 보면, 이 함수는 F(n-1)에서 파생된 모든 스택에 있는 함수와 구문을 처리한 다음, F(n-2) 에서 파생된 스택에 내용을 처리하게 된다.</p>
</blockquote>
<h2 id="📍-재귀를-이용한-문제해결">📍 재귀를 이용한 문제해결</h2>
<p>나의 경우, 이해하는 것과 직접해보는 것에 꽤 많은 차이를 느끼는데 그 차이를 줄이는 방법은 많이 해보는 것 밖에 없었다.</p>
<p>문제를 해결 절차는 앞서 설명한 <a href="#implements">재귀함수 구현 방법</a>을 따르면서, 단순한 문제들을 의식적으로 <code>Reclusion</code>하게 풀어보자.</p>
<h3 id="problem-1-1-부터-n까지-합을-구하는-함수를-재귀적으로-작성하시오">Problem 1. 1 부터 N까지 합을 구하는 함수를 재귀적으로 작성하시오.</h3>
<ul>
<li><p>Step1 - Define a base case</p>
<pre><code class="language-python">if ( n == 0 ):
  return n</code></pre>
</li>
<li><p>Step2 - Define a recursive case</p>
<pre><code class="language-python">sum(n) = n + sum(n-1)</code></pre>
</li>
<li><p>Step3 - Ensure the recursion terminates</p>
<pre><code class="language-python"># if sum(0) is called, then recursion terminates</code></pre>
</li>
<li><p>Step4 - Combine the solution</p>
<pre><code class="language-python">def reclusiveSum(n):
if (n == 0):
    return n
else:
    return n + reclusiveSum(n-1)</code></pre>
<h3 id="problem-2-회문palindrome-똑바로-읽으나-거꾸로-읽으나-동일한-단어인지-확인하는-함수를-재귀적으로-구현하시오">Problem 2. 회문(Palindrome) 똑바로 읽으나 거꾸로 읽으나 동일한 단어인지 확인하는 함수를 재귀적으로 구현하시오.</h3>
</li>
<li><p>Step1 - Define a base case</p>
<pre><code class="language-python"># 단어의 길이가 1이거나 0이면 가장 짧은 회문이므로 True 반환
if (len(str) == 0 | len(str) == 1))
  return True
</code></pre>
</li>
</ul>
<h1 id="양-끝이-같지-않으면-회문이-아니므로-false-반환">양 끝이 같지 않으면 회문이 아니므로 False 반환</h1>
<p>if ( str[0] != str[-1] )
    return False</p>
<pre><code>* Step2 - Define a recursive case
```python
# 양 끝이 같으면 양 끝을 뺀 문자열로 다시 비교
# 만약, abba 이면 양 끝 a 를 빼고 bb 만 다시 비교
if(str[0] == str[-1]):
    return fun(str[1:-1])</code></pre><ul>
<li><p>Step3 - Ensure the recursion terminates</p>
<pre><code class="language-python"># str = &quot;a&quot; 일때, fun(str) 이 호출되면 재귀함수는 종료 됨</code></pre>
</li>
<li><p>Step4 - Combine the solution</p>
<pre><code class="language-python">def isPalindrome(str):

  if(len(str) == 0 | len(str)==1):
      return True
  elif(str[0] == str[-1]):
      return isPalindrome(str[1:-1])
  else:
      return False</code></pre>
</li>
</ul>
<h3 id="problem-3-reverse-string">Problem 3. Reverse String</h3>
<ul>
<li><p>Step1 - Define a base case</p>
<pre><code class="language-python"># Returns &quot;&quot; if the length of the string is zero.
if (len(str) == 0):
  return &quot;&quot;</code></pre>
</li>
<li><p>Step2 - Define a recursive case</p>
<pre><code class="language-python">fun(str[1:]) + str[-1]</code></pre>
</li>
<li><p>Step3 - Ensure the recursion terminates</p>
<pre><code class="language-python"># The recursion terminates when the string length is zero.</code></pre>
</li>
<li><p>Step4 - Combine the solution</p>
<pre><code class="language-python">def reverseString(str):

  if (len(str) == 0):
      return &quot;&quot;
  else:
      return reverseString(str[1:]) + str[-1]</code></pre>
</li>
</ul>
<h3 id="problem-4-decimal--to-binary-conversion">Problem 4. decimal  to binary conversion</h3>
<ul>
<li><p>Step1 - Define a base case</p>
<pre><code class="language-python">if(number == 1)
  return str(number)
else:
  return &quot;&quot;</code></pre>
</li>
<li><p>Step2 - Define a recursive case</p>
<pre><code class="language-python">fun(number//2) + str(number%2)</code></pre>
</li>
<li><p>Step3 - Ensure the recursion terminates</p>
<pre><code class="language-python"># If you continue to divide by 2, finally the quotient is either 0 or 1.</code></pre>
</li>
<li><p>Step4 - Combine the solution</p>
<pre><code class="language-python">def decimalToBinary(number):

  if(number ==1):
      return str(number)
  elif(number == 0)
      return &quot;&quot;
  else:
      return decimalToBinary(number//2) + str(number%2)</code></pre>
</li>
</ul>
<h2 id="📍-마무리">📍 마무리</h2>
<p>재귀적으로 문제를 풀기위해서는 아래 4가지를 기억하면 된다.</p>
<ul>
<li>재귀적 프로그래밍에는 <code>Recursive Case</code>와 <code>Base Case</code>가 존재한다.</li>
<li><code>Base Case</code>는 프로그램을 종료 시키는 역할을 한다.</li>
<li>재귀적 호출은 새로운 각각의 함수를 생성하여 스택에 저장하도록 한다.</li>
<li>무한 재귀 호출은 실행시 <code>OutOfStackMemory</code>를 이끈다.</li>
</ul>
<p><code>Iterative</code>하게 구현하는 것과 다른 건,
<strong><code>스택</code>의 특성을 사용한다는 것</strong>과 반복문에서 하던 작업을 스스로 호출을 통해 처리한다는 점이다.</p>
<h2 id="📍-에필로그">📍 에필로그</h2>
<p><code>Reclusive Programing</code>과 <code>Iterative Programing</code>의 장단점 따위는 신경 쓰고 싶지 않았다. 오로지 구현에만 집중하고 싶었다. <code>재귀용법</code> 으로 해결 할 수 있는 문제가 얼마나 될까? 하지만 <code>재귀용법</code>을 이해하지 못하면 다음 단계로 가지 못한다. 내가 <code>DFS 문제</code>를 풀때 헤메는 이유. 그 문제의 답을 보아도 이해가 가지 않는 이유가 거기에 있지 않을까?</p>
<h2 id="📍-참고-사이트">📍 참고 사이트</h2>
<ul>
<li><a href="https://www.geeksforgeeks.org/introduction-to-recursion-data-structure-and-algorithm-tutorials/">Introduction to Recursion – Data Structure and Algorithm Tutorials</a></li>
<li><a href="https://www.w3resource.com/python-exercises/data-structures-and-algorithms/python-recursion.php">Python: Recursion - Exercises, Practice, Solution</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[python: Simple Sorting Algorithm]]></title>
            <link>https://velog.io/@_choongyul/python-Simple-Sorting-Algorithm</link>
            <guid>https://velog.io/@_choongyul/python-Simple-Sorting-Algorithm</guid>
            <pubDate>Tue, 28 Mar 2023 21:20:29 GMT</pubDate>
            <description><![CDATA[<h2 id="📍-bubble-sort">📍 Bubble Sort</h2>
<h3 id="🔍-about-bubble-sort">🔍 About Bubble Sort</h3>
<p>버블 정렬의 Key Concept은 <strong>현재 원소가 그다음 원소의 값보다 크면 바꾸는 것</strong>이다. 이 작업을 정렬이 완료될 때까지 반복하면 된다.
<img src="https://velog.velcdn.com/images/_choongyul/post/78068444-6b2e-4857-a6d8-881a00073370/image.png" alt=""></p>
<blockquote>
<p><strong>Key Concept</strong></p>
</blockquote>
<ul>
<li>현재 원소(A<sub>[i]</sub>)와 다음 원소(A<sub>[i+1]</sub>)를 비교한다.</li>
<li>현재 원소가 다음 원소 보다 크면 서로의 자리를 바꾼다.</li>
</ul>
<h3 id="⛏️-implementation">⛏️ Implementation</h3>
<pre><code class="language-python">def bubble_sort(list):
    list_length = len(list)
    for try_count in range(0, list_length-1):
        sorted = False
        for index in range(1, list_length - try_count):
            if list[index-1] &gt; list[index]:
                list[index-1], list[index] = list[index],  list[index-1]
                sorted = True

        if sorted == False:
            break 

    return list</code></pre>
<h2 id="📍-insertion-sort">📍 Insertion Sort</h2>
<h3 id="🔍-about-insertion-sort">🔍 About Insertion Sort</h3>
<p>삽입 정렬의 Key Concept은 카드를 정렬하는 방법과 동일하다. </p>
<p>생각해 보자. 카드를 일렬로 펼쳐서 카드 배열을 만든다. 그 다음 두번째 카드를 선택하고 첫 번째 카드와 비교한다. 만약, 두번째 카드가 값이 작으면 첫번째 자리에 <strong>두번째 카드를 삽입</strong>한다. 이제 이 카드 배열은 두 번째 카드까지 정렬되었다. 다음에는 세번째 카드를 선택한다. 그리고 앞으로 가면서 값을 비교하여, 세번째 카드의 값이 작다면 <strong>그 앞에 카드를 삽입</strong>한다. 첫 번째 카드까지 이 동작을 반복한다. 이제 세번째 카드 위치까지 정렬되었다. 다음은 네 번째 카드, 다섯 번째 카드, 그리고 마지막 카드까지 이 동작을 반복한다.</p>
<p><img src="https://velog.velcdn.com/images/_choongyul/post/7c1770a7-b1c3-4086-96bc-3759d9eb31fb/image.png" alt=""></p>
<blockquote>
<p><strong>Key Concept</strong></p>
</blockquote>
<ul>
<li>선택한 원소(A<sub>[i+1]</sub>)의 왼쪽(from A<sub>[i-1]</sub> to A<sub>[0]</sub>) 은 정렬되어 있다.</li>
<li>왼쪽의 정렬된 배열의 정렬된 상태가 유지될 수 있도록 선택한 원소의 위치를 찾아 삽입한다.</li>
</ul>
<h3 id="⛏️-implementation-1">⛏️ Implementation</h3>
<pre><code class="language-python">def insert_sort(list):
    for standard in range(1, len(list)-1):
        for index in range(standard, 0, -1):
            if list[index] &lt; list[index-1]:
                list[index-1], list[index] = list[index], list[index-1]

    return list</code></pre>
<h2 id="📍-selection-sort">📍 Selection Sort</h2>
<h3 id="🔍-about-selection-sort">🔍 About Selection Sort</h3>
<p>선택정렬의 Key Concept은 배열에서 <strong>최소값을 찾아 맨 앞에 위치한 값과 교체하는 것</strong>이다.
<img src="https://velog.velcdn.com/images/_choongyul/post/0a9c0777-34c6-4181-92d0-8952e0f7760f/image.png" alt=""></p>
<blockquote>
<p><strong>Key Concept</strong></p>
</blockquote>
<ul>
<li>가장 작은 값부터 정렬시켜 나가는 전략을 취한다.</li>
<li>배열의 정렬되지 않은 부분에서 가장 작은 값을 찾는다. ( 회가 거듭할 수록 배열의 왼쪽 부분은 정렬되어 간다. 반면, 오른쪽은 정렬되어 있지 않은 상태를 유지한다. )</li>
<li>가장 작은 값을 정렬되지 않은 부분의 맨 앞의 값과 교체한다.</li>
</ul>
<h3 id="⛏️-implementation-2">⛏️ Implementation</h3>
<pre><code class="language-python">def selection_sort(list):
    for stage in range(0, len(list)):
        min_index = stage
        for index in range(stage+1, len(list)):
            if list[min_index] &gt; list[index]:
                min_index = index
        list[stage], list[min_index] = list[min_index], list[stage]

    return list</code></pre>
<h2 id="🛰️-reference">🛰️ Reference</h2>
<ul>
<li><a href="https://www.hackerearth.com/practice/algorithms/sorting/bubble-sort/tutorial/">hackerearth-Bubble Sort</a></li>
<li><a href="https://www.hackerearth.com/practice/algorithms/sorting/insertion-sort/tutorial/">hackerearth-Insertion Sort</a></li>
<li><a href="https://www.hackerearth.com/practice/algorithms/sorting/selection-sort/tutorial/">hackerearth-Selection Sort</a></li>
<li><a href="https://www.geeksforgeeks.org/bubble-sort/">geeksforgeeks-Bubble Sort Algorithm</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Max Heap in python]]></title>
            <link>https://velog.io/@_choongyul/Max-Heap-in-python</link>
            <guid>https://velog.io/@_choongyul/Max-Heap-in-python</guid>
            <pubDate>Thu, 09 Mar 2023 21:24:32 GMT</pubDate>
            <description><![CDATA[<h2 id="🔍-about-max-heap">🔍 About Max Heap</h2>
<p>최대 힙(Max Heap)은 트리의 마지막 단계(Level)에서 오른쪽을 뺀 나머지 부분이 가득 채워져 있다는 점에서 완전 이진 트리이다. 그리고 각 노드의 원소가 자식들의 원소보다 크다는 특성을 갖는다. 따라서 루트는 트리의 가장 큰 원소가 된다.</p>
<blockquote>
<p><strong>Max Heap 특성</strong></p>
</blockquote>
<ul>
<li>완전 이진 트리이다.</li>
<li>각 노드의 원소가 자식들의 원소보다 크다</li>
<li>루트는 노드의 가장 큰 원소가 된다.</li>
</ul>
<p>Heap은 최소 힙(Min Heap)과 최대 힙(Max Heap)으로 구분할 수 있는데, 
Max Heap은 각 노드의 원소가 자식들의 원소보다 <strong>크다는 특성</strong>을 갖고,
Min Heap은 각 노드의 원소가 자식들의 원소보다 <strong>작다는 특성</strong>을 갖는다.</p>
<h2 id="🏷️-requirement">🏷️ Requirement</h2>
<p>내가 구현하려는 Max Heap의 요구사항은 두 가지이다.</p>
<ol>
<li>Max Heap 의 특성을 을 유지하면서 노드를 삽입하는 <code>insert</code> 함수를 구현하라.</li>
<li>Max Heap 의 특성을 을 유지하면서 최대 노드를 삭제하는 <code>extract_max</code> 함수를 구현하라.</li>
</ol>
<h2 id="⛏️-implement">⛏️ Implement</h2>
<p>일반적으로 Max Heap 은 배열(Array) 자료구조를 활용하여 구현하게 되는데, 배열을 활용하는 이유는 인덱스를 사용하여 접근할 수 있기 때문이다. 아래의 그림은 부모 노드 Index와 자식노드 Index사이에 특별한 규칙 표현하고 있다. Heap 이 어떻게 배열로 대칭되는지 쉽게 확인 할 수 있다.</p>
<p>※ 구현의 편의를 위해 배열의 시작 인덱스는 1부터 시작하는 것으로 한다.</p>
<p><img src="https://velog.velcdn.com/images/_choongyul/post/8762dea7-69b5-415d-8620-a5be1e58da41/image.png" alt=""></p>
<blockquote>
<p><strong>규칙</strong></p>
</blockquote>
<ul>
<li>왼쪽 자식 노드 인덱스 = 부모 노드 인덱스 X 2</li>
<li>오른쪽 자식 노드 인덱스 = ( 부모 노드 인덱스 X 2 ) + 1</li>
<li>부모 노드 인덱스 = 자식 노드 인덱스 / 2</li>
</ul>
<p>규칙을 <code>python code</code>으로 표현하면 아래와 같다. 이 함수들을 구현에 활용한다.</p>
<pre><code class="language-python">def __get_parent_node_index(self, parent_index):
    return parent_index // 2

def __get_left_child_node_index(self, parent_index):
    return 2 * parent_index

def __get_right_child_node_index(self, parent_index):
    return (2*parent_index) + 1

def __is_leaf(self, parent_index):
    max_index = self.__get_max_index()

    return (parent_index * 2) &gt; max_index

def __get_max_index(self):
    return len(self.maxheap) - 1</code></pre>
<h3 id="📍-maxheap-class를-정의한다">📍 MaxHeap Class를 정의한다.</h3>
<p> 구현의 편의를 위해 배열의 시작 인덱스는 1부터 시작하는 것으로 했으므로
<code>MaxHeap</code> Class의 <code>__init__</code>는 0번 <code>index</code>에는 <code>None</code>을 입력하고, 1번 <code>index</code>에는 인자로 받은 <code>value</code>를 값을 저장하도록 한다.</p>
<pre><code class="language-python">class MaxHeap:
    def __init__(self, value):
        self.maxheap = list()
        self.maxheap.append(None)
        self.maxheap.append(value)</code></pre>
<h3 id="📍-maxheap-class에-insert-기능을-구현한다">📍 MaxHeap Class에 <code>insert</code> 기능을 구현한다.</h3>
<p>MaxHeap에서 <code>insert</code> 함수의 구현은 2 Step으로 나누어 생각한다. </p>
<ol>
<li>데이터를 마지막에 삽입한다.</li>
<li>부모노드와 비교하여 값이 크다면, 부모노드와 위치를 바꾼다.</li>
</ol>
<pre><code class="language-python">def insert(self, value):
    # 1. just insert the value at the end
    self.maxheap.append(value)
    # 2. if the child node value is greater than the parent node, replace iteratively

    current_index = len(self.maxheap) - 1

    while (True):
        is_swap, parent_node_index = self.__is_moveup(current_index)

        if(not is_swap):
           break;

        self.maxheap[parent_node_index], self.maxheap[current_index] = 
        self.maxheap[current_index], self.maxheap[parent_node_index]
        current_index = parent_node_index


def __is_moveup(self, current_index):
    return_value = False

    parent_node_index = self.__get_parent_node_index(current_index)

    if (parent_node_index == 0):
        return return_value, parent_node_index

    if(self.maxheap[parent_node_index] &lt; self.maxheap[current_index]):
        return_value = True
        return return_value, parent_node_index

    return return_value, parent_node_index</code></pre>
<h3 id="📍-maxheap-class에-extract_max-기능을-구현한다">📍 MaxHeap Class에 <code>extract_max</code> 기능을 구현한다.</h3>
<p>MaxHeap에서 <code>extract_max</code> 함수의 구현은 3 Step으로 나누어 생각한다.</p>
<ol>
<li>최상단 노드(Root) 를 삭제한다.</li>
<li>가장 마지막에 추가한 노드를 Root 노드에 복사한다.</li>
<li>Root 노드 값이 Child 노드 보다 작을 경우, Root 노드의 Child 노드 중 가장 큰 값을 가진 노드와 Root 노드 위치를 바꾸주는 작업을 반복한다.</li>
</ol>
<pre><code class="language-python">def __is_movedown(self, current_index):
    return_value = False

    if(self.__is_leaf(current_index)):
        return return_value, current_index
    else: 
        left_child_node_index = self.__get_left_child_node_index(current_index)
        right_child_node_index = self.__get_right_child_node_index(current_index)
        max_index = self.__get_max_index()

        # has just left node
        if (right_child_node_index &gt; max_index):
            if(self.maxheap[left_child_node_index] &gt; self.maxheap[current_index]):                    
                return_value = True
                return return_value, left_child_node_index
            else:
                return_value = False
                return return_value, current_index
        # has two children node
        else:
            greater_value_node_index = self.__get_greater_value_node_index(left_child_node_index, right_child_node_index)
            if(self.maxheap[greater_value_node_index] &gt; self.maxheap[current_index]):                    
                return_value = True
                return return_value, greater_value_node_index
            else:
                return_value = False
                return return_value, current_index   

def extract_max(self):
    current_index = 1
    max_value = self.maxheap[1]
    last_node_value = self.maxheap.pop()

    self.maxheap[1] = last_node_value

    while(True):
        is_movedown, child_node_index = self.__is_movedown(current_index)
        if(not is_movedown):
            break;

        self.maxheap[child_node_index], self.maxheap[current_index] = 
        self.maxheap[current_index], self.maxheap[child_node_index]
        current_index = child_node_index

    return max_value</code></pre>
<h3 id="📍-max-heap-class에-기능을-확인한다">📍 Max Heap Class에 기능을 확인한다.</h3>
<p>이제 구현된 Max Heap Class 의 기능을 점검한다. 
<img src="https://velog.velcdn.com/images/_choongyul/post/865955d4-5948-4ac1-a636-d695fc15b1e1/image.png" width="70%"/></p>
<blockquote>
<ul>
<li>insert 기능 확인 : 위 모양의 Max Heap를 <code>insert</code> 함수를 통해 만들고 값을 출력한다. </li>
</ul>
</blockquote>
<ul>
<li><p>extract_max 기능 확인 : <code>extract_max</code> 를 이용해 값을 삭제 했을 때 Heap 의 특성이 유지되는지 확인한다.</p>
</li>
<li><p>** insert 기능 확인**</p>
<pre><code class="language-python">maxheap = MaxHeap(15)
maxheap.insert(10)
maxheap.insert(8)
maxheap.insert(5)
maxheap.insert(4)
maxheap.insert(20)
maxheap.insert(6)
maxheap.insert(9)
maxheap.insert(3)
maxheap.insert(2)
</code></pre>
</li>
</ul>
<p>maxheap.print()</p>
<pre><code>**output**</code></pre><p>parent_value : 20 left_child_value : 10 right_child_value : 15
parent_value : 10 left_child_value : 9 right_child_value : 4
parent_value : 15 left_child_value : 8 right_child_value : 6
parent_value : 9 left_child_value : 5 right_child_value : 3
parent_value : 4 left_child_value : 2</p>
<pre><code>
* ** extract 기능 확인**
```python
extract_value = maxheap.extract_max()
print(&quot;Extract Value : &quot; + str(extract_value))
maxheap.print()</code></pre><p><strong>output</strong></p>
<pre><code>Extract Value : 20
parent_value : 15 left_child_value : 10 right_child_value : 8
parent_value : 10 left_child_value : 9 right_child_value : 4
parent_value : 8 left_child_value : 2 right_child_value : 6
parent_value : 9 left_child_value : 5 right_child_value : 3</code></pre><h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<p><a href="https://st-lab.tistory.com/205">Stranger&#39;s LAB-배열을 이용한 Heap 구현하기</a>
<a href="https://www.geeksforgeeks.org/min-heap-in-python/">geeksforgeeks-Min Heap in Python</a>
<a href="https://www.crazyforcode.com/heap-data-structure/">CrazyforCode-Heap Data Structure</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Binary Search Tree in Python]]></title>
            <link>https://velog.io/@_choongyul/binary-serch-tree</link>
            <guid>https://velog.io/@_choongyul/binary-serch-tree</guid>
            <pubDate>Tue, 21 Feb 2023 21:44:57 GMT</pubDate>
            <description><![CDATA[<h2 id="🔍-about-binary-search-tree">🔍 About Binary Search Tree</h2>
<p>이진 탐색 트리(Binary Search Tree)는 최대 두 개의 자식 노드를 가지는 트리형태의 자료구조로, 아래의 특징을 갖는다.</p>
<blockquote>
<ul>
<li>이진트리는 루트가 있다.</li>
</ul>
</blockquote>
<ul>
<li>모든 노드가 적어도 두 개의 자식 노드를 가진 정렬 트리이다.</li>
<li>루트 트리는 당연히 레벨(루트로부터의 거리)이라는 개념을 가지고 있다.</li>
<li>모든 노드에 대해서 하위 레벨로 연결되는 노드로 자식 노드의 개념을 정리할 수 있다.</li>
</ul>
<p>위 이진 트리의 특성에 아래 네가지 특성을 <strong>더</strong> 하면 이진 탐색 트리가 된다.</p>
<blockquote>
<ul>
<li>각 노드에 값이 있다.</li>
</ul>
</blockquote>
<ul>
<li>값들은 전순서가 있다.</li>
<li>노드의 왼전 서브트리에는 그 노드의 값보다 작은 값들을 지닌 노드들로 이루어져 있다.</li>
<li>노드의 오른쪽 서브트리에는 그 노드의 값보다 큰 값들을 지닌 노드들로 이루어져 있다.</li>
<li>좌우 하위 트리는 각각이 다시 이진 탐색트리여야 한다.</li>
</ul>
<h2 id="🏷️-requirement">🏷️ Requirement</h2>
<p>내가 구현하려고 하는 이진탐색트리의 요구사항은 총 4가지이다.</p>
<ul>
<li>노드를 검색하여 값이 존재하면 노드를 리턴한다.</li>
<li>노드를 검색하여 값이 존재하지 않으면 <code>None</code>을 리턴한다.</li>
<li>이진 탐색 트리를 유지하면서 노드를 <strong>삽입</strong>한다.</li>
<li>이진 탐색 트리를 유지하면서 노드를 <strong>삭제</strong>한다.</li>
</ul>
<h2 id="⛏️-implement">⛏️ Implement</h2>
<h3 id="📍-데이터를-저장하는-node-class를-정의한다">📍 데이터를 저장하는 Node Class를 정의한다.</h3>
<p>Node 클래스는 노드값(value)와 좌/우 노드(left, right) 이렇게 총 세 개의 속성을 갖는다.</p>
<pre><code class="language-python">class Node:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

    def __str__(self):
        return str(self.value)        </code></pre>
<h3 id="📍-binary-search-tree를-유지하며-node를-관리할-binarysearchtree-class-를-정의한다">📍 Binary Search Tree를 유지하며 Node를 관리할 BinarySearchTree Class 를 정의한다.</h3>
<pre><code class="language-python">class BinarySearchTree:
    pass</code></pre>
<h3 id="📍-binarysearchtree-class-에-insert-기능을-구현한다">📍 BinarySearchTree Class 에 insert 기능을 구현한다.</h3>
<p>새로운 값은 트리의 잎 노드에 연결된다. 구현 할 때 고려해야 할 사항은 새로운 값을 연결시킬 잎 노드의 부모 노드를 찾는 것이다.</p>
<ol>
<li><p><code>root</code> 가 <code>None</code> 이면 <code>root</code> 에 <code>insert_node</code>를 연결시킨다.</p>
</li>
<li><p><code>insert_node</code>보다 크면 오른쪽으로, <code>insert_node</code>보다 작으면 왼쪽으로 순회하면서 <code>leaf node</code>를 찾는다.</p>
</li>
<li><p>크면 오른쪽에, 작으면 왼쪽에 <code>insert_node</code>를 연결시킨다.</p>
<pre><code class="language-python">def insert(self, value):
 insert_node = Node(value)

 # if first
 if self.root is None:
     self.root = insert_node
     return

 current_node = self.root

 # search parent node for linking
 while current_node is not None:
     if current_node.value &lt; value:                
         parent_node = current_node
         current_node = current_node.right
     else:
         parent_node = current_node
         current_node = current_node.left

 # connect parent node with insert node
 if(parent_node.value &lt; value ):
     parent_node.right = insert_node
 else:
     parent_node.left = insert_node</code></pre>
</li>
</ol>
<h3 id="📍-binarysearchtree-class-에-inorder-traversal-기능을-구현한다">📍 BinarySearchTree Class 에 Inorder Traversal 기능을 구현한다.</h3>
<p>Inorder Traversal은 중위 순회라고 한다. 중위 순회는 다음과 방법으로 진행된다.</p>
<ol>
<li>왼쪽 서브트리를 중위 순회한다.</li>
<li>노드를 방문한다.</li>
<li>오른쪽 서브 트리를 중위 순회 한다.</li>
</ol>
<p><strong>간략히 이야기 하면 Left -&gt; Root -&gt; Right 순서로 노드를 방문한다.</strong> 트리를 중위 순회하면 노드의 값을 오름차순으로 방문하게 된다. 그래서 중위 순회는 이진 탐색 트리에서 값을 찾을 때 많이 사용된다.
<img src="https://velog.velcdn.com/images/_choongyul/post/d5c009ed-b2fb-416e-b80a-78647325dc50/image.png" width="70%"></p>
<pre><code class="language-python">def inorder(self):
    stack = []
    current_node = self.root

    while((current_node is not None) or (len(stack) != 0)):
        if(current_node is not None):
            # print(&quot;current node:&quot; + str(current_node.value))
            # print(&quot;current left:&quot; + str(current_node.left.value))
            stack.append(current_node)
            current_node = current_node.left
        else:
            parent_node = stack.pop()
            print(parent_node.value)
            current_node = parent_node.right</code></pre>
<h3 id="📍-binarysearchtree-class-에-search-기능을-구현한다">📍 BinarySearchTree Class 에 <code>search</code> 기능을 구현한다.</h3>
<p><code>BinarySearchTree</code> 에서 값을 찾아 해당 노드를 <code>Return</code>하는 함수를 구현한다. </p>
<ol>
<li><p>subtree root 값과 찾으려 하는 값을 비교한다.</p>
</li>
<li><p>값이 동일하면 종료한다.</p>
</li>
<li><p>값이 크면 오른쪽 서브트리로 이동한다.</p>
</li>
<li><p>값이 작으면 왼쪽 서브트리로 이동한다.</p>
</li>
<li><p>값을 찾을때 까지 1번과 4번을 반복한다.</p>
</li>
<li><p>값이 존재하지 않으면 <code>None</code> 을 리턴한다.</p>
<pre><code class="language-python">def search(self, search_value):
 current_node = self.root

 while current_node is not None:
     if(current_node.value == search_value):
         return current_node

     if(current_node.value &gt; search_value):
         current_node = current_node.left
     else:
         current_node = current_node.right

 return None</code></pre>
<h3 id="📍-binarysearchtree-class-에-delete-기능을-구현한다">📍 BinarySearchTree Class 에 <code>delete</code> 기능을 구현한다.</h3>
<p>원하는 노드를 삭제하는 함수를 구현한다. 삭제기능의 구현은 조금 까다로운데, 삭제 후에도<code>BinarySearchTree</code>의 특성을 유지해야 하기 때문이다. 자식 노드가 없다면 그냥 삭제하면 되지만 자식노드가 존재하는 경우는 어떻게해야 하나? 노드를 삭제하고 나면 자식 노드들과의 연결이 다 끊겨서 접근이 불가능해 질 텐데 말이다.</p>
</li>
</ol>
<p>아래의 그림에서 값 4를 가진 <code>Node</code>를 삭제하면, 저 저리에 어떤 노드를 두어야 <code>BinarySearchTree</code>의 특성 유지할 수 있을지 고민해야 한다.
<img src="https://velog.velcdn.com/images/_choongyul/post/4834514c-38a7-4492-95ab-53fc23f8b8c2/image.png" alt=""></p>
<p>전체를 한 번에 바라 보려면 어렵게 때문에 <code>BinarySearchTree</code>에서 삭제는 3가지 경우의 수로 나누어 생각한다.</p>
<p><strong>Case1. Leaf Node, 자식이 없는 노드를 삭제할 때</strong></p>
<ul>
<li>단순히 삭제 한다.</li>
</ul>
<img src="https://velog.velcdn.com/images/_choongyul/post/a4869e45-ec12-4d92-81eb-f807b9c11451/image.png" width="70%" style="diplay:block; margin: 0px auto;">

<p><strong>Case2. 자식을 하나 가지고 있는 노드를 삭제할 때</strong></p>
<ul>
<li>자식 노드를 삭제할 노드에 복사한다.</li>
<li>노드를 삭제한다.</li>
</ul>
<img src="https://velog.velcdn.com/images/_choongyul/post/ca96f33b-e578-4fdd-8789-60ff0dedc3bc/image.png" width="70%" style="diplay:block; margin: 0px auto;">

<p><strong>Case3. 자식을 두개 가지고 있는 노드를 삭제할 때</strong></p>
<ul>
<li>후임자(삭제할 노드의 오른쪽 자식 중 가장 작은 값)를 가진 노드를 찾는다.</li>
<li>후임자를 삭제할 노드에 복사한다.</li>
<li>노드를 삭제한다.
<img src="https://velog.velcdn.com/images/_choongyul/post/c5cdc474-8e84-48d8-a738-a9403160bb4a/image.png" alt=""></li>
</ul>
<p>이를 Loop와 Reclusive(재귀함수)를 사용하여 각각 구현하면 아래와 같다.</p>
<p><strong>Loop를 이용한 구현</strong></p>
<pre><code class="language-python">def delete_using_loop(self, delete_value):
    # step1. search node
    current_node = self.root
    parent_node = self.root
    search = False

    while current_node is not None:
        if current_node.value == delete_value:
            search = True
            break

        parent_node = current_node
        if current_node.value &gt; delete_value:
            current_node = parent_node.left
        else:
            current_node = parent_node.right

    if current_node is None:
        print(&quot;can not find the node you want to delete&quot;)
        return search

    # step2. delete
    # case 1. leaf node
    if current_node.left is None and current_node.right is None:
        del(current_node)
        if parent_node.value &gt; delete_value:
            # print(&quot;greater than :&quot; + str(parent_node.value))
            parent_node.left = None
        else:
            # print(&quot;less than: &quot; + str(parent_node.value))
            parent_node.right = None
        # End case 1.
        return 

    # case 2. only one child
    if current_node.left is None or current_node.right is None:
        if parent_node.value &gt; delete_value:
            if current_node.left is not None:
                parent_node.left = current_node.left
            else:
                parent_node.left = current_node.right
        else:
            if current_node.left is not None:
                parent_node.right = current_node.left
            else:
                parent_node.right = current_node.right
        # End case 2.
        return

    # case 3. two children
    if current_node.left is not None and current_node.right is not None:
        minimum_parent_node = current_node.right
        minimum_node = minimum_parent_node

        while minimum_node.left is not None:
            temp = minimum_node
            minimum_node = minimum_parent_node.left
            minimum_parent_node = temp
        # print(&#39;minimum node value : &#39; + str(minimum_node.value))

        if parent_node.value &gt; delete_value:
            parent_node.left = minimum_node
        else:
            parent_node.right = minimum_node

        if minimum_node.left is None and minimum_node.right is None:
            minimum_parent_node.left = None
            minimum_node.left = current_node.left
            minimum_node.right = current_node.right
        elif minimum_node.left is None and minimum_node.right is not None:
            minimum_node.left = current_node.left

        del(current_node)
        # End case 3.
        return</code></pre>
<p><strong>재귀를 이용한 구현</strong></p>
<pre><code class="language-python">def search_min_node(self, root):
    if root.left is None:
        return root
    else:
        minimum = self.search_min_node(root.left)
    return minimum

def delete_using_reclusive(self, delete_value):
    self._delete_value(self.root, delete_value)

def _delete_value(self, root, delete_value):
    if root is None:
        return

    if root.value &gt; delete_value:
        root.left = self._delete_value(root.left, delete_value)
    elif root.value &lt; delete_value:
        root.rigth = self._delete_value(root.right, delete_value)
    else:
        # case 1. leaf node or only one child
        if root.left is None:
            temp = root.right
            root = None
            return temp
        elif root.right is None:
            temp = root.left
            root = None
            return temp
        else:
            # case 2. it has two children.
            # search minimum node
            minimum = self.search_min_node(root.right)
            # copy minimum node
            root.value = minimum.value
            # delete minimum node
            root.right = self._delete_value(root.right, minimum.value)
    return root</code></pre>
<h3 id="📍-binarysearchtree의-기능을-확인한다">📍 <code>BinarySearchTree</code>의 기능을 확인한다.</h3>
<p>아래 모양의 BinarySearchTree를 <code>insert</code> 함수를 통해 만들고, <code>search</code> 함수를 통해 값을 검색해 보고, 두 개의 <code>delete</code> 함수를 사용하여 노드를 삭제해 보면서 기능을 검증한다.
<img src="https://velog.velcdn.com/images/_choongyul/post/d5c009ed-b2fb-416e-b80a-78647325dc50/image.png" width="70%"></p>
<ul>
<li><strong><code>insert</code> 기능 확인</strong><pre><code class="language-python">bst = BinarySearchTree()
bst.insert(6)
bst.insert(2)
bst.insert(1)
bst.insert(4)
bst.insert(3)
bst.insert(5)
bst.insert(8)
bst.insert(7)
bst.insert(10)
bst.insert(9)
</code></pre>
</li>
</ul>
<p>bst.inorder()</p>
<pre><code>#### output</code></pre><p>1
2 
3 
4 
5 
6 
7 
8 
9 
10</p>
<pre><code>* **`search` 기능 확인**
```python
searched_node = bst.search(7)
if searched_node is not None:
    print(&quot;search value: &quot; + str(searched_node.value))
else:
    print(&quot;There is no data.&quot;)</code></pre><h4 id="output">output</h4>
<pre><code>search value: 7</code></pre><ul>
<li><strong><code>delete</code> 기능 확인</strong><pre><code class="language-python"># case3: delete the node has 2 children
bst.delete_using_loop(2)
# case2: delete the node has only child
bst.delete_using_reclusive(9)
# case1: delete leaf node
bst.delete_using_loop(1)
</code></pre>
</li>
</ul>
<p>bst.inorder()</p>
<pre><code>#### output</code></pre><p>3
4
5
6
7
8
10</p>
<p>```</p>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/%EC%9D%B4%EC%A7%84_%ED%8A%B8%EB%A6%AC">wikipedia-이진 트리</a></li>
<li><a href="https://ratsgo.github.io/data%20structure&amp;algorithm/2017/10/22/bst/">ratsgo&#39;s-이진 탐색 트리</a></li>
<li><a href="https://gnujoow.github.io/ds/2016/09/01/DS4-TreeTraversal/">Binary Tree Traveral에 대해 알아보자</a></li>
<li><a href="https://ko.wikipedia.org/wiki/%ED%8A%B8%EB%A6%AC_%EC%88%9C%ED%9A%8C">wikipedia-트리 순회</a></li>
<li><a href="https://www.geeksforgeeks.org/binary-search-tree-set-1-search-and-insertion/">geeksforgeeks-Binary Search Tree | Set 1 (Search and Insertion)</a></li>
<li><a href="https://www.geeksforgeeks.org/deletion-in-binary-search-tree/?ref=lbp">geeksforgeeks-Deletion in Binary Search Tree</a></li>
<li><a href="http://ejklike.github.io/2018/01/09/traversing-a-binary-tree-1.html">파이썬을 사용한 이진 트리와 순회 알고리즘 구현 (1)</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hard Link 는 어떻게 동작하는 것일까요?]]></title>
            <link>https://velog.io/@_choongyul/Hard-Link-%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%BC%EA%B9%8C%EC%9A%94</link>
            <guid>https://velog.io/@_choongyul/Hard-Link-%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%BC%EA%B9%8C%EC%9A%94</guid>
            <pubDate>Tue, 14 Feb 2023 22:24:52 GMT</pubDate>
            <description><![CDATA[<h2 id="🥗-prologue">🥗 Prologue</h2>
<p>나는 데이터 전달을 보장하는 솔루션을 운영한다. 파일을 주고받는 단순한 요건을 처리하는데 에러가 발생했다. Target Directory에 파일이 저장되지 않는다.</p>
<p><strong>&quot;어? 왜이러지?&quot;</strong></p>
<h2 id="🎪-a-namediagramsystem-configuration-diagrama">🎪 <a name="diagram">System Configuration Diagram</a></h2>
<p>먼저, 문제를 분석하기 위해 환경 구성을 확인한다. <strong>정상적인 환경과 비정상적인 환경을 비교하여 차이점을 찾는다.</strong> 대부분 답은 <strong>다름</strong>에 있다.</p>
<p>환경 구성은 단순하다. Middleware를 통해 Endpoint는 File을 전달 받는다. <strong>특이한 점이 있다면 2개의 다른 파일 시스템을 쓴다는 것!</strong>
<img src="https://velog.velcdn.com/images/_choongyul/post/e5ba6489-b7df-4cd3-8ff4-e143bafecdf9/image.png" width="80%"></p>
<blockquote>
<ol>
<li>Middleware로 부터 파일 조각을 전달 받는다.</li>
<li>파일 조각을 <code>/home/usr/temp</code> 경로에 순차적으로 <code>write</code>한다.</li>
<li><code>/home/usr/temp</code> 에 파일이 전체가 <code>write</code> 되면 <code>/app/dist</code> 로 파일을 옮긴다.</li>
</ol>
</blockquote>
<p><em>※ <code>/home/usr/temp</code> 는 <code>/app/dist</code> 서로 다른 파일 시스템이다.</em></p>
<h2 id="🔑-problem">🔑 Problem</h2>
<p>로그를 보니 아래의 코드에서 문제가 발생했다. 임시 경로인 <code>/home/usr/temp</code> 에는 정상적으로 파일이 존하는데, Target Dirctory인 <code>/app/dist</code>는 파일이 존재하지 않는다. <code>rename</code> 함수를 통해 로 옮기는 부분에서 <code>0</code>이 아니라 <code>-1</code>이 리턴하면서, 파일이 정상적으로 수신되지 않은 것 처럼 보이는 것이다.</p>
<pre><code class="language-c">if(0 != rename(frompath, targetpath)) {
    sprintf(&quot;Error write target file&quot;)
}</code></pre>
<h2 id="🔪-analysis">🔪 Analysis</h2>
<p><code>rename</code> 함수는 <code>&lt;stdlib.h&gt;</code> C언어의 표준 라이브러리에서 제공하는 함수인데 왜 안되지? <code>rename</code> 함수에는 문제가 있을리 없다. 단지, 내가 모르는 부분이 있을 뿐! </p>
<h3 id="🔍-how-to-implement-rename-function-in-c-language">🔍 How to implement rename function in C language?</h3>
<p><code>rename</code> 함수의 실제 구현을 살펴보면 로직은 매우 단순한데 아래 두개의 Step 이 전부다.</p>
<blockquote>
<ul>
<li><code>__link</code> 함수를 통해 <code>old</code> 파일에 <code>new</code>라는 hard link 를 생성한다.</li>
</ul>
</blockquote>
<ul>
<li><code>__unlink</code> 함수를 통해 기존 <code>hard link</code> <code>old</code> 를 제거한다.</li>
</ul>
<p><code>rename</code> 함수에서 <code>hard link</code>가 주요한 역할을 함으로 주의 깊게 살펴보아야 할 것 같다.</p>
<pre><code class="language-c">int
rename (const char *old, const char *new)
{
  int save = errno;
  if (__link (old, new) &lt; 0)
    {
      if (errno == EEXIST)
    {
      __set_errno (save);
      /* Race condition, required for 1003.1 conformance.  */
      if (__unlink (new) &lt; 0
          || __link (old, new) &lt; 0)
        return -1;
    }
      else
    return -1;
    }
  if (__unlink (old) &lt; 0)
    {
      save = errno;
      if (__unlink (new) == 0)
    __set_errno (save);
      return -1;
    }
  return 0;
}</code></pre>
<h3 id="🔍-managing-hard-link">🔍 Managing Hard Link</h3>
<p>컴퓨팅 시스템에는 파일을 위한 2개의 링크가 존재한다. 하나는 <code>Hard Link</code> 이고 또 다른 하나는 <code>Soft Link</code> 이다. 나는 이 두개를 이렇게 이해하는데,</p>
<blockquote>
<ul>
<li><code>Hard Link</code>는 파일 이름으로 실제 데이터에 접근할 수 있도록 하는 것</li>
</ul>
</blockquote>
<ul>
<li><code>Soft Link</code>는 바로가기. 원본 파일의 이름 시스템(<code>Hard Link</code>?)을 가르키도록 하는 것<h6 id="사실-hard-link는-inode와-관련되어-있고-inode-대한-건-여기서-확인-할-수-있다"><em>사실 <code>Hard Link</code>는 <code>Inode</code>와 관련되어 있고, <code>Inode</code> 대한 건 <a href="https://ko.wikipedia.org/wiki/%EC%95%84%EC%9D%B4%EB%85%B8%EB%93%9C">여기</a>서 확인 할 수 있다.</em></h6>
</li>
</ul>
<p>&#39;<code>Hard Link</code>는 파일 이름으로 실제 데이터에 접근할 수 있도록 한다.&#39;라고 정의하면 왜 모든 파일이 적어도 하나의 <code>Hard Link</code> 가 존재해야 하는지 이해 수 있다. <strong>바로, 파일 이름으로 실제 데이터(physical data)에 접근해야 하기 때문이다.</strong> </p>
<p>아래의 그림은 <code>Hard Link</code>의 동작 방법을 표현한 그림이다. 디렉터리 dirA, dirB, dirC의 파일 이름 name1, name2, name3은 <code>Inode 12345</code>를 가르키고 있다. <code>Inode 12345</code>에는 파일에 대한 내용과 그리고 <code>Hard Link</code>의 갯수에 대한 정보를 가지고 있다. 
<img src="https://velog.velcdn.com/images/_choongyul/post/cb358159-7714-43a0-a21a-8e7cd4fb5bc7/image.png" width="70%"></p>
<p>여기서 dirD에 파일 이름 name4로 <code>Hard Link</code>를 추가하면, 추가된 <code>Hard Link</code>는 <code>Inode 12345</code>를 가르키고 <code>Hard Link</code> 수를 4로 변경한다. <code>Inode</code>를 새로 생성하지 않는다.
<img src="https://velog.velcdn.com/images/_choongyul/post/fa6caecd-24d0-40fd-a8fc-f45ff80dcc5a/image.png" width="70%"></p>
<p>반면에, <code>Soft Link</code> 를 생성하면 <code>Inode</code>가 생성된다. 그리고 <code>Inode</code>에 실제 파일 경로를 저장한다. 아래 그림은 <code>Soft Link</code>의 동작 방법을 보여준다. dirA의 파일 이름 name1은 <code>Inode 12345</code>를 가르키고 있다. 여기에 dirB의 name2 라는 <code>Soft Link</code>를 생성하면 새로운 <code>Inode 13579</code>가 생성된다. <code>Inode 13579</code>는 실제 파일(physical data)의 /dirA/name 의 경로를 가르킨다.
<img src="https://velog.velcdn.com/images/_choongyul/post/f18a3936-cc77-4054-858b-1dcb2eb4b22c/image.png" width="80%"></p>
<p>dirA에 있는 name1 파일을 삭제하면, dirB에 있는 <code>Soft Link</code> 파일 name2는 존재하지 않는 파일 경로를 가리키 때문에 액세스할 때 오류가 발생한다.</p>
<h3 id="🔍-a-namelimithard-link-of-limitationa">🔍 <a name="limit">Hard Link of Limitation</a></h3>
<p><code>Hard Link</code>에는 제약 사항이 존재한다.</p>
<ul>
<li><code>Hard Link</code>는 디렉토리에 생성할 수 없다.</li>
<li><strong><code>Hard Link</code>는 같은 파일 시스템에서 사용가능하다.</strong> 그 대신 <code>Soft Link</code>를 사용해야 한다. </li>
<li>하나의 파일에 대한 <code>Hard Link</code>의 최대 개수는 2<sup>32</sup> 이다.</li>
</ul>
<h2 id="🌠-how-to-solve-the-problem">🌠 How to Solve the Problem</h2>
<p><code>Hard Link</code>에 대해 공부하다 보니 문제 발생 원인이 명확하게 보인다.</p>
<p><a href="#limit">Hard Link 제약사항</a>과 <a href="#diagram">System Configuration Diagram</a>을 보면 내가 운영하는 솔루션이 <code>Hard Link</code>의 제약사항을 위반 하고 있음을 알 수 있다.</p>
<blockquote>
<p><code>Hard Link</code>는 같은 파일 시스템에서 사용가능하다.</p>
</blockquote>
<p><code>/home/usr/temp</code> 과 <code>/app/dist</code> 은 서로 다른 파일 시스템이기 때문에 문제가 발생하는 것이다. </p>
<p>문제의 해결법은 두가지 이다.</p>
<blockquote>
<p>첫째, <code>rename</code> 함수를 사용하지 않는 것
둘째, Target Directory 와 Temp Directory를 같은 파일 시스템으로 변경하는 것</p>
</blockquote>
<p>팀원들과 협의 끝에 나는 두 번째 방법을 선택해 수정했다.</p>
<h2 id="🍰-epilogue">🍰 Epilogue</h2>
<p>일을 하면 할 수록 운영체제와 컴퓨터 구조론과 같은 Computer Sicence이 지식이 필요함을 느낀다. 다 필요한 것들인데 대학에서 공부 할 때는 왜 이리 쓸모 없게 느껴졌을까?</p>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://github.com/gcc-mirror/gcc/blob/master/libiberty/rename.c">rename-gcc</a></li>
<li><a href="https://codebrowser.dev/glibc/glibc/sysdeps/posix/rename.c.html">rename-codebrowser</a></li>
<li><a href="https://velog.io/@redgem92/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%8C%8C%EC%9D%BC-%EC%8B%9C%EC%8A%A4%ED%85%9C-inode-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC">[운영체제] 파일 시스템, inode 방식에 대하여</a></li>
<li><a href="https://www.geeksforgeeks.org/c-program-to-create-hard-link-and-soft-link/">C program to create hard link and soft link</a></li>
<li><a href="https://ko.wikipedia.org/wiki/%EC%95%84%EC%9D%B4%EB%85%B8%EB%93%9C">아이노드-Wikipedia</a></li>
<li><a href="https://docs.datafabric.hpe.com/70/AdministratorGuide/Hardlinks.html">Managing Hard Links-HP</a></li>
<li><a href="https://en.wikipedia.org/wiki/Hard_link">Hard link-wikipedia</a></li>
<li><a href="https://ehclub.co.kr/1320">파일 테이블과 파일 디스크립터</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JNDI: JBoss NS는 어떻게 동작 할 까요?]]></title>
            <link>https://velog.io/@_choongyul/Deep-Dive-into-JNDI</link>
            <guid>https://velog.io/@_choongyul/Deep-Dive-into-JNDI</guid>
            <pubDate>Sun, 29 Jan 2023 21:06:12 GMT</pubDate>
            <description><![CDATA[<h2 id="🥗-prologue">🥗 Prologue</h2>
<p>시스템을 재시작하고 <code>클라이언트</code> 연결이 정상적으로 되지 않는 문제가 발생했다. 관련자들이 모여 재현 테스트를 진행하는데 시작부터 기분이 좋지 않다. 문제를 해결하려고 하는 사람이 보이지 않는다. 모두 자기 문제가 아니라고 한다. 그중 한 명은 테스트가 종료가 되지도 않았는데 로그하나 던져주고는 이렇게 말한다.</p>
<p><strong>&quot;이제 어떻게 정리하실 거에요?&quot;</strong></p>
<p>근데 다들 저 로그가 뭘 뜻하는지 알고나 저렇게 말하는 건지! 애들 대하 듯 하나하나 로그를 읽어 주었다. 그랬더니 이렇게 말한다.</p>
<p>&quot;여기는 그런거 볼 사람 없어요!&quot;
**
아 진짜!!
모르는 건 자랑이 아닌데!
계속 모른다고!
몰라서 못 한다고만 한다. 👊 **</p>
<h2 id="🎪-a-namehead1system-configuration-diagrama">🎪 <a name="head1">System Configuration Diagram</a></h2>
<p>문제를 해결하기 위해서는 주변을 먼저 파악하는 것이 중요하다. 먼저, 시스템이 어떤구성으로 이루어져 있는지 확인한다. 아래는 내가 운영하는 시스템을 도식화한 그림이다.
<img src="https://velog.velcdn.com/images/_choongyul/post/ed7d1966-0a7a-4c5e-bcbc-39d3fd9ff959/image.png" width="70%"></p>
<p>간단하게 설명하면,</p>
<ul>
<li>고객은 JEUS Application Server(AS)에서</li>
<li>내가 배포한 my-api.jar 를 이용하여</li>
<li>내가 운영하는 JBoss AS와 연결한다.</li>
<li>이 기종 WAS 를 연결하는 기술로는 JNDI 와 RMI 가 사용된다.</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<p><a href="#head1">위 구성</a>에서 JBoss AS를 재시작 하니 JEUS AS 와 연결을 맺지 못 한다. 문제를 일으키는 부분은 <code>InitialContext</code> 를 생성 하는 부분으로 아래와 같다. <code>org.jnp.interfaces.NamingContextFactory</code> 의 진입점을 JEUS에서 못 찾는 듯하다.</p>
<p><a name="code1">Problem Code</a></p>
<pre><code class="language-java">Properties properties = new Properties();
properties.put(Context.SECURITY_PRINCIPAL, &quot;guest&quot;);
properties.put(Context.SECURITY_CREDENTIALS, &quot;guest&quot;);
properties.put(Context.PROVIDER_URL, &quot;IP:PORT&quot;);
properties.put(Context.INITIAL_CONTEXT_FACTORY, 
&quot;org.jnp.interfaces.NamingContextFactory&quot;);

// the error occur on this line!
Context context = new InitialContext(properties);</code></pre>
<p><a name="log1">Error Log</a>
<img src="https://velog.velcdn.com/images/_choongyul/post/7bba0bff-4904-43ba-bf64-92361fd312b0/image.png" alt="Error_Log"></p>
<h2 id="🔪-analysis">🔪 Analysis</h2>
<p><a href="#log1">로그</a> 의 <code>javax.naming.InitialContext.init</code> 부분을 통해 에러는 JNDI 관련 부분에서 발생하는 것을 확인 할 수 있다. 그럼 <code>JNDI</code> 를 시작점으로 차근차근 분석 해볼까!</p>
<h3 id="🔍-what-is-jndi">🔍 What is JNDI?</h3>
<p><code>JNDI</code> 는 <code>J2EE</code> 서비스 중 하나로 The Java Naming and Directory Interface 의 줄임말이다. 왠지 영어가 기니까 어렵게 느껴지는데. 단지 용어가 낯선 것일 뿐이지 쉽다. <code>Naming Service</code> 와 <code>Directory Service</code> 만 이해하면 된다. 글을 읽는데 필요한 정도로만 이해해 보자.</p>
<blockquote>
</blockquote>
<p>🚩 Naming Service
이름을 이용해 컴퓨터 자원을 접근하도록 도와 주는 서비스</p>
<blockquote>
</blockquote>
<p>🚩 Directory Service
디렉터리 안에 속성(attribute) 대해서 생성, 추가, 조회, 수정, 조회 할 수 있도록 인터페이스를 제공하는 것</p>
<p>낯선 이름 이지만 우리는 해당 서비스를 이미 사용해 왔다. 단지 너무 자연스러워 사용하는 것 조차 몰랐을 뿐!</p>
<p>우리가 <code>run.sh</code> 라는 shell script 를 사용하여 프로그램을 실행 시킬 수 있는 건, <code>File System</code> 이 <code>run.sh</code> 에 해당하는 reference 를 이미 연결시켰기 때문에 가능한 일이다. <code>File System</code> 은 우리가 가장 많이 사용하는 <code>Naming Service</code> 로 생각할 수 있다.</p>
<pre><code class="language-console">run.sh</code></pre>
<p>디렉터리 안에 어떤 자원을 찾기 위해 때로는 <code>find</code> 명령을 사용하는 데, 이는 <code>File System</code>이 해당 디렉터리안에 속성을 모두 관리하기 때문에 사용 가능 하다. 그럼 <code>File System</code> 은 <code>Directory Service</code> 로도 생각할 수 있다.</p>
<pre><code class="language-console"># 현재 위치에서 log가 들어가는 파일 모두 찾기
find . -name &quot;*log*&quot;</code></pre>
<p><code>JNDI</code>는 앞에 <code>Java</code>가 붙었으니까 이렇게 이해하면 된다.</p>
<blockquote>
</blockquote>
<p><strong>🚩 JNDI 는,</strong>
<strong>Java Language에서 객체를 이름으로 접근할 수 있도록 도와주는 인터페이스이다.</strong></p>
<h3 id="🔍-jndi-architecture">🔍 JNDI Architecture</h3>
<p>앞서 <code>JNDI</code> 가 무엇을 하는 것인지 알았음으로 JNDI를 좀더 들여다 보자. 그림에서 보는 것처럼 <code>JNDI</code>는 <code>API</code> 와 <code>SPI</code> 로 구성된다. 
<img src="https://velog.velcdn.com/images/_choongyul/post/73d548a1-f54f-4c3c-9a9b-6ec7265c9943/image.png" width="70%"></p>
<blockquote>
</blockquote>
<p>🚩 Application Programming Iterface(API) 는 개발자가 사용한다. 
🚩 Service Provider Interface(SPI) 는 Vendor 가 구현하여 제공한다.</p>
<p>이렇게 두개의 Layer를 두는 이유는 Vendor 의 종속성을 제거하기 위해서 인데, 이를 통해 Application의 변경을 최소화 할 수 있다.</p>
<p>이상적으로, <em>(아주 이상적으로)</em>
Application이 운영되는 환경이 <code>Jboss</code> 에서 <code>WebSphere</code> 로 변경된다 하여도 우리는 Application 을 변경할 필요가 없어진다. 단지 참조하는 <code>jar</code> 를 <code>WebSphere</code> 에서 제공하는 <code>jar</code> 로 교체하기만 하면(SPI Layer만 교체하면) 될 뿐이다. 그럼 Application은 <code>WebSphere</code> 환경에서 정상적으로 동작한다.</p>
<p>아래 그림은 JNDI 구조를 내가 운영하는 환경에 맞게 투형시킨 것이다.
<img src="https://velog.velcdn.com/images/_choongyul/post/57838dbe-a356-4299-a5a8-7b8d351533b5/image.png" width="90%"></p>
<p>JESU에서 JBoss JNDI Service에 접속하기 위해 아래의 절차로 동작하는데, <strong>로그를 통해 유추해 보면 문제는 SPI 구현체를 찾는 부분</strong> 2번에서 발생하는 듯 보인다.</p>
<ol>
<li>Application은 <code>JNDI Format</code>를 사용하여 연결을 요청한다.</li>
<li><code>JNDI API</code> 는 <code>Namging Manager</code>를 이용하여 <code>SPI</code> 구현체를 찾는다.</li>
<li>그리고 <code>SPI</code> 구현체를 초기화 한다.</li>
<li><code>JNDI SPI</code>는 요청을 <code>SPI</code> 구현체, 즉 Jboss Naming Service에 보낸다.</li>
<li>Jboss Naming Service 는 요청을 처리하여 응답을 반환한다.</li>
<li><code>JNDI SPI</code>는 응답을 <code>JNDI Format</code> 으로 변경한다.</li>
<li><code>Namging Manager</code>는 구현체 이름을 등록한다.</li>
<li><code>JNDI API</code> 는 응답을 Application 에 보낸다.</li>
</ol>
<p><em>Application 은 내가 배포한 my-api.jar 를 사용하여 JNDI 연결을 시도한다.</em></p>
<h3 id="🔍-using-jndi-api">🔍 Using JNDI API</h3>
<p><code>Java</code> 에서는 실제로 Namaing Service에 접근하기 위한 클래스를 제공하는 데 그중 가장 중요한 클래스는 <code>Context</code>와 <code>InitialContext</code>이다.</p>
<blockquote>
</blockquote>
<p><strong>🚩 javax.naming.Context</strong></p>
<ul>
<li>JNDI 명세에 기반한 Interface 이다.</li>
<li>Object 를 생성, 추가, 조회, 수정, 조회 가능한 Method를 제공한다.</li>
<li>이 인터페이스는 Vendor에 의해서 구현되어야 한다.</li>
</ul>
<blockquote>
</blockquote>
<p><strong>🚩 javax.naming.InitialContext</strong></p>
<ul>
<li><code>javax.naming.Context</code> 의 구현체이다.</li>
<li>객체 이름을 확인하기 위한 시작점을 제공한다.</li>
<li>Naming Service의 환경 정보는 <code>InitialContext</code> 생성자 파라미터로 전달된다.</li>
<li><code>InitialContext</code>를 생성할 때 <code>Context</code>를 바로 생성 할지 아니면 사용 전까지 대기 할지는 Vendor 의 구현을 따른다. <span style="color:#019267;font-size:0.7em;font-style:italic">
  "젠장, 문제의 원인을 밝히기 위해서는 JBoss의 Naming Service가 어떻게 동작하지는도 확인해 봐야 할 것 같다. 💧"
</span>

</li>
</ul>
<p><code>javax.naming.InitialContext</code> 생성을 위서는 Servier Provider 정보가 필요한다. 속성값으로 전달한다.</p>
<p><strong>첫째, INITIAL_CONTEXT_FACTORY 설정</strong></p>
<ul>
<li>KEY: <code>Context.INITIAL_CONTEXT_FACTORY</code></li>
<li>VALUE: <code>org.jnp.interfaces.NamingContextFactory</code></li>
</ul>
<p><code>org.jnp.interfaces.NamingContextFactory</code>는 <code>javax.naming.spi.InitialContextFactory</code>의 구현체로 JBoss Naming Service를 사용할 수 있게 해준다.</p>
<p><strong>둘째, PROVIDER_URL 설정</strong></p>
<ul>
<li>KEY: <code>Context.PROVIDER_URL</code></li>
<li>VALUE: <code>IP:PORT</code></li>
</ul>
<p><code>IP:PORT</code>는 해당 IP와 PORT로 Naming Service 제공하는 서버를 가르킨다.</p>
<p>전체 코드는 <strong>🔑 Problem 파트</strong>에서 보여준 <a href="#code1">Problem Code</a> 와 동일하다. <code>jboss-as-all-client.jar</code>가 ClassPath에 있다면 해당 코드는 문제 없이 컴파일 되는 것을 확인 할 수 있다.
<a name="code2">JNDI Connect Code</a></p>
<pre><code class="language-java">public class App {
    public static void main(String[] args) {
        try {
            Properties properties = new Properties();
            properties.put(Context.SECURITY_PRINCIPAL, &quot;guest&quot;);
            properties.put(Context.SECURITY_CREDENTIALS, &quot;guest&quot;);
            properties.put(Context.PROVIDER_URL, &quot;IP:PORT&quot;);
            properties.put(Context.INITIAL_CONTEXT_FACTORY, 
            &quot;org.jnp.interfaces.NamingContextFactory&quot;);

            // the error occur on this line!
            Context context = new InitialContext(properties);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }
}</code></pre>
<p>그럼 동일한 코드인데 왜 에러가 발생하고 Reconnect이 되지 않았을까? 우리는 그 단서를 로그에서 찾을 수 있다.</p>
<pre><code>at jeus.service.archive.ArchiveArrayClassLoader.getResources(ArchiveArrayClassLoader.java:256)
at com.sun.naming.internal.ResourceManager.getApplicationResources(ResourceManager.java:571)
at com.sun.naming.internal.ResourceManager.getInitialEnvironment(ResourceManager.java:256)
at javax.naming.InitialContext.init(InitialContext.java:251)
at javax.naming.InitialContext.&lt;init&gt;(InitialContext.java:227)</code></pre><p><code>InitialContext</code> 에서 <code>Context</code>를 생성하기 위해서 뭔가를 찾는거 같은데 뭘 찾는 걸까? 
<strong>🚩 javax.naming.InitialContext</strong>에서 이야기했지만 <code>Context</code>를 생성하는 건 Vendor 의존적이다. Reconnect 할때 <code>InitialContext</code> 에서 발생하는 에러를 이해하기 위해서는 JBoss Naming Service가 동작하는지 먼저 이해해야 한다.</p>
<h3 id="🔍-understanding-the-jbossns">🔍 Understanding the JBossNS</h3>
<p>JBossNS 는 Java socket과 RMI 기반의 <code>javax.naming.Context</code> 인터페이스의 구현체이다. Client와 Server 구조로 되어있어 서로 다른 JVM에서 서비스를 연결하는 것에 최적화 되어 있다. 같은 JVM 을 사용할 경우 socket이 사용하지 않으며, 그대신 <code>global singleton</code> 처럼 객체에 접근을 가능하도록 설계되어 있다.</p>
<p>아래 JBossNS 를 구성하는 주요 클래스 구성도만 보아도 앞서 이야기한 <a href="#code2">JNDI Connect Code</a> 가 어떻게 동작할지 대략적으로 짐작할 수 있을 것 같다.
<img src="https://velog.velcdn.com/images/_choongyul/post/da2ed432-06fd-44f2-982d-f4d8da8df101/image.png" width="90%"></p>
<p>하지만 왜 에러가 발생하는지 원인을 알기 위해서는 <code>javax.naming.InitialContext</code>가 어떻게 <code>javax.naming.Context</code>를 생성하는지 상세히 알아야 한다.</p>
<p>Client에서 JBossNS 속성과 함께 <code>InitialContext</code> 를 생성할 때, <code>Context</code> 를 생성하기 위해서 <code>org.jnp.interfaces.NamingContextFactory</code> 가 사용된다. <code>NamingContextFactory</code> 는 <code>javax.naming.spi.InitialContextFactory</code> 인터페이스의 JBossNS 구현체 이다. <code>NamingContextFactory</code> 가 <code>Context</code> 를 생성하라고 요청받으면, <code>InitialContext</code> 의 생성자 파라미터의 환경 정보와 전역 JNDI Namespace를 이용하여 <code>org.jnp.interfaces.NamingContext</code>를 생성한다.</p>
<blockquote>
<p>JNDI determines each property&#39;s value by merging the values from the following two sources, in order:</p>
<ol>
<li>The first occurrence of the property from the constructor&#39;s environment parameter and (for appropriate properties) the applet parameters and system properties.</li>
<li>The application resource files (jndi.properties).
출처 : <a href="https://docs.oracle.com/javase/8/docs/api/javax/naming/InitialContext.html#constructor_detail">javadoc-InitialContext</a></li>
</ol>
</blockquote>
<p><code>NamingContext</code>는 실제 <code>Context</code>의 구현체이고, JBossNS 서버에 연결하는 작업을 수행한다. <code>Context.PROVIDER_URL</code>는 <code>NamingServer</code> 의 RMI Reference 를 가르키고 있다.</p>
<p><code>NamingContext</code> 인스턴스와 <code>NamingServer</code> 의 연결은 <code>Context</code>의 첫 번째 동작에서 이루어진다. <strong>즉, <code>InitialContext</code>를 생성할 때 <code>Context</code>를 생성하지 않는다 뜻이다. Lazy Fashion하게 동작한다.</strong> <code>Context</code> 에서 어떤 작업을 수행할 때, <code>NamingContext</code>는 <code>NamingServer</code>와 연결을 확인한다. 연결이 없다면 <code>NamingContext</code>는 <code>Context.PROVIDER_URL</code> 값을 확인한다. <code>PROVIDER_URL</code> 값이 존재 한다면, <code>NamingContext</code> 는 <code>Naming</code> 인스턴스를 찾기위해 <code>NamingContext class static map</code>에서 host와 port 값으로 이루어진 Key를 찾는다. <span style="color:#019267;font-weight:800;"><strong>Key(Host와 Port 의 쌍으로 이루어진 값)가 존재하면 <code>Naming</code> 인스턴스가 이미 생성되어 JVM상에 존재하고 있으므로 이미 생성된 <code>Naming</code> 인스턴스를 사용한다.</strong></span> Key가 없으면, <code>NamingContext</code>는 <code>java.net.Socket</code>을 사용하여 <code>Naming RMI stub</code>을 JBossNS Server로 부터 얻어온다. <code>NamingContext</code>는 새로이 얻어온 <code>Naming</code> 객체를 <code>NamingContext</code> server map 저장한다. </p>
<blockquote>
<p>요약하면,</p>
<ol>
<li><code>org.jnp.interfaces.NamingContextFactory</code> 는 <code>org.jnp.interfaces.NamingContext</code> 생성</li>
<li><code>NamingContext</code> 는 이미 JBossNS Server연결 하여 <code>Naming</code>(JBossNS에서의 <code>Context</code>) 객체가 JVM에 있는지 확인 </li>
<li>없고 처음이면 JBossNS Server 부터 <code>Naming</code>를 받아 옴</li>
<li>받아온 <code>Naming</code> 객체를 Chache에 저장</li>
</ol>
</blockquote>
<p><code>Context.PROVIDER_URL</code> 값이 존재 하지 않는 경우는 단순하게 <code>Main</code> MBean 에서 값을 가져온다.</p>
<p><code>Context</code> 의 구현체인 <code>NamingContext</code>는 모든 동작을 <code>Naming</code> 인스턴스에 위임한다. 대신, <code>NamingContext</code> 는 전달받은 JNDI Name을 JBossNS server 가 인식할 수 있는 이름으로 변경한다. 왜냐면 JNDI Name 은 매우 상대적이거나 URL 일수 있기 때문이다. 이런 기능을 제공함으로써 <code>NamingContext</code>는 <code>Naming</code> 의 Context 역할을 수행한다.</p>
<p>정상적인 상황에서 Heap Dump 와 Thread Dump를 확인해 보면 필요한 클래스가 모두 있다는 걸 확인 할 수 있다.</p>
<pre><code>3CLTEXTCLASS               org/jnp/interfaces/NamingContextFactory(0x0000000031587200)

3CLTEXTCLASS               org/jnp/interfaces/NamingContext(0x0000000031589D00)

3CLTEXTCLASS               org/jnp/server/NamingServer_Stub(0x0000000031595600)

3CLTEXTCLASS               org/jnp/interfaces/Naming(0x0000000031594F00)</code></pre><p><span style="color:#019267;font-weight:800;"><strong>문제의 발생은 <code>org/jnp/interfaces/Naming</code> 각 Heap 존재 하지 않아 발생하는 것이 아닐까?</strong></span> 유추해 볼수 있다. </p>
<p>4월에 있을 다음 재현 테스트 내 가정이 맞을지 틀릴지 확인 할 수 있겠지!</p>
<h2 id="🍰-epilogue">🍰 Epilogue</h2>
<p>글로 정리하는게 너무 어려웠다. 솔직히 너무 어려워 이 글을 누가 볼까 싶다. 이 글을 완성하고 나서 더러운 기분이 모두 사라졌다. 내가 세운 논리가 완벽하고 조화롭다. 여기까지 스스로 생각하고 이루어낸 내가 너무도 대견하다. <strong>👏</strong></p>
<blockquote>
<p>나는 돈을 은행에 보관하고 싶다.
은행 계좌가 없으므로 은행가서 계좌를 만들었다.
그 은행계좌를 종이에 적어 금고에 넣어 둔다.
은행에 송금하려고 하는데 송금이 되지 않는다.
난 계좌가 적힌 종이를 확인하려 금고를 열었다.
<strong>어! 종이는 있는데 계좌가 지워져 있네.</strong>
계좌번호를 알기위해 난 어떻게 해야 할까?</p>
</blockquote>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://tekmarathon.com/2012/10/03/jndi-architecture/">Java Naming and Directory Interface (JNDI), JNDI Architecture</a></li>
<li><a href="https://docs.oracle.com/cd/E19462-01/819-6717/gcszc/index.html">Java Authentication Service Provider Interface for Containers</a></li>
<li><a href="https://access.redhat.com/documentation/en-us/jboss_enterprise_application_platform/4.3/html/server_configuration_guide/naming_on_jboss-the_jbossns_architecture">6.2. The JBossNS Architecture</a></li>
<li><a href="https://access.redhat.com/documentation/en-us/jboss_enterprise_application_platform/4.3/html/server_configuration_guide/naming_on_jboss-the_naming_initialcontext_factories">6.3. The Naming InitialContext Factories</a></li>
<li><a href="https://docs.oracle.com/cd/E19830-01/819-4725/abmdz/index.html">Looking Up the Data Source Using JNDI To Obtain a Connection</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/javax/naming/InitialContext.html">InitialContext</a></li>
<li><a href="https://docs.oracle.com/javase/tutorial/jndi/index.html">jndi tutorial</a></li>
<li><a href="https://www.ibm.com/support/pages/jvm-classloader-hierarchy">ibm jvm-classloader-hierarchy</a></li>
<li><a href="https://docs.oracle.com/javase/jndi/tutorial/beyond/misc/classloader.html">classloader</a></li>
<li><a href="https://www.golinuxcloud.com/log4j2-vulnerability-analysis-wireshark/">How to do Log4j2 Vulnerability Analysis with Wireshark</a></li>
<li><a href="https://docs.oracle.com/cd/E19900-01/819-4741/abfas/index.html">J2EE Platform Overview</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[20230120 TIL | ord function in python]]></title>
            <link>https://velog.io/@_choongyul/20230120-TIL-ord-function-in-python</link>
            <guid>https://velog.io/@_choongyul/20230120-TIL-ord-function-in-python</guid>
            <pubDate>Tue, 24 Jan 2023 20:59:42 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>20<sup>th</sup> January 2023</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<p>강의를 듣는데 처음보는 <code>ord()</code> 함수!
너는 누구니?</p>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://www.geeksforgeeks.org/ord-function-python/">ord() function in Python</a></li>
</ul>
<h2 id="🎽-learn">🎽 Learn</h2>
<h3 id="1-about-ord-function">1. About <code>ord()</code> function</h3>
<ul>
<li><code>ord()</code> 는 인수(argument) 에 해당하는 Unicode를 반환한다.</li>
<li>인수(argument)는 8bit string 이다.</li>
</ul>
<h3 id="2-ord-syntax">2. <code>ord()</code> syntax</h3>
<pre><code class="language-python">ord(ch)</code></pre>
<h3 id="3-ord-example">3. <code>ord()</code> example</h3>
<p><code>ord()</code> 는 8bit로 표현할 수 있는 unicode를 인수(argument)로 받을 수 있음을 확인 한다.</p>
<p>** 🚩 영문자를 인수로 넘기면,**</p>
<ul>
<li>&#39;a&#39; 를 인수로 넘기면 &#39;a&#39;에 해당하는 97을 반환한다.<pre><code class="language-python">print(ord(&#39;a&#39;))</code></pre>
</li>
</ul>
<h4 id="output">output</h4>
<pre><code>97</code></pre><p>** 🚩 한글을 인수로 넘기면,**</p>
<ul>
<li><code>ord()</code>는 인수(argument)로 8bit string 를 허용한다.</li>
<li>&#39;가&#39; 를 파라미터로 넘기면 44032가 반환된다.<pre><code class="language-python">print(ord(&#39;가&#39;))</code></pre>
</li>
</ul>
<h4 id="output-1">output</h4>
<pre><code>44032</code></pre><p>** 🚩 특수문자를 인수로 넘기면,**</p>
<pre><code class="language-python">print(ord(&#39;★&#39;))</code></pre>
<h4 id="output-2">output</h4>
<pre><code>9733</code></pre><p>** 🚩 8bit 이상되는 문자를 인수로 넘기면,**</p>
<ul>
<li>8bit를 초과 했으므로 에러가 발생한다.<pre><code class="language-python">print(ord(&#39;ab&#39;))</code></pre>
</li>
</ul>
<h4 id="output-3">output</h4>
<pre><code>Traceback (most recent call last):
  File &quot;d:\vscode_ws\DataStructure_N_Algorithm\ord_function_example.py&quot;, line 10, in &lt;module&gt;
    print(ord(&#39;ab&#39;))
TypeError: ord() expected a character, but string of length 2 found</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Linked List in Python ]]></title>
            <link>https://velog.io/@_choongyul/Linked-List-in-Python</link>
            <guid>https://velog.io/@_choongyul/Linked-List-in-Python</guid>
            <pubDate>Wed, 18 Jan 2023 22:09:22 GMT</pubDate>
            <description><![CDATA[<h2 id="about-linked-list">About Linked List</h2>
<p><code>Linked List</code> 를 구현하기 전에 간단하게 <code>Linked List</code> 에 대해 정리하고 넘어가자. </p>
<p><code>Linked List</code> 는 문자 그대로, item 이 물리적으로 연결된 구조를 갖는다. <code>Linked List</code>에 item을 <code>node</code> 라고 부르는데, <code>node</code>는 2개의 <code>field</code>로 이루어져 있다.</p>
<ol>
<li>Data : 값을 저장하고 있다.</li>
<li>Next : 다음 <code>node</code> 참조 값을 저장하고 있다.</li>
</ol>
<p>그림으로 표현하면 아래와 같이 생겼다.
<img src="https://velog.velcdn.com/images/_choongyul/post/3134f806-d56f-495d-b556-3dfb9bc9e705/image.png" width="70%"></p>
<p><code>Linked List</code>에서 가장 앞의 <code>node</code> 를 특별하게 <code>head</code>라고 부르는다. 우리는 <code>head</code>를 시작점으로 <code>list</code>를 순회 할 수 있다.
<img src="https://velog.velcdn.com/images/_choongyul/post/e276635c-292f-4f7a-8222-442459a220de/image.png" width="70%"></p>
<h2 id="requirement">Requirement</h2>
<p>내가 제작하려는 <code>Linked List</code> 의 요구사항은 아래와 같다.</p>
<ul>
<li><code>List</code> 를 이용하여 <code>Linked List</code> 를 초기화 할 수 있을 것.</li>
<li><code>print()</code> 함수를 사용하여 <code>Linked List</code> 의 모든 <code>Node</code> 를 출력할 수 있을 것.</li>
<li><code>Linked List</code>를 <code>iterable</code> 하게 구성할 것.</li>
<li><code>Node</code> 를 제일 앞에 추가하는 함수를 구현할 것.</li>
<li><code>Node</code> 를 제일 끝에 추가하는 함수를 구현할 것.</li>
<li>특정 <code>Node</code> 앞에 새로운 <code>Node</code> 를 추가하는 함수를 구현할 것.</li>
<li>특정 <code>Node</code> 뒤에 새로운 <code>Node</code> 를 추가하는 함수를 구현할 것.</li>
<li>특정 <code>Node</code> 를 삭제하는 함수를 구현할 것.</li>
</ul>
<h2 id="implement">Implement</h2>
<h3 id="a-데이터를-저장하는-node-class를-정의한다">A. 데이터를 저장하는 Node Class를 정의한다.</h3>
<pre><code class="language-python">class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

    def __str__(self):
        return str(self.data)</code></pre>
<h3 id="b-node를-관리하는-linkedlist-class를-정의한다">B. Node를 관리하는 LinkedList Class를 정의한다.</h3>
<pre><code class="language-python">class LinkedList:
    pass</code></pre>
<h3 id="c-linkedlist-class에-list를-이용하여-linked-list를-초기화-하는-init-함수를-구현한다">C. LinkedList Class에 List를 이용하여 Linked List를 초기화 하는 <strong>init</strong>() 함수를 구현한다.</h3>
<pre><code class="language-python">def __init__(self, nodes=None):
    self.head = None

    if nodes is None:
        return

    node = Node(nodes[0])
    self.head=node

    nodes.remove(nodes[0])

    for element in nodes:
        nextNode = Node(element)
        node.next = nextNode
        node = nextNode</code></pre>
<p><code>List</code> 를 사용하여 <code>LinkedList</code>를 초기화 할수 있는지 확인한다.</p>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])

node = linkedList.head
while node is not None:
    print(node.data)
    node = node.next</code></pre>
<h5 id="output">Output:</h5>
<pre><code>A
B
C
D
E</code></pre><h3 id="d-print를-손쉽게-하기-위해-str-함수를-추가한다">D. <code>print()</code>를 손쉽게 하기 위해 <strong>str</strong>() 함수를 추가한다.</h3>
<pre><code class="language-python">def __str__(self):
    nodes = []
    node = self.head
    while node is not None:
        nodes.append(str(node.data))
        node = node.next

    nodes.append(str(None))
    return &#39; -&gt; &#39;.join(nodes)</code></pre>
<p><code>___str__()</code> 함수가 정상적으로 동작하는지 확인한다.</p>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])
print(linkedList)</code></pre>
<h5 id="output-1">Output:</h5>
<pre><code>A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; None</code></pre><h3 id="e-linkedlist-class-를-iterable하게-만든다">E. LinkedList Class 를 iterable하게 만든다.</h3>
<p><code>iterater</code> 프로토콜을 구현하기 위해 <code>__iter__()</code> 함수를 <code>Generator</code> 로 구현한다.</p>
<pre><code class="language-python">def __iter__(self):
    node = self.head
    while node is not None:
        yield node
        node = node.next</code></pre>
<p><code>LinkedList</code> 가 <code>iterable</code> 하기 때문에 이제 <code>for</code> 문을 사용할 수 있다.</p>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])


for element in linkedList:
    print(ele)</code></pre>
<h5 id="output-2">Output:</h5>
<pre><code>A
B
C
D
E</code></pre><h3 id="f-data를-linkedlist-제일-앞에-추가하는-함수를-구현한다">F. Data를 LinkedList 제일 앞에 추가하는 함수를 구현한다.</h3>
<pre><code class="language-python">def add_first(self, data):
    node = Node(data)
    node.next = self.head
    self.head = node</code></pre>
<ol>
<li>새로운 <code>Node</code>의 <code>next</code>를 <code>head</code>를 가르키게 한다.</li>
<li><code>head</code>를 새로운 <code>Node</code>로 변경한다.</li>
</ol>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])
print(linkedList)
linkedList.add_first(&quot;0&quot;)
print(linkedList)</code></pre>
<h5 id="output-3">Output</h5>
<pre><code>A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; None
0 -&gt; A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; None</code></pre><h3 id="g-data를-linkedlist-제일-끝에-추가하는-함수를-구현한다">G. Data를 LinkedList 제일 끝에 추가하는 함수를 구현한다.</h3>
<pre><code class="language-python">def add_last(self, data):
    newNode = Node(data)

    node = self.head
    while node.next is not None:
        node = node.next

    node.next = newNode</code></pre>
<ol>
<li>맨 마지막 노드(<code>node.next</code> 가 <code>None</code> 인 것)를 찾는다.</li>
<li>마지막 노드에 새로운 <code>Node</code> 를 추가한다.</li>
</ol>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])
print(linkedList)
linkedList.add_last(&quot;F&quot;)
print(linkedList)</code></pre>
<h5 id="output-4">Output</h5>
<pre><code>A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; None
A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; F -&gt; None</code></pre><h3 id="h-특정-노드-앞에-data를-추가하는-함수를-구현한다">H. 특정 노드 앞에 Data를 추가하는 함수를 구현한다.</h3>
<pre><code class="language-python">def add_before(self, target, addData):
    newNode = Node(addData)

    node = self.head
    while True:
        if node is None:
            raise Exception(&#39;Can not find value: &#39; + str(addData))

        if node.data == str(target):
            preNode.next = newNode
            newNode.next = node
            break
        preNode = node
        node = node.next</code></pre>
<ol>
<li><code>node</code> 가 <code>None</code> 이면 <code>Exception</code>을 발생시킨다.
1-1. <code>node</code> 가 <code>None</code>이라는 건 데이터를 찾지 못한 경우이다.</li>
<li><code>node.data</code> 가 <code>target</code> 과 동일 한지 확인한다.</li>
<li>동일하면, 
3-1. 이전 <code>node.next</code>에 <code>newNode</code>를 추가한다.
3-2. <code>newNode.next</code>에 현재 <code>node</code>를 추가한다.</li>
</ol>
<blockquote>
<p><code>target</code> 앞에 추가해야 함으로, 이전 <code>node</code> 정보를 저장하고 있어야 한다.</p>
</blockquote>
<pre><code class="language-python">preNode = node
node = node.next</code></pre>
<p><code>target</code>을 못 찾는 경우, 에러가 발생하는지 확인한다.</p>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])
linkedList.add_before(&quot;F&quot;, &quot;0&quot;)
print(linkedList)</code></pre>
<h5 id="output-5">output</h5>
<pre><code>Traceback (most recent call last):
  File &quot;d:\vscode_ws\DataStructure_N_Algorithm\LinkedList.py&quot;, line 75, in &lt;module&gt;
    linkedList.add_before(&quot;F&quot;, &quot;0&quot;)
  File &quot;d:\vscode_ws\DataStructure_N_Algorithm\LinkedList.py&quot;, line 61, in add_before
    raise Exception(&#39;Can not find value: &#39; + str(addData))
Exception: Can not find value: 0</code></pre><p><code>C</code> <strong><em>앞</em></strong>에 <code>0</code>을 추가한다.</p>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])

print(linkedList)
linkedList.add_before(&quot;C&quot;, &quot;0&quot;)
print(linkedList)</code></pre>
<h5 id="output-6">output</h5>
<pre><code>A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; None
A -&gt; B -&gt; 0 -&gt; C -&gt; D -&gt; E -&gt; None</code></pre><h3 id="i-특정-노드-뒤에-data를-추가하는-함수를-구현한다">I. 특정 노드 뒤에 Data를 추가하는 함수를 구현한다.</h3>
<pre><code class="language-python">def add_after(self, target, addData):
    newNode = Node(addData)

    node = self.head
    while True:            
        if node is None:
            raise Exception(&#39;Can not find value: &#39; + str(target))

        if node.data == str(target):
            newNode.next = node.next
            node.next = newNode
            break

        node = node.next</code></pre>
<ol>
<li><code>node</code> 가 <code>None</code> 이면 <code>Exception</code>을 발생시킨다.
1-1. <code>node</code> 가 <code>None</code>이라는 건 데이터를 찾지 못한 경우이다.</li>
<li><code>node.data</code> 가 <code>target</code> 과 동일 한지 확인한다.</li>
<li>동일하면, 
3-1. <code>newNode.next</code>에 <code>node.next</code>를 연결 시킨다.
3-2. <code>node.next</code>에 <code>newNode</code>를 연결 시킨다.</li>
</ol>
<p><code>C</code> <strong><em>뒤</em></strong>에 <code>0</code>을 추가한다.</p>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])

print(linkedList)
linkedList.add_after(&quot;C&quot;, &quot;0&quot;)
print(linkedList)</code></pre>
<h5 id="output-7">output</h5>
<pre><code>A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; None
A -&gt; B -&gt; C -&gt; 0 -&gt; D -&gt; E -&gt; None</code></pre><h3 id="j-특정-노드를-삭제하는-함수를-구현한다">J. 특정 노드를 삭제하는 함수를 구현한다.</h3>
<pre><code class="language-python">def remove(self, target):

    if self.head.data == str(target):
        self.head = self.head.next
        return

    node = self.head

    while True:
        if node is None:
            raise Exception(&#39;Can not find value: &#39; + str(target))

        if node.data == str(target):
            preNode.next = node.next
            del node
            break

        preNode = node
        node = node.next</code></pre>
<ol>
<li><code>head.data</code> 와 <code>target</code> 동일하다면,
1-1. <code>head</code>가 <code>head.next</code>를 가르키게 한다.</li>
<li><code>head.data</code> 와 <code>target</code> 다르면,
2-1. 순회하면서 <code>target</code> 같은 값을 가진 <code>Node</code>를 찾는다.</li>
<li><code>node.data</code> 가 <code>target</code> 과 동일하면
3-1. 이전 <code>node.next</code>에 현재 <code>node.next</code>를 연결시킨다.
3-2. 현재 <code>node</code>를 삭제한다.</li>
</ol>
<p><code>head</code> 값, 제일 앞에 있는 값을 삭제한다.</p>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])
print(linkedList)
linkedList.remove(&quot;A&quot;)
print(linkedList)</code></pre>
<h5 id="output-8">output</h5>
<pre><code>A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; None
B -&gt; C -&gt; D -&gt; E -&gt; None</code></pre><p>중간 값을 삭제한다.</p>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])
print(linkedList)
linkedList.remove(&quot;B&quot;)
print(linkedList)</code></pre>
<h5 id="output-9">output</h5>
<pre><code>A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; None
A -&gt; C -&gt; D -&gt; E -&gt; None</code></pre><p>마지막 값을 삭제한다.</p>
<pre><code class="language-python">linkedList = LinkedList([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;])
print(linkedList)
linkedList.remove(&quot;E&quot;)
print(linkedList)</code></pre>
<h5 id="output-10">output</h5>
<pre><code>A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; None
A -&gt; B -&gt; C -&gt; D -&gt; None</code></pre><h2 id="reference">Reference</h2>
<p><a href="https://realpython.com/linked-lists-python/">Linked Lists in Python: An Introduction</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[20230109 TIL | Java NIO Channel 의 read와 write 함수 이해하기]]></title>
            <link>https://velog.io/@_choongyul/20230109-TIL-NIO-Channel-%EC%9D%98-read%EC%99%80-write-%ED%95%A8%EC%88%98-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@_choongyul/20230109-TIL-NIO-Channel-%EC%9D%98-read%EC%99%80-write-%ED%95%A8%EC%88%98-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 10 Jan 2023 04:51:22 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>10<sup>th</sup> January 2023</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<p>Buffer에 쓴다고 하는데 <code>read()</code> 함수를 사용한다.
Buffer로 부터 읽는데 <code>write()</code> 함수를 사용한다.</p>
<p>이해가 되지 않는다. 도대체 왜 저리 만들었을까?</p>
<h3 id="writing-data-to-a-buffer">Writing Data to a Buffer</h3>
<pre><code class="language-java">//read into buffer.
int bytesRead = inChannel.read(buf); </code></pre>
<h3 id="reading-data-from-a-buffer">Reading Data from a Buffer</h3>
<pre><code class="language-java">//read from buffer into channel.
int bytesWritten = inChannel.write(buf);</code></pre>
<hr>
<p><strong>* note *</strong></p>
<p>개발하면서 Channel과 Buffer를 사용했던 기억이 있다.
근데 왜, 모르는 것이냐!</p>
<hr>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://jenkov.com/tutorials/java-nio/buffers.html">Java NIO Buffer</a></li>
</ul>
<h2 id="🎽-learn">🎽 Learn</h2>
<h3 id="1-channel-과-buffer의-사전적-정의">1. Channel 과 Buffer의 사전적 정의</h3>
<p>일단 Channel 과 Buffer를 사전적 정의를 확인하자. </p>
<ul>
<li><p>Channel</p>
<blockquote>
<p><strong>noun.</strong> a medium for communication or the passage of information</p>
</blockquote>
</li>
<li><p><em>verb.*</em> direct toward a paricular end or object</p>
</li>
<li><p>Buffer</p>
<blockquote>
<p><strong>noun.</strong> COMPUTING
a temporary memory area in which data is stored while it is being processed or transferred, especially one used while streaming video or downloading audio.</p>
</blockquote>
</li>
</ul>
<p>Buffer는 컴퓨터 공학에서 어떤 의미로 사용되는지 사전에서 확인 할 수 있다. 하지만 Channel은 우리가 아는 것과 조금 다르다. <mark style="background-color: #93FFD8">단순히 TV Channel이 아니다.</mark></p>
<p><a href="https://languages.oup.com/">Oxford Languages</a> 에 따르면 Channel의 정의에는 <mark style="background-color: #93FFD8">의사 소통 또는 정보 전달을 위한 매개체</mark> 정의가 포함되어 있다.</p>
<h3 id="2-channel-과-buffer의-역할">2. Channel 과 Buffer의 역할</h3>
<p>우리는 <mark style="background-color: #93FFD8">매개체(medium)</mark>라는 말에 집중 할 필요가 있다. 매개체는 맺어 주는 행위에 집중하는 단어이다. Java NIO에서도 <code>Channel</code>은 <strong>매개체</strong>이다.</p>
<blockquote>
<p><strong>매개체</strong>
중간에서 어떤 일을 맺어 주는 것.</p>
</blockquote>
<p>현실에서 <strong>매개체</strong>가 끝이라고 생각하지 않듯 <code>Programming</code>에서도 <code>Channel</code> 끝이 아니다. <code>Programming</code>에서 <code>끝</code>은 정보를 저장할 수 있는 무엇이라 생각할 수 있다. <code>Programming</code>에서 <code>끝</code>은 파일과 메모리이다. Programmer는 <code>끝</code>에 접근하여 데이터를 변경한다. <code>매개체</code>를 이용하여 데이터를 전송한다.</p>
<ul>
<li>Programmer는 <code>Channel</code>을 이용하여 데이터를 전송한다.</li>
<li>Programmer는 <code>Buffer</code>에 접근하여 데이터를 변경한다.</li>
</ul>
<h3 id="3-read-와-write-동작">3. read() 와 write() 동작</h3>
<p>이제 <code>Channel</code>의 <code>read()</code>와 <code>write()</code> 가 어떻게 동작하는지 살펴보자.</p>
<h4 id="channel-얻어오기"><code>Channel</code> 얻어오기</h4>
<p>아래는 <code>FileChannel</code> 을 얻어 오는 코드이다.</p>
<pre><code class="language-java">RandomAccessFile file = new RandomAccessFile(&quot;data/data.txt&quot;, &quot;rw&quot;);
FileChannel fileChannel = file.getChannel();</code></pre>
<p><code>FileChannel</code> 은 <code>file.getChannel()</code> 함수를 통해 얻어지는 것을 확인 할 수 있다. 그러므로 <code>FileChannel</code>은 <code>File</code>에 종속되어 있다. 동일하게 <code>SocketChannel</code> 은 <code>Socket</code> 에 종속되어 있고 <code>ServerSocketChannel</code> 은 <code>ServerSocket</code>에 종속되어 있다.</p>
<h4 id="channel-의-read-함수"><code>Channel</code> 의 <code>read()</code> 함수</h4>
<p><code>read()</code> 함수는 <code>Channel</code>이 종속된 Datasource에서 데이터를 읽어온다. 아래의 소스 코드에서 Datasource는 <code>File</code>이다. <code>Channel</code>은 <code>File</code>에서 데이터를 읽어와 <code>Buffer</code> 로 보낸다.</p>
<pre><code class="language-java">RandomAccessFile file = new RandomAccessFile(&quot;data/data.txt&quot;, &quot;rw&quot;);
FileChannel fileChannel = file.getChannel();

ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = fileChannel.read(buffer)</code></pre>
<ul>
<li><p><code>Channel</code>은 Datasource 로 부터 데이터를 읽는다.</p>
<img src="https://velog.velcdn.com/images/_choongyul/post/d7ad588d-83e8-4ff0-b192-75a0e8a08aba/image.png" width="75%">
</li>
<li><p>읽은 데이터를 <code>Buffer</code> 에 쓴다.</p>
<img src="https://velog.velcdn.com/images/_choongyul/post/3d387278-14ef-44e9-8505-75efd98688bf/image.png" width="75%">

</li>
</ul>
<h4 id="channel-의-write-함수"><code>Channel</code> 의 <code>write()</code> 함수</h4>
<p><code>read()</code> 함수는 <code>Channel</code>이 종속된 Datasource에 데이터를 쓴다. 그럼 어떤 데이터를 쓸까?? <code>Buffer</code> 에 있는 데이터를 Datasource에 쓴다.</p>
<ul>
<li><p><code>Channel</code>은 Datasource에 데이터를 쓴다.</p>
<img src="https://velog.velcdn.com/images/_choongyul/post/7806d0f0-157d-4333-a91a-3310109f01a0/image.png" width="75%">
</li>
<li><p><code>Buffer</code> 로 부터 읽어온 데이터를 쓴다.</p>
<img src="https://velog.velcdn.com/images/_choongyul/post/c2e6c707-dbb8-4ab5-b5d8-effa24b84967/image.png" width="75%">


</li>
</ul>
<h2 id="🎁-summary">🎁 Summary</h2>
<p>이렇게 정리하면 될 것 같다. </p>
<pre><code class="language-java">FileChannel fileChannel = file.getChannel();
SocketChannel socketChannel = SocketChannel.open();
ServerSocketChannel serverSocketChannle = ServerSocketChannel.open();</code></pre>
<ul>
<li><code>Channel</code>은 Datasource 종속된 매개체이다.</li>
</ul>
<pre><code class="language-java">int bytesRead = inChannel.read(buf); </code></pre>
<ul>
<li><code>Channel</code> 이 <code>read()</code> 하는 대상은 Datasource 이다.</li>
<li><code>read()</code> 한 데이터를 어디로 보낼까 ? <code>Buffer</code>로 보낸다.</li>
</ul>
<pre><code class="language-java">//read from buffer into channel.
int bytesWritten = inChannel.write(buf);</code></pre>
<ul>
<li><code>Channel</code> 이 <code>write()</code> 하는 대상은 Datasource 이다.</li>
<li>어떤 데이터를 <code>write()</code> 할까? <code>Buffer</code> 있는 데이터이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[log4j: SocketAppender 사용하기]]></title>
            <link>https://velog.io/@_choongyul/log4j-SocketAppender-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@_choongyul/log4j-SocketAppender-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 08 Jan 2023 21:02:38 GMT</pubDate>
            <description><![CDATA[<h2 id="🥗-prologue">🥗 Prologue</h2>
<p>시스템을 잘 관리하기 위해서는 프로그램을 주기적으로 Restart 해주는 것이 좋다. 서버를 Restart하면 더 좋고! 하지만, 대부분의 시스템은 복잡하게 얽혀 있어 시스템을 재시작하는 건 간단하지 않다. 신경 쓸 것이 한 두개가 아니다.</p>
<p>그중에 가장 신경 써야 하는 건 고객들! 정해진 시간 동안 시스템에 접근할 수 없다고 고객에게 공지해야 한다. 그리고 정상적으로 연결 되었는지 확인하라고 요청해야 한다. 고객들에게 정중하게 메일을 보낸다.</p>
<p><strong>&quot;고객님, 시스템 휴지가 있습니다.
2시간 동안 시스템 접근이 불가능합니다.&quot;</strong></p>
<p>문제가 발생하자 고객은 일단 메일을 받은 적이 없다고 우긴다.
결국, 우린 너무 고귀해서 <code>모니터링</code> 같은거 못하겠다고 한다. 시스템 <code>모니터링</code>은 너희가 해줘야 하는 서비스라고 한다.</p>
<p><strong><em>고객님! 이러시면 안 되요! 👿</em></strong></p>
<h2 id="🔑-problem">🔑 Problem</h2>
<p>내가 관리하는 시스템은 서버 역할하고 고객 시스템은 클라이언트 역할을 한다. 서버와 클라이언트가 연결되어 있는 상태에서 서버를 재 시작한다. 서버는 어떤 클라이언트가 성공적으로 연결되었는지 알 수 있으나, 연결되지 않은 클라이언트가 어떤 상태인지는 알 수 없다.</p>
<ul>
<li>고객 시스템에 연결 상태를 알 수 없다.</li>
</ul>
<h2 id="💡-idea">💡 Idea</h2>
<p>시스템 상태는 로그에 쓰여진다. 우린 로그만 있으면 고객 시스템의 상태를 확인 할 수 있다.</p>
<ul>
<li>원격지에 있는 EndPoint의 <code>로그</code>를 수집하자!<img src="https://velog.velcdn.com/images/_choongyul/post/1edc4b05-ae78-4f59-877a-ebd9166b1405/image.png" width="70%">

</li>
</ul>
<h2 id="📝-requirement">📝 Requirement</h2>
<p>고객의 요구사항은 다음과 같다.</p>
<ul>
<li>시스템 변경을 최소화 해야 함.<ul>
<li>고객 시스템에 새로운 프로그램 설치 금지</li>
<li>참조 Library 변경 최소화</li>
<li>Cross Platform 지원</li>
<li>Perform에 영향을 미치지 않을 것</li>
</ul>
</li>
<li>Logstash Input plugins이 제공되는 Middleware를 사용해야 함.</li>
</ul>
<h2 id="✂️-analyze">✂️ Analyze</h2>
<p>내가 관리하는 시스템의 로깅 환경부터 확인한다.</p>
<ul>
<li>jdk1.7</li>
<li>log4j1.2.11</li>
</ul>
<h2 id="🍰-solve">🍰 Solve</h2>
<p>나는 이 문제를 <code>SocketAppender</code>를 이용해 풀어보려한다. 일단 컨셉일 뿐이고 대용량 시스템에 어떻게 적용해야 할지는 꽤나 많은 검증이 필요하다. 아래 소스코드는 <a href="https://github.com/choongyul/log4j-socket-appender-test">Github</a> 에서 확인 가능하다.</p>
<h4 id="1-socketappender를-사용한다">1. <code>SocketAppender</code>를 사용한다.</h4>
<p><code>SocketAppender</code>를 선택한 이유는 3가지 이다.</p>
<ul>
<li>Log4j1.2.11에서 제공하는 기본적인 Appender</li>
<li>현재 상태에서 추가되는 라이브러리 없음</li>
<li>Logstash 에서 <code>SocketAppender</code>를 위한 <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-log4j.html#plugins-inputs-log4j-host">Input Plugins</a>를 제공 함<img src="https://velog.velcdn.com/images/_choongyul/post/6134bc38-6381-4161-9df4-e11a4675bbcc/image.png" width="70%">

</li>
</ul>
<p><code>SocketAppender</code>는 고객 요구사항에 완벽하게 부합한다.</p>
<h4 id="2-simplesocketserver-실행시키자">2. <code>SimpleSocketServer</code> 실행시키자!</h4>
<p><code>SimpleSocketServer</code> 는 특정 <code>Port</code> 로 부터 송신된 로그를 수신한다. 그리고 자체적으로 로그를 생성한다. 사용 방법은 매우 단순하며, <code>log4j1.2</code> <a href="https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/net/SimpleSocketServer.html">Javadoc</a>에서 확인 할 수 있다.</p>
<blockquote>
<p><strong>Usage:</strong> java org.apache.log4j.net.SimpleSocketServer port configFile
where port is a part number where the server listens and 
  configFile is a configuration file fed to the PropertyConfigurator or to DOMConfigurator if an XML file.</p>
</blockquote>
<p><code>SimpleSocketServer</code> 가 사용하는 <code>&lt;Port&gt;</code> 는 <code>9999</code>를 사용하기로 한다.
자제적으로 생성하는 로그는 <code>&lt;configFile&gt;</code>을 통해 설정하는데, 단순하게 콘솔화면으로 출력하도록 구성한다.</p>
<pre><code class="language-properties"># log4j-server.properties
log4j.rootLogger=INFO,stdout

# stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%t :: %d :: %m%n</code></pre>
<p>위 내용을 <code>log4j-server.properties</code> 파일로 적당한 위치에 저장한다. 나 같은 경우는 <code>D:/log4j-socket-server</code>에 저장하였다. 그리고 아래 명령으로 서버를 실행한다. </p>
<pre><code class="language-console">java -classpath d:/log4j-socket-server/Log4j-1.2.11.jar org.apache.log4j.net.SimpleSocketServer 9999 D:/log4j-socket-server/log4j-server.properties</code></pre>
<h4 id="3-socketappender-구성하자">3. <code>SocketAppender</code> 구성하자!</h4>
<p>다음으로 <code>SimpleSocketServer</code>로 로그를 전송하는 <code>SocketAppender</code>를 구성하자. <code>SocketAppender</code> 는 <code>LoggingEvent</code> 객체를 원격 로그 서버로 전송하는 역할을 담당한다.</p>
<p><code>SocketAppender</code>의 특성은 <code>log4j1.2</code> <a href="https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/net/SocketAppender.html">Javadoc</a> 에서 확인 할 수 있다.</p>
<p>내가 관리하는 시스템은 Programatically 하게 <code>log4j</code> 를 사용하므로 아래와 같이 <code>Log4jSocketAppenderTest.java</code> 를 작성하여 실행한다.</p>
<pre><code class="language-java">package com.choong.log4j.test;

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.net.SocketAppender;

public class Log4jSocketAppenderTest {
    public static void main(String[] args) {
        Logger logger = Logger.getLogger(Log4jSocketAppenderTest.class);

        PatternLayout patternLayout = new PatternLayout(&quot;%t :: %d :: %m%n&quot;);

        ConsoleAppender consoleAppender = new ConsoleAppender(patternLayout);
        SocketAppender socketAppender = new SocketAppender(&quot;localhost&quot;, 9999);

        logger.addAppender(consoleAppender);
        logger.addAppender(socketAppender);

        logger.info(&quot;Hello, ChoongYul!&quot;);
    }
}</code></pre>
<h4 id="4-simplesocketserver-출력결과">4. <code>SimpleSocketServer</code> 출력결과</h4>
<p>마지막으로 <code>SimpleSocketServer</code> 의 출력결과를 확인한다.</p>
<p>ouput:</p>
<pre><code>D:\&gt;java -classpath d:/log4j-socket-server/Log4j-1.2.11.jar org.apache.log4j.net.SimpleSocketServer 9999 D:/log4j-socket-server/log4j-server.properties
main :: 2023-01-08 12:57:10,386 :: Listening on port 9999
main :: 2023-01-08 12:57:10,417 :: Waiting to accept a new client.
main :: 2023-01-08 12:57:25,681 :: Connected to client at /127.0.0.1
main :: 2023-01-08 12:57:25,682 :: Starting new socket node.
main :: 2023-01-08 12:57:26,117 :: Waiting to accept a new client.
main :: 2023-01-08 12:57:25,774 :: Hello, ChoongYul!
Thread-0 :: 2023-01-08 12:57:26,385 :: Caught java.net.SocketException closing conneciton.</code></pre><ul>
<li>9999 Port로 새로운 클라이언트가 연결되었고,</li>
<li>클라이언트는 &#39;Hello, ChoongYul!&#39; 이라는 로그를 전송했다.</li>
<li><code>SimpleSocketServer</code>는 <code>log4j-server.properties</code>에서 설정한 <code>ConsoleAppender</code>를 통해 &#39;Hello, ChoongYul!&#39;을 출력하였다.</li>
</ul>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://stackoverflow.com/questions/11759196/log4j-how-to-use-socketappender">log4j: How to use SocketAppender?</a></li>
<li><a href="http://hmvermeulen.blogspot.com/2009/10/using-log4js-socketappender.html">Using log4j&#39;s SocketAppender</a></li>
<li><a href="https://behonestar.tistory.com/46">sockAppender로 log4j 로그 입력</a></li>
<li><a href="https://github.com/kikonen/log4j-share/blob/master/src/main/java/org/apache/log4j/net/SimpleSocketServer.java">SimpleSocketServer.java </a></li>
<li><a href="https://discuss.elastic.co/t/log4j-socketappender-logstash-tcp-input-ok-log4j-input-ko-solved/36184">Log4j SocketAppender. Logstash : </a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[20230105 TIL | Iterator 와 Iterable 은 굳이 왜 나누어 놓았을까요!]]></title>
            <link>https://velog.io/@_choongyul/20230105-TIL-iterator-%EC%99%80-iterable</link>
            <guid>https://velog.io/@_choongyul/20230105-TIL-iterator-%EC%99%80-iterable</guid>
            <pubDate>Thu, 05 Jan 2023 22:01:39 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>5<sup>th</sup> January 2023</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<ul>
<li><code>python</code>으로 <code>LinkedList</code> 를 자료구조를 구현해보라고?</li>
</ul>
<hr>
<p><strong>* note *</strong></p>
<p><code>LinkedList</code> 를 언제쯤 구현할 수 있을까?
이제 <code>__iter__()</code> 함수가 보인다.
<code>iterator</code> 와 <code>iterable</code> 의 차이는 또 무엇인고?
아~ 진짜 <code>LinkedList</code> 만들기 어렵다.</p>
<hr>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://docs.python.org/3/glossary.html#term-iterable">iterable</a></li>
<li><a href="https://docs.python.org/3/glossary.html#term-iterator">iterator</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#iterator-types">iterator-types</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#generator-types">generator-types</a></li>
</ul>
<h2 id="🎽-learn">🎽 Learn</h2>
<p>왜? <code>Iterable</code> 과 <code>Iterator</code> 를 나누어 놓아서 혼란스럽게 하나?
굳이 구분해야 싶기도 하다.</p>
<p>일단 모르니, 정의 부터!</p>
<h4 id="1-iterable-과-iterator-의-정의">1. Iterable 과 Iterator 의 정의</h4>
<p>파이썬 공식문서를 보면 Iterable 과 Iterator 를 아래와 같이 정의한다.</p>
<ul>
<li><p><strong>Iterable</strong></p>
<blockquote>
<p>An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as list, str, and tuple) and some non-sequence types like dict, file objects, and objects of any classes you define with an <code>__iter__()</code> method or with a <code>__getitem__()</code> method that implements sequence semantics.
Iterables can be used in a for loop and in many other places where a sequence is needed (zip(), map(), …). When an iterable object is passed as an argument to the built-in function iter(), it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call iter() or deal with iterator objects yourself. The for statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop. See also iterator, sequence, and generator.</p>
</blockquote>
</li>
<li><p><strong>Iterator</strong></p>
<blockquote>
<p>An object representing a stream of data. Repeated calls to the iterator’s <code>__next__()</code> method (or passing it to the built-in function next()) return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its <code>__next__()</code> method just raise StopIteration again. Iterators are required to have an <code>__iter__()</code> method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a list) produces a fresh new iterator each time you pass it to the iter() function or use it in a for loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.</p>
</blockquote>
</li>
</ul>
<p>그래서 저 영어 속에서 차이를 어떻게 찾아내야 하냐고!</p>
<h4 id="2-iterable-과-iterator-의-차이">2. Iterable 과 Iterator 의 차이</h4>
<p><strong>Iterator</strong> 는 간단히 말해</p>
<p><strong>* 연속적인 데이터를 저장하고 객체이다. 그리고 반복적인<code>next()</code> 를 사용하여 연속적인 <code>item</code> 에 접근할 수 있어야 한다. *</strong></p>
<blockquote>
<p>An object representing a stream of data. Repeated calls to the iterator’s <code>__next__()</code> method (or passing it to the built-in function next()) return successive items in the stream.</p>
</blockquote>
<p>여기서 한번 더 집고 넘어가야 할건!
<code>next()</code> 함수를 _명시적_으로 사용할 수 있어야 <strong>Iterator</strong> 이다.</p>
<p>그럼, <strong>Iterable</strong> 은 무엇일까?</p>
<p><strong><em>한 번에 하나씩 저장된 값들을 가져올 수 있는 객체다.</em></strong></p>
<blockquote>
<p>An object capable of returning its members one at a time.</p>
</blockquote>
<p>아래의 <code>MyCount</code> 클래스는 <code>Iterable</code> 하지만 <code>Iterator</code>는 아니다.</p>
<p><code>for</code> 문으로 <strong><em>한 번에 하나씩 저장된 값들을 가져</em></strong> 올 수 있지만,
<strong><em>명시적으로 <code>iter()</code> 와 <code>next()</code>를 호출</em></strong> 할수 없기 때문이다.</p>
<pre><code class="language-python">class MyCount:
    def __init__(self, stop):
        self.start = 1
        self.stop = stop

    def __iter__(self):
       while self.start != self.stop:
        yield int(self.start)
        self.start = self.start +1

myCount = MyCount(10)
for i in myCount:
    print(i)

iterMyCount = iter(myCount)
print(next(iterMyCount))</code></pre>
<p>output</p>
<pre><code>1
2
3
4
5
6
7
8
9

Traceback (most recent call last):
  File &quot;/workspace/firstContainer/data_structure/Iterable_Test.py&quot;, line 15, in &lt;module&gt;
    next(myCount)
TypeError: &#39;MyCount&#39; object is not an iterator</code></pre><p><code>MyCount</code> 같은 클래스를 <code>Generator Types</code>의 <code>iterator</code> 라고 한다.</p>
<h2 id="🎁-summary">🎁 Summary</h2>
<p>자! 이렇게 정리하자!</p>
<ul>
<li><code>Iterator</code> : 명시적으로 <code>next()</code> 와 <code>iter()</code>를 호출할 수 있는 것</li>
<li><code>Iterable</code> : 암묵적으로 <code>next()</code> 와 <code>iter()</code>를 호출할 수 있는 것</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[20230103 TIL | __repr__() 함수는 왜 쓸까요?]]></title>
            <link>https://velog.io/@_choongyul/20230102-TIL-repr-%ED%95%A8%EC%88%98%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C</link>
            <guid>https://velog.io/@_choongyul/20230102-TIL-repr-%ED%95%A8%EC%88%98%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C</guid>
            <pubDate>Tue, 03 Jan 2023 22:14:13 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>3<sup>rd</sup> January 2023</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<ul>
<li><code>python</code>으로 <code>LinkedList</code> 를 자료구조를 구현해보라고?</li>
</ul>
<hr>
<p><strong>* note *</strong></p>
<p><code>LinkedList</code> 를 구현한 걸 인터넷에서 찾아 보니 <code>__repr__(self)</code> 함수가 보인다. 안의 내용은<code>LinkedList</code> 내부 노드를 출력하기 위해 한 것인데 처음 보는 놈이다. 아놔 저건 뭐하는 놈인고!</p>
<hr>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://www.pythontutorial.net/python-oop/python-__repr__/">Understanding The Python <strong>repr</strong> Mehtod</a></li>
<li><a href="https://wikidocs.net/134994">점프 투 파이썬 - 라이브러리 예제 편 - 05 str() 함수와 repr() 함수</a></li>
</ul>
<h2 id="🎽-learn">🎽 Learn</h2>
<p>클래스를 정의할 때 <code>__init__</code> 함수를 정의하게 된다. 이렇게 함수명 앞뒤로 <code>__</code> 가 붙어 있는 함수를 <strong>Dunder 또는 Magic 함수</strong> 라고 부른다.</p>
<h4 id="1-dunder-또는-magic-함수-란">1. Dunder 또는 Magic 함수 란?</h4>
<ul>
<li>파이썬에서 Dunder 또는 Magic 함수란 함수명 앞뒤로 <code>__</code> 언더스코어가 붙은 함수를 말한다.</li>
<li>파이썬 내장 클래스인 <code>int</code> 함수를 살펴보면, 많은 Dunder 함수가 정의되어 있음을 확인할 수 있다.</li>
</ul>
<pre><code>&gt;&gt;&gt; print(dir(int))</code></pre><p>output:</p>
<pre><code>[&#39;__abs__&#39;, &#39;__add__&#39;, &#39;__and__&#39;, &#39;__bool__&#39;, &#39;__ceil__&#39;, &#39;__class__&#39;, &#39;__delattr__&#39;, &#39;__dir__&#39;, &#39;__divmod__&#39;, &#39;__doc__&#39;, &#39;__eq__&#39;, &#39;__float__&#39;, &#39;__floor__&#39;, &#39;__floordiv__&#39;, &#39;__format__&#39;, &#39;__ge__&#39;, &#39;__getattribute__
&#39;, &#39;__getnewargs__&#39;, &#39;__gt__&#39;, &#39;__hash__&#39;, &#39;__index__&#39;, &#39;__init__&#39;, &#39;__init_subclass__&#39;, &#39;__int__&#39;, &#39;__invert__&#39;, &#39;__le__&#39;, &#39;__lshift__&#39;, &#39;__lt__&#39;, &#39;__mod__&#39;, &#39;__mul__&#39;, &#39;__ne__&#39;, &#39;__neg__&#39;, &#39;__new__&#39;, &#39;__or__&#39;, &#39;__
pos__&#39;, &#39;__pow__&#39;, &#39;__radd__&#39;, &#39;__rand__&#39;, &#39;__rdivmod__&#39;, &#39;__reduce__&#39;, &#39;__reduce_ex__&#39;, &#39;__repr__&#39;, &#39;__rfloordiv__&#39;, &#39;__rlshift__&#39;, &#39;__rmod__&#39;, &#39;__rmul__&#39;, &#39;__ror__&#39;, &#39;__round__&#39;, &#39;__rpow__&#39;, &#39;__rrshift__&#39;, &#39;__rshi
ft__&#39;, &#39;__rsub__&#39;, &#39;__rtruediv__&#39;, &#39;__rxor__&#39;, &#39;__setattr__&#39;, &#39;__sizeof__&#39;, &#39;__str__&#39;, &#39;__sub__&#39;, &#39;__subclasshook__&#39;, &#39;__truediv__&#39;, &#39;__trunc__&#39;, &#39;__xor__&#39;, &#39;bit_length&#39;, &#39;conjugate&#39;, &#39;denominator&#39;, &#39;from_bytes&#39;, &#39;i
mag&#39;, &#39;numerator&#39;, &#39;real&#39;, &#39;to_bytes&#39;]</code></pre><p><code>__repr__</code> 함수를 처음 봤을 때는 <code>java</code> 의 <code>toString</code> 과 비슷한 줄 알았다. 하지만, 공식 문서를 찾아 보니 전혀 <strong>아니다.</strong></p>
<h4 id="2-reprobject-함수란">2. <code>repr(object)</code> 함수란?</h4>
<blockquote>
<p>Return a string containing a printable representation of an object. For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(); otherwise, the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this function returns for its instances by defining a <code>__repr__()</code> method. If sys.displayhook() is not accessible, this function will raise RuntimeError.</p>
</blockquote>
<p>&quot;Return a string containing a printable representation of an object&quot; 문장 때문에 <code>__repr__()</code>  을 <code>__str__()</code> 처럼 재 정의 해서 사용하는 경우가 있는 것이 아닌지?</p>
<p>더 중요한 건!</p>
<p><strong>* eval(repr(object)) 를 했을 때 객체를 생성할 수 있어야 한다는 것! *</strong> 이다.</p>
<p>예를 들어 설명하면,</p>
<ul>
<li><code>repr()</code> 함수를 사용하면 <code>eval()</code> 함수로 객체를 생성할 수 있다.<pre><code class="language-python">a = &#39;Difference between str() and repr()&#39;
b = repr(a)
c = eval(b)
</code></pre>
</li>
</ul>
<p>print(c)</p>
<pre><code>output:</code></pre><p>Difference between str() and repr()</p>
<pre><code>
* `str()` 을 사용하면 `eval()` 함수로 객체를 사용할 수 없다.

```python
a = &#39;Difference between str() and repr()&#39;
b = str(a) # b에 str(a) 를 사용한 것에 집중!
c = eval(b)</code></pre><p>output:</p>
<pre><code>Traceback (most recent call last):
  File &quot;/workspace/firstContainer/data_structure/Person.py&quot;, line 27, in &lt;module&gt;
    c = eval(b)
  File &quot;&lt;string&gt;&quot;, line 1
    Difference between str() and repr()
                     ^
SyntaxError: invalid syntax</code></pre><h4 id="3-__repr__-함수를-재정의-해보자">3. <code>__repr()__</code> 함수를 재정의 해보자!</h4>
<p>이제 <code>__repr()__</code>  함수를 재정의 하여 eval() 를 이용하여 객체를 생성할 수 있도록 하자.</p>
<pre><code class="language-python">class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age</code></pre>
<p>클래스를 위와 같이 정리해 놓고 객체를 생성하여 <code>print()</code> 로 출력해 보자.
한 번은 그냥 출력하고, 또 한번은 <code>repr()</code> 함수를 사용하여 출력한다.</p>
<pre><code class="language-python">tom = Person(&#39;Tom&#39;, 20)
print(tom)
print(repr(tom))</code></pre>
<p>output:</p>
<pre><code>&lt;__main__.Person object at 0x7f85cd1dd490&gt;
&lt;__main__.Person object at 0x7f85cd1dd490&gt;</code></pre><p><code>print(tom)</code> 와 <code>print(repr(tom))</code> 모두 동일한 값을 출력 함을 알 수 있다.</p>
<p>이제 <code>__str()__</code> 함수를 재정의 하고 객체를 <code>print()</code> 로 출력해 보자.</p>
<pre><code class="language-python">class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __del__(self):
        print(&#39;Bye! Bye! &#39; + self.name)

    def __str__(self):
        return &#39;My name is &#39; + self.name + &#39;. I am &#39; + str(self.age) + &#39;.&#39;

tom = Person(&#39;Tom&#39;, 20)
print(tom)
print(repr(tom))</code></pre>
<p>output:</p>
<pre><code>My name is Tom. I am 20.
&lt;__main__.Person object at 0x7f27e44a9590&gt;</code></pre><p>자, 이제 <code>eval()</code> 함수를 이용하여 객체를 생성할 수 있도록 <code>__repr()__</code> 를 재정의하자.</p>
<p>** 어려울 것 없다. **</p>
<p>그 객체에 해당하는 ** 생성자 ** 를 호출하는 구문을 넘겨주면 된다.
Tom 의 경우는 <code>repr(tom)</code> 을 했을 경우 <code>Person(&#39;Tom&#39;, 20)</code> 값이 반환되면 된다.</p>
<pre><code class="language-python">class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __del__(self):
        print(&#39;Bye! Bye! &#39; + self.name)

    def __str__(self):
        return &#39;My name is &#39; + self.name + &#39;. I am &#39; + str(self.age) + &#39;.&#39;

    def __repr__(self):
        return &#39;Person(\&#39;&#39;+ self.name +&#39;\&#39;, &#39; + str(self.age) +&#39;)&#39;

print(tom)
print(repr(tom))
print(eval(repr(tom)))</code></pre>
<p>output:</p>
<pre><code>My name is Tom. I am 20.
Person(&#39;Tom&#39;, 20)
My name is Tom. I am 20.</code></pre><p><code>print(repr(tom))</code> 값이 생성자를 호출하는 <code>문자열</code> 임을 결과에서 볼수 있다.
그리고 <code>eval()</code> 함수를 통해 객체를 생성하고 <code>print()</code> 함수를 호출했을 때, <code>__str__(self)</code> 가 호출되어 결과가 나오는 것을 확인 할 수 있다.</p>
<h2 id="💎-thinking">💎 Thinking</h2>
<ul>
<li><code>class</code> 를 정의할 때 <code>print()</code> 함수 대신 <code>__str__(self)</code> 를 정의해서 쓰자!</li>
<li><code>__repr__(self)</code> 는 <code>eval()</code> 을 사용하여 객체를 사용할 수 있도록 재정의 하자!</li>
<li><code>__str__(self)</code> 가 정의되어 있지 않으면, <code>default</code> 로 <code>__repr__(self)</code> 가 호출된다는 것을!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[20230102 TIL | 파이썬 초기화 메서드, 소멸자 메서드]]></title>
            <link>https://velog.io/@_choongyul/20230102-TIL-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%B4%88%EA%B8%B0%ED%99%94-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%86%8C%EB%A9%B8%EC%9E%90-%EB%A9%94%EC%84%9C%EB%93%9C</link>
            <guid>https://velog.io/@_choongyul/20230102-TIL-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%B4%88%EA%B8%B0%ED%99%94-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%86%8C%EB%A9%B8%EC%9E%90-%EB%A9%94%EC%84%9C%EB%93%9C</guid>
            <pubDate>Mon, 02 Jan 2023 07:18:47 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>1<sup>st</sup> January 2023</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<ul>
<li><code>python</code>으로 <code>LinkedList</code> 를 자료구조를 구현해보라고?</li>
</ul>
<hr>
<p><strong>* note *</strong></p>
<p><code>python</code> 으로 클래스를 만드는 법은 확인했다. 그럼 어떻게 객체를 초기화하고, 어떻게 삭제하는 거지? Check! Check!</p>
<hr>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://www.fun-coding.org/PL&amp;OOP1-4.html">파이썬 객체지향 프로그래밍</a></li>
</ul>
<h2 id="🎽-learn">🎽 Learn</h2>
<h4 id="1-생성자--__init__self">1. 생성자 : <code>__init__(self)</code></h4>
<ul>
<li>객체가 생성될 때 자동으로 호출된다.</li>
<li>Method 이므로 첫 번째 Argument는 반드시 <code>self</code> 이다.<pre><code class="language-python">class Person:
 def __init__(self, name, age):
     self.name = name
     self.age = age
</code></pre>
</li>
</ul>
<p>tom = Person(&#39;Tom&#39;, 20)</p>
<pre><code>&gt; `self` 는 `class` 가 아닌 `object` 를 나타낸다.

#### 2. 소멸자 : `__del__(self)`
* 객체가 소멸할 때 자동으로 호출된다.
* Method 이므로 첫 번째 Argument는 반드시 `self` 이다.
```python
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __del__(self):
        print(&#39;bye! bye! &#39; + self.name)

tom = Person(&#39;Tom&#39;, 20)
del tom</code></pre><p>Output</p>
<pre><code>Bye! Bye! Tom</code></pre><h2 id="💎-thinking">💎 Thinking</h2>
<h3 id="생성자-함수와-초기화-함수에-대해">생성자 함수와 초기화 함수에 대해</h3>
<p><code>ptyhon</code> 에서는 생성자 함수와 초기화 함수가 분리되어 있다고 한다.</p>
<ul>
<li><code>__init__</code> 은 초기화 함수</li>
<li><code>__new__</code> 는 생성자 함수</li>
</ul>
<p>별거 없지 않나, <strong>그냥 메모리를 할당하는 부분과 변수를 초기화 하는 부분이 다른 언어와 다르게 분리되어 있다는 것!</strong> 근데 문법을 보면 이해가 간다.</p>
<pre><code class="language-python">### 파이썬에서 객체 생성하기
tom = Person(&#39;Tom&#39;, 20)</code></pre>
<pre><code class="language-java">// JAVA에서 객체 생성하기
Person tom = new Person(&quot;Tom&quot;, 20);</code></pre>
<p><code>JAVA</code> 에서 보는 <strong>뚜렷한</strong> <code>new</code> 키워드!
<code>JAVA</code> 문법을 풀어보면 </p>
<ul>
<li><code>new</code> 메모리를 할당하라!</li>
<li><code>Person(&quot;Tom&quot;, 20)</code> 변수를 초기화 하라!
위와 같이 되지 않을까?</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[20221223 TIL | python으로 class를 어떻게 만들지?]]></title>
            <link>https://velog.io/@_choongyul/20221223-TIL-Linked-List%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%84%B8%EC%9A%94.-%EC%B2%AB%EB%B2%88%EC%A7%B8</link>
            <guid>https://velog.io/@_choongyul/20221223-TIL-Linked-List%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%84%B8%EC%9A%94.-%EC%B2%AB%EB%B2%88%EC%A7%B8</guid>
            <pubDate>Fri, 30 Dec 2022 05:36:47 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>23<sup>rd</sup> December 2022</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<ul>
<li><code>python</code>으로 <code>LinkedList</code> 를 자료구조를 구현해보라고?</li>
</ul>
<hr>
<p><strong>* note *</strong></p>
<p><code>LinkedList</code> 를  구현하기 위해서는 데이터를 저장하는 변수 <code>data</code> 와 다음 데이터의 위치를 저장하는 <code>next</code> 가 필요하다. <code>data</code> 와 <code>next</code> 를 하나로 관리하기 위해서 <code>class</code> 를 만들어보자. 근데 <code>python</code> 으로 <code>class</code> 를 어떻게 만드는 거야? <code>python</code> 문법 부터 공부! 공부!</p>
<hr>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://www.fun-coding.org/PL&amp;OOP1-3.html">파이썬 객체지향 프로그래밍</a></li>
</ul>
<h2 id="🎽-learn">🎽 Learn</h2>
<ol>
<li><code>class</code> 를 선언하자<pre><code class="language-python">class Person:
 pass
</code></pre>
</li>
</ol>
<pre><code>* `class` 키워드 뒤에 유일한 클래스 이름을 지어준다.
* 클래스명은 PEP Coding Convention에 가이드된 대로 각 단어의 첫 문자를 대문자로 하는 CapWords 방식을 사용한다.
* 클래스 이름 뒤에는 `:` 을 반드시 붙여준다.

2. `attribute` 를 넣어보자
```python
class Person:
    name = name = &#39;No Name&#39;
    age = 20
</code></pre><ul>
<li><code>Person</code> 을 대표 할 수 있는 <code>name</code> 과 <code>age</code> 를 <code>attribute</code> 로 넣는다.</li>
</ul>
<ol start="3">
<li><code>attribute</code> 에 접근해 보자<pre><code class="language-python">tom = Person()
</code></pre>
</li>
</ol>
<p>tom.name
tom.age</p>
<pre><code>
* `object_name.attribute` 이런 형식으로 `attribute` 에 접근한다.  `class` 명이 아니라 `object` 를 사용한다는 점. 그리고 `.` 을 이용한다는 점을 기억한다.

4. `method` 를 넣어보자
```python
class Person:
    name = &#39;No Name&#39;
    age = 20

    def print_your_name(self):
        return &quot;my name is &quot; + self.name
</code></pre><ul>
<li>파이썬 Method는 항상 첫 번째 파라미터로 self를 사용해야 한다.</li>
<li>Method 안에서 클래스 <code>attribute</code>를 접근할 때는 <code>self.attribute</code> 로 접근해야 한다.</li>
</ul>
<ol start="5">
<li><p>객체의 <code>Method</code> 에 접근해보자</p>
<pre><code class="language-python">class Person:
 name = &#39;No Name&#39;
 age = 20

 def print_my_name(self):
     return &quot;My name is &quot; + self.name

 def set_my_info(self, name, age):
     self.name=name
     self.age=age</code></pre>
</li>
</ol>
<ul>
<li><code>객체명.method명</code> 으로 접근한다.<pre><code class="language-python">tom = Person()
tom.set_my_info(&#39;Tom&#39;, 20)
tom.print_my_name()</code></pre>
실행결과:<pre><code>My name is Tom</code></pre></li>
</ul>
<h2 id="🎁-summary">🎁 Summary</h2>
<h3 id="python-oop"><code>Python</code> OOP</h3>
<ul>
<li>class를 선언하기 위해 <code>class</code> 키워드를 사용한다.</li>
<li><code>self</code> 키워드는 <code>instance</code> 를 나타내는 의미 이외에는 존재하지 않는다.</li>
<li><code>instance</code> 귀속 된 <code>method</code> 와 <code>attribute</code> 에 접근할 때 명확하게 하기위해 <code>self</code> 키워드를 사용하는 것 뿐이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022년 회고록]]></title>
            <link>https://velog.io/@_choongyul/2022%EB%85%84-%ED%9A%8C%EA%B3%A0%EB%A1%9D-anyovspb</link>
            <guid>https://velog.io/@_choongyul/2022%EB%85%84-%ED%9A%8C%EA%B3%A0%EB%A1%9D-anyovspb</guid>
            <pubDate>Thu, 29 Dec 2022 04:31:19 GMT</pubDate>
            <description><![CDATA[<h3 id="2023년을-위해-2022년을-정리해-보자">2023년을 위해 2022년을 정리해 보자.</h3>
<p>어느덧 12월인가 싶더니 벌써 12월 마지막 주가 되었다. 올 12월은 유난히 빨리 흐르는 듯하다. 그리고 모든 것들이 살짝 느슨해진다. 
내 마음도, 내 생활도. </p>
<p>2023년을 위해 얼마 남지 않은 2022년을 정리하려 한다. 열심히 살았던 2022년의 내 발걸음이 밑거름이 되어 다가올 2023년을 더 풍성하게 할 것을 믿으니까 말이다.</p>
<h3 id="새로운-업무에-적응하기">새로운 업무에 적응하기!</h3>
<p>2021년 7월, 운영 조직으로 팀을 옮겼다. 6개월 동안은 배움의 기간을 가졌고 2021년 12월에 내가 운영해야 할 사이트를 배정받았다. 2021년 12월부터 일이 몰려들기 시작하더니 오랜만에 컴퓨터 앞에서 단내 나는 경험을 했다. 퇴근할 때면 뻐근해져 오는 손목과 마르는 입안을 느끼며 한숨을 쉬었다. 그렇게 한 달을 채워갔다. 한 달이 지난 후 월급의 숫자는 나를 또 한숨 쉬게 만들었다. 이 돈 받고 개미처럼 일하는 것에 회의를 느끼게 했다.</p>
<p>‘이직을 해야겠다.’ 는 생각이 마음 속에 가득 차기 시작했다.</p>
<p>10년 넘게 관리하지 않았던 이력서와 경력기술서를 손보고 몇 군데 회사에 지원했다. 하지만 결과는 면접조차 가지 못했다. </p>
<h3 id="언제나-문제는-나에게-있다">언제나 문제는 나에게 있다.</h3>
<p>‘뭐가 문제야!’ </p>
<p>울분이 쌓이고 마음이 시리더라. 하지만 어쩌랴 결국 문제는 나에게 있는걸. 답이 정해져 있는 시키는 일만 했던 과거의 업보였다. 업무에 임하는 마음을 바꿔야겠다고 다짐했다.</p>
<p><span style="color:#FD841F;font-weight:800;"><strong>답을 찾는 건 당연한 것!</strong></span>
<span style="color:#FD841F;font-weight:800;"><strong>먼저, 문제 정의하자!</strong></span>
<span style="color:#FD841F;font-weight:800;"><strong>내가 찾은 문제를 내가 제일 먼저 풀어 버리자!</strong></span></p>
<p>주변에 문제들을 애써 외면하며, 누군가 시키면 마지못해 일했던 나에게서 벗어나야 했다. 주변을 살펴 문제를 정의하고 그 문제에 답을 찾는 연습 통해 실력을 키워야 했다. 그다음 이직을 진행하기로 마음 먹었다.</p>
<h3 id="반드시-안해도-되는-일에-도전하기">반드시 안해도 되는 일에 도전하기!</h3>
<p>업무가 바뀐 지 얼마 되지 않아 업무에서 문제를 발견하는 건 쉽지 않았다. 그 대신, 안해도 되는 일. 누가 시키지 않을 일을 찾았다. </p>
<ul>
<li>사내 MSA 자격시험</li>
<li>진급 시험</li>
<li>빅데이터 분석 기사 시험</li>
<li>PM 시험</li>
</ul>
<p>2022년 동안 총 4개의 시험에 응시했다. 
결과는 모두 합격! 
마지막 시험을 보고 결과를 확인할 때는 마음이 조금 &#39;허&#39;하더라. </p>
<h3 id="틈을-어떻게-메우느냐에-따라-나의-삶은-달라진다">&#39;틈&#39;을 어떻게 메우느냐에 따라 나의 삶은 달라진다.</h3>
<p>2022년을 보내며, 시간이 없는 핑계로 항상 도전하지 않고 살았던 지난날의 나를 반성한다. 시간이 없다고 생각하니 시간이 없는 거였다. 언제나 ‘틈’은 있고, 그 ‘틈’을 어떻게 메우느냐에 따라 하루의 밀도가 달라지는 경험을 했다.</p>
<p>딱 정해진 시간에 무언가를 하려 하면 그 시간은 절대 오지 않더라.
설령 그 시간이 온다고 해도, 또 다른 핑계를 만들어 난 달아나 버리더라.</p>
<p><span style="color:#FD841F;font-weight:800;"><strong>‘그냥, 하면 된다. 경계를 무너트리고, 틈이 날 때마다.’</strong></span></p>
<h3 id="뿌리-깊은-나무가-되길">뿌리 깊은 나무가 되길!</h3>
<p>2023년에는 나의 역량의 근본을 다져야겠다. IT 쪽 일을 하고 있으니 IT의 근간인 컴퓨터 공학, 자료구조, 알고리즘에 관한 학습을 해야겠다.
이제서야 깨달은 것 같다. ‘뿌리가 단단한 나무는 흔들리지 않는다’는 말의 의미를.</p>
<p><span style="color:#FD841F;font-weight:800;"><strong>잘가라! 2022년!</strong></span>
<span style="color:#FD841F;font-weight:800;"><strong>환영한다! 2023년!</strong></span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[20221222 TIL | 내가 자랑스러워하는 내모습은 어떤 모습일까?]]></title>
            <link>https://velog.io/@_choongyul/20221222-TIL-%EB%82%B4%EA%B0%80-%EC%9E%90%EB%9E%91%EC%8A%A4%EB%9F%AC%EC%9B%8C%ED%95%98%EB%8A%94-%EB%82%B4%EB%AA%A8%EC%8A%B5%EC%9D%80-%EC%96%B4%EB%96%A4-%EB%AA%A8%EC%8A%B5%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@_choongyul/20221222-TIL-%EB%82%B4%EA%B0%80-%EC%9E%90%EB%9E%91%EC%8A%A4%EB%9F%AC%EC%9B%8C%ED%95%98%EB%8A%94-%EB%82%B4%EB%AA%A8%EC%8A%B5%EC%9D%80-%EC%96%B4%EB%96%A4-%EB%AA%A8%EC%8A%B5%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Thu, 22 Dec 2022 12:43:30 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>22<sup>nd</sup> December 2022</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<ul>
<li>내가 자랑스러워 하는 내모습은 어떤 모습일까?</li>
</ul>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li>나는 나이기에 가치있고, 나 다울 때 가장 빛납니다. (생각의 기쁨 - 유병욱 지음)</li>
</ul>
<h2 id="💎-thinking">💎 Thinking</h2>
<p>점심을 먹고 나오는 길에 동료가 내 머리를 보며 이야기했다.
&quot;혹시, 매달 파마하는 거예요?&quot;</p>
<p>&quot;아니요. 저 원래 곱슬이에요&quot; 라고 난 답했다.</p>
<p>그 말에 동료는 매우 놀라며
&quot;아니, 자연산 곱슬이 왜 이리 이뻐!&quot; 라고 말했다.</p>
<p>신기한 경험이었다. 내가 가진 것들 중 가장 싫어하는 것 중 하나인 곱슬머리가 남에게 좋아 보일 수 있다니. 사실, 난 저 곱슬거리는 머리 때문에 항상 힘들었다. 어렸을 때는 친구에게 머리에 폭탄 맞았냐며 놀림을 당했다. 좀 더 커서는 뭘해도 정리되지 않은 저 머리 때문에 계속해서 신경 쓰였고, 대학 기간 동안 삭발하고 다녔던 기간도 꽤 있었다. 다시 태어난다면 길게 뻗은 직모로 태어나고 싶었다.</p>
<p>&#39;근데, 내 머리가 예쁘다니!&#39;</p>
<p>정말 책에 있는 그대로인 것 같다.</p>
<blockquote>
<p>&#39;내가 남들의 어떤 부분을 부러워하는 만큼, 누군가가 어딘가에서 나의 어떤 부분을 부러워하고 있나 보다.&#39; (생각의 기쁨 중에서)</p>
</blockquote>
<p>어차피 바꿀 수 없는 것! 이럴바에야! 
남들에게서 부족한 부분을 찾으려 하는 것이 아니라, 나에게 시선을 돌려 자랑스러운 모습을 찾아 봐야겠다. </p>
<p>찬찬히 공들여 찾아봐야겠다.</p>
<p>따뜻한 시선을 나에게로 돌려 놓아야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[20221221 TIL | List 를 사용하여 Queue 자료구조를 구현하세요.]]></title>
            <link>https://velog.io/@_choongyul/20221221-TIL-List-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Queue-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%98%EC%84%B8%EC%9A%94</link>
            <guid>https://velog.io/@_choongyul/20221221-TIL-List-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Queue-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%98%EC%84%B8%EC%9A%94</guid>
            <pubDate>Wed, 21 Dec 2022 02:44:38 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>21<sup>st</sup> December 2022</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<ul>
<li><code>python</code> <code>List</code> 를 사용해서 <code>Queue</code> 자료구조를 구현해보라고?</li>
</ul>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://docs.python.org/ko/3.7/library/queue.html#queue.SimpleQueue">동기화된 큐 클래스</a></li>
<li><a href="https://docs.python.org/ko/3/tutorial/datastructures.html">파이썬 자습서 &gt; 5.자료구조</a></li>
</ul>
<h2 id="🎽-learn">🎽 Learn</h2>
<ul>
<li><code>java</code>에서 사용하는 <code>java.util.Queue</code> 나 <code>C++</code> 에서 제공하는 <code>STL: Standard Template Library</code> 수준으로 Queue 자료 구조를 구현하는 건 매우 복잡하고 어려운 일이다.</li>
<li>여기서 필요한 건 <code>enqueue</code> 와 <code>dequeue</code> 를 <code>List</code> 를 사용해서 어떻게 구현 할 것지? 가 관건!</li>
<li>기능을 살펴보면,<ul>
<li>enqueue : 데이터를 넣는다.</li>
<li>dequeue : 데이터를 반환하고, 삭제한다.</li>
</ul>
</li>
<li>이제 <code>List</code> 를 사용해 기능을 구현해보면,</li>
</ul>
<pre><code class="language-python">q = [] # 큐로 사용할 List 생성

def enqueue(item):
    q.append(item)

def dequeue():
    if len(q) &gt; 0
        return q.pop(0)</code></pre>
<h2 id="🎁-summary">🎁 Summary</h2>
<h3 id="list-api">List API</h3>
<ul>
<li><code>list.append(x)</code> : 리스트 끝에 항목을 더한다. <code>list[len(list):]=[x]</code> 와 같다.</li>
<li><code>list.pop([i])</code> : 리스트에서 주어진 위치에 있는 항목을 삭제하고, 그 항목을 돌려준다. 인덱스를 지정하지 않으면, <code>list.pop()</code>은 리스트의 마지막 항목을 삭제하고 돌려준다. ( 메서드 시그니처에서 <code>i</code> 를 둘러싼 대괄호는 매개변수가 선택적임을 나타낸다. 그 위치에 대괄호를 입력해야 한다는 뜻이 아니다. )</li>
<li><code>list[i]</code> : 인덱스에 있는 값에 접근 할 수 있다.</li>
<li><code>del list[i]</code> : 리스트에서 값 대신 인덱스를 사용해서 항목을 삭제하는 방법이다.<pre><code class="language-python"># del example
a = [-1, 1, 66, 25, 333, 333, 1234.5]
del a[0] # -1 을 삭제한다.
# 결과값 [1, 66, 25, 333, 333, 1234.5]
del a[2:4] # 25, 333 을 삭제한다.
# 결과값 [1, 66, 333, 1234.5]
del a # 변수 a 자체를 삭제한다. 이후에 이름을 삭제할 수 없다.</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[20221220 TIL | 1부터 10까지 역순으로 숫자 출력하세요.]]></title>
            <link>https://velog.io/@_choongyul/TIL</link>
            <guid>https://velog.io/@_choongyul/TIL</guid>
            <pubDate>Tue, 20 Dec 2022 05:17:44 GMT</pubDate>
            <description><![CDATA[<h2 id="📆-date">📆 Date</h2>
<ul>
<li>20<sup>th</sup> December 2022</li>
</ul>
<h2 id="🔑-problem">🔑 Problem</h2>
<ul>
<li><code>python</code>에서 역순으로 10부터 1까지 숫자 출력하려면 Index를 어떻게 해야하지?</li>
</ul>
<h2 id="🛰️-reference-site">🛰️ Reference Site</h2>
<ul>
<li><a href="https://cs.stanford.edu/people/nick/py/python-range.html">Python range() Function</a></li>
<li><a href="https://cs.stanford.edu/people/nick/py/">Stanford Python Guide</a></li>
</ul>
<h2 id="🎽-learn">🎽 Learn</h2>
<ul>
<li><code>JAVA</code>는 for 문의 index를 하나씩 감소하면서 출력하면 된다.<pre><code class="language-java">for(int i=10; i&gt;0; i--) {
  print(i)
}</code></pre>
</li>
<li><code>python</code>은 `range(start, stop, step) 함수를 사용한다.<pre><code class="language-python">for i in range(10, 0, -1)
  print(i)</code></pre>
</li>
<li><code>JAVA</code>의 <code>IntStream.range(int startInclusive, int endExclusive)</code> 와 비슷한데, <code>step</code> 이 파라미터로 추가되어 사용하기 더 편하다.<h2 id="🎁-summary">🎁 Summary</h2>
</li>
<li>range(stop): range(10)은 0, 1, 2, 3, 4, 5, 6, 7, 8, 9</li>
<li>range(start, stop): range(1,11) 은 1, 2, 3, 4, 5, 6, 7, 8, 9, 10</li>
<li>range(start, stop, step):<ul>
<li>start, stop, step은 음수로 지정 가능</li>
<li>range(0,20,2)은 0, 2, 4, 6, 8, 10, 12, 14, 16, 18</li>
<li>range(10,0,-1)은 10, 9, 8, 7, 6, 5, 4, 3, 2, 1</li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>