<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sung-je-kim</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 02 Jan 2025 06:42:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sung-je-kim</title>
            <url>https://velog.velcdn.com/images/sung-je-kim/profile/98b6e4dc-10e1-4f1b-a9f9-370d24421ab5/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sung-je-kim. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sung-je-kim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[자료구조] 이진트리 (feat. python)]]></title>
            <link>https://velog.io/@sung-je-kim/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A7%84%ED%8A%B8%EB%A6%AC-feat.-python</link>
            <guid>https://velog.io/@sung-je-kim/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A7%84%ED%8A%B8%EB%A6%AC-feat.-python</guid>
            <pubDate>Thu, 02 Jan 2025 06:42:55 GMT</pubDate>
            <description><![CDATA[<h2 id="binary-tree">Binary Tree</h2>
<p>트리 구조의 형태를 띄고 있으며, 일반적으로 0~2의 자식 노드를 가지고 있는 것이 특징이다.</p>
<blockquote>
<p>용어:</p>
</blockquote>
<ul>
<li>Root Node: 제일 상위에 있는 부모 노드</li>
<li>Leaf Node: 제일 하단에 있는 자식 노드</li>
<li>Node: Root 또는 Leaf가 아닌 내부 노드</li>
<li>Link or Edge: Node와 Node를 잇는 간선</li>
<li>Key: 노드가 가지고 있는 값</li>
<li>$$h$$: 트리의 높이</li>
<li>path: $$x_1$$ ~ $$x_2$$ 노드까지 최단 경로 Edge의 갯수</li>
</ul>
<u>
</u>

<h2 id="binary-tree의-종류">Binary Tree의 종류</h2>
<p><strong>일반 이진 트리</strong>(Binary Tree)</p>
<ul>
<li><u>가장 기본적</u>인 이진 트리를 칭하고, <u>0~2개의 Children Node</u>를 가지고 있다.</li>
<li>구조에 대한 제약이 없다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/2e74052f-3dd0-4e47-9a67-63fbf4108d80/image.png" alt=""><br/>

</li>
</ul>
<p><strong>포화 이진 트리</strong>(Full Binart Tree)</p>
<ul>
<li><u>모든 노드가 0 or 2 개의 Children Node</u>를 가진다.</li>
<li>모든 내부에 있는 Node는 정확히 두 개의 Children Node를 가진다.</li>
<li>서로 다른 <u>Leaf Node는 동일한 Lv가 아니여도 된다</u>.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/ef4fb7ca-2e15-4539-87f3-d368b5b3f266/image.png" alt=""><br/>

</li>
</ul>
<p><strong>완전 이진 트리</strong>(Complete Binary Tree)</p>
<ul>
<li>마지막 Lv을 제외한 모든 Lv이 완전히 채워져 있다.</li>
<li><u>마지막 Lv은 왼쪽부터</u> 채워져야 하되, <u>가득 차있을 필요는 없다</u>.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/3521d3a7-c9c3-4b5f-9a1c-283b4335710f/image.png" alt=""><br/>

</li>
</ul>
<p><strong>완벽 이진 트리</strong>(Perfect Binary Tree)</p>
<ul>
<li>Leaf Node를 제외한 <u>모든 Node는 정확히 두 개의 Children Node를 가진다</u>.</li>
<li>서로 다른 <u>Leaf Node는 모두 동일한 Lv</u>에 해당해야 한다.</li>
<li>$$N$$이 노드의 총 갯수라고 하면 총 갯수 수식은 아래와 같다.
수식: $$N = 2^{h+1} - 1$$
<img src="https://velog.velcdn.com/images/sung-je-kim/post/e9df8a3f-7cce-4a49-bc24-c4e09a2c1f08/image.png" alt=""><br/>


</li>
</ul>
<p><strong>균형 이진 트리</strong>(Balanced Binary Tree) </p>
<ul>
<li>모든 노드에서 <u>Left-SubTree($$LH$$)와 Right-SubTree($$RH$$)의 높이 차이가 최대 1</u>이다.
수식: $$|LH - RH| = 0$$ or $$|LH - RH| = 1$$</li>
<li>효율적인 탐색, 삽입, 삭제 연산을 보장한다.</li>
<li>ex) AVL Tree, Red-Black Tree 가 균형 이진 트리에 해당한다.
$$\rightarrow$$ 추후에 다룰 예정
<img src="https://velog.velcdn.com/images/sung-je-kim/post/2fbe6fe9-d57a-44f4-8209-297ec1591497/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] HashTable (feat. python)]]></title>
            <link>https://velog.io/@sung-je-kim/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-HashTable-feat.-python</link>
            <guid>https://velog.io/@sung-je-kim/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-HashTable-feat.-python</guid>
            <pubDate>Mon, 16 Dec 2024 08:28:38 GMT</pubDate>
            <description><![CDATA[<h2 id="hashtable">HashTable</h2>
<p>평균적으로 매우 빠른 삽입, 삭제, 탐색이 가능하며 이는 사전식(key, value) 자료구조를 가지고 있기 때문이다.</p>
<blockquote>
<ul>
<li><strong>HashTable 성능 척도</strong></li>
</ul>
</blockquote>
<ol>
<li><strong>Less Collision</strong> → 적은 충돌</li>
<li><strong>Fast Compution</strong> → 빠른 계산</li>
</ol>
<p>1과 2는 trade-off 관계이기 때문에, 주어진 자원과 상황에 맞게 적절한 조화를 가지는 것이 중요하다.</p>
<p><br/><br/><br/></p>
<h3 id="--hashfunction의-등급">- HashFunction의 등급</h3>
<blockquote>
<ul>
<li>$$S =$$ 입력값의 집합</li>
</ul>
</blockquote>
<ul>
<li><p>$$h =$$ 해시 함수</p>
</li>
<li><p>$$m =$$ 슬롯(Slot)의 갯수 $$\ \fallingdotseq$$ $$\ h(x)$$의 범위 크기</p>
</li>
<li><p>$$P(h(x_1) = h(x_2)) =$$ 서로 다른 값의 충돌(Collision) 확률</p>
</li>
<li><p><strong>완전 해시 함수 (Perfect HashFunction</strong> $$=$$ Injective HashFunction)
<strong>완전 해시 함수</strong>는 충돌이 없는 해시 함수이다, 특정한 집합 $$S$$의 각 원소를 고유한 정수로 매핑한다. 상수 시간 복잡도($$O(1)$$)로 데이터를 검색할 수 있어 매우 효율적이다.</p>
<p>수식:
$$ 
h(x)=  {0, 1, \ldots, m-1}
$$
즉, 완전 해시 함수는 $$(key : slot = 1 : 1)$$ 관계이다.
<br/><br/></p>
</li>
<li><p><strong>유니버설 해시 함수 (Universal HashFunction)</strong></p>
<blockquote>
<p>조건: m &gt; 0 의 상수</p>
</blockquote>
<p>두 개의 <u>서로 다른 키가 같은 값으로 해싱될 확률을 최소화</u>하는 성질을 가진다. 
즉, $$S$$에 포함된 서로 다른 두 키 $$x_1$$과 $$x_2$$에 대해, 이들이 동일한 값으로 충돌할 확률은 최대 $$1/m$$이다.</p>
<p>수식:
$$ 
\quad P(h(x_1) = h(x_2)) \leq \frac{1}{m}
$$
<br/><br/></p>
</li>
<li><p><strong>C-유니버설 해시 함수 (C-Universal HashFunction)</strong></p>
<blockquote>
<p>조건: m &gt; 0 의 상수, C &gt; 0 의 상수</p>
</blockquote>
<p><strong>C-유니버설 해시 함수</strong>는 유니버설 해싱 개념을 확장하여 <u>충돌 확률을 제어</u>할 수 있도록 합니다. 
이 경우, $$S$$에 포함된 서로 다른 두 키 $$x_1$$과 $$x_2$$가 동일한 값으로 충돌할 확률은 최대 $$C/m$$입니다.</p>
<p>수식:
$$ 
\quad P(h(x_1) = h(x_2)) \leq \frac{C}{m}
$$</p>
</li>
</ul>
<blockquote>
<p><strong>요약</strong></p>
</blockquote>
<ul>
<li>*<em>완전 해시 함수: *</em>
주어진 키 집합에 대해 충돌을 완전히 제거</li>
<li>*<em>유니버설 해시 함수: *</em>
일반적인 사용 사례에서 모든 가능한 키 쌍 간의 충돌 확률을 최소화</li>
<li>*<em>C-유니버설 해시 함수: *</em>
조정 가능한 상수 인자를 통해 충돌 확률을 제한</li>
</ul>
<p><br/><br/><br/></p>
<h3 id="--hashfunction의-종류들">- HashFunction의 종류들</h3>
<blockquote>
<ul>
<li>if typeof key === &#39;int&#39;:    <ol>
<li>Division </li>
<li>Mutiplication</li>
<li>Folding </li>
<li>Mid Squeares</li>
<li>Extraction </li>
</ol>
</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>if typeof key === &#39;string&#39;:    <ol>
<li>Additive </li>
<li>Rotating </li>
</ol>
</li>
</ul>
</blockquote>
<p><br/><br/><br/></p>
<h3 id="--충돌-회피-방법-collision-resolution-method">- 충돌 회피 방법 (Collision Resolution Method)</h3>
<h4 id="open-addressing-개방-주소법">Open Addressing (개방 주소법)</h4>
<ol>
<li><p><strong>Linear Probing (선형 탐사)</strong>  </p>
<ul>
<li><strong>설명</strong>: 충돌이 발생하면 현재 충돌이 발생한 인덱스에서 <strong>다음 인덱스</strong>를 순차적으로 검사하며 비어 있는 슬롯을 찾음.  </li>
<li>예: 해시값이 3에서 충돌이 발생하면, 4, 5, ... 순서로 빈 슬롯을 탐색.  </li>
<li><strong>장점</strong>: 구현이 간단.  </li>
<li><strong>단점</strong>: 클러스터링(Clustering) 문제가 발생할 수 있으며, 이는 데이터가 특정 영역에 몰려 평균 탐색 시간이 증가하는 현상.</li>
</ul>
</li>
<li><p><strong>Quadratic Probing (이차 탐사)</strong>  </p>
<ul>
<li><strong>설명</strong>: 충돌이 발생하면 <strong>제곱수의 간격</strong>으로 다음 슬롯을 탐색.  </li>
<li>예: 해시값이 3에서 충돌이 발생하면, 4(1²), 7(2²), 12(3²) 순서로 빈 슬롯을 탐색.  </li>
<li><strong>장점</strong>: 클러스터링 문제를 줄일 수 있습니다.  </li>
<li><strong>단점</strong>: 여전히 이차 클러스터링 문제가 발생할 수 있으며, 테이블의 크기가 소수(prime number)가 아니면 모든 슬롯을 탐색하지 못할 수 있음.</li>
</ul>
</li>
<li><p><strong>Double Hashing (이중 해싱)</strong>  </p>
<ul>
<li><strong>설명</strong>: 두 개의 서로 다른 해시 함수를 사용하여 충돌 시 이동 폭(probe step)을 계산. 첫 번째 해시 함수는 초기 위치를 결정하고, 두 번째 해시 함수는 충돌 시 이동 간격을 제공.  </li>
<li>예: 
$$h_1(k) = k \mod m$$, 
$$h_2(k) = 1 + (k \mod (m-1))$$. 
충돌 시: $$h(k, i) = (h_1(k) + i \cdot h_2(k)) \mod m$$.  </li>
<li><strong>장점</strong>: 클러스터링 문제를 효과적으로 해결할 수 있음.  </li>
<li><strong>단점</strong>: 구현이 복잡하며 추가적인 계산 비용이 필요.</li>
</ul>
</li>
</ol>
<hr>
<h4 id="chaining-체이닝">Chaining (체이닝)</h4>
<ul>
<li><strong>설명</strong>: 각 해시 테이블 슬롯에 연결 리스트(Linked List)를 사용하여 데이터를 저장합니다. 충돌이 발생하면 해당 슬롯의 연결 리스트에 노드를 추가합니다.  </li>
<li><strong>장점</strong>: 테이블의 크기 제한 없이 데이터를 저장할 수 있습니다. 최악의 경우에도 테이블 크기와 무관하게 데이터를 계속 추가할 수 있습니다.  </li>
<li><strong>단점</strong>: 연결 리스트가 길어질 경우 검색, 삽입, 삭제의 시간 복잡도가 $$O(n)$$으로 증가할 수 있습니다. 이를 해결하기 위해 연결 리스트 대신 트리 구조(Binary Search Tree)를 사용할 수도 있습니다.</li>
</ul>
<p><br/><br/><br/></p>
<h3 id="--주요-함수set-remove-search의-동작들이-영향-받는-것">- 주요 함수(set, remove, search)의 동작들이 영향 받는 것</h3>
<ol>
<li><p>Cluster의 크기에 영향:</p>
<ul>
<li>평균적으로 $$2n \leq m$$ 과 같이 최소 50% 이상 빈 Slot을 놔두면 Cluster의 평균 Size가 $$O(1)$$이 되게 할 수 있다.</li>
</ul>
</li>
<li><p>Cluster의 크기는 아래의 세가지에 영향
 2-1. HashFunction 성능</p>
<p> 2-2. Collision Resolution method 성능</p>
<ul>
<li><p>$$\frac{c}{n}= \frac{number \ of \ collision}{items}$$ 와 같은 방법으로 성능 평가 가능</p>
<p>2-3. Load Factor: </p>
</li>
<li><p>$$\frac{n}{m} = \frac{items}{slots}$$</p>
</li>
</ul>
</li>
</ol>
<p><br/><br/><br/></p>
<h3 id="--hashtable-remove-시-유의할-점">- HashTable remove 시 유의할 점</h3>
<blockquote>
<ul>
<li><strong>상황 가정</strong></li>
</ul>
</blockquote>
<ul>
<li>해시함수: 
Division HashFunction $$\rightarrow h(x) =$$ $$(x\ mod \ m)$$</li>
<li>충돌회피방법: 
Linear Probing</li>
<li>Slot Size:
$$m = 10$$<pre><code class="language-python">0: S = [10, 2, 22, 24, ...]
1: for s in S: Add(s)
2: ....
3: remove(2)
4: search(22)</code></pre>
위와 같은 상황 가정에서 코드(0<del>1 line)를 실행할 경우 아래와 같이 $$m$$에 데이터가 저장될텐데
0</del>1 line: [10, *, 2, 22, 24, *, *, *, *, *]
0~3 line: [10, *, *, 22, 24, *, *, *, *, *] $$\rightarrow$$ 3-index에 저장된 &#39;22&#39;를 2-index로 옮겨줘야 하는 재보정이 필요함.
삭제(remove) 이후 전체적인 데이터의 보정이 없다면, 그 이후 실행되는 탐색(search)이 영향을 받는다.</li>
</ul>
<p><br/><br/><br/></p>
<h3 id="--hashtable-만들어보기">- HashTable 만들어보기</h3>
<blockquote>
<ul>
<li><strong>해시함수</strong>: Division HashFunction</li>
</ul>
</blockquote>
<ul>
<li><strong>충돌 회피 방법</strong>: Linear Probing &amp; Chaining</li>
</ul>
<pre><code class="language-python">from typing import TypeVar, List

V_T = TypeVar(&#39;value_type&#39;)
TableType = List[List[tuple[str, V_T]]]

class HashTable:
    def __init__(self, length=10):
        self.max_len = length
        self.table: TableType = [[] for _ in range(self.max_len)]

    def __len__(self):
        return self.max_len

    def __str__(self):
        return str(self.table)

    def __iter__(self):
        for item in self.table:
            yield item

    def __hash__(self, key: str) -&gt; int:
        target = sum([ord(s) for s in key])
        return target % self.max_len

    def __eq__(self, other):
        return self.table == other.table

    def find_slot(self, key) -&gt; int:
        return self.__hash__(key)

    def set(self, key, value):
        index = self.find_slot(key)
        for i, (k, v) in enumerate(self.table[index]):
            if k == key:
                self.table[index][i] = (key, value)
                return
        self.table[index].append((key, value))

    def search(self, key):
        index = self.find_slot(key)
        for k, v in self.table[index]:
            if k == key:
                return v
        return None

    def remove(self, key) -&gt; None or str:
        index = self.find_slot(key)
        for i, (k, v) in enumerate(self.table[index]):
            if k == key:
                del self.table[index][i]
                return key
        return None</code></pre>
<blockquote>
<p>참고 영상 및 문서:</p>
</blockquote>
<ul>
<li>영상
[1] <a href="https://youtu.be/Bzmepm6pYQI?si=gfXz_zUrUDKwGf_8">https://youtu.be/Bzmepm6pYQI?si=gfXz_zUrUDKwGf_8</a>
[2] <a href="https://youtu.be/Bj4pd9rJp5c?si=SZO2gXpvBUhfrtAm">https://youtu.be/Bj4pd9rJp5c?si=SZO2gXpvBUhfrtAm</a>
[2] <a href="https://youtu.be/ghjWopXXUeA?si=7GaY4OD515iGYGyj">https://youtu.be/ghjWopXXUeA?si=7GaY4OD515iGYGyj</a></li>
<li>문서
[1] <a href="https://ryu-e.tistory.com/87">https://ryu-e.tistory.com/87</a>
[2] <a href="https://www.nossi.dev/436b3b11-fa77-4dcc-9d24-f6b034d6770e">https://www.nossi.dev/436b3b11-fa77-4dcc-9d24-f6b034d6770e</a>
[3] <a href="https://engineerinsight.tistory.com/332">https://engineerinsight.tistory.com/332</a>
[4] <a href="https://jun-n.tistory.com/26">https://jun-n.tistory.com/26</a>
[5] <a href="https://velog.io/@jaehwan0129/%EC%84%A0%ED%98%95-%EC%A1%B0%EC%82%AC%EB%B2%95Linear-Probing">https://velog.io/@jaehwan0129/%EC%84%A0%ED%98%95-%EC%A1%B0%EC%82%AC%EB%B2%95Linear-Probing</a>
[6] <a href="https://blog.naver.com/beaqon/221300416700">https://blog.naver.com/beaqon/221300416700</a>
[7] <a href="https://eminentstar.tistory.com/40">https://eminentstar.tistory.com/40</a>
[8] <a href="https://nyang-in.tistory.com/117">https://nyang-in.tistory.com/117</a>
[9] <a href="https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%EC%B6%A9%EB%8F%8C">https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%EC%B6%A9%EB%8F%8C</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] LinkedList (feat. python)]]></title>
            <link>https://velog.io/@sung-je-kim/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-LinkedList-feat.-python</link>
            <guid>https://velog.io/@sung-je-kim/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-LinkedList-feat.-python</guid>
            <pubDate>Mon, 02 Dec 2024 06:24:24 GMT</pubDate>
            <description><![CDATA[<h3 id="단방향-연결-리스트">단방향 연결 리스트</h3>
<hr>
<pre><code class="language-python">class Node:
    def __init__(self, key=None):
        self.key = key
        self.next = None

    def __str__(self):
        return str(self.key)


class SinglyLinkedList:
    def __init__(self):
        self.head_node = Node()
        self.size = 0

    def __len__(self):
        return self.size

    def __iter__(self):
        v: Node = self.head_node
        while v is not None:
            yield v
            v = v.next

    def search_by_key(self, key):
        if len(self) == 0: return None

        v: Node = self.head_node
        while v is not None:
            if v.key == key:
                return v
            v = v.next
        return None

    def search_by_index(self, index):
        if len(self) == 0 or index &lt; 0 or len(self) &lt; index:
            return None

        i = 0
        v: Node = self.head_node
        while i != index:
            v = v.next
            i += 1
        return v


    def push_front(self, key):
        new_node = Node(key)
        new_node.next = self.head_node
        self.head_node = new_node
        self.size += 1

    def push_back(self, key):
        new_node = Node(key)
        if len(self) == 0:
            self.head_node = new_node
        else:
            tail: Node = self.head_node
            while tail.next is not None:
                tail = tail.next
            tail.next = new_node
        self.size += 1

    def pop_front(self):
        if len(self) == 0:
            return None

        target: Node = self.head_node
        self.head_node = target.next
        self.size -= 1

        key = target.key
        del target
        return key

    def pop_back(self):
        if len(self) == 0:
            return None

        prev: Node = None
        tail: Node = self.head_node
        while tail.next is not None:
            prev, tail = tail, tail.next

        if len(self) == 1:
            self.head_node = None
        else:
            prev.next = None
        self.size -= 1
        key = tail.key
        del tail
        return key

    def insert(self, index, key):
        if len(self) == 0 or index &lt; 0 or len(self) &lt; index:
            # raise IndexError(&#39;Index Error&#39;)
            return None
        new_node = Node(key)
        i = 0
        prev: Node = None
        target: Node = self.head_node
        while i != index:
            prev, target = target, target.next
            i += 1
        prev.next = new_node
        new_node.next = target
        self.size += 1


    def remove(self, index):
        if len(self) == 0 or index &lt; 0 or len(self) &lt; index:
            # raise IndexError(&#39;Index Error&#39;)
            return None
        i = 0
        prev: Node = None
        target: Node = self.head_node
        while i != index:
            prev, target = target, target.next
            i += 1
        prev.next = target.next
        key = target.key
        self.size -= 1
        del target
        return key


#==========================이하 테스트 코드==========================
ll = SinglyLinkedList()
for i in range(10):
    ll.push_back(i)

print(&#39;INIT: &#39;)
for v in ll:
    print(f&#39;{v}&#39;, end=&#39; &#39;)
print()

popped = ll.pop_back()
print(f&#39;popped: {popped}&#39;)

searched = ll.search_by_index(2)
print(f&#39;search_by_index - 2: {searched}&#39;)

searched = ll.search_by_key(4)
print(f&#39;search_by_key - 4: {searched}&#39;)

removed = ll.remove(2)
print(f&#39;removed - 2: {removed}&#39;)

ll.push_front(100)

print(&#39;PUSH-F: &#39;)
for v in ll:
    print(f&#39;{v}&#39;, end=&#39; &#39;)
print()

ll.insert(100, 3)
# expected Index Error

print(&#39;INSERT: &#39;)
for v in ll:
    print(f&#39;{v}&#39;, end=&#39; &#39;)
print()</code></pre>
<p><br/><br/><br/></p>
<h3 id="양방향-연결-리스트">양방향 연결 리스트</h3>
<pre><code class="language-python">class Node:
    def __init__(self, key=None):
        self.key = key
        self.prev, self.next = self, self

    def __str__(self):
        return str(self.key)

class CircularDoublyLinkedList:
    def __init__(self):
        self.head_node = Node()
        self.size = 0

    def __len__(self):
        return self.size

    def __iter__(self):
        if len(self) == 0:
            return
        v = self.head_node.next
        while v is not self.head_node:
            yield v
            v = v.next

    def splice_list(self, a: Node, b: Node, x: Node):
        a_prev, b_next, x_next = a.prev, b.next, x.next

        # [a .. b] 까지 자른 후, a_prev 와 b_next 를 봉합
        a_prev.next = b_next    # a_prev 의 다음 노드는 b_next
        b_next.prev = a_prev    # b_next 의 이전 노드는 a_prev

        # 이하 x - [a .. b] - x_next 를 위한 코드
        # x 와 a 를 봉합
        x.next = a              # x 의 next link 는 a
        a.prev = x              # a 의 prev link 는 x

        # b 와 x.next 를 봉합
        b.next = x_next         # b 의 next link 는 x_next 로 설정
        x_next.prev = b         # x_next 의 prev link 는 b 로 설정

        # 즉, 아래의 1번 상황에서 2번 상황으로 변경됨
        # 1. x - x_next - .. - a_prev - [ a .. b ] - b_next
        # 2. x - [ a .. b ] - x_next - .. - a_prev - .. - b_next

    def search_by_key(self, key):
        if len(self) == 0:
            return None

        v = self.head_node.next
        while v is not self.head_node:
            if v.key == key:
                return v
            v = v.next
        return None

    def search_by_index(self, index):
        if len(self) == 0 or index &lt; 0 or len(self) &lt; index:
            return None

        i = 0
        v = self.head_node.next
        while i != index:
            v = v.next
            i += 1
        return v

    def move_after(self, a, x):
        self.splice_list(a, a, x)

    def move_before(self, a, x):
        self.splice_list(a, a, x.prev)

    def insert_after(self, key, x):
        self.move_after(Node(key), x)
        self.size += 1

    def insert_before(self, key, x):
        self.move_before(Node(key), x)
        self.size += 1

    def insert_using_index(self, index, key):
        target = self.search_by_index(index)
        self.insert_before(key, target)

    def push_front(self, key):
        self.insert_after(key, self.head_node)

    def push_back(self, key):
        self.insert_before(key, self.head_node)

    def remove(self, x: Node):
        if x is None or x == self.head_node:
            return None
        x_prev, x_next = x.prev, x.next

        x_prev.next = x_next
        x_next.prev = x_prev
        key = x.key
        self.size -= 1

        del x
        return key

    def pop_front(self):
        if len(self) == 0:
            return None
        return self.remove(self.head_node.next)

    def pop_back(self):
        if len(self) == 0:
            return None
        return self.remove(self.head_node.prev)

    def remove_using_index(self, index):
        target = self.search_by_index(index)
        return self.remove(target)


#==========================이하 테스트 코드==========================
ll = CircularDoublyLinkedList()
for i in range(10):
    ll.push_back(i)

print(&#39;INIT:&#39;)
for v in ll:
    print(f&#39;{v}&#39;, end=&#39; &#39;)
print()

print(f&#39;POP_FRONT: {ll.pop_front()}&#39;)

for v in ll:
    print(f&#39;{v}&#39;, end=&#39; &#39;)
print()

print(f&#39;REMOVE_USING_INDEX: {ll.remove_using_index(3)}&#39;)

for v in ll:
    print(f&#39;{v}&#39;, end=&#39; &#39;)
print()

searched = ll.search_by_key(3)
print(f&#39;SEARCH_KEY: {searched}&#39;)

searched = ll.search_by_index(7)
print(f&#39;SEARCH_INDEX: {searched}&#39;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] Stack, Queue (feat. python)]]></title>
            <link>https://velog.io/@sung-je-kim/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Stack-Queue-feat.-python</link>
            <guid>https://velog.io/@sung-je-kim/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Stack-Queue-feat.-python</guid>
            <pubDate>Tue, 19 Nov 2024 08:44:28 GMT</pubDate>
            <description><![CDATA[<h2 id="stack">Stack</h2>
<hr>
<p>선형 자료구조이며, 기본 개념은 <strong>LIFO(Last In First Out)</strong>이다.</p>
<p>구현할 메소드는 아래와 같다.</p>
<blockquote>
<ul>
<li><strong><strong>init</strong></strong>(self)</li>
</ul>
</blockquote>
<ul>
<li><strong><strong>len</strong></strong>(self)</li>
<li><strong>push</strong>(self, val)</li>
<li><strong>pop</strong>(self)</li>
<li><strong>top</strong>(self)</li>
</ul>
<pre><code class="language-python">class Stack:
    def __init__(self):
        self.items = []

    def __len__(self):
        return len(self.items)

    def push(self, val):
        self.items.append(val)

    def pop(self):
        if len(self) == 0:
            return None
        return self.items.pop()


    def top(self):
        if len(self) == 0:
            return None
        return self.items[-1]</code></pre>
<br/>

<p><strong>STACK이 활용될 수 있는 예시</strong></p>
<ol>
<li>웹 브라우저 방문 기록(뒤로가기)</li>
<li>실행 취소(가장 나중에 실행된 것)</li>
<li>JS CallStack(함수 호출 구조)</li>
</ol>
<p><br/><br/><br/></p>
<h2 id="queue">Queue</h2>
<hr>
<p>선형 자료구조이며, 기본 개념은 <strong>FIFO(First In First Out)</strong>이다.</p>
<p>공부하며 찾아보니 자료구조 Queue를 구현할 때, <strong>맴버 변수의 선언 및 활용</strong>에 있어서 두 가지의 차이점이 있었다.</p>
<blockquote>
<p><strong>CASE_1 )</strong></p>
</blockquote>
<ul>
<li><strong>self.items</strong> : 요소들을 담는 리스트</li>
<li><strong>self.front_index</strong> : 제일 앞의 위치를 저장하는 상수
<del><strong>self.rear_index</strong>까지 선언하는 경우도 확인할 수 있었음</del></li>
</ul>
<blockquote>
<p><strong>CASE_2 )</strong></p>
</blockquote>
<ul>
<li><strong>self.items</strong> : 요소들을 담는 리스트</li>
</ul>
<p>위와 같은 두 가지의 경우가 있었고, 코드는 아래와 같다.
구현할 메소드는 공통적으로 아래와 같다.</p>
<blockquote>
<ul>
<li><strong><strong>init</strong></strong>(self)</li>
</ul>
</blockquote>
<ul>
<li><strong><strong>len</strong></strong>(self)</li>
<li><strong>is_empty</strong>(self)</li>
<li><strong>enqueue</strong>(self, item)</li>
<li><strong>dequeue</strong>(self)</li>
<li><strong>front</strong>(self)</li>
<li><strong>back</strong>(self)</li>
</ul>
<pre><code class="language-python"># **CASE_1**
class Queue:
    def __init__(self):
        self.items = []
        self.front_index = 0

    def __len__(self):
        return len(self.items) - self.front_index

    def is_empty(self):
        return len(self) == self.front_index

    def enqueue(self, item):
        self.items.append(item)

    def dequeue(self):
        if self.is_empty():
            return None
        item = self.items[self.front_index]
        self.front_index += 1

        # 리스트의 절반 이상이 비었을 때 리스트를 정리
        if self.front_index &gt; len(self.items) // 2:
            self.items = self.items[self.front_index:]
            self.front_index = 0

        return item

    def front(self):
        if self.is_empty():
            return None
        return self.items[self.front_index]

    def back(self):
        if self.is_empty():
            return None
        return self.items[-1]</code></pre>
<p><br/><br/><br/></p>
<pre><code class="language-python"># **CASE_2**
class Queue:
    def __init__(self):
        self.items = []

    def __len__(self):
        return len(self.items)

    def is_empty(self):
        return len(self.items) == 0

    def enqueue(self, item):
        self.items.append(item)

    def dequeue(self):
        if not self.is_empty():
            return self.items.pop(0)
        return None

    def front(self):
        if not self.is_empty():
            return self.items[0]
        return None

    def back(self):
        if not self.is_empty():
            return self.items[-1]
        return None</code></pre>
<p><strong>QUEUE가 활용될 수 있는 예시</strong></p>
<ol>
<li>예약 사이트 대기 번호(트래픽이 몰린 상황)</li>
<li>Cache</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[SSAFY 10기 1학기 회고]]></title>
            <link>https://velog.io/@sung-je-kim/SSAFY-10term-NL</link>
            <guid>https://velog.io/@sung-je-kim/SSAFY-10term-NL</guid>
            <pubDate>Wed, 06 Dec 2023 17:13:51 GMT</pubDate>
            <description><![CDATA[<h2 id="--잡담">- 잡담</h2>
<p>오랜만에 쓰는 글이여서 그런지 뭐 부터 시작을 해야 될 지 모르겠다.
<strong>어쨌든 SSAFY 1학기가 끝났다<del>!</del>!<del>!</del>!</strong></p>
<p>걱정되는 마음으로 SSAFY에 가는 내 모습이 엊그제 같은데 1학기 수료라니 참 시간이 달릴 땐 빠른 거 같다.</p>
<p>현재 방학인 상태인데 너무 늘어지게 쉬어서 그런지 아주 좋으면서 반면에, 너무 생산적이지 못 한 거 같아 불안함을 조금 느끼긴 한다. 그래서 마냥 쉴 순 없기 때문에 일단 카페에 나와서 회고록이라도 쓰기로 하였다.</p>
<h2 id="--7월">- 7월</h2>
<p>7월에는 반이 확정되자마자 주로 Java에 대해서 배웠다. 근데 이 때 Java라는 언어가 너무 어려웠고 내가 여태 해왔던 JavaScript와 너무나도 다른 언어였다...비유하자면 JavaScript는 ENFP 같다면, Java는 ISTJ 같달까?
어려운 이유는 아래와 같다.</p>
<blockquote>
<ol>
<li>변수의 타입<ul>
<li>Array가 있고 List가 있고, int가 있고 Integer가 있고, 같은 거 같으면서 다른 타입들</li>
<li>그렇다고 TypeScript에 있는 Union Type도 없었던 점</li>
</ul>
</li>
<li>어렵게 느껴지는 이론적 언어들<ul>
<li>추상화, 다형성, 자료형과 래퍼 클래스...</li>
</ul>
</li>
<li>몸에 익어 있던 map, filter, forEach 등과 같은 JS의 배열 메서드들 쓰겠다는 똥고집<ul>
<li>for문을 쓰기 싫어서 구글링해서 찾은게 Java에 List.forEach(x -&gt; x)</li>
<li>람다식도 화살표 함수 같이 생겨서 개인적으로 너무 마음에 들었었음 ㅋ.</li>
</ul>
</li>
</ol>
</blockquote>
<p>위와 같은 이유로 어려웠고, &#39;이걸로 나중에 어떻게 알고리즘을 하나&#39;하는 생각과 함께 공부를 해나갔다.</p>
<h2 id="--8월">- 8월</h2>
<p>본격적으로 알고리즘을 배우는 달이였다.
나는 사실 SSAFY 오기 전에는 알고리즘을 안했고 재귀, 완탐(BFS, DFS), 조합, 순열 등과 같은 개념을 모르기도 했고 언제 어떤 걸 적용 시켜야 되는지도 몰랐다.</p>
<blockquote>
<p>여담으로 SSAFY 들어 가려면 코딩 테스트 본다길래 2주동안 Python 공부하며 백준 문제 풀고 있었는데, 못 풀던 문제를 <strong>GPT</strong>한테 물어봤다. 
<img width='400' src="https://velog.velcdn.com/images/sung-je-kim/post/00ef69bf-9a0a-4060-a6e0-7206b6c30961/image.png"/>
나 : <strong>&quot;Dynamic programming...? 아 GPT 또 할루시네이션 걸렸네&quot;</strong>
DP는 들어도 보지 못 했을 정도로 알고리즘에 대해서 아무것도 모르던 시절.</p>
</blockquote>
<p>알고리즘 공장 수준으로 맨날 알고리즘을 풀었다. 사실 후회가 좀 되는 부분은 하기 싫을 때는 풀지도 않고 꿍해 있었는데, 누군가에게 피해를 주진 않았지만 너무 어리게 행동한 거 같아서 돌이켜보면 부끄럽다.
또한 &#39;<strong>SSAFY를 들어오기 전과 후로 알고리즘 공부하는 나</strong>를 비교해보면 들어오기 전의 내가 알고리즘 공부을 더 열정적으로 했었던 거 같다.</p>
<p>그래도 위와 같은 부끄러움을 이제와서만 느낀 것이 아니라 저 당시에도 느꼈었기 때문에 열심히 새로운 지식을 배우며 부족한 지식들을 채워 나갔다.</p>
<h2 id="--9월">- 9월</h2>
<p>힘들었던 알고리즘의 시기가 지나고 Web을 배우는 시기가 왔다. 
본격적으로 Web에 대해서 배우고 프론트엔드와 백엔드의 기초 기술들에 대해서 배웠다.</p>
<blockquote>
<ul>
<li>FrontEnd : HTML5, CSS3, JavaScript(ES6), Bootstrap 등</li>
</ul>
</blockquote>
<ul>
<li>BackEnd : Tomcat, Servelt, JSP, Session, Cookie 등</li>
</ul>
<p>백앤드에 대해서 배울 때는 정말 생소했다. </p>
<blockquote>
<ol>
<li>Tomcat이 적절한 Servlet에게 Client 요청을 전달</li>
<li>DispatcherServlet이 알맞은 Controller에 위임</li>
<li>Controller -&gt; Service -&gt; Dao -&gt; DB</li>
<li>ViewResolver가 응답을 View로 전달</li>
<li>View에 반영 후 Client에게 View 반환</li>
</ol>
</blockquote>
<p>위와 같은 Flow로 진행되는 거 보고 백앤드도 재밌게 느껴지곤 했다.</p>
<h2 id="--10월">- 10월</h2>
<p>Web 프론트와 백의 기초를 배우고 Framework에 대해서 배우는 기간이였다.
<del>(Spring Boot와 MyBatis에 대해서 배웠지만 굳이 적진 않겠습니다.)</del>
드디어 기다리고 기다리던 Vue3에 대해서 배웠다. 사실 Spring Boot 막바지에 미리 Vue3의 상태 관리 함수나, 컴포넌트 라이프 사이클 훅 등 짤막하게 쉬는 시간이나 등하교 시간 때 예습을 해두었는데 큰 도움이 되었다. 
Vue3를 함게 배우며 Pinia 또한 배웠는데, Pinia는 전역 상태관리 라이브러리라고 보면 된다.
일단 Vue3를 배우면서 느낀점은 아래와 같았다.</p>
<blockquote>
<ol>
<li>문서가 React에 비해서 너무 적다.</li>
<li>그래서 Vue2의 문법들도 알아야 한다.</li>
<li>근데 1을 어느정도 상쇄시킬 정도로 공식문서가 잘되있다고 생각한다.</li>
<li>JSX 같지 않고 굉장히 HTML 같은 구조였다.</li>
<li>Vue만의 문법을 배우는 느낌이 강하게 들었다. (즉, React가 왜 라이브러리인지 알게 된 계기였다.)</li>
</ol>
</blockquote>
<p>Vue에 대한 내용(?), 후기(?)는 추후 다시 블로그에 업로드할 예정이니 이정도만 얘기 하겠다.</p>
<h2 id="--11월">- 11월</h2>
<p>이제 1학기의 마지막 교육 일정이였다. 이제 배운 것들을 정리하고, 1학기 최종 프로젝트를 진행하는 시간이였다. 
2인 1팀으로 구성되어 프로젝트를 진행하였고, 공공데이터를 활용해서 프로젝트를 만드는 것이였다.
나는 프론트엔드를 전적으로 담당하고, 내 페어 분은 백엔드를 담당하셨다.</p>
<p>프로젝트 완성까지 총 3주라는 시간 주어졌다.
프론트앤드의 큰 틀의 기술스택은 Vue3, Pinia, SCSS로 잡아두고 시작하였다.</p>
<ul>
<li>첫째주는 <strong>UI 구상</strong>과 함께 <strong>기초 컴포넌트(Button, Input, SelectBox, ...)를 구현</strong>을 하며 페어와 같이 <strong>기획</strong>을 하였다.
UI 구상과 컴포넌트 설계를 동시에 진행하였다.</li>
<li>둘째주는 기초 컴포넌트를 기반으로 <strong>합성된 컴포넌트(Header, Modal, LoginInput, DropBox, ...)를 구현</strong>하며 페이지 단위 구현까지 넘어갔다. 또한 <strong>백앤드 API들을 접목</strong>시켰다.
이 때부터 본격적으로 <strong>Pinia 구축</strong>도 진행하였고, <strong>Naver Map API도 접목</strong>시켰다.</li>
<li>셋째주는 프로젝트가 70/100 정도 완성이 된 상태여서 QA를 진행하려 했는데, 마지막에 페이지를 하나 생성할 정도의 기능이 추가가 되어서 계획보다 QA를 많이 하지 못하고 기능 구현을 했다.</li>
<li>마지막 날에는 발표를 위한 문서작업을 하며 발표를 성공리에 마쳤고 프로젝트도 끝이 났다.</li>
</ul>
<blockquote>
<p>그리고 프로젝트를 했는지도 까먹었을 때 쯤, SSAFY에서 마지막이라고 캠퍼스 내에서 전국 축제를 진행하고 마지막에 프로님이 수료증을 나눠 주는데, <strong>최종 프로젝트 1등은....!! 김성제, ㅇㅈㄱ!<del>!</del>!</strong>
1학기 수료해서 &#39;어휴 드디어....&#39; 했는데 생각치도 못한 최우수상 받아서 기분이 조았습니다 ^___^
<img width='400' src='https://velog.velcdn.com/images/sung-je-kim/post/3700e531-a1ea-40a8-9d30-b9e97e2a4c4f/image.jpeg'/></p>
</blockquote>
<h2 id="--마무리">- 마무리</h2>
<p>SSAFY를 다니면서 많이 도움도 주고, 그만큼 많이 도움도 받고 재밌게 생활해서 너무 좋았다.
많은 사람들과 함께 지내다 보니 이런 장점이 있는 사람도 있고, 저런 장점이 있는 사람도 있어서 좋았다.
&#39;개발적으로 더 많이 대화를 하면 좋았을텐데&#39;라는 생각도 한다.
<strong>나중에 서울에서 보자 10기 대전 8반...!!!!!</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[회고록] VanillaJS 과제 돌이켜보기 (feat. 처음부터 현재까지)]]></title>
            <link>https://velog.io/@sung-je-kim/memoirs-vanilla-js-task</link>
            <guid>https://velog.io/@sung-je-kim/memoirs-vanilla-js-task</guid>
            <pubDate>Tue, 04 Apr 2023 04:50:57 GMT</pubDate>
            <description><![CDATA[<p>운이 좋게도 지원한 회사에서 서류 합격을 받아 과제 전형을 진행하게 되었다. 
HTML, CSS, JS(ES6+)만을 이용해서 요구 사항, 디자인에 맞게 구현을 해내야 했다.
주요 요구 사항에는 반응형, Cross Browsing 정도였다.</p>
<h2 id="--react를-먼저-접해본-사람">- React를 먼저 접해본 사람.</h2>
<hr>
<p>물론 동아리 활동을 진행하면서 VanillaJS를 이용해 TODOList를 만들었다.
그것이 내 인생 중에서 처음 개발이자, 처음으로 브라우저와의 소통이였다.
지금 와서 보면 정말 간단하게 그지 없는 TODOList였다.
<a href="https://github.com/modern-agile-team/wooahan-agile-frontend-task-3term">(TODOList 만들기 GitHub)</a>
그런 식으로 일주 정도인지 이주 정도인지 TODOList 작업을 하고 바로 React로 미니프로젝트를 했었다.</p>
<p>사실 React를 배우기 전에 몇몇은 &quot;React가 어렵다.&quot;, &quot;React를 배우기 <strong>힘들 거 같다</strong>&quot;, &#39;너무 어렵지 않을까?&#39;라는 의견을 종종 들어왔다. 
하지만 나는 신기술에 배우기 최적화 되어 있는지, 개인적으로 위와 같은 생각은 들지 않고, 아래와 같은 생각이 들었다.</p>
<blockquote>
<h4 id="우리가-개발하기-편하라고-react-같은-게-나오지-않았을까">&quot;우리가 개발하기 편하라고 React 같은 게 나오지 않았을까?&quot;</h4>
</blockquote>
<h4 id="지금보다-불편하고-어려우면-왜-쓰지">&quot;지금보다 불편하고 어려우면 왜 쓰지?&quot;</h4>
<h4 id="오히려-좋겠지">&quot;오히려 좋겠지~~&quot;</h4>
<p>그렇게 React와 함께 여러 프레임워크, 라이브러리 등등...같이 사용하며 미니 프로젝트, 본 프로젝트, 개인 프로젝트를 진행했다.</p>
<p>그리고 React를 이용한 프로젝트들을 주력 삼아서 회사에 지원했는데, 과제를 VanillaJS로 받았다.
정말이지 요구 사항과 디자인을 보자마자 React로 어떻게 해야 하는지 보였다.</p>
<p>그게 문제였다. <strong>VanillaJS로는 어떻게 해야 하는지 생각 조차나지 않았고, &#39;React로 이건 이렇게, 저건 저렇게&#39;라는 생각만 들었다.</strong></p>
<h2 id="--cra-vs-">- CRA vs ???</h2>
<hr>
<p>우선 그래서 정신 차리고 먼저 든 생각이 <strong>&#39;Cross Browsing 테스트를 어떻게 하지?&#39;</strong>라는 생각이 먼저 들었다.
<strong>&#39;어떻게 하긴 배포하면 정확하겠지&#39;</strong>하고 최근에 AWS 배포 관련 글 쓰면서 공회전 중인 EC2가 있어서 거기에 코드를 띄워서 확인하기로 했다.
근데 VanillaJS를 어떻게 세팅해야 하는지 생각하는데 뇌가 멈췄다. 
&#39;Bundler가 없는데 local에서 확인하는 건 그렇다 치고, 어떻게 EC2에 코드를 띄우지...?&#39;라는 생각이 들어서 webpack과 eslint를 우선 설정하였다.</p>
<blockquote>
<h4 id="기초-틀은-아래의-링크를-참고하여-설정하고-내-폴더구조와-ec2-환경-설정에-맞게-수정하였다">기초 틀은 아래의 링크를 참고하여 설정하고, 내 폴더구조와 EC2 환경 설정에 맞게 수정하였다.</h4>
<p><a href="https://yujo11.github.io/javascript/Vanilla%20JS%20%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%20%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0/">https://yujo11.github.io/javascript/Vanilla%20JS%20%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%20%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0/</a></p>
</blockquote>
<p>성공적으로 설정을 한 후에, API 요청을 먼저 진행해서 응답을 확인하고 배포를 하고 또 확인했는데 어랍쇼?
브라우저로 확인을 해보니까 좌상단에 &#39;Invalid host header&#39;라고 띡 하나 떠있고 네트워크 요청이 가지 않고 있었다. FrontEnd가 BackEnd를 바라보고 있지 않아서 생기는 이슈였다.
추측컨데, NginX 설정 중에서 upstream 설정을 통해서 서버와 연결을 해야 하는데 급한대로 NginX를 삭제했다.
그리고 EC2 IP를 통해서 확인해보니 잘 되는 것을 확인했다.</p>
<h2 id="--usestate-vs-">- useState vs ???</h2>
<h4 id="부제-react-router-dom-vs-">부제: react-router-dom vs ???</h4>
<hr>
<p>컴포넌트들을 거의 만들고 이제 리스트 중에 Card 하나를 누르면, 해당 Card 정보를 Details 컴포넌트에 띄워야 했다.
우선 제일 먼저 든 생각이 &#39;Card를 누르면 URL을 ID로 Routing을 시키고 ID 값을 쿼리로 붙이자!!!&#39;해서 &#39;localhost:3000/id?id=123123123&#39;과 같은 식으로 구현을 하려고 했다.
근데 웬 걸 &#39;localhost:3000/id&#39;가 되자 HTML, CSS, JS를 해당 디렉토리에서 찾아내지 못해서 아래와 같은 오류를 보였다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/698ba79e-5715-451e-9545-89912f509a84/image.png" alt="">
그래서 아래 코드를 통해서 &#39;localhost:3000/?id=123123123&#39; 식으로 했는데 Card를 누르면 URL이 바뀌면서 새로고침이 되었다. e.preventDefault()를 이용해도 새로고침이 되었다.</p>
<pre><code class="language-JavaScript">const searchQuery = new URLSearchParams({ id: res.id });
window.location.search = searchQuery;</code></pre>
<p>여기서 새로고침이 되는 이슈가 얼마나 치명적이냐면, 새로고침 때문에 GET API 요청을 다시 해서 상태를 초기값으로 되돌려버린다.</p>
<p>그래서 window.history.pushState라는 함수가 있어서 이용해 라우팅을 해보려고 했다.
정상적으로 새로고침 없이 작동이 되고, 값을 가져오는데도 문제가 없지만 정말 뭔가 맘에 들지 않는 방법이였다.</p>
<pre><code class="language-JavaScript">window.history.pushState({ path: res.id }, &quot;&quot;, &quot;?id=&quot; + res.id);

const searchQueryValue = new URLSearchParams(window.location.search).get(&quot;id&quot;);</code></pre>
<p>pushState 함수를 쓸 때 가독성이 개인적으로 맘에 들지 않았다. 뭔가 URL 관련 코드라는 건 알겠는데, 한눈에 안 들어오는 느낌이랄까....?</p>
<h4 id="그래서-usestate를-구현하기로-했다">그래서 useState를 구현하기로 했다.</h4>
<p>생각보다 별 거 없었던 거 같다. useState를 간단하게나마 구현을 해서 그런지 기분이 좀 좋았다 ^___^</p>
<pre><code class="language-JavaScript">import Details from &quot;../components/reservationDetails&quot;;

let initialState = null;

const useState = (state) =&gt; {
  if (initialState === null) initialState = state;

  const handler = (newState) =&gt; {
    initialState = newState;
    Details();
  };

  return {
    state: initialState,
    handler: handler,
  };
};</code></pre>
<h2 id="--useeffect-vs-">- useEffect vs ???</h2>
<hr>
<p>브라우저의 사이즈를 변동을 감지하기 위해서 React의 useEffect를 이용해서 아래와 같이 구현한 적이 있다.</p>
<pre><code class="language-JavaScript">function useCheckDeviceSize() {
  const dispatch = useDispatch();
  const changeDeviceSize = () =&gt; {
    dispatch(
      getDeviceSize(
        (() =&gt; ({
          deviceWidth: window.innerWidth,
          deviceHeight: window.innerHeight,
        }))()
      )
    );
  };

  useEffect(() =&gt; {
    changeDeviceSize();
    window.addEventListener(&quot;resize&quot;, changeDeviceSize);
    return () =&gt; {
      changeDeviceSize();
      window.removeEventListener(&quot;resize&quot;, changeDeviceSize);
    };
  });
}</code></pre>
<p>위와 같은 코드를 VanillaJS를 이용해서 구현해야 하는데, 어찌 해야 하는지 참 막막했다.
근데 구글링을 해보니 matchMedia라는 것이 있어서 그것을 사용해서 아래와 같이 구현했다.
matchMedia의 와 resize를 이용한 차이점이 궁금해서 두 코드 모두 사용해서 브라우저의 사이즈를 줄여가며 테스트를 해보았다.</p>
<pre><code class="language-JavaScript">const mediaQuery = window.matchMedia(&quot;(max-width: 720px)&quot;);

function windowMatchMedia(e) {
  if (e.matches) console.log(&quot;in MatchMedia: &quot;, &quot;SmallScreens&quot;);
  else console.log(&quot;in MatchMedia: &quot;, &quot;LargeScreens&quot;);
}
mediaQuery.addEventListener(&quot;change&quot;, windowMatchMedia);

/** ---------------------------------------------------------------- */

function windowResize() {
  if (window.innerWidth &lt; 720) console.log(&quot;in resize: &quot;, &quot;SmallScreens&quot;);
  else console.log(&quot;in resize: &quot;, &quot;LargeScreens&quot;);
}
window.addEventListener(&quot;resize&quot;, windowResize);</code></pre>
<h4 id="matchmedia와-resize-비교-결과">matchMedia와 resize 비교 결과</h4>
<p><del>아래에 더 자세한 수치 비교 결과 또한 있음. 또한 그 수치가 다소 편향되어 있을 수 있습니다.</del>
<img src="https://velog.velcdn.com/images/sung-je-kim/post/16fe9b05-093e-454d-9ba3-5e0008b3bbaf/image.png" alt="">
결과 사진만 보아도 resize 함수의 실행빈도가 매우 많은 것을 알 수 있다.
matchMedia는 addEventListener의 함수를 설정해둔 기준값을 통과할 때만 실행시키지만, resize는 기준값을 통과하지 않아도 addEventListener의 함수를 실행시킨다.
그리고 968px에서 51px까지 왔다 갔다 한 번해서 프로파일링을 해봤다.</p>
<blockquote>
<h4 id="resize-테스트51px968px">resize 테스트(51px~968px)</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/adbce57a-53a5-4fdb-8901-fbc9294e4dcb/image.png" alt=""></p>
</blockquote>
<blockquote>
<h4 id="matchmedia-테스트51px968px">matchMedia 테스트(51px~968px)</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/1539efdd-53f3-4e8a-a01c-c61061271fd7/image.png" alt="">
크게 뭐가 많지 않은 어플리케이션임에도 불구하고 matchMedia가 성능상으로 좀 더 유의미하게 좋아보였다.</p>
</blockquote>
<p><strong>이 부분에 대해서 좀 더 다른 생각이나 결과가 다르신 분이 있다면 댓글 남겨주시면 감사하겠습니다!!</strong></p>
<h2 id="--react-vs-vanillajs">- React vs VanillaJS</h2>
<hr>
<p>개발을 한지 1년 정도 되어가는데, 지표를 나타내보자면 아래와 같이 그 중에서 React가 정말 많이 차지 할 정도로 함께한 시간이 많은 라이브러리다.
그리고 React만 써서 그런지, VanillaJS를 이용하면서 React Hook의 필요성도 크게 느끼고, React Hook을 이용한 개발이 나에게 참 많이 녹아든 거 같아 아쉬웠다... 무언가에 갇혀있는 듯한 느낌이랄까.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/39d1b21e-0a0d-4821-8d2e-9d3fa2982862/image.png" alt="">
그렇지만, 또는 그래서인지 이번을 계기로 VanillaJS의 매력을 역시 튜닝의 끝은 순정인가 싶을 정도로 큰 매력을 느꼈다.
React는 양산형 공장 처럼 개발하는 느낌이라면, VanillaJS는 그 &#39;한땀한땀&#39; 개발하는 느낌이 크게 들어서 좋았다.
VanillaJS를 쓰면서 머리 빠지는 느낌이 들었는데, 그 느낌이 좋은 거일지도....</p>
<h3 id="긴-글-읽어주셔서-감사합니다">긴 글 읽어주셔서 감사합니다.</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/5a70bc96-9c54-49f0-8f2f-f3e3679783f1/image.jpeg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iTerm2] ⌘ + (⌫, ←, →)or ⌥ + (⌫, ←, →) 설정하기]]></title>
            <link>https://velog.io/@sung-je-kim/iTerm2-command-delete-command-arrow-key-option-arrow-key-cmd</link>
            <guid>https://velog.io/@sung-je-kim/iTerm2-command-delete-command-arrow-key-option-arrow-key-cmd</guid>
            <pubDate>Thu, 23 Mar 2023 07:09:08 GMT</pubDate>
            <description><![CDATA[<p>터미널을 iTerm을 이용하는데 왜인지 모르겠지만 iTerm만 그런지 모르겠지만 
<strong>⌘</strong>(command) 또는 <strong>⌥</strong>(option) 를 이용한 <strong>←</strong>(left), <strong>→</strong>(right) 또는 <strong>⌫</strong>(delete)가 평소 쓰던대로 작동하지 않아서 굉장히 불편함을 느끼고 있던 상태였다.</p>
<p>그래서 iTerm 배경 꾸미는 김에 key settiong까지 편집하기로 마음 먹었다.</p>
<hr>
<h2 id="1-편집-영역으로-이동">1. 편집 영역으로 이동</h2>
<p>iTerm2에서 Preferences를 열어 Key Mappings를 편집할 것이다.</p>
<blockquote>
<h3 id="1-1-iterm2-연-후에-preferences-열기">1-1. iTerm2 연 후에 Preferences 열기</h3>
<p>iTerm을 연 후에 <strong>⌘</strong>(command) + <strong>,</strong>(comma)로 Preferences를 연다.
아니면 mac 화면 좌측 상단에 사과 모양 옆에 iTerm2를 눌러 Preferences를 열 수도 있다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/708fcc40-feec-4020-85fa-3d75ca4ba28e/image.png" alt="">
그리고 Profiles → Keys → Key Mappings로 이동한다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/54e809fd-662b-42fd-a456-303f8ae96349/image.png" alt=""></p>
</blockquote>
<hr>
<h2 id="2-key-편집하기">2. Key 편집하기</h2>
<p><strong>⌥</strong>(option) + <strong>←</strong>(left)와 <strong>⌥</strong>(option) + <strong>→</strong>(right)는 이미 있는 상태인데 편집을 할 것이고</p>
<ul>
<li><strong>⌘</strong>(command)+ <strong>←</strong>(left)</li>
<li><strong>⌘</strong>(command) + <strong>→</strong>(right)</li>
<li><strong>⌘</strong>(command) + <strong>⌫</strong>(delete)</li>
<li><strong>⌥</strong>(option) + <strong>⌫</strong>(delete)
위 네가지는 키 추가를 해야 되는 상태다.</li>
</ul>
<blockquote>
<h3 id="2-1-⌥option--←left-와-⌥option--→right-편집하기">2-1. <strong>⌥</strong>(option) + <strong>←</strong>(left) 와 <strong>⌥</strong>(option) + <strong>→</strong>(right) 편집하기</h3>
<p><strong>⌥</strong>(option) + <strong>←</strong>(left)를 더블 클릭하여 편집을 하는데 아래와 같이 편집하면 된다.</p>
</blockquote>
<pre><code>Action: Send Escape Sequence
Esc+ : b</code></pre><p><img src="https://velog.velcdn.com/images/sung-je-kim/post/539e01a2-00f8-4020-941a-780ab2ab1c1b/image.png" alt="">
<strong>⌥</strong>(option) + <strong>→</strong>(right) 또한 편집을 할 건인데 Esc+ 만 다르게 해주면 된다.</p>
<pre><code>Action: Send Escape Sequence
Esc+ : f</code></pre><p><img src="https://velog.velcdn.com/images/sung-je-kim/post/ae4a050e-01f9-40d3-9a57-606eefb010dc/image.png" alt=""></p>
<blockquote>
<h3 id="2-2-존재하지-않던-key-추가하기">2-2. 존재하지 않던 Key 추가하기</h3>
<p>앞서 말했던 존재하지 않던 4가지들은 추가를 해줄 것이다. 추가를 하는 방법은 아래 사진과 같이 + 를 누르면된다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/f5e54a56-c462-4de6-9960-4977881e3a8b/image.png" alt="">
그리고 Click to set을 누르면 키보드 동작을 인식하는데, 한 예시로 <strong>⌘</strong>(command) + <strong>⌫</strong>(delete) 누르고 아래와 같이 편집하면 된다.</p>
</blockquote>
<pre><code>Action: Send Hex Code
Code : 0x15</code></pre><p>위와 같이 추가하지 않은 부분들을 추가해주면 된다. 총 정리는 아래와 같다.</p>
<hr>
<h2 id="3-총정리">3. 총정리</h2>
<blockquote>
<p><strong>⌘</strong>(command) + <strong>←</strong>(left)</p>
</blockquote>
<pre><code>Action: Send Hex Code
Code : 0x01</code></pre><blockquote>
<p><strong>⌘</strong>(command) + <strong>→</strong>(right)</p>
</blockquote>
<pre><code>Action: Send Hex Code
Code : 0x05</code></pre><blockquote>
<p><strong>⌥</strong>(option) + <strong>⌫</strong>(delete)</p>
</blockquote>
<pre><code>Action: Send Hex Code
Code : 0x17</code></pre><blockquote>
<p><strong>⌘</strong>(command) + <strong>⌫</strong>(delete)</p>
</blockquote>
<pre><code>Action: Send Hex Code
Code : 0x15</code></pre><blockquote>
<p><strong>⌥</strong>(option) + <strong>←</strong>(left)</p>
</blockquote>
<pre><code>Action: Send Escape Sequence
Esc+ : b</code></pre><blockquote>
<p><strong>⌥</strong>(option) + <strong>→</strong>(right)</p>
</blockquote>
<pre><code>Action: Send Escape Sequence
Esc+ : f</code></pre><p>누군가에게는 도움이 되었길 바랍니다, 감사합니다 ^<strong>__</strong>^</p>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/89c3551f-bf4c-497c-b484-d2ad41a6439f/image.jpeg" alt=""></p>
<blockquote>
<h1 id="큰-도움이-되었던-문서">큰 도움이 되었던 문서</h1>
</blockquote>
<ul>
<li><a href="https://stackoverflow.com/questions/196357/making-iterm-to-translate-meta-key-in-the-same-way-as-in-other-oses">https://stackoverflow.com/questions/196357/making-iterm-to-translate-meta-key-in-the-same-way-as-in-other-oses</a></li>
<li><a href="https://stackoverflow.com/questions/15733312/iterm2-delete-line">https://stackoverflow.com/questions/15733312/iterm2-delete-line</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS EC2] 배포를 해야 하는데, 막막할 때 (feat. pm2, NGINX, Certbot, Route53)]]></title>
            <link>https://velog.io/@sung-je-kim/AWS-EC2-deploy-feat.-pm2-NGINX-Certbot-Route53</link>
            <guid>https://velog.io/@sung-je-kim/AWS-EC2-deploy-feat.-pm2-NGINX-Certbot-Route53</guid>
            <pubDate>Wed, 22 Mar 2023 10:06:33 GMT</pubDate>
            <description><![CDATA[<p>이번에 개인 프로젝트 배포를 어떻게 하였는지, 배포할 때 마주쳤던 것들을 위주로 적을 예정이다.</p>
<p>필자는 <strong>NextJS를 이용한 개인 프로젝트</strong>를 배포해야 했고, <strong>가비아에서 도메인을 구입한 상황</strong>이다.</p>
<p><a href="https://ap-northeast-2.console.aws.amazon.com/ec2/v2/home">https://ap-northeast-2.console.aws.amazon.com/ec2/v2/home</a>
위 링크에 들어가서 인스턴스 시작을 할 것이다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/6f35a40d-959a-4266-b27d-027d659cc752/image.png" alt=""></p>
<hr>
<h2 id="1-인스턴스-생성">1. 인스턴스 생성</h2>
<p>인스턴스 시작을 누르면 생성창으로 이동하게 되는데, 이 부분은 사실 다른 글들과 유사하다.
그저 따라오기만 하면 되는 부분이라고 볼 수 있다.</p>
<blockquote>
<h3 id="1-1-이름과-os-설정">1-1. 이름과 OS 설정</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/95baaab9-a602-48a7-81e4-2447facc2d5f/image.png" alt="">
여러 글에서 보았는데, <strong>OS는</strong> 다들 <strong>Ubuntu의 22.04가 무난</strong>한 선택이라고 하셔서, 필자도 그렇게 하였다.</p>
</blockquote>
<blockquote>
<h3 id="1-2-인스턴스-유형-선택">1-2. 인스턴스 유형 선택</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/7795ea88-3529-416a-9b1b-17a06aee1fa7/image.png" alt="">
또한 <strong>인스턴스 유형</strong>에 있어서도 t2.micro가 프리티어(회원가입 후 12개월 무료) 사용도 가능하고 무난하다고 하여서 선택한 이유다.</p>
</blockquote>
<blockquote>
<h3 id="1-3-키페어-생성">1-3. 키페어 생성</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/6341c8ce-934f-427f-8642-045dfbba19ff/image.png" alt="">
새 키페어 생성을 누른다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/75ecd59a-b7b5-4400-949f-ca9d4b4d1345/image.png" alt="">
마음에 드는 키페어 이름을 적은 후에 키 페어 설정 버튼을 누르면 키페어가 생성이 된다.</p>
</blockquote>
<h4 id="⚠️주의--키페어는-ssh-접속의-인증-수단으로-각별히-관리해야-한다⚠️"><strong>⚠️주의 : 키페어는 ssh 접속의 인증 수단으로, 각별히 관리해야 한다.⚠️</strong></h4>
<p>필자는 아래와 같이 해당 프로젝트 폴더와 나란히 놔둠.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/6c66b6bd-d52f-47f9-9999-a002a6614b23/image.png" alt=""></p>
<blockquote>
<h3 id="1-4-네트워크-설정-스토리지-구성-고급-세부-정보-등">1-4. 네트워크 설정, 스토리지 구성, 고급 세부 정보 등</h3>
<p>이제 키페어 이후의 생성 시 설정은 일단 넘어가고, 네트워크 설정 부분은 인스턴스를 생성 완료한 후에 설정을 손 볼 것이다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/35584fe4-02ff-4c34-98b2-a2151fb32efc/image.png" alt="">
이제 우측 하단의 <strong>인스턴스 시작</strong>을 누르면, 인스턴스가 생성이 된다.</p>
</blockquote>
<hr>
<h2 id="2-탄력적-ip">2. 탄력적 IP</h2>
<h4 id="탄력적-ip란-고정된-퍼블릭-ip라고-생각을-하면-된다">탄력적 IP란, 고정된 퍼블릭 IP라고 생각을 하면 된다.</h4>
<p>우리가 <strong>생성한 인스턴스</strong> 즉, 우리가 AWS에서 빌린 EC2라는 컴퓨터를 킬 때마다, 해당 인스턴스는 새로운 IP를 할당 받는데 그것이 경험상 ssh 접속을 할 때마다 굉장히 우리를 귀찮게 한다.</p>
<h4 id="그래서-ec2의-변덕스러운-ip를-고정시키는-것이-탄력적-ip다">그래서 <strong>EC2의 변덕스러운 IP를 고정시키는 것이 탄력적 IP다.</strong></h4>
<blockquote>
<h3 id="2-1-탄력적-ip-생성">2-1. 탄력적 IP 생성</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/99b7af46-9a60-4d52-8804-a6b78a8ab30c/image.png" alt="">
탄력적 IP <strong>(EC2 기능)</strong>으로 이동하여
<img src="https://velog.velcdn.com/images/sung-je-kim/post/73680689-2f26-4a86-be7d-77195e906a24/image.png" alt="">
<strong>탄력적 IP 주소 할당</strong>을 클릭한다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/842fb571-669f-43e6-b80c-bf78a0aeb7ca/image.png" alt="">
태그 같은 경우에는 필수적이진 않고, 이제 우측 하단에 &#39;할당&#39;을 클릭하게 되면 탄력적 IP 하나가 생성이 된다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/937fd0da-5936-4810-9114-a7af3f411ead/image.png" alt="">
이렇게 생성된 IP를 나는 일단 velog-sung-je-kim으로 이름을 바꿨다.
<del>(이전에 생성한 것과 헷갈리지 않기 위해 이름을 부여 해줬다.)</del></p>
</blockquote>
<blockquote>
<h3 id="2-2-탄력적-ip를-ec2에-할당">2-2. 탄력적 IP를 EC2에 할당</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/9b7d5412-8300-40d7-a508-96e6bd22bebb/image.png" alt="">
해당 탄력적 IP 주소 리스트에서 방금 막 만든 탄력적 IP를 선택한 후에 <strong>&#39;작업&#39;을 선택하고, &#39;탄력적 IP 주소 연결&#39;</strong>을 누른다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/738ee28a-c60b-42e8-ad5d-8ca91fea5306/image.png" alt="">
연결을 원하는 인스턴스를 선택을 하고, 프라이빗 IP 주소를 선택하는데 필자는 AWS에서 추천하는(?) 거 하나가 뜨길래 그걸로 연결하였다.
크게 중요한 부분은 아닌 거 같다.
그리고 우측 하단에 연결을 누르게 되면 성공적으로 연결이 되는 모습을 볼 수 있다.</p>
</blockquote>
<blockquote>
<h3 id="2-3-탄력적-ip-확인">2-3. 탄력적 IP 확인</h3>
<p>해당 인스턴스로 이동하여 해당 인스턴스의 세부 정보를 확인을 해보게 되면 정상적으로 할당이 된 모습을 볼 수 있다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/3961049c-b6c4-49e5-8a61-dffd17b0c836/image.png" alt=""></p>
</blockquote>
<hr>
<h2 id="3-인바운드-규칙">3. 인바운드 규칙</h2>
<p>이제는 인바운드 규칙을 편집할 것이다. 여기서 <strong>&quot;인바운드 규칙&quot;</strong>이라는 것을 설명하자면,
<strong>&quot;&#39;내 방화벽으로 들어올 때&#39;의 규칙&quot;</strong>이 인바운드 규칙이다.
밖에서 안으로들어오는 IP별의 요청을 제한할 수 있고, PORT 별로도 요청을 제한할 수도 있는 것을 설정하는 것이 인바운드 규칙 설정하기다.</p>
<p>추가적으로 <strong>아웃바운드 규칙을 설정해야 되는 경우</strong>를 간단히 예시만 짚고 넘어가자면,
어떠한 해커가 &#39;내 사이트&#39;에서 해커가 외부로 출금 요청을 보내고 있다면, 지금 당장 아웃바운드 규칙 설정 글로 가보시는게 좋을 듯 하다.</p>
<blockquote>
<h3 id="3-1-인바운드-규칙-확인">3-1. 인바운드 규칙 확인</h3>
<p>인스턴스 목록으로 이동하고, 해당 인스턴스를 클릭한 후에 &#39;보안&#39;을 눌러보면, 인바운드 규칙이 하나 밖에 없을 것이다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/6275ca9a-0b09-4581-9e9a-43366268a479/image.png" alt="">
보안 그룹을 클릭하여 이동을 한 뒤에
<img src="https://velog.velcdn.com/images/sung-je-kim/post/dcfa773f-1e13-44f8-9ff8-827b2b6cfda3/image.png" alt="">
인바운드 규칙 편집을 눌러주어서 편집을 진행한다.</p>
</blockquote>
<blockquote>
<h3 id="3-2-인바운드-규칙-설정">3-2. 인바운드 규칙 설정</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/216b38bb-5f0f-4802-b16c-66a37bfe6030/image.png" alt="">
필자는 위와 같이 설정했다.</p>
</blockquote>
<h4 id="사용자가-8080-port를-통해-들어올-수-있도록-인바운드-규칙을-설정하였고">사용자가 8080 PORT를 통해 들어올 수 있도록 인바운드 규칙을 설정하였고,</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/98a1fc0e-a07f-4027-86d6-2c6722232168/image.png" alt=""></p>
<h4 id="packagejson의-build된-파일들을-start로-실행시키는데-start-script-또한-8080-port로-수정해줬다">package.json의 build된 파일들을 start로 실행시키는데, <strong>start script 또한 8080 PORT로 수정해줬다.</strong></h4>
<h4 id="⚠️겪은-문제⚠️"><strong>⚠️겪은 문제⚠️</strong></h4>
<p>ssh를 왜 0.0.0.0/0으로 설정했는지는 아래와 같다.
ssh 같은 경우에 &#39;내 IP&#39;로 설정하는 경우도 있는데 필자 또한 &#39;내 IP&#39;로 설정하였다가 설정 당일날은 정말 정상적으로 잘 되었는데, 
다음날 ssh 접속을 하려니 안됐어서 확인해보니, 내 IP가 카페 wifi 때문인지 지속적으로 바뀌는 현상이 있어서 ssh 접속 안된다고 3시간을 버린 기억이 있다.</p>
<hr>
<h2 id="4-인스턴스에-접속">4. 인스턴스에 접속</h2>
<p>이제 .pem을 저장해둔 곳으로 가서 ssh 접속을 해볼 것이다.
ssh 통해 해당 인스턴스로 접속을 해서, 우리가 빌린 AWS EC2 컴퓨터 기초 세팅할 것이다.
우리가 사용하게 될 것은 아래와 같다.</p>
<ul>
<li><strong>nvm</strong>을 통해서 <strong>Node</strong> 버전 관리</li>
<li><strong>npm 또는 yarn</strong>과 같은 사용했던 package manager</li>
<li>GitHub에 저장되어 있는 내 프로젝트 코드 인스턴스에 삽입하기</li>
<li>무중단 배포를 위한 <strong>&#39;pm2&#39;</strong></li>
<li>리버스 프록시 및 HTTPS 설정을 위한 <strong>&#39;NGINX&#39;</strong></li>
<li>Let&#39;s Encrypt에서 발급해주는 SSL 인증서 발급 그리고 자동 갱신 도구 <strong>&#39;Certbot&#39;</strong></li>
</ul>
<blockquote>
<h3 id="4-1-ssh-명령어-확인-후-인스턴스에-연결">4-1. ssh 명령어 확인 후 인스턴스에 연결</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/2c899c52-675b-4445-b9dd-f85a5f57e9f9/image.png" alt="">
위 사진 처럼 인스턴스 리스트로 이동하게 되면 &#39;연결&#39;을 클릭한 후 확인하게 되면 AWS에서 친절하게 어떻게 명령어를 치면 연결이 되는지 순차적으로 설명 되어 있다.
iTerm(터미널)을 킨 뒤에 해당 .pem 이 있는 곳으로 이동하여 아래 명령어를 사용하였다.</p>
</blockquote>
<pre><code>- chmod 400 &quot;.pem 파일 이름&quot;
- ssh -i &quot;.pem 파일 이름&quot; ubuntu@&quot;퍼블릭 IPv4 DNS&quot;</code></pre><p>위와 같이 명령어를 기입하고, 아래와 같이 yes라고 하면?
<img src="https://velog.velcdn.com/images/sung-je-kim/post/3f8913b1-c598-435f-9101-35fd9e2d5f25/image.png" alt="">
<img src="https://velog.velcdn.com/images/sung-je-kim/post/e8434a89-6b8a-418b-88c6-30986638eb53/image.png" alt="">
성공적으로 접속한 모습을 볼 수 있다~~!!</p>
<blockquote>
<h3 id="4-2-인스턴스-ubuntu-업데이트">4-2. 인스턴스 ubuntu 업데이트</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/3b27adb6-7f81-4ef6-af4a-ee0540217f66/image.png" alt="">
위 사진과 같이 git을 제외하고는 package manager도 없는 상태임을 알 수 있다.</p>
</blockquote>
<pre><code>- sudo apt-get update</code></pre><p>명령어를 입력하여 ubuntu 관리 버전을 업데이트 시켜준다. 뭐가 길게 줄줄 뜨는데 기다리면 완료가 된다.</p>
<blockquote>
<h3 id="4-3-nvm-설치">4-3. nvm 설치</h3>
<p>아래 두 명령어를 통해서 설치, 설치 확인을 할 것이다.</p>
</blockquote>
<pre><code>- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
- command -v nvm /** 버전 확인 */</code></pre><p>설치를 진행하고 command -v nvm을 치면 nvm이 나오면 성공적으로 설치가 완료된 것이다.
<strong>⚠️설치를 했는데 버전 확인 명령어에 무반응이예요⚠️</strong>
<img src="https://velog.velcdn.com/images/sung-je-kim/post/1cdbf08d-cc4e-4217-b4e0-8fef9a90ec5a/image.png" alt="">
이런 식으로 nvm이 뜨지 않는다면 iTerm(터미널)을 껐다가 다시 켜보면 된다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/c90b9a91-12eb-43d9-a7ae-775a31291171/image.png" alt="">
위 처럼 nvm이 나오면 성공적으로 설치된 것이다.</p>
<blockquote>
<h3 id="4-4-node-설치">4-4. Node 설치</h3>
</blockquote>
<pre><code>- nvm install &quot;버전&quot;
- nvm use &quot;버전&quot;</code></pre><p><img src="https://velog.velcdn.com/images/sung-je-kim/post/2eeb8f5e-4f75-46d3-8a78-039c19758e99/image.png" alt="">
필자는 &quot;버전&quot;을 18.15.0으로 설치를 하였고, 설치를 진행한 뒤에 정상적으로 설치되었는지 확인해보았다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/409c4115-3c77-402d-8402-9aaa8f8caa0f/image.png" alt="">
GOOD.</p>
<blockquote>
<h3 id="4-5-yarn-설치">4-5. Yarn 설치</h3>
<p>yarn을 사용하지 않는다면 이 부분은 건너 뛰어도 무방하다.
이미 위에서 확인을 했겠지만 npm은 node를 설치할 때 같이 설치 되었기 때문이다.</p>
</blockquote>
<pre><code>- npm i -g yarn /** yarn 설치 */
- yarn --version /** yarn이 성공적으로 설치 되었는지 확인 */</code></pre><p><img src="https://velog.velcdn.com/images/sung-je-kim/post/2fe3a506-2235-43ac-911b-738ff748e833/image.png" alt="">
위 처럼 yarn이 성공적으로 설치된 것을 확인할 수 있다.</p>
<blockquote>
<h3 id="4-6-프로젝트-코드-인스턴스에-삽입">4-6. 프로젝트 코드 인스턴스에 삽입</h3>
<p>EC2 인스턴스에서 배포할 프로젝트를 clone을 진행한다.</p>
</blockquote>
<pre><code>- git clone &quot;프로젝트 레포지터리 주소&quot;</code></pre><p><img src="https://velog.velcdn.com/images/sung-je-kim/post/4056df1f-d90e-4f52-93bd-625313446332/image.png" alt=""></p>
<pre><code>- npm i
- yarn install</code></pre><p>clone을 성공적으로 진행했다면 사용하는 package manager를 통해 install을 진행한 후, 이제 필요한 환경 변수들을 넣어주고, package manager를 통해서 build를 진행한다.</p>
<blockquote>
<h3 id="4-7-브라우저에서-확인해보기">4-7. 브라우저에서 확인해보기</h3>
<p>성공적으로 build 되었다면, yarn start를 통해서 확인을 해본다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/04594f30-c283-478e-9e02-84bd8e2af0c4/image.png" alt="">
앞서 start script에 -p 8080을 추가해주고, 인바운드 규칙에서 사용자가 8080으로 들어올 수 있도록 설정을 해주어서, <strong>&#39;퍼블릭 IPv4 주소:8080&#39;</strong>으로 브라우저에서 들어가보면
<img src="https://velog.velcdn.com/images/sung-je-kim/post/46f000c5-c78e-4c82-9307-e73afd721e8a/image.png" alt="">
성공적으로 나오는 모습을 확인할 수 있다.</p>
</blockquote>
<hr>
<h2 id="5-무중단-배포를-위한-pm2">5. 무중단 배포를 위한 pm2</h2>
<p>내 컴퓨터가 꺼지게 된다면 또는 내가 인스턴스와 연결을 종료한다면, 내가 켜둔 인스턴스가 연결 종료와 함께 수면 상태에 들어간다.
365일 24시간 무중단으로 인스턴스를 활용해먹어야 하니 pm2 설정을 해야 한다.</p>
<blockquote>
<h3 id="5-1-pm2-설치">5-1. pm2 설치</h3>
<p>아래의 명령어를 이용해서 pm2를 설치해주도록 하자</p>
</blockquote>
<pre><code>- npm i -g pm2</code></pre><p>해당 명령어를 실행한 후 </p>
<pre><code>pm2 list</code></pre><p>를 쳐봤을 때 아래와 같이 나오면 성공적으로 설치가 된 것이다
<img src="https://velog.velcdn.com/images/sung-je-kim/post/b49b7aba-7fee-423c-b6f4-257747fe66e8/image.png" alt="">
그리고 list에는 아무것도 뜨지 않을 것이다.</p>
<blockquote>
<h3 id="5-2-무중단-배포-활성화">5-2. 무중단 배포 활성화</h3>
<p>사용하는 package manager 활용해서 pm2를 활성화 시켜주면 된다.</p>
</blockquote>
<pre><code>- pm2 start yarn -w -i 0 --name &quot;velog-sung-je-kim&quot; -- start
/** yarn 활용해서 시작 */
- pm2 start npm --name &quot;velog-sung-je-kim&quot; -w -i 0 -- run start
/** npm 활용해서 시작 */</code></pre><ul>
<li>-w(--watch) : 파일의 변경 시 자동으로 재시작되는 옵션을 활성화 시키는 것</li>
<li>-i 0(코어 갯수) : Node.js의 싱글 스레드를 보완하기 위한 CPU 코어 수만큼 프로세스를 생성하는 클러스터 모드
<img src="https://velog.velcdn.com/images/sung-je-kim/post/22fe0b2a-6e33-4c72-9bde-5b88ca83a500/image.png" alt="">
pm2 list에 성공적으로 하나 생긴 것이 보인다. 그리고 &#39;~.&#39;을 통해서 인스턴스를 빠져나가도 무중단 배포가 활성화 되어 있기 때문에 지속적으로 서버가 활성화 되어 있다.</li>
</ul>
<hr>
<h2 id="6-도메인-연결">6. 도메인 연결</h2>
<p>Route53을 통해서 DNS를 구축하고, 도메인을 인스턴스의 퍼블릭IP로 라우팅 시켜줄 작업니다.</p>
<blockquote>
<h3 id="6-1-route53-호스팅-영역-생성">6-1. Route53 호스팅 영역 생성</h3>
<p>AWS에서 Route53 페이지로 이동을 한다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/a0164bca-0985-48c1-905a-0f31a9b42764/image.png" alt="">
좌측 사이드바에서 호스팅 영역을 클릭한 후에 호스팅 영역 생성을 클릭한다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/67674463-6884-4d8b-9620-0c05fc3b8b49/image.png" alt="">
도메인 주소를 기재하고 새 호스팅 영역을 생성한다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/8974a0a0-356c-45ce-a408-fbbb5245cf31/image.png" alt="">
그러면 해당 도메인의 레코드들이 생기는데, 우리는 하나의 레코드를 더 만들어줄 것이다. 
레코드 생성을 누른 뒤에 아래와 같이 값에 퍼블릭 IP를 기재해주고 레코드를 생성한다. 퍼블릭 IP로 라우팅해줄 레코드다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/47ba1c79-3edf-4786-867a-6ad937c0c731/image.png" alt=""></p>
</blockquote>
<blockquote>
<h3 id="6-2-가비아에서-네임서버-편집">6-2. 가비아에서 네임서버 편집</h3>
<p>가비아로 이동해서 Route53에서 기재한 도메인의 관리 탭으로 이동한다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/8f47090f-f2b2-4b34-b1c4-1426a49376f8/image.png" alt="">
관리 탭으로 이동하여 네임서버 &#39;설정&#39;을 눌러 편집할 것이다. 
1차, 2차, 3차, 4차를 이전에 AWS에서 생성한 호스팅 영역의 <strong>NS 유형에 해당하는 값/트래픽 라우팅 대상 4가지</strong>로 대체할 것이다.
편집을 완료하고 적용되는데 시간이 좀 걸리기 때문에 기다렸다 도메인을 통해 접속이 가능하다.</p>
</blockquote>
<hr>
<h2 id="7-nginx">7. NginX</h2>
<p>NginX에 대해서는 아직 미흡해서 추후에 또 NginX 관련 글을 쓸 예정이다.
NginX를 이용하여 리버스 프록시를 적용할 것이다.
간단히 설명하자면 NginX를 클라이언트와 우리 어플리케이션 사이에 배치를 하는 것이다.</p>
<blockquote>
<h3 id="7-1-nginx-설치">7-1. NginX 설치</h3>
<p>아래 명령어를 통해서 NginX를 설치해준다.</p>
</blockquote>
<pre><code>- sudo apt-get install nginx -y    /** NginX 설치 */
- nginx -v     /** 잘 설치 되어 있는지 확인 */</code></pre><p>설치 중에 아래와 같이 터미널이 변하면 편하게 엔터 누르면 된다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/e7799f30-0912-492a-adcc-1bda67b57326/image.png" alt=""></p>
<blockquote>
<h3 id="7-2-리버스-프록시-적용">7-2. 리버스 프록시 적용</h3>
<p>우리는 아래의 파일을 vim으로 편집을 해서 리버스 프록시를 적용할 것이다.</p>
</blockquote>
<pre><code>- sudo vim /etc/nginx/sites-available/default /** 편집할 파일 */</code></pre><pre><code>/** 첨부할 코드 */
server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name 도메인;
        location / {
                proxy_pass http://퍼블릭IP:8080;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
        }
}</code></pre><p><img src="https://velog.velcdn.com/images/sung-je-kim/post/75c0a1e4-d125-4682-9335-730ed60b553c/image.png" alt="">
위에 대해서 설명을 첨부하자면 우리는 여태 8080 PORT로 사용자의 접속을 허용했다. 근데 사용자가 접속을 하려면 <strong>&#39;도메인:8080&#39; 이런 식으로 PORT 번호까지 기입</strong>을 해야지 해당 서비스로 이동할 수 있는데, 이것은 사용자 입장에서 너무 불편하다.
그래서 <strong>사용자와 내 어플리케이션(PORT === 8080)</strong> 사이에 <strong>NginX(PORT === 80)</strong>를 배치하여 PORT 번호를 기입하지 않고도 사용자가 우리의 서비스에 접속을 하면 8080 PORT로 이동 시키는 것이다.</p>
<blockquote>
<h3 id="7-3-nginx-서비스-재시작">7-3. NginX 서비스 재시작</h3>
<p>아래의 명령어로 NginX 서비스를 리로드해주고 브라우저에서 확인해보면,</p>
</blockquote>
<pre><code>- sudo nginx -t
- sudo systemctl reload nginx</code></pre><p>첫 번째 사진은 이전에 8080 PORT를 기입해줘야 접속이 가능했는데, 두 번째 사진을 보면 8080 PORT를 기입하지 않아도 내 어플리케이션으로 접속할 수 있는 모습을 볼 수 있다. 
<img src="https://velog.velcdn.com/images/sung-je-kim/post/2d6df72b-27a2-4e66-b2c8-4a8485ad392e/image.png" alt="적용 전">
<img src="https://velog.velcdn.com/images/sung-je-kim/post/c012d975-b9af-40d0-9644-2fe1a017b824/image.png" alt="적용 후">
도메인을 통해서도 확인 가능.</p>
<hr>
<h2 id="8-https-적용">8. HTTPS 적용</h2>
<p>Let&#39;s Encrypt에서 90일 유효기간이 있는 SSL 인증서 발급을 받은 뒤, Certbot으로 SSL 인증서를 자동 갱신하는 작업을 해줄 것이다.</p>
<blockquote>
<h3 id="8-1-certbot-설치-후-인증서-발급">8-1. Certbot 설치 후 인증서 발급</h3>
</blockquote>
<pre><code>- sudo snap install certbot --classic
/** certbot 설치 */
- sudo certbot --nginx
/** SSL 인증서 발급 */</code></pre><p><img src="https://velog.velcdn.com/images/sung-je-kim/post/c443a8f1-74b8-4b90-9f40-7491bc0a8dcb/image.png" alt="">
이메일을 입렵하라고 위 처럼 나오는데 만료 기간이 얼마 남지 않았을 때, 메일을 받을 주소를 기입하고 Enter를 누르면 된다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/3f15b148-c0a3-4553-99a3-82bea6bb1851/image.png" alt="">
무슨 서비스에 동의 하는지에 대해서 물어보는데 must agree라고 하니 동의한다고 해준다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/776f0e64-4187-4a59-844d-e6279866c61e/image.png" alt="">
뉴스레터 이메일 받는 것의 동의여부에 대해서 물어보는데, 필자는 굳이 싶어서 동의하지 않았다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/f895aa9f-ef9e-4cd9-bd61-67aaf23d3a05/image.png" alt="">
어떠한 도메인에 대해서 SSL 인증서를 받을 것인지에 대해서 물어보는데, 발급 받을 도메인의 번호를 기입하면 된다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/3e9c171b-30d4-4b76-8422-8494fcd8fc9c/image.png" alt="">
성공적으로 90일 짜리 SSL 인증서를 발급 받았다.</p>
<blockquote>
<h3 id="8-2-certbot으로-자동-갱신-설정">8-2. Certbot으로 자동 갱신 설정</h3>
<p>아래  명령어를 통해서 설정 파일을 열게 되면 사진과 같이 편집기를 선택하라고 뜨는데, 거기서 편한 편집기를 사용하면 된다.</p>
</blockquote>
<pre><code>- sudo crontab -e
/** 설정 파일 열기 */</code></pre><p><img src="https://velog.velcdn.com/images/sung-je-kim/post/071bef28-e5de-4375-a6cf-70716ab696c5/image.png" alt="">
그리고 맨 밑에 아래 명령어를 기입해주고 저장한다.</p>
<pre><code>0 18 1 * * certbot renew --renew-hook=&quot;sudo systemctl restart nginx&quot;</code></pre><p>기입하고 Ctrl + X -&gt; y -&gt; Enter로 빠져나오면 된다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/082428b1-2420-4c92-a604-00bd8f4cef4b/image.png" alt="">
이렇게 설정하면 매월 1일 오후 6시마다 인증서를 갱신하고 NGINX를 재시작하는 명령어가 실행된다. 추후에 certbot에 대해서도 깊게 다뤄볼 예정이다.</p>
<hr>
<h2 id="9-최종-확인">9. 최종 확인</h2>
<p>HTTPS가 설정 되었는지 확인을 해보면 HTTPS가 잘 적용된 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/db8ee3fc-3ea7-4903-83d0-0ef1104d9711/image.png" alt=""></p>
<p>이렇게 해서 어떻게 배포를 하는지에 대해서 알아보았다.
현업에서는 지금 필자가 했던 방법보다 더 복잡하고 심도 있을 거라고 생각하고, 더 심도 있게 알기 위해서 추후에 더 공부한 뒤에 또 다른 글로 하나하나 다뤄볼 예정이다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/9ebd199b-a099-447a-8cb2-cf833ffc6f06/image.jpeg" alt=""></p>
<blockquote>
<h1 id="큰-도움이-되었던-문서">큰 도움이 되었던 문서</h1>
</blockquote>
<ul>
<li><h3 id="aws-ec2로-nextjs-배포하기">AWS EC2로 Next.js 배포하기</h3>
<a href="https://puterism.com/deploy-next-js-with-ec2/">https://puterism.com/deploy-next-js-with-ec2/</a></li>
<li>AWS 인바운드 규칙 설정하기
<a href="https://dbjh.tistory.com/65">https://dbjh.tistory.com/65</a></li>
<li>AWS EC2 와 도메인 연결하기 (feat. 가비아)
<a href="https://developer-ping9.tistory.com/320">https://developer-ping9.tistory.com/320</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NextJS] 13v에 새로 추가 된 @next/font
(feat. @emotions)]]></title>
            <link>https://velog.io/@sung-je-kim/NextJS-13v-nextfontgoogle-with-emotionreact</link>
            <guid>https://velog.io/@sung-je-kim/NextJS-13v-nextfontgoogle-with-emotionreact</guid>
            <pubDate>Thu, 12 Jan 2023 09:49:38 GMT</pubDate>
            <description><![CDATA[<p>NextJS를 활용하여 프로젝트를 진행하는데, <strong>@next/font</strong>라는 라이브러리가 있길래 사용을 해보았다.</p>
<pre><code>yarn add @next/font || npm install @next/font
</code></pre><p>필자는 yarn을 이용해서 설치를 하였다. </p>
<p>@next/font는 아래 두가지로 크게 나뉜다.</p>
<ul>
<li><p><strong>@next/font/google</strong>
  (fonts가 내장 되어 있어 Browser가 Google에게 요청을 보내지 않는다고 한다.)</p>
</li>
<li><p><strong>@next/font/local</strong></p>
</li>
</ul>
<p>필자는 @next/font/google을 사용해서, 해당 글에서 아쉽게도 local에 대한 얘기는 다루지 않는다.</p>
<blockquote>
<h3 id="folder">folder</h3>
</blockquote>
<pre><code>src --- style --- fonts --- index.tsx
                           |
                         |- notoSans.tsx
                         |
                          - roboto.tsx</code></pre><blockquote>
<h3 id="nextfontgoogle">@next/font/google</h3>
</blockquote>
<pre><code class="language-typescript">/* style/fonts/notoSans.tsx */
import React from &quot;react&quot;;
import { Noto_Sans_KR } from &quot;@next/font/google&quot;;
const bold = Noto_Sans_KR({
  weight: &quot;700&quot;,
  display: &quot;fallback&quot;,
  subsets: [&quot;korean&quot;],
  style: &quot;normal&quot;,
  variable: &quot;--noto-sans_KR-bold&quot;,
  fallback: [&quot;system-ui&quot;],
});
const medium = Noto_Sans_KR({
  weight: &quot;500&quot;,
  display: &quot;fallback&quot;,
  subsets: [&quot;korean&quot;],
  style: &quot;normal&quot;,
  variable: &quot;--noto-sans_KR-medium&quot;,
  fallback: [&quot;system-ui&quot;],
});
export {
  bold as notoSansKrBold,
  medium as notoSansKrMedium,
}</code></pre>
<pre><code class="language-typescript">/* style/fonts/roboto.tsx */
import React from &quot;react&quot;;
import { Roboto } from &quot;@next/font/google&quot;;
const bold = Roboto({
  weight: &quot;700&quot;,
  display: &quot;fallback&quot;,
  subsets: [&quot;latin&quot;],
  style: &quot;normal&quot;,
  variable: &quot;--roboto-bold&quot;,
  fallback: [&quot;system-ui&quot;],
});
const medium = Roboto({
  weight: &quot;500&quot;,
  display: &quot;fallback&quot;,
  subsets: [&quot;latin&quot;],
  style: &quot;normal&quot;,
  variable: &quot;--roboto-medium&quot;,
  fallback: [&quot;system-ui&quot;],
});
export {
  bold as robotoBold,
  medium as robotoMedium,
}</code></pre>
<p>위와 같이 Noto-Sans-KR font와 Roboto font를 생성하였다. 해당 key 값들의 설명은 아래와 같다.</p>
<ul>
<li><strong>weight</strong>: <strong>폰트의 가중치</strong>를 설정하는 key</li>
<li><strong>display</strong>: css에서 <strong>font-display</strong> 키워드를 설정 하는 key</li>
<li><strong>subsets</strong>: font가 <strong>적용할 수 있는 언어 중</strong>에서 <strong>먼저 가져 올 나라의 언어를 설정</strong>하는 key</li>
<li><strong>style</strong>: <strong>font의 스타일</strong>이 normal인지 italic인지 설정하는 key</li>
<li><strong>variable</strong>: <strong>CSS 변수</strong> 방식으로 사용할 때의 <strong>이름을 정의</strong>하는 key</li>
<li><strong>fallback</strong>: 해당 font를 가져오지 못하였을 때의 <strong>대체 글꼴을 설정</strong>하는 key</li>
</ul>
<p>더 많은 key들 또는 해당 key의 type을 확인하고 싶다면, <a href="https://nextjs.org/docs/api-reference/next/font#nextfontgoogle">NextJS @next/font의 API</a> 문서가 존재하니 들어가서 확인해보면 된다. 참고로 type은 모든 key에 다 적혀 있진 않다.</p>
<blockquote>
<h3 id="내가-적용한-방법">내가 적용한 방법</h3>
</blockquote>
<pre><code class="language-typescript">/* style/fonts/index.tsx */
import React from &quot;react&quot;;
import { css } from &quot;@emotion/react&quot;;
import {
  notoSansKrBold,
  notoSansKrMedium,
  notoSansKrRegular,
  notoSansKrLight,
  notoSansKrThin,
} from &quot;./notoSans&quot;;
import {
  robotoBold,
  robotoMedium,
  robotoRegular,
  robotoLight,
  robotoThin,
} from &quot;./roboto&quot;;
const boldFont = css`
  font-family: ${notoSansKrBold.style.fontFamily},
    ${robotoBold.style.fontFamily};
`;
const mediumFont = css`
  font-family: ${notoSansKrMedium.style.fontFamily},
    ${robotoMedium.style.fontFamily};
export { boldFont, mediumFont };</code></pre>
<p> 위 처럼 Noto-Sans-KR font와 Roboto font를 import를 한 후에, <strong>emotion/react의 css 함수를 이용</strong>해 font-family 키워드에 삽입하였다.</p>
<pre><code class="language-typescript">/* example */
const fontName = css`
    font-family: ${????.style.fontFamily};
`;</code></pre>
<blockquote>
<h3 id="안됐던-방법">안됐던 방법</h3>
<p>아래의 형식은 반복이 될 형식이여서</p>
</blockquote>
<pre><code class="language-typescript">  display: &quot;fallback&quot;,
  subsets: [&quot;latin&quot;],
  style: &quot;normal&quot;,
  fallback: [&quot;system-ui&quot;],</code></pre>
<pre><code class="language-typescript">interface WeightParams {
  weight: &quot;100&quot; | &quot;300&quot; | &quot;400&quot; | &quot;500&quot; | &quot;700&quot; | &quot;900&quot;;
}
const createFontByWeight = ({ weight }: WeightParams) =&gt; {
  return Roboto({
    weight: weight,
    display: &quot;fallback&quot;,
    subsets: [&quot;latin&quot;],
    style: &quot;normal&quot;,
    variable: `--roboto-${weight}`,
    fallback: [&quot;system-ui&quot;],
  }).style.fontFamily;
};</code></pre>
<p>와 같이 적용해서 함수만 import해서 weight만 넣어주고 사용하려 했는데
<img src="https://velog.velcdn.com/images/sung-je-kim/post/d87a6cc5-b097-4b78-a20c-9069fe9847d7/image.png" alt="">
위와 같은 Error가 떴다.
우리가 @next/font/google 로 가져온 font 함수가 함수 스코프에 갇히면 안되는 이슈였다.</p>
<p>또 다르게 적용할 수 있는 여러 방법들이 있는데, <a href="https://nextjs.org/docs/basic-features/font-optimization">NextJS의 @next/font</a>에서 확인하면 된다. 
공식 문서에서 글로 적어두기도 했지만, 짧게 Youtube 영상으로도 설명해두었다.</p>
<blockquote>
<h3 id="error">Error</h3>
<p>@next/font/google을 이용해서 Roboto font와 Noto-Sans-KR font를 사용하였다.
그리고 해당 프로젝트를 build를 하였는데 Noto-Sans-KR font를 fetch 하는데 error가 발생했다.
그래서 구글링을 해보니 NextJS에서도 <a href="https://github.com/vercel/next.js/pull/42637">@next/font/google fetch error dev</a>라는 issue를 생성해두었다. 
p.s) 가끔 dev에서도 Noto-Sans-KR font 가져오기를 실패했다는 error가 뜰 때도 있었다.
<img src="https://velog.velcdn.com/images/sung-je-kim/post/a04a6164-1d8e-455f-87b9-d4e85e470a97/image.png" alt=""></p>
</blockquote>
<h4 id="긴-글-읽어주셔서-감사합니다">긴 글 읽어주셔서 감사합니다.</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/adb34764-2f56-42b7-9ce0-a79804baede9/image.jpeg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTML, CSS] Insert Text with Line Break (feat. XSS)]]></title>
            <link>https://velog.io/@sung-je-kim/HTML-CSS-Insert-line-breakn</link>
            <guid>https://velog.io/@sung-je-kim/HTML-CSS-Insert-line-breakn</guid>
            <pubDate>Thu, 05 Jan 2023 11:15:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="결론">결론</h4>
</blockquote>
<ul>
<li><strong>dangerouslySetInnerHTML</strong> 또는 
html-react-parser의 <strong>HtmlParser를</strong> 사용할 땐 XSS를 고려해야 한다.</li>
<li>상황이 따라주지 않을 땐, 위와 같은 방법보단 HTML 또는 CSS를 이용하는 것이 좋은 선택이다.</li>
</ul>
<p>진행한 프로젝트에서 마주친 수많은 이슈 중에서,
\n이 포함되어 있는 string을 element 안에 넣어야 할 때 마주친 이슈들을 정리한 것이다.</p>
<hr>
<blockquote>
<h4 id="element-안에-text-넣기">Element 안에 Text 넣기</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/3ca8cf85-bf17-421b-ad9f-4ca9f0771c79/image.png" alt="">
위와 같은 코드를 작성하고 browser에서 확인해보니 아래와 같았다.</p>
</blockquote>
<blockquote>
<h3 id="결과">결과</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/e29beee0-a211-4b09-a63c-b4d9fba77913/image.png" alt=""></p>
</blockquote>
<p>그래서 정규표현식을 통해서 \n 을  &amp;ltbr /&amp;gt로 바꿔, <strong>dangerouslySetInnerHTML</strong> 
또는 html-react-parser의 <strong>HtmlParser</strong>를 이용해 삽입을 해야겠다는 생각이 들었다.</p>
<hr>
<h3 id="n-을-ltbr-gt로-변환">\n 을 &amp;ltbr /&amp;gt로 변환</h3>
<blockquote>
<h4 id="parents">Parents<img src="https://velog.velcdn.com/images/sung-je-kim/post/bfdf9287-f6d7-43da-a250-4e60bb6b3a01/image.png" alt=""></h4>
</blockquote>
<blockquote>
<h4 id="dangerousbox">DangerousBox</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/c2119f16-85cc-47d3-be78-9f9c3a0b6a01/image.png" alt=""></p>
</blockquote>
<blockquote>
<h4 id="htmlparserbox">HtmlParserBox</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/f9d005e9-5214-4d90-a52f-ec51d8676086/image.png" alt=""></p>
</blockquote>
<blockquote>
<h3 id="결과-1">결과</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/83c702f2-b803-4d04-b06e-354c67c60102/image.png" alt=""></p>
</blockquote>
<p>굉장히 효과적이였다? 라고 생각했다. <strong>악성 사용자를 만나기 전까진....</strong></p>
<hr>
<h3 id="문제의-발생">문제의 발생</h3>
<blockquote>
<h4 id="악성-사용자의-코드-삽입">악성 사용자의 코드 삽입</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/660921c9-074d-43c7-b329-b42eaa53ca14/image.png" alt=""></p>
</blockquote>
<ul>
<li>DangerousBox와 HtmlParserBox 코드는 두번째 예시와 같습니다.</li>
</ul>
<blockquote>
<h3 id="결과-2">결과</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/101388db-a91c-47c8-83d7-f1f63ded6550/image.png" alt=""></p>
</blockquote>
<p>\n을 &amp;ltbr /&amp;gt로 변경하고 그것을 dangerouslySetInnerHTML 또는 HtmlParser를 사용한 곳에 넘겨주고 있는 상황이다. 근데 사용자가 악성 script를 작성하였더니, alert가 뜨게 되는 모습을 보게 되었다.</p>
<p>\n을 &amp;ltbr /&amp;gt로 변경하는 방법은 오히려 더 좋지 않는 것이라고 판단을 하였다.
\n 때문에 머리가 아팠는데, 아래와 같은 CSS 키워드를 사용해보았다.</p>
<pre><code>white-space: pre-wrap;
word-wrap: break-word;</code></pre><ul>
<li>white-space : 요소 내부의 공백을 처리하는 방법을 설정 합니다.</li>
<li>word-wrap : 텍스트가 자신의 콘텐츠 박스 밖으로 over-flow 할 때 줄을 바꿀 지 지정합니다.</li>
</ul>
<p>사실 해당 글에서는 &quot;white-space: pre-wrap;&quot; 키워드가 중요하다.</p>
<hr>
<h3 id="css-키워드-사용으로-문제의-해결">CSS 키워드 사용으로 문제의 해결</h3>
<blockquote>
<h4 id="parents-1">Parents</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/1bfbd409-c9c7-4711-bb9f-592a5e30ad0b/image.png" alt=""></p>
</blockquote>
<blockquote>
<h4 id="goodbox">GoodBox</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/8a2cf613-b904-4ffb-ac73-43119db35e77/image.png" alt=""></p>
</blockquote>
<blockquote>
<h3 id="결과-3">결과</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/ba75cde3-8752-4b02-a2ee-e72d87181839/image.png" alt=""></p>
</blockquote>
<p>악성 script 삽입으로 인한 alert도 뜨지 않고 \n도 정상적으로 처리된 것을 볼 수 있다.
CSS의 &quot;white-space: pre-wrap;&quot; 키워드를 사용해서 해결할 수도 있지만
HTML에서의 &amp;ltpre /&amp;gt tag를 사용해서도 \n을 잘 처리할 수도 있다.</p>
<hr>
<blockquote>
<h3 id="ps">P.S</h3>
<p>마지막으로 draft.js를 사용한 곳에서는 XSS에 관한 이슈가 일어나지 않았다.
dangerouslySetInnerHTML에 대해서 찾아보다가 <a href="https://ko.reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml">dangerouslySetInnerHTML 관련 React 공식 문서</a>를 확인하였는데, 생소한 단어들이 오고 가면서 draft.js 라이브러리의 언급이 있었다.
draft.js를 통해 받은 입력을 dangerouslySetInnerHTML을 통해서 보여주고 있었는데, draft.js는 XSS를 막아주는 코드가 내장되어 있나보다.</p>
</blockquote>
<h3 id="긴-글-읽어주셔서-감사합니다">긴 글 읽어주셔서 감사합니다.</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/15888f18-629e-40d8-807a-31308eb273ff/image.jpeg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Re-rendering Case]]></title>
            <link>https://velog.io/@sung-je-kim/re-rendering-case-in-react</link>
            <guid>https://velog.io/@sung-je-kim/re-rendering-case-in-react</guid>
            <pubDate>Fri, 23 Dec 2022 12:55:02 GMT</pubDate>
            <description><![CDATA[<p><strong>Re-rendering이란 어떠한 값의 상태 변화가 일어났을 때</strong>, 일어나는 것을 Re-rendering이라고 한다.</p>
<blockquote>
<h2 id="결론--re-rendering-되는-경우">결론 : Re-rendering 되는 경우</h2>
</blockquote>
<ol>
<li><strong>props</strong>의 변화가 일어났을 때</li>
<li><strong>state</strong>의 변화가 일어났을 때</li>
<li><strong>부모 Component 의 state 변화</strong>가 일어났을 때</li>
<li><strong>Redux store</strong> 의 변화가 일어났을 때 (아래에 예시가 존재하지 않음)</li>
</ol>
<p>Components가 Re-rendering 되는지에 대해서 확인을 하려면 <a href="https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi">React Developer Tools</a>를 활용하여 개발자 도구에서 확인할 수 있다. 해당 도구를 통해서 위의 <strong>Re-rendering 되는 경우를 확인</strong>해볼 것이다.</p>
<p>예시 코드는 아래와 같다.</p>
<hr>
<h2 id="코드">코드</h2>
<blockquote>
<h3 id="parents-component">Parents Component</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/3d72e450-6c12-46ac-a507-6fc254a67c83/image.png" alt=""></p>
</blockquote>
<blockquote>
<h3 id="goodchild-component">GoodChild Component</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/48c66bcc-6257-4222-9bb0-a04bfa84c88e/image.png" alt=""></p>
</blockquote>
<blockquote>
<h3 id="troublechild-component">TroubleChild Component</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/c8818241-39b1-45a3-891a-9d941ab031af/image.png" alt=""></p>
</blockquote>
<hr>
<ul>
<li><h2 id="components-의-역할">Components 의 역할</h2>
<ul>
<li><h3 id="parents-component-1">Parents Component</h3>
<p>Parents Component가 mount가 되었을 때 Parents Component에 선언된 state를 3초 뒤에 setter를 통해서 상태를 변경 시켜준다.
또한 Children Component들에게 props를 넘겨주고 있는 상태이다.</p>
</li>
<li><h3 id="goodchildren-component">GoodChildren Component</h3>
<p>props로 받은 text가 div element 안에 들어가 있는 Component다.</p>
</li>
<li><h3 id="troublechildren-component">TroubleChildren Component</h3>
<p>props로 받은 onClickProps가 onClick handler로 할당된 30개의 div element 요소를 만들어내는 Component다.</p>
</li>
</ul>
</li>
</ul>
<hr>
<p>React Developer Tools의 Profiler로 확인해본 결과 아래와 같다.</p>
<blockquote>
<h4 id="결과">결과</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/6f2cdbaa-0a26-43c1-9b84-b99a05ea3237/image.png" alt=""></p>
</blockquote>
<p>Parents Component의 state 변화가 일어나자 
두 개의 Children Component들 또한 Re-rendering 된 것을 확인할 수 있다.</p>
<h3 id="그렇다면-props를-넘겨주고-있어서-그런가props를-다-없애볼까">그렇다면 props를 넘겨주고 있어서 그런가...props를 다 없애볼까?</h3>
<p>라는 생각이 들어서 props를 없애버렸다.</p>
<hr>
<h2 id="props-제거한-코드">props 제거한 코드</h2>
<blockquote>
<h3 id="parents-component-2">Parents Component</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/31a6b024-e264-48ad-8bbe-b452d7100600/image.png" alt=""></p>
</blockquote>
<blockquote>
<h3 id="goodchild-component-1">GoodChild Component</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/0802dddd-c2b7-478e-bd07-5d8a0de674ee/image.png" alt=""></p>
</blockquote>
<blockquote>
<h3 id="troublechild-component-1">TroubleChild Component</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/aa886e90-5ead-4fa5-966e-52abafcdde5d/image.png" alt=""></p>
</blockquote>
<hr>
<p>props에 관련된 내용들은 모두 없애고, React Developer Tools의 Profiler로 확인해본 결과 아래와 같다.</p>
<blockquote>
<h4 id="props를-제거한-결과">props를 제거한 결과</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/e0496ef0-29ab-4275-911e-0ddea0b8beb6/image.png" alt=""></p>
</blockquote>
<p>props에 관련된 내용을 지우고 props를 넘겨주지 않지만, 
Children Component들은 <strong>Re-rendering이 일어나는 것을 확인</strong>할 수 있었다.</p>
<hr>
<h3 id="도대체-그럼-어떻게-해야지-re-rendering이-일어나지-않을-수-있을까">도대체 그럼 어떻게 해야지 Re-rendering이 일어나지 않을 수 있을까?</h3>
<p>일어나지 않게 하는 거는 없을 거 같고 최소화 시킬 수 있는 방법은 있다고 한다.</p>
<ul>
<li>useMemo( ) : value memoization</li>
<li>useCallback( ) : function memoization</li>
</ul>
<p>그래서 위 memoization hook들을 적용시키게 된다면 아래와 같은 코드가 된다.</p>
<hr>
<h2 id="memoization-hook-적용-코드">memoization hook 적용 코드</h2>
<blockquote>
<h3 id="parents-component-3">Parents Component</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/7ff1d293-a215-4de9-876e-14e79abf8640/image.png" alt=""></p>
</blockquote>
<ul>
<li>Children Component들 같은 경우에는 첫번째로 보여드렸던 코드 예시와 같습니다.</li>
</ul>
<hr>
<p>memoization 관련 훅을 모두 적용해준 후, 
Parents Component에서 state의 변화를 발생시킨 결과를 React Developer Tools의 Profiler로 확인해본 결과 아래와 같다.</p>
<blockquote>
<h4 id="결과-1">결과</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/9328aa9f-165a-441e-86c1-9ab8ac02a9ac/image.png" alt=""></p>
</blockquote>
<p>memoization이 가능하다는 useMemo와 useCallback을 적용 시켰는데도 Re-rendering이 일어나는 것을 확인할 수 있다.
이정도면 Re-rendering 그냥 못 막는 거 아닌가 싶은데....</p>
<p>그래서 <strong>React의 동작원리</strong>를 잠깐 살펴봐보자. 단계가 두가지가 있다.</p>
<blockquote>
<h4 id="1-render-phase">1. Render Phase</h4>
</blockquote>
<ul>
<li><p>Component를 호출하여 새로운 Virtual DOM을 반환한다. </p>
</li>
<li><p>initial rendering이 아니라면 재조정(Reconciliation) 과정을 거친다.</p>
<pre><code>  - 재조정(Reconciliation) : 이전 Virtual DOM과 현재 Virtual DOM을 비교하는 것.</code></pre><h4 id="2-commit-phase">2. Commit Phase</h4>
</li>
<li><p>Render Phase에서 확인해둔 변경 사항들을 Real DOM과 합성시킨다. 
변경 사항들이 없다면 Commit Phase는 건너 뛴다. </p>
<p>즉, memoization hook을 활용해서 Commit Phase는 건너 뛰었다. 
하지만 <strong>memoization hook을 활용해도 Render Phase는 건너 뛸 수 없다.</strong></p>
</li>
</ul>
<p><strong>Render Phase를 건너 뛰기 위해서</strong>는 아래의 키워드를 사용해주면 된다.</p>
<ul>
<li>React.memo( ) : Component memoization</li>
</ul>
<p>React.memo는 전달 받은 props가 이전과 같으면 Component를 Re-rendering 하지 않는다.
그렇다면 이제 Children Component들에게 React.memo를 적용 시켜보겠다.</p>
<hr>
<h2 id="children-component들에게-reactmemo-적용-코드">Children Component들에게 React.memo 적용 코드</h2>
<blockquote>
<ul>
<li>Parents Components는 위 첫번째 예시와 같습니다.</li>
</ul>
</blockquote>
<h3 id="goodchild-component-2">GoodChild Component</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/ca82d8b4-99b6-42f1-ba23-f55e6475d5b6/image.png" alt=""></p>
<h3 id="troublechild-component-2">TroubleChild Component</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/e8b8d5da-35fa-4d02-b23e-0cd675b9b45d/image.png" alt=""></p>
<hr>
<p>memoization hook 그리고 React.memo 까지 모두 적용해준 후, 
Parents Component에서 state의 변화를 발생시킨 결과를 React Developer Tools의 Profiler로 확인해본 결과 아래와 같다.</p>
<blockquote>
<h4 id="결과-2">결과</h4>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/000307c0-9911-4fed-97cf-ea4ceb922b99/image.png" alt=""></p>
</blockquote>
<p>위 결과를 마지막으로 useMemo, useCallback을 활용해도 Re-rendering은 일어난다는 것을 알았다.
React.memo를 통해서 완전하게 Re-rendering을 방지할 순 없지만, 최소화 시킬 수 있다는 것도 알았다.
현업자분들의 얘기나 여러 자료들을 확인해보면, React 에서 제공해주는 memoization 함수를 통해서 Re-rendering을 방지하고 좋은 기능을 하는 것은 많지만, 그렇다고 무분별하게 모든 Components에 사용을 하게 되면 오히려 더 서비스의 성능이 악화될 수도 있다고 한다.
뭐든 간에 <strong>과유불급</strong>이라고 적재적소에 활용하는 것이 매우 좋다고 생각한다.</p>
<h3 id="긴-글-읽어주셔서-감사합니다">긴 글 읽어주셔서 감사합니다.</h3>
<p><img src="https://velog.velcdn.com/images/sung-je-kim/post/869e1b8a-f9f3-49a8-ab93-d42766938287/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>