<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>yuyupapa님의 블로그</title>
    <link>https://yuyupapa-bc.tistory.com/</link>
    <description>내 기억의 외장하드</description>
    <language>ko</language>
    <pubDate>Mon, 6 Apr 2026 06:26:14 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>yuyupapa-bc</managingEditor>
    <image>
      <title>yuyupapa님의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/7512565/attach/4d7ae0438e314d8a88271edc2afc1c94</url>
      <link>https://yuyupapa-bc.tistory.com</link>
    </image>
    <item>
      <title>MPT 의 구조와 역할</title>
      <link>https://yuyupapa-bc.tistory.com/63</link>
      <description>&lt;p&gt;머클 페트리시아 트리(Merkle Patricia Trie)는 이더리움의 핵심 데이터 구조 중 하나로, 상태(state), 거래(transaction), 영수증(receipt) 등을 저장하고 검증 가능한 방식으로 구성하기 위해 사용됩니다. 다음은 그 구조, 특징, 작동방식, 이더리움에서의 역할을 기술 블로그 수준으로 자세히 설명한 내용입니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;개요: 왜 머클 페트리시아 트리를 사용하는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이더리움은 모든 상태를 블록체인 위에서 신뢰 가능하게 저장하고 검증할 수 있어야 합니다. 이를 위해 사용하는 것이 &lt;strong&gt;머클 트리(Merkle Tree)&lt;/strong&gt;와 &lt;strong&gt;패트리시아 트라이(Trie)&lt;/strong&gt;의 결합 구조인 &lt;strong&gt;머클 페트리시아 트리(MPT)&lt;/strong&gt;입니다.&lt;br&gt;    •    머클 트리: 데이터의 무결성을 해시로 보장.&lt;br&gt;    •    패트리시아 트라이: 키-값 쌍을 압축하여 효율적인 저장을 가능하게 함.&lt;br&gt;    •    머클 페트리시아 트리: 위 두 개의 장점을 결합하여 이더리움의 상태 저장 및 검증에 사용.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;구조: 머클 트리 + 패트리시아 트라이&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(1) 기본 구성&lt;/p&gt;
&lt;p&gt;MPT는 키-값(key-value) 구조의 데이터를 저장하며 다음 세 가지 노드로 구성됩니다:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;노드 타입&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Leaf Node&lt;/td&gt;
&lt;td&gt;최종 값(계정 정보, 스토리지 값 등)을 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extension Node&lt;/td&gt;
&lt;td&gt;중간 경로(prefix)를 저장하여 트리 깊이 축소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Branch Node&lt;/td&gt;
&lt;td&gt;16진수(0~f) 기반 16개의 포인터 + 값 필드를 가짐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;(2) 키 구조&lt;/p&gt;
&lt;p&gt;MPT는 키를 16진수 nibble로 변환한 후 사용합니다. 예:&lt;br&gt;    •    키: 0xa1b3 → nibble: [a, 1, b, 3]&lt;/p&gt;
&lt;p&gt;압축 경로(prefix compression)를 통해 중복된 경로는 Extension으로, 분기되는 지점은 Branch로, 최종 값은 Leaf로 표현됩니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;작동 방식&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(1) 삽입&lt;br&gt;    1.    키를 nibble로 변환하여 경로로 사용.&lt;br&gt;    2.    경로가 겹치는 경우 extension/branch를 만들어 분기.&lt;br&gt;    3.    최종 경로에 값 저장 (Leaf Node).&lt;/p&gt;
&lt;p&gt;(2) 조회&lt;br&gt;    1.    루트에서 시작하여 nibble 경로를 따라 노드 순회.&lt;br&gt;    2.    Leaf 또는 Branch의 값 필드에서 최종 결과 도달.&lt;/p&gt;
&lt;p&gt;(3) 삭제&lt;br&gt;    1.    해당 경로의 Leaf 제거.&lt;br&gt;    2.    경로가 중복되지 않게 되면 Extension/Branch 재구성.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;해시와 머클 구조&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;각 노드는 RLP(RLP 인코딩)된 형태로 해싱되며, 이 해시들이 상위 노드에 포함되어 루트 해시까지 이어집니다. 이를 통해:&lt;br&gt;    •    전체 트리의 무결성 보장&lt;br&gt;    •    특정 데이터가 존재함을 루트 해시만으로 증명 가능 (Merkle Proof)&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;이더리움에서의 역할&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이더리움은 3개의 주요 MPT를 유지합니다:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;트리 이름&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;루트 해시 위치&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;State Trie&lt;/td&gt;
&lt;td&gt;주소 → 계정 상태 (Nonce, Balance, CodeHash, StorageRoot)&lt;/td&gt;
&lt;td&gt;블록 헤더의 stateRoot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage Trie&lt;/td&gt;
&lt;td&gt;계정의 스토리지 슬롯 → 값&lt;/td&gt;
&lt;td&gt;계정의 storageRoot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction Trie&lt;/td&gt;
&lt;td&gt;트랜잭션 인덱스 → 트랜잭션&lt;/td&gt;
&lt;td&gt;블록 헤더의 transactionsRoot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Receipt Trie&lt;/td&gt;
&lt;td&gt;트랜잭션 인덱스 → receipt&lt;/td&gt;
&lt;td&gt;블록 헤더의 receiptsRoot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;예: 계정 정보 조회 흐름&lt;br&gt;    1.    상태 루트 (stateRoot)를 통해 State Trie 접근&lt;br&gt;    2.    계정 주소 → RLP → keccak256 → 경로&lt;br&gt;    3.    해당 노드에서 계정 정보 추출 (Balance 등)&lt;br&gt;    4.    스토리지 필요 시, storageRoot를 사용해 Storage Trie 접근&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;요약: MPT의 장점&lt;/li&gt;
&lt;/ol&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;무결성 보장&lt;/td&gt;
&lt;td&gt;루트 해시만으로 전체 트리의 정합성 검증 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;변경 추적&lt;/td&gt;
&lt;td&gt;데이터 하나만 바뀌어도 루트 해시가 바뀜 (불변성)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;경로 최적화&lt;/td&gt;
&lt;td&gt;중복되는 경로는 압축하여 저장 (효율적)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이더리움 적합&lt;/td&gt;
&lt;td&gt;블록체인의 탈중앙 상태 검증과 궁합이 뛰어남&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;참고: MPT 시각화 예시&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;        Root Hash
           |
       Branch (a)
      /     |     \
   Ext     ...    Ext
   (&amp;quot;1&amp;quot;)         (&amp;quot;3&amp;quot;)
     |             |
  Leaf(a1b)     Leaf(a3f)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⸻&lt;/p&gt;</description>
      <category>이더리움</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/63</guid>
      <comments>https://yuyupapa-bc.tistory.com/63#entry63comment</comments>
      <pubDate>Tue, 6 May 2025 14:09:17 +0900</pubDate>
    </item>
    <item>
      <title>[DEX] UniSwap 기본개념</title>
      <link>https://yuyupapa-bc.tistory.com/62</link>
      <description>&lt;p&gt;⸻&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Uniswap의 핵심 개념&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;1.1 자동화된 시장 조성자(AMM)&lt;/p&gt;
&lt;p&gt;Uniswap은 AMM 모델을 사용하여 거래를 자동으로 체결합니다. 이는 전통적인 중앙화 거래소의 오더북 시스템과 달리, 유동성 풀(Liquidity Pool)을 통해 거래를 수행합니다. 유동성 풀은 사용자가 예치한 두 가지 토큰으로 구성되며, 거래자는 이 풀을 통해 토큰을 교환합니다. ￼ ￼&lt;/p&gt;
&lt;p&gt;1.2 유동성 공급자와 LP 토큰&lt;/p&gt;
&lt;p&gt;사용자는 두 가지 토큰을 유동성 풀에 예치함으로써 유동성 공급자가 될 수 있습니다. 예치한 대가로 LP(Liquidity Provider) 토큰을 받게 되며, 이는 유동성 풀에서의 지분을 나타냅니다. 거래 수수료는 유동성 공급자에게 분배되어 수익을 창출할 수 있습니다. ￼&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;가격 결정 메커니즘&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;2.1 상수 곱 공식 (x * y = k)&lt;/p&gt;
&lt;p&gt;Uniswap은 상수 곱 공식(Constant Product Formula)을 사용하여 가격을 결정합니다. 이는 풀에 있는 두 토큰의 수량을 곱한 값이 항상 일정하게 유지되도록 하는 방식입니다. 이 공식에 따라 거래가 이루어지며, 유동성 풀의 균형을 유지합니다. ￼&lt;/p&gt;
&lt;p&gt;2.2 슬리피지와 가격 영향&lt;/p&gt;
&lt;p&gt;거래 규모가 클수록 유동성 풀의 비율이 크게 변동하여 슬리피지(Slippage)가 발생할 수 있습니다. 이는 예상한 가격과 실제 체결 가격 사이의 차이를 의미하며, 거래자는 이를 고려하여 거래를 수행해야 합니다. ￼&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Uniswap의 서비스 아키텍처&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;3.1 스마트 계약 구조&lt;/p&gt;
&lt;p&gt;Uniswap은 스마트 계약을 통해 탈중앙화된 거래를 구현합니다. 주요 스마트 계약 구성 요소는 다음과 같습니다: ￼&lt;br&gt;    •    Factory Contract: 새로운 유동성 풀을 생성하고 관리합니다.&lt;br&gt;    •    Pair Contract: 각 유동성 풀의 거래 로직과 유동성 관리를 담당합니다.&lt;br&gt;    •    Router Contract: 사용자 인터페이스와 스마트 계약 간의 상호작용을 중개합니다. ￼&lt;/p&gt;
&lt;p&gt;3.2 버전별 특징&lt;br&gt;    •    Uniswap V2: 기본적인 AMM 모델을 구현하여 다양한 토큰 간의 거래를 지원합니다.&lt;br&gt;    •    Uniswap V3: 집중된 유동성(Concentrated Liquidity)과 다중 수수료 계층을 도입하여 유동성 효율성과 수익성을 향상시켰습니다. ￼&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;</description>
      <category>이더리움</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/62</guid>
      <comments>https://yuyupapa-bc.tistory.com/62#entry62comment</comments>
      <pubDate>Tue, 15 Apr 2025 21:23:29 +0900</pubDate>
    </item>
    <item>
      <title>binary Heap vs. RBT</title>
      <link>https://yuyupapa-bc.tistory.com/61</link>
      <description>&lt;p&gt;  Heap vs Red-Black Tree (RBT) 비교 요약&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;Binary Heap&lt;/th&gt;
&lt;th&gt;Red-Black Tree (RBT)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;구조&lt;/td&gt;
&lt;td&gt;완전 이진 트리 (배열 기반)&lt;/td&gt;
&lt;td&gt;이진 탐색 트리 (포인터 기반)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정렬 기준&lt;/td&gt;
&lt;td&gt;힙 속성 유지 (부모 ≥ 자식)&lt;/td&gt;
&lt;td&gt;이진 탐색 트리 속성 유지 (정렬된 순서)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;삽입/삭제&lt;/td&gt;
&lt;td&gt;O(log n) (heapify)&lt;/td&gt;
&lt;td&gt;O(log n) (트리 재구성)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;순위 기반 검색&lt;/td&gt;
&lt;td&gt;❌ 불가&lt;/td&gt;
&lt;td&gt;✅ 중위 순회로 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특정 값 탐색&lt;/td&gt;
&lt;td&gt;느림 (배열 순회 필요)&lt;/td&gt;
&lt;td&gt;빠름 (이진 검색 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최댓값 접근&lt;/td&gt;
&lt;td&gt;✅ 즉시 (heap[1])&lt;/td&gt;
&lt;td&gt;❌ 최댓값은 오른쪽 끝 노드&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;✅ 언제 RBT가 유리할까?&lt;br&gt;• 정렬된 데이터에 범위 검색 (range query) 이 필요한 경우&lt;br&gt;• 중간값, 특정 키, 순위 기반 탐색 (top 10, 3번째 큰 값)이 필요한 경우&lt;br&gt;• 트리 구조 유지가 중요할 때 (예: 상태 동기화, trie 구조 등)&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;✨ 예: RBT 기반 스마트 컨트랙트 응용&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;응용 예&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;가격 기반 주문서&lt;/td&gt;
&lt;td&gt;orderBook[price]으로 가격 정렬 및 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정렬된 스테이킹 기록&lt;/td&gt;
&lt;td&gt;amount 기준 탐색 및 정렬&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;경쟁 점수 랭킹&lt;/td&gt;
&lt;td&gt;상위 10개 유지 (sorted insert, delete)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  Solidity에 RBT 구현이 가능할까?&lt;/p&gt;
&lt;p&gt;✔️ 가능합니다!&lt;br&gt;하지만 다음 제약을 고려해야 합니다:&lt;br&gt;• 포인터 대신 mapping 구조 사용&lt;br&gt;• 왼쪽/오른쪽 자식도 모두 mapping(uint =&amp;gt; Node)&lt;br&gt;• 컬러(bit), 균형 조건 재조정 필요 → 코드 복잡도 높음&lt;br&gt;• 라이브러리로 많이 활용됨 (예: solidity-rbtree)&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;이미 만들어져 있는 rbt 라이브러리&lt;br&gt;&lt;a href=&quot;https://github.com/saurfang/solidity-treemap&quot;&gt;solidity-treemap&lt;/a&gt;&lt;/p&gt;</description>
      <category>Solidity</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/61</guid>
      <comments>https://yuyupapa-bc.tistory.com/61#entry61comment</comments>
      <pubDate>Wed, 26 Mar 2025 12:43:23 +0900</pubDate>
    </item>
    <item>
      <title>Solidity: RBT 구현 예제</title>
      <link>https://yuyupapa-bc.tistory.com/60</link>
      <description>&lt;pre&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * Red-Black Tree in Solidity (확장 버전)
 * - Self-balancing binary search tree
 * - Insert, Delete, Min/Max, Exists, In-order, Range Query 지원
 */

library RedBlackTreeLib {
    uint private constant NULL = 0;

    struct Node {
        uint parent;
        uint left;
        uint right;
        bool red;
        bool exists;
    }

    struct Tree {
        mapping(uint =&amp;gt; Node) nodes;
        uint root;
    }

    function insert(Tree storage self, uint key) internal {
        require(key != NULL, &amp;quot;invalid key&amp;quot;);
        require(!self.nodes[key].exists, &amp;quot;already exists&amp;quot;);

        uint cursor = self.root;
        uint parent = NULL;

        while (cursor != NULL) {
            parent = cursor;
            if (key &amp;lt; cursor) {
                cursor = self.nodes[cursor].left;
            } else {
                cursor = self.nodes[cursor].right;
            }
        }

        self.nodes[key] = Node({ parent: parent, left: NULL, right: NULL, red: true, exists: true });

        if (parent == NULL) {
            self.root = key;
        } else if (key &amp;lt; parent) {
            self.nodes[parent].left = key;
        } else {
            self.nodes[parent].right = key;
        }

        insertFixup(self, key);
    }

    function insertFixup(Tree storage self, uint key) private {
        while (key != self.root &amp;amp;&amp;amp; self.nodes[self.nodes[key].parent].red) {
            uint parent = self.nodes[key].parent;
            uint grandparent = self.nodes[parent].parent;

            if (parent == self.nodes[grandparent].left) {
                uint uncle = self.nodes[grandparent].right;
                if (self.nodes[uncle].red) {
                    self.nodes[parent].red = false;
                    self.nodes[uncle].red = false;
                    self.nodes[grandparent].red = true;
                    key = grandparent;
                } else {
                    if (key == self.nodes[parent].right) {
                        key = parent;
                        rotateLeft(self, key);
                        parent = self.nodes[key].parent;
                    }
                    self.nodes[parent].red = false;
                    self.nodes[grandparent].red = true;
                    rotateRight(self, grandparent);
                }
            } else {
                uint uncle = self.nodes[grandparent].left;
                if (self.nodes[uncle].red) {
                    self.nodes[parent].red = false;
                    self.nodes[uncle].red = false;
                    self.nodes[grandparent].red = true;
                    key = grandparent;
                } else {
                    if (key == self.nodes[parent].left) {
                        key = parent;
                        rotateRight(self, key);
                        parent = self.nodes[key].parent;
                    }
                    self.nodes[parent].red = false;
                    self.nodes[grandparent].red = true;
                    rotateLeft(self, grandparent);
                }
            }
        }
        self.nodes[self.root].red = false;
    }

    function rotateLeft(Tree storage self, uint x) private {
        uint y = self.nodes[x].right;
        self.nodes[x].right = self.nodes[y].left;
        if (self.nodes[y].left != NULL) {
            self.nodes[self.nodes[y].left].parent = x;
        }
        self.nodes[y].parent = self.nodes[x].parent;
        if (self.nodes[x].parent == NULL) {
            self.root = y;
        } else if (x == self.nodes[self.nodes[x].parent].left) {
            self.nodes[self.nodes[x].parent].left = y;
        } else {
            self.nodes[self.nodes[x].parent].right = y;
        }
        self.nodes[y].left = x;
        self.nodes[x].parent = y;
    }

    function rotateRight(Tree storage self, uint x) private {
        uint y = self.nodes[x].left;
        self.nodes[x].left = self.nodes[y].right;
        if (self.nodes[y].right != NULL) {
            self.nodes[self.nodes[y].right].parent = x;
        }
        self.nodes[y].parent = self.nodes[x].parent;
        if (self.nodes[x].parent == NULL) {
            self.root = y;
        } else if (x == self.nodes[self.nodes[x].parent].right) {
            self.nodes[self.nodes[x].parent].right = y;
        } else {
            self.nodes[self.nodes[x].parent].left = y;
        }
        self.nodes[y].right = x;
        self.nodes[x].parent = y;
    }

    function getMin(Tree storage self) internal view returns (uint) {
        uint current = self.root;
        require(current != NULL, &amp;quot;empty tree&amp;quot;);
        while (self.nodes[current].left != NULL) {
            current = self.nodes[current].left;
        }
        return current;
    }

    function getMax(Tree storage self) internal view returns (uint) {
        uint current = self.root;
        require(current != NULL, &amp;quot;empty tree&amp;quot;);
        while (self.nodes[current].right != NULL) {
            current = self.nodes[current].right;
        }
        return current;
    }

    function exists(Tree storage self, uint key) internal view returns (bool) {
        return self.nodes[key].exists;
    }

    function inOrder(Tree storage self, uint node, uint[] storage output) internal view {
        if (node == NULL) return;
        inOrder(self, self.nodes[node].left, output);
        output.push(node);
        inOrder(self, self.nodes[node].right, output);
    }

    function inOrderTraversal(Tree storage self) internal view returns (uint[] memory) {
        uint[] memory temp = new uint[](1000); // max 1000 nodes for example
        uint[] storage output;
        inOrder(self, self.root, output);
        return output;
    }

    function rangeQuery(Tree storage self, uint min, uint max, uint[] storage results) internal view {
        _rangeQuery(self, self.root, min, max, results);
    }

    function _rangeQuery(Tree storage self, uint node, uint min, uint max, uint[] storage results) private view {
        if (node == NULL) return;
        if (min &amp;lt; node) _rangeQuery(self, self.nodes[node].left, min, max, results);
        if (min &amp;lt;= node &amp;amp;&amp;amp; node &amp;lt;= max) results.push(node);
        if (max &amp;gt; node) _rangeQuery(self, self.nodes[node].right, min, max, results);
    }
}

contract RedBlackTreeTest {
    using RedBlackTreeLib for RedBlackTreeLib.Tree;
    RedBlackTreeLib.Tree private tree;

    function insert(uint key) external {
        tree.insert(key);
    }

    function getMinimum() external view returns (uint) {
        return tree.getMin();
    }

    function getMaximum() external view returns (uint) {
        return tree.getMax();
    }

    function checkExists(uint key) external view returns (bool) {
        return tree.exists(key);
    }

    function getRange(uint min, uint max) external view returns (uint[] memory result) {
        tree.rangeQuery(min, max, result);
    }

    function getInOrder() external view returns (uint[] memory result) {
        result = tree.inOrderTraversal();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;함수&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;insert(uint)&lt;/td&gt;
&lt;td&gt;트리에 값 삽입 (자동 균형 유지)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;getMin()&lt;/td&gt;
&lt;td&gt;최소값 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;getMax()&lt;/td&gt;
&lt;td&gt;최대값 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;내부 함수&lt;/td&gt;
&lt;td&gt;insertFixup, rotateLeft, rotateRight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;exists(key)&lt;/td&gt;
&lt;td&gt;특정 키 존재 여부 확인 (mapping 기반)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;inOrderTraversal()&lt;/td&gt;
&lt;td&gt;중위 순회 (작은 값부터 정렬된 리스트 반환)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rangeQuery(min, max)&lt;/td&gt;
&lt;td&gt;특정 범위 내의 값들만 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;  구조 설명:&lt;br&gt;    •    mapping(uint =&amp;gt; Node)을 통해 포인터 대신 키 기반 연결&lt;br&gt;    •    Node는 parent, left, right, red, exists 필드로 구성&lt;br&gt;    •    rotateLeft와 rotateRight로 균형 조정&lt;br&gt;    •    mapping(uint =&amp;gt; Node)을 통해 트리 구조 유지&lt;br&gt;    •    insert 시 자동으로 균형 맞춤 (insertFixup)&lt;br&gt;    •    중위 순회 및 범위 탐색을 통해 정렬된 데이터 활용 가능&lt;br&gt;    •    최대 1000개 노드 기준으로 inOrderTraversal() 지원 (확장 가능)&lt;/p&gt;</description>
      <category>Solidity</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/60</guid>
      <comments>https://yuyupapa-bc.tistory.com/60#entry60comment</comments>
      <pubDate>Wed, 26 Mar 2025 12:37:54 +0900</pubDate>
    </item>
    <item>
      <title>Solidity : 최대값 검색을 위한 Heap Tree 구현</title>
      <link>https://yuyupapa-bc.tistory.com/59</link>
      <description>&lt;pre&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * ✅ Max Heap (Binary Heap 기반)
 * - Efficient insert and extractMax (O(log n))
 * - Implemented as a 1-based array (0th index unused)
 */
contract MaxHeap {
    uint[] private heap;

    constructor() {
        heap.push(0); // index 0은 사용하지 않음
    }

    function insert(uint value) external {
        heap.push(value);
        _heapifyUp(heap.length - 1);
    }

    function extractMax() external returns (uint) {
        require(heap.length &amp;gt; 1, &amp;quot;heap is empty&amp;quot;);

        uint max = heap[1];
        heap[1] = heap[heap.length - 1];
        heap.pop();
        _heapifyDown(1);
        return max;
    }

    function peekMax() external view returns (uint) {
        require(heap.length &amp;gt; 1, &amp;quot;heap is empty&amp;quot;);
        return heap[1];
    }

    function size() external view returns (uint) {
        return heap.length - 1;
    }

    function getAll() external view returns (uint[] memory) {
        uint[] memory result = new uint[](heap.length - 1);
        for (uint i = 1; i &amp;lt; heap.length; i++) {
            result[i - 1] = heap[i];
        }
        return result;
    }

    function _heapifyUp(uint index) internal {
        while (index &amp;gt; 1) {
            uint parent = index / 2;
            if (heap[parent] &amp;gt;= heap[index]) {
                break;
            }
            (heap[parent], heap[index]) = (heap[index], heap[parent]);
            index = parent;
        }
    }

    function _heapifyDown(uint index) internal {
        uint length = heap.length;
        while (2 * index &amp;lt; length) {
            uint left = 2 * index;
            uint right = 2 * index + 1;
            uint largest = index;

            if (heap[left] &amp;gt; heap[largest]) {
                largest = left;
            }
            if (right &amp;lt; length &amp;amp;&amp;amp; heap[right] &amp;gt; heap[largest]) {
                largest = right;
            }
            if (largest == index) break;

            (heap[index], heap[largest]) = (heap[largest], heap[index]);
            index = largest;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;✨ 주요 기능:&lt;br&gt;    •    insert(uint) → 값을 힙에 추가 (O(log n))&lt;br&gt;    •    extractMax() → 최대값 추출 (O(log n))&lt;br&gt;    •    peekMax() → 최대값 확인 (읽기 전용)&lt;br&gt;    •    getAll() → 전체 힙 배열 반환&lt;br&gt;    •    내부적으로 1-based 배열로 구현하여 부모/자식 인덱스 계산을 직관적으로 구성&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;</description>
      <category>Solidity</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/59</guid>
      <comments>https://yuyupapa-bc.tistory.com/59#entry59comment</comments>
      <pubDate>Wed, 26 Mar 2025 12:32:38 +0900</pubDate>
    </item>
    <item>
      <title>EVM 의 스토리지 구조에 따른 array vs. mapping</title>
      <link>https://yuyupapa-bc.tistory.com/58</link>
      <description>&lt;p&gt;이건 EVM(Ethereum Virtual Machine)의 스토리지 구조의 근본적인 이해와 관련된 주제로,&lt;br&gt;왜 Solidity에서 배열보다 mapping이 많이 사용되는지에 대한 배경이기도 합니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;✅ 먼저 요약부터&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;접근 구조&lt;/th&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;th&gt;어떤 자료구조가 유리한가?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;✅ 랜덤 접근 기반 (EVM)&lt;/td&gt;
&lt;td&gt;연속된 메모리 주소가 없음, 해시로 위치 계산&lt;/td&gt;
&lt;td&gt;✅ mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✅ 연속 메모리 기반 (CPU, 일반 컴퓨터)&lt;/td&gt;
&lt;td&gt;배열은 캐시 친화적, 빠른 순회&lt;/td&gt;
&lt;td&gt;✅ array (배열)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  1. EVM의 Storage 구조: 왜 랜덤 접근인가?&lt;/p&gt;
&lt;p&gt;EVM에서의 storage는 “key-value 형태의 거대한 해시 테이블”로 이해할 수 있어요.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uint256 public foo; // 저장됨: slot 0
mapping(address =&amp;gt; uint256) balances; // 저장됨: slot keccak256(encodePacked(address, slot))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;  중요한 사실:&lt;br&gt;    •    EVM은 연속된 물리적 메모리를 사용하지 않아요.&lt;br&gt;    •    대신, storage는 2^256 개의 슬롯을 가진 가상 공간이며,&lt;br&gt;    •    접근은 keccak256(slot) 계산을 통해 이뤄져요.&lt;/p&gt;
&lt;p&gt;➕ 예시: mapping(address =&amp;gt; uint)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;slot = keccak256(abi.encodePacked(key, mappingSlot))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;✔️ 이 말은, 각 키가 전혀 다른 위치에 저장되며, 해시 기반으로 빠르게 접근할 수 있다는 뜻입니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  왜 EVM에서는 mapping이 유리할까?&lt;/p&gt;
&lt;p&gt;✅ 이유 1. 슬롯이 연속되지 않음&lt;br&gt;    •    배열의 arr[i]처럼 slot = base + i로 계산할 수 없는 경우가 많음&lt;/p&gt;
&lt;p&gt;✅ 이유 2. 해시 접근이 기본 설계&lt;br&gt;    •    mapping은 해시 기반으로 바로 슬롯을 찾아가기 때문에 속도는 빠르고, 구조가 심플&lt;/p&gt;
&lt;p&gt;✅ 이유 3. gas 효율성&lt;br&gt;    •    배열은 크기를 늘리거나 반복할 때 storage 반복이 필요&lt;br&gt;    •    mapping은 특정 키만 저장 → 불필요한 루프 없이 직접 접근 가능&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  반대로: 왜 일반 컴퓨터에서는 배열이 유리할까?&lt;/p&gt;
&lt;p&gt;✅ CPU의 메모리 접근은 연속 메모리 기반&lt;br&gt;    •    배열은 메모리에 연속으로 배치됨&lt;br&gt;    •    CPU 캐시, branch prediction, SIMD 등과 결합되어 폭발적인 순회 속도 제공&lt;/p&gt;
&lt;p&gt;예:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int arr[100];
for (int i = 0; i &amp;lt; 100; i++) {
  arr[i] += 1; // 매우 빠름 (CPU 캐시 프렌들리)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;  반면 해시 테이블 (HashMap 등)은:&lt;br&gt;    •    포인터와 해시 충돌을 고려해야 해서&lt;br&gt;    •    순회 성능은 느릴 수 있음&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  비교 요약&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구조&lt;/th&gt;
&lt;th&gt;일반 컴퓨터 (CPU/메모리)&lt;/th&gt;
&lt;th&gt;EVM (블록체인 가상 머신)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;메모리 구조&lt;/td&gt;
&lt;td&gt;연속된 물리적 메모리&lt;/td&gt;
&lt;td&gt;2^256개의 해시 슬롯&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;배열 성능&lt;/td&gt;
&lt;td&gt;✅ 빠름 (cache-friendly)&lt;/td&gt;
&lt;td&gt;❌ 느림 (slot 순회 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mapping 성능&lt;/td&gt;
&lt;td&gt;❌ 해시 계산 필요&lt;/td&gt;
&lt;td&gt;✅ 빠름 (keccak 기반 접근)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;순회&lt;/td&gt;
&lt;td&gt;배열이 유리&lt;/td&gt;
&lt;td&gt;mapping은 순회 불가 (keys 저장 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  결론&lt;/p&gt;
&lt;p&gt;✅ EVM에서 mapping이 배열보다 더 유리한 이유는 다음과 같습니다:&lt;br&gt;    1.    EVM의 스토리지는 연속 메모리 접근이 불가능함&lt;br&gt;    2.    keccak256(slot) 기반 랜덤 슬롯 계산이 기본 설계&lt;br&gt;    3.    mapping은 필요한 데이터에만 직접 접근 가능&lt;br&gt;    4.    배열은 순회와 크기 증가 시 gas 소모가 크고, 확장성 낮음&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;</description>
      <category>이더리움</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/58</guid>
      <comments>https://yuyupapa-bc.tistory.com/58#entry58comment</comments>
      <pubDate>Wed, 26 Mar 2025 12:27:46 +0900</pubDate>
    </item>
    <item>
      <title>연결 리스트 구현 개선</title>
      <link>https://yuyupapa-bc.tistory.com/57</link>
      <description>&lt;p&gt;  추가된 항목:&lt;br&gt;    1.    LinkedListLib&lt;br&gt;    •    append, remove, getAll 함수를 포함한 재사용 가능한 라이브러리&lt;br&gt;    •    mapping 기반으로 설계된 List 구조체 포함&lt;br&gt;    2.    GenericLinkedList 컨트랙트&lt;br&gt;    •    위 라이브러리를 사용한 일반화된 Linked List&lt;br&gt;    •    코드 재사용 가능, 깔끔한 구조&lt;br&gt;    3.    Gas 최적화&lt;br&gt;    •    불필요한 상태 변경 제거&lt;br&gt;    •    라이브러리화를 통해 반복되는 코드 최소화&lt;/p&gt;
&lt;p&gt;✅ 확장 완료된 기능:&lt;/p&gt;
&lt;p&gt;  LinkedListLib에 추가된 고급 기능&lt;br&gt;    1.    insertAfter(afterId, data)&lt;br&gt;특정 노드 뒤에 새로운 노드 삽입&lt;br&gt;    2.    findByData(target)&lt;br&gt;값을 기준으로 노드 ID 탐색&lt;br&gt;    3.    clear()&lt;br&gt;전체 리스트 초기화 (모든 노드 제거)&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  GenericLinkedList 컨트랙트에 연동된 함수&lt;br&gt;    •    insertAfter(...)&lt;br&gt;    •    find(...)&lt;br&gt;    •    clearAll()&lt;/p&gt;
&lt;p&gt;이제 리스트 구조는 완전한 CRUD + 검색 + 초기화 + 재사용까지 가능한 구조입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

library LinkedListLib {
    struct Node {
        uint data;
        uint next;
        bool exists;
    }

    struct List {
        mapping(uint =&amp;gt; Node) nodes;
        uint head;
        uint length;
        uint idCounter;
    }

    function init(List storage list) internal {
        list.head = 0;
        list.nodes[0] = Node({ data: 0, next: 0, exists: true });
        list.idCounter = 1;
    }

    function append(List storage list, uint _data) internal {
        uint current = list.head;
        while (list.nodes[current].next != 0) {
            current = list.nodes[current].next;
        }

        uint newId = list.idCounter++;
        list.nodes[newId] = Node({ data: _data, next: 0, exists: true });
        list.nodes[current].next = newId;
        list.length++;
    }

    function insertAfter(List storage list, uint afterId, uint _data) internal {
        require(list.nodes[afterId].exists, &amp;quot;after node not found&amp;quot;);

        uint newId = list.idCounter++;
        list.nodes[newId] = Node({ data: _data, next: list.nodes[afterId].next, exists: true });
        list.nodes[afterId].next = newId;
        list.length++;
    }

    function remove(List storage list, uint id) internal {
        require(id != list.head, &amp;quot;cannot remove head&amp;quot;);
        require(list.nodes[id].exists, &amp;quot;not found&amp;quot;);

        uint prev = list.head;
        while (list.nodes[prev].next != id &amp;amp;&amp;amp; list.nodes[prev].next != 0) {
            prev = list.nodes[prev].next;
        }
        require(list.nodes[prev].next == id, &amp;quot;not found&amp;quot;);

        list.nodes[prev].next = list.nodes[id].next;
        delete list.nodes[id];
        list.length--;
    }

    function findByData(List storage list, uint target) internal view returns (uint) {
        uint current = list.nodes[list.head].next;
        while (current != 0) {
            if (list.nodes[current].data == target) {
                return current;
            }
            current = list.nodes[current].next;
        }
        return 0; // not found
    }

    function getAll(List storage list) internal view returns (uint[] memory) {
        uint[] memory result = new uint[](list.length);
        uint current = list.nodes[list.head].next;
        uint i = 0;
        while (current != 0) {
            result[i++] = list.nodes[current].data;
            current = list.nodes[current].next;
        }
        return result;
    }

    function clear(List storage list) internal {
        uint current = list.nodes[list.head].next;
        while (current != 0) {
            uint next = list.nodes[current].next;
            delete list.nodes[current];
            current = next;
        }
        list.nodes[list.head].next = 0;
        list.length = 0;
    }
}

contract GenericLinkedList {
    using LinkedListLib for LinkedListLib.List;
    LinkedListLib.List private list;

    constructor() {
        list.init();
    }

    function add(uint value) external {
        list.append(value);
    }

    function insertAfter(uint afterId, uint value) external {
        list.insertAfter(afterId, value);
    }

    function remove(uint id) external {
        list.remove(id);
    }

    function find(uint value) external view returns (uint) {
        return list.findByData(value);
    }

    function all() external view returns (uint[] memory) {
        return list.getAll();
    }

    function clearAll() external {
        list.clear();
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Solidity</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/57</guid>
      <comments>https://yuyupapa-bc.tistory.com/57#entry57comment</comments>
      <pubDate>Wed, 26 Mar 2025 09:35:03 +0900</pubDate>
    </item>
    <item>
      <title>Solidity 로 연결 리스트 구현하기</title>
      <link>https://yuyupapa-bc.tistory.com/56</link>
      <description>&lt;p&gt;Solidity는 기본적으로 배열(array)과 mapping(map)은 제공하지만,&lt;br&gt;✅ linked list 자료구조는 제공하지 않습니다.&lt;br&gt;→ 따라서 직접 구현해야 합니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  왜 Solidity는 Linked List를 제공하지 않을까?&lt;br&gt;    •    EVM의 storage 구조가 랜덤 접근 기반이기 때문에&lt;br&gt;→ 연속된 메모리보다 keccak256(slot) 방식의 mapping 기반 인덱싱이 더 유리합니다.&lt;br&gt;    •    Solidity의 gas 비용 구조상, linked list는 효율적이지 않은 경우가 많습니다.&lt;br&gt;    •    그래도   순차적 삽입/삭제가 필요한 경우엔 여전히 유용합니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;✅ 예제1: 단일 연결 리스트 (Singly Linked List)&lt;/p&gt;
&lt;p&gt;아래는 Solidity로 구현한 간단한 Singly Linked List 예제입니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  Solidity 코드 (0.8.x 이상)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract LinkedList {
    struct Node {
        uint data;
        uint next;
        bool exists;
    }

    mapping(uint =&amp;gt; Node) public nodes;
    uint public head;
    uint public length;
    uint private idCounter;

    constructor() {
        head = 0; // 초기에는 0이 head (0은 dummy head 역할)
        nodes[head] = Node({ data: 0, next: 0, exists: true });
        idCounter = 1;
    }

    /// @notice 리스트 끝에 추가
    function append(uint _data) external {
        uint current = head;
        while (nodes[current].next != 0) {
            current = nodes[current].next;
        }

        uint newId = idCounter++;
        nodes[newId] = Node({ data: _data, next: 0, exists: true });
        nodes[current].next = newId;
        length++;
    }

    /// @notice 특정 노드 ID 기준으로 삭제 (head는 삭제 불가)
    function remove(uint id) external {
        require(id != head, &amp;quot;cannot remove dummy head&amp;quot;);
        require(nodes[id].exists, &amp;quot;node not exists&amp;quot;);

        uint prev = head;
        while (nodes[prev].next != id &amp;amp;&amp;amp; nodes[prev].next != 0) {
            prev = nodes[prev].next;
        }
        require(nodes[prev].next == id, &amp;quot;node not found&amp;quot;);

        nodes[prev].next = nodes[id].next;
        delete nodes[id];
        length--;
    }

    /// @notice 전체 리스트 조회
    function getAll() external view returns (uint[] memory) {
        uint[] memory result = new uint[](length);
        uint current = nodes[head].next;
        uint i = 0;
        while (current != 0) {
            result[i++] = nodes[current].data;
            current = nodes[current].next;
        }
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  핵심 설명&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;요소&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;mapping(uint =&amp;gt; Node)&lt;/td&gt;
&lt;td&gt;노드를 ID로 저장 (linked list의 key 역할)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node 구조체&lt;/td&gt;
&lt;td&gt;data, next, exists 필드 포함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;head&lt;/td&gt;
&lt;td&gt;항상 0번 노드 (dummy head)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;idCounter&lt;/td&gt;
&lt;td&gt;노드마다 고유한 ID 부여&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;✅ 사용 예시&lt;br&gt;    1.    append(100) → 노드 추가&lt;br&gt;    2.    getAll() → [100]&lt;br&gt;    3.    append(200) → [100, 200]&lt;br&gt;    4.    remove(1) → [200] (ID 1번 노드 삭제)&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  추가 팁&lt;br&gt;    •    양방향 리스트도 구현 가능: prev 필드를 추가하면 됩니다&lt;br&gt;    •    LinkedListLib로 만들면 재사용 가능&lt;br&gt;    •    메모리 구조와 storage 구조를 구분해서 설계하세요&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;✅ 예제2: 단일/양방향/Enumable(반복가능한) 연결 리스트&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * ✅ Singly Linked List
 */
contract SinglyLinkedList {
    struct Node {
        uint data;
        uint next;
        bool exists;
    }

    mapping(uint =&amp;gt; Node) public nodes;
    uint public head;
    uint public length;
    uint private idCounter;

    constructor() {
        head = 0; // dummy head
        nodes[head] = Node({ data: 0, next: 0, exists: true });
        idCounter = 1;
    }

    function append(uint _data) external {
        uint current = head;
        while (nodes[current].next != 0) {
            current = nodes[current].next;
        }

        uint newId = idCounter++;
        nodes[newId] = Node({ data: _data, next: 0, exists: true });
        nodes[current].next = newId;
        length++;
    }

    function remove(uint id) external {
        require(id != head, &amp;quot;cannot remove head&amp;quot;);
        require(nodes[id].exists, &amp;quot;not found&amp;quot;);

        uint prev = head;
        while (nodes[prev].next != id &amp;amp;&amp;amp; nodes[prev].next != 0) {
            prev = nodes[prev].next;
        }
        require(nodes[prev].next == id, &amp;quot;not found&amp;quot;);

        nodes[prev].next = nodes[id].next;
        delete nodes[id];
        length--;
    }

    function getAll() external view returns (uint[] memory) {
        uint[] memory result = new uint[](length);
        uint current = nodes[head].next;
        uint i = 0;
        while (current != 0) {
            result[i++] = nodes[current].data;
            current = nodes[current].next;
        }
        return result;
    }
}

/**
 * ✅ Doubly Linked List
 */
contract DoublyLinkedList {
    struct Node {
        uint data;
        uint next;
        uint prev;
        bool exists;
    }

    mapping(uint =&amp;gt; Node) public nodes;
    uint public head;
    uint public tail;
    uint public length;
    uint private idCounter;

    constructor() {
        head = 0;
        tail = 0;
        idCounter = 1;
    }

    function append(uint _data) external {
        uint newId = idCounter++;
        nodes[newId] = Node({ data: _data, next: 0, prev: tail, exists: true });

        if (length == 0) {
            head = newId;
        } else {
            nodes[tail].next = newId;
        }

        tail = newId;
        length++;
    }

    function remove(uint id) external {
        require(nodes[id].exists, &amp;quot;not found&amp;quot;);

        if (nodes[id].prev != 0) {
            nodes[nodes[id].prev].next = nodes[id].next;
        } else {
            head = nodes[id].next;
        }

        if (nodes[id].next != 0) {
            nodes[nodes[id].next].prev = nodes[id].prev;
        } else {
            tail = nodes[id].prev;
        }

        delete nodes[id];
        length--;
    }

    function getAllForward() external view returns (uint[] memory) {
        uint[] memory result = new uint[](length);
        uint current = head;
        uint i = 0;
        while (current != 0) {
            result[i++] = nodes[current].data;
            current = nodes[current].next;
        }
        return result;
    }

    function getAllBackward() external view returns (uint[] memory) {
        uint[] memory result = new uint[](length);
        uint current = tail;
        uint i = 0;
        while (current != 0) {
            result[i++] = nodes[current].data;
            current = nodes[current].prev;
        }
        return result;
    }
}

/**
 * ✅ Enumerable Iterable Linked List (Mapping + Array)
 */
contract EnumerableList {
    struct Item {
        uint data;
        bool exists;
    }

    mapping(uint =&amp;gt; Item) public items;
    uint[] public keys;

    function add(uint key, uint value) external {
        require(!items[key].exists, &amp;quot;already exists&amp;quot;);
        items[key] = Item({ data: value, exists: true });
        keys.push(key);
    }

    function remove(uint key) external {
        require(items[key].exists, &amp;quot;not exists&amp;quot;);
        delete items[key];
        for (uint i = 0; i &amp;lt; keys.length; i++) {
            if (keys[i] == key) {
                keys[i] = keys[keys.length - 1];
                keys.pop();
                break;
            }
        }
    }

    function getAll() external view returns (uint[] memory values) {
        uint count = 0;
        for (uint i = 0; i &amp;lt; keys.length; i++) {
            if (items[keys[i]].exists) count++;
        }

        values = new uint[](count);
        uint j = 0;
        for (uint i = 0; i &amp;lt; keys.length; i++) {
            if (items[keys[i]].exists) {
                values[j++] = items[keys[i]].data;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Solidity</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/56</guid>
      <comments>https://yuyupapa-bc.tistory.com/56#entry56comment</comments>
      <pubDate>Wed, 26 Mar 2025 09:33:07 +0900</pubDate>
    </item>
    <item>
      <title>스마트 컨트랙트 개발도구: web3.js vs. ethers.js</title>
      <link>https://yuyupapa-bc.tistory.com/55</link>
      <description>&lt;p&gt;web3.js와 ethers.js는 둘 다 Ethereum과 상호작용할 수 있게 해주는 대표적인 JavaScript/TypeScript 라이브러리입니다.&lt;br&gt;하지만 내부 설계, API 스타일, 기능 지원, 타입 안전성 등에서 꽤 큰 차이가 있어요.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  web3.js vs ethers.js v6 비교분석&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;web3.js&lt;/th&gt;
&lt;th&gt;ethers.js v6&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;  설계 철학&lt;/td&gt;
&lt;td&gt;API 친숙함 &amp;amp; 전통적인 JS 스타일&lt;/td&gt;
&lt;td&gt;경량화 + 함수형 + 타입 안정성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  최신 버전&lt;/td&gt;
&lt;td&gt;v1.10.x (2024 기준)&lt;/td&gt;
&lt;td&gt;✅ v6.x (대폭 리팩토링됨)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  모듈화&lt;/td&gt;
&lt;td&gt;대부분 하나의 번들로 제공&lt;/td&gt;
&lt;td&gt;✅ 모듈별 tree-shaking 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  번들 크기&lt;/td&gt;
&lt;td&gt;크고 무겁다 (~800KB)&lt;/td&gt;
&lt;td&gt;✅ 작고 가볍다 (~100KB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  타입스크립트 지원&lt;/td&gt;
&lt;td&gt;제한적 (@types/web3)&lt;/td&gt;
&lt;td&gt;✅ 완전 지원, 타입 안전성 강력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  Provider 체계&lt;/td&gt;
&lt;td&gt;단순한 연결 모델&lt;/td&gt;
&lt;td&gt;✅ 정교한 Provider 구조 (JsonRpcProvider, WebSocketProvider, etc)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  단위 변환&lt;/td&gt;
&lt;td&gt;.toWei(), .fromWei() 등 util 중심&lt;/td&gt;
&lt;td&gt;✅ parseEther(), formatUnits() 등 직관적 함수형 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  ABI 사용&lt;/td&gt;
&lt;td&gt;복잡하거나 manual한 처리 필요&lt;/td&gt;
&lt;td&gt;✅ Contract 생성이 깔끔하고 직관적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  Wallet/Signer&lt;/td&gt;
&lt;td&gt;불편하고 외부 패키지 필요&lt;/td&gt;
&lt;td&gt;✅ Wallet, Signer 내장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  플러그인 구조&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;✅ 커스터마이징 가능한 Plugin 설계 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  테스트 환경 호환&lt;/td&gt;
&lt;td&gt;Hardhat, Ganache 등과 호환&lt;/td&gt;
&lt;td&gt;✅ 완벽한 Hardhat 연동 (ethers.provider)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  상태관리 (call/send)&lt;/td&gt;
&lt;td&gt;.methods.foo().call()&lt;/td&gt;
&lt;td&gt;✅ 함수처럼 contract.foo()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  코드 비교 예시&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;잔액 조회&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;  web3.js&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Web3 = require(&amp;quot;web3&amp;quot;);
const web3 = new Web3(&amp;quot;https://mainnet.infura.io/v3/KEY&amp;quot;);

const balanceWei = await web3.eth.getBalance(&amp;quot;0x...&amp;quot;);
const balanceEth = web3.utils.fromWei(balanceWei, &amp;quot;ether&amp;quot;);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;  ethers v6&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { ethers } from &amp;quot;ethers&amp;quot;;
const provider = new ethers.JsonRpcProvider(&amp;quot;https://mainnet.infura.io/v3/KEY&amp;quot;);

const balance = await provider.getBalance(&amp;quot;0x...&amp;quot;);
console.log(ethers.formatEther(balance));&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;⸻&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Contract 호출&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;  web3.js&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const contract = new web3.eth.Contract(abi, address);
const name = await contract.methods.name().call();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;  ethers v6&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const contract = new ethers.Contract(address, abi, provider);
const name = await contract.name();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;✅ ethers는 .methods.call() 패턴 없이 자연스럽게 동작합니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;✅ 장단점 요약&lt;/p&gt;
&lt;p&gt;  web3.js&lt;/p&gt;
&lt;p&gt;✅ 장점&lt;br&gt;    •    문서 많고 사용자가 많음&lt;br&gt;    •    Ethereum 초창기부터 사용된 안정된 라이브러리&lt;br&gt;    •    간단한 기능은 배우기 쉽다&lt;/p&gt;
&lt;p&gt;❌ 단점&lt;br&gt;    •    타입스크립트 지원 부족&lt;br&gt;    •    모듈 크고 무거움&lt;br&gt;    •    .methods().call() 등 불편한 호출 방식&lt;br&gt;    •    비동기 체계와 예외 처리 어려움&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  ethers.js v6&lt;/p&gt;
&lt;p&gt;✅ 장점&lt;br&gt;    •    완전한 TypeScript 지원 (타입 추론 정확)&lt;br&gt;    •    더 깔끔하고 모던한 API&lt;br&gt;    •    가볍고 빠른 빌드 타임&lt;br&gt;    •    Hardhat과 궁합 좋음 (기본 내장)&lt;br&gt;    •    Wallet, Signer, Provider, Interface 등이 모두 잘 구조화됨&lt;/p&gt;
&lt;p&gt;❌ 단점&lt;br&gt;    •    v6은 v5와 호환 안 되므로 일부 사용법 변경 필요&lt;br&gt;    •    최신 기능 학습이 조금 필요&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  선택 가이드&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상황&lt;/th&gt;
&lt;th&gt;추천&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;TypeScript 프로젝트&lt;/td&gt;
&lt;td&gt;✅ ethers.js v6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardhat 기반 테스트&lt;/td&gt;
&lt;td&gt;✅ ethers.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;오래된 코드/문서 기반 유지&lt;/td&gt;
&lt;td&gt;web3.js (v1.x)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;속도/용량 최적화 필요&lt;/td&gt;
&lt;td&gt;✅ ethers.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;단순한 학습용/시작용&lt;/td&gt;
&lt;td&gt;web3.js 가능하지만 오래된 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;✅ 결론&lt;/p&gt;
&lt;p&gt;실전 프로젝트, 특히 TypeScript 기반이라면&lt;br&gt;  ethers.js v6가 사실상 표준이며 최선의 선택입니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;</description>
      <category>이더리움</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/55</guid>
      <comments>https://yuyupapa-bc.tistory.com/55#entry55comment</comments>
      <pubDate>Tue, 25 Mar 2025 22:18:18 +0900</pubDate>
    </item>
    <item>
      <title>스마트 컨트랙트 개발도구: JavaScript vs. TypeScript</title>
      <link>https://yuyupapa-bc.tistory.com/54</link>
      <description>&lt;p&gt;JavaScript(JS)와 TypeScript(TS)는 모두 스마트 컨트랙트 개발에서 널리 사용되지만,&lt;br&gt;  툴링,   디버깅,   테스트 작성,   스마트 컨트랙트와 상호작용 같은 실제 개발 업무에서는 TypeScript가 훨씬 더 유리합니다.&lt;/p&gt;
&lt;p&gt;아래에서 스마트 컨트랙트 개발자 입장에서 JS와 TS를 기능/생산성/안정성/도구 호환성 측면으로 비교 분석해드릴게요.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  개요: JavaScript vs TypeScript&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;JavaScript (JS)&lt;/th&gt;
&lt;th&gt;TypeScript (TS)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;기본 언어&lt;/td&gt;
&lt;td&gt;동적(dynamic), 느슨한 타입&lt;/td&gt;
&lt;td&gt;정적(static), 엄격한 타입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실행 대상&lt;/td&gt;
&lt;td&gt;바로 실행 가능&lt;/td&gt;
&lt;td&gt;컴파일 후 실행 (.ts → .js)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;타입 검사&lt;/td&gt;
&lt;td&gt;없음 (런타임 오류)&lt;/td&gt;
&lt;td&gt;✅ 컴파일 시점 오류 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;학습 난이도&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;중간 (타입 선언 등 추가 개념 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  스마트 컨트랙트 개발 관점 비교&lt;/p&gt;
&lt;p&gt;1️⃣ ✅ 타입 안정성 &amp;amp; 자동완성&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;JavaScript&lt;/th&gt;
&lt;th&gt;TypeScript&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;컨트랙트 ABI 자동 완성&lt;/td&gt;
&lt;td&gt;❌ 없음&lt;/td&gt;
&lt;td&gt;✅ 지원 (TypeChain, Hardhat, ethers.js)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;balanceOf(address) 호출 시&lt;/td&gt;
&lt;td&gt;주소 누락해도 실행됨 → 런타임 오류&lt;/td&gt;
&lt;td&gt;✅ 타입 미스 오류 컴파일 시 감지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;오탈자 방지&lt;/td&gt;
&lt;td&gt;❌ 없음&lt;/td&gt;
&lt;td&gt;✅ 강력한 IDE 지원 (VSCode 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;  정확한 타입은 테스트 코드, 배포 스크립트, 유저 인터랙션에서 필수 요소입니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;2️⃣   Hardhat, ethers.js, web3.js와의 호환성&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;라이브러리&lt;/th&gt;
&lt;th&gt;JS 지원&lt;/th&gt;
&lt;th&gt;TS 지원&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Hardhat&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (TS가 기본 권장)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ethers.js&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ 완벽 (v6는 TS 기반으로 작성됨)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;web3.js&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (@types/web3 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeChain&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ 스마트 컨트랙트 → 정적 타입 자동 생성 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;✅ TypeScript는 최신 Ethereum 개발 툴체인과 가장 잘 맞는 환경입니다.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;3️⃣   테스트 및 배포 스크립트&lt;/p&gt;
&lt;p&gt;  JavaScript&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const balance = await token.methods.balanceOf(account).call();&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;•    오타 → 실행 중 실패
•    인자 부족 → 런타임 오류&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  TypeScript&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const balance: bigint = await token.balanceOf(account);&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;•    account 누락 시 컴파일 에러
•    반환값 타입 자동 추론 (bigint, string, BigNumber 등)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;4️⃣   확장성과 유지보수&lt;/p&gt;
&lt;p&gt;|항목|    JavaScript|    TypeScript|&lt;br&gt;|팀 협업|    ❌ 타입 추론 어려움|    ✅ 강력한 타입 계약으로 협업 수월|&lt;br&gt;|코드 규모 확장|    ❌ 오류 잦음|    ✅ 구조적 리팩토링 용이|&lt;br&gt;|테스트 커버리지|    ❌ IDE 도움 부족|    ✅ VSCode, IntelliJ 등에서 완벽한 추적 지원|&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;5️⃣   프론트엔드 연계 (React, Next.js 등)&lt;br&gt;    •    dApp 개발 시 React 연동이 많음 → ethers.js + TS 조합이 사실상 표준&lt;br&gt;    •    wagmi, viem, useContractRead() 등 모두 TS 기반으로 설계&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;✅ 요약: 스마트 컨트랙트 개발자 기준 비교표&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;JavaScript&lt;/th&gt;
&lt;th&gt;TypeScript&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;타입 안정성&lt;/td&gt;
&lt;td&gt;❌ 없음&lt;/td&gt;
&lt;td&gt;✅ 있음 (컴파일 타임 확인)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도구 통합&lt;/td&gt;
&lt;td&gt;보통&lt;/td&gt;
&lt;td&gt;✅ 최적화됨 (ethers, hardhat, TypeChain 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ABI 활용&lt;/td&gt;
&lt;td&gt;수동&lt;/td&gt;
&lt;td&gt;✅ 자동 타입 생성 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;생산성&lt;/td&gt;
&lt;td&gt;초반 빠름&lt;/td&gt;
&lt;td&gt;✅ 중장기 더 안정적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;유지보수&lt;/td&gt;
&lt;td&gt;어렵고 위험&lt;/td&gt;
&lt;td&gt;✅ 큰 규모에 강함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;권장 여부&lt;/td&gt;
&lt;td&gt;❌ 단기 실습용&lt;/td&gt;
&lt;td&gt;✅ 실전 개발자용 (사실상 표준)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;p&gt;  결론&lt;/p&gt;
&lt;p&gt;✅ 스마트 컨트랙트 개발자의 실전 선택지는 TypeScript입니다.&lt;br&gt;특히 Hardhat, Ethers.js, TypeChain, React와의 궁합이 탁월해요.&lt;/p&gt;
&lt;p&gt;⸻&lt;/p&gt;</description>
      <category>이더리움</category>
      <author>yuyupapa-bc</author>
      <guid isPermaLink="true">https://yuyupapa-bc.tistory.com/54</guid>
      <comments>https://yuyupapa-bc.tistory.com/54#entry54comment</comments>
      <pubDate>Tue, 25 Mar 2025 22:14:14 +0900</pubDate>
    </item>
  </channel>
</rss>