<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>black_birds_beak.log</title>
        <link>https://velog.io/</link>
        <description>배움을 찾는 사람이 되자!</description>
        <lastBuildDate>Mon, 17 Mar 2025 16:08:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>black_birds_beak.log</title>
            <url>https://velog.velcdn.com/images/black_birds_beak/profile/1bc252f5-b9f1-4240-a0b2-b3235e3658cf/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. black_birds_beak.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/black_birds_beak" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Ethereum 정리_EVM, Account]]></title>
            <link>https://velog.io/@black_birds_beak/Ethereum-%EC%A0%95%EB%A6%ACEVM-Account</link>
            <guid>https://velog.io/@black_birds_beak/Ethereum-%EC%A0%95%EB%A6%ACEVM-Account</guid>
            <pubDate>Mon, 17 Mar 2025 16:08:56 GMT</pubDate>
            <description><![CDATA[<h2 id="ethereum">Ethereum?</h2>
<p>BlockChain 기술을 기반으로 Smant Contract 기능을 구현하기 위한 분산 컴퓨팅 플랫폼이다.
Ethereum 네트워크의 모든 사람들의 동의하는 하나의 표준 컴퓨터 (Ethereum Virtual Machine, EVM)가 있습니다. Ethereum에 참여하는 모든 참가자들은 EVM의 상태 복사본을 보관하고 있습니다.</p>
<h2 id="evm-정의">EVM 정의</h2>
<p>Ethereum Virtual Machine(EVM)은 Ethereum 네트워크를 구성하는 Client를 실행하고 있는 수천 개의 연결된 컴퓨터에 의해서 유지되는 하나의 단일 개체를 의미합니다.</p>
<p>Ethereum 프로토콜은 EVM이 중단되지 않고 지속적으로 불변하는 작동을 유지하기 위한 목적으로 존재합니다. BlockChain의 상태는 언제나 하나의 정규 상태를 가지며, EVM은 블록에서 블록으로 새로운 유효 상태를 계산하는 규칙을 정의합니다.</p>
<h2 id="ethereum의-상태">Ethereum의 상태</h2>
<p>Ethereum은 <code>SmartContract</code>를 기반으로 각 노드에 분산 <a href="https://ko.wikipedia.org/wiki/%EC%9C%A0%ED%95%9C_%EC%83%81%ED%83%9C_%EA%B8%B0%EA%B3%84">유한 상태 머신</a> 데이터로 볼 수 있습니다. Ethereum의 상태는 계정, 잔액, 기계 상태를 보유하고 있는 대규모 데이터 구조로서 미리 정의된 규칙에 다라서 블록에서 블록으로 변경할 수 있고 임의의 코드를 실행할 수 있습니다. 블록에서 블록으로 상태를 변경하는 규칙은 EVM에 의해서 정의를 합니다.</p>
<p><img src="https://ethereum.org/content/developers/docs/evm/evm.png" alt="EVM"></p>
<h3 id="ethereum의-상태-전이-함수">Ethereum의 상태 전이 함수</h3>
<p>EVM은 입력이 주어지게 되면 결정론적인 출력을 생성합니다. 
$$
Y(S , T) = S&#39;
$$
기존에 검증된 S와 새로 검증된 Transaction (T)을 Ethereum 상태 transation 기능 <strong>Y(S, T)</strong> 에 넣어 새로운 형태 S&#39;을 생성합니다.</p>
<h4 id="state">State</h4>
<p>Ethereum의 맥락에서, State는 <a href="https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/">Modified Merkle Patricia Trie</a> 라고 불리는 구조의 모든 계정의 Hash로 연결된 하나의 Single root hash 유지하고 있습니다.</p>
<h4 id="transactions">Transactions</h4>
<p>Transaction은 계정에서 암호로 서명된 데이터 입니다. Transaction은 호출된 메시지의 결과가 담겨 있는 transaction과 contract의 결과가 담겨있는 2가지 타입의 종류가 있습니다.
Contract는 <code>smart contract</code>의 bytecode가 포함된 새로운 contract 계정이 생성됩니다. 다른 계정이 해당 계약으로 메시지를 호출할 때마다 해당하는 바이트 코드를 실행합니다.</p>
<h2 id="ethereum에서의-account">Ethereum에서의 Account</h2>
<p><code>Ethereum</code>에서의 계정은 <code>Ethereum</code> 네트워크에서 거래를 보낼 수 있는 <code>Ethereum</code>(ETH) 잔액을 가진 대상을 말합니다. 계정은 사용자가 제어하거나 스마트 컨트랙트를 배포할 수 있습니다.</p>
<p><code>Ethereum</code>에서는 2가지 타입의 계정이 존재합니다.</p>
<h3 id="ethereum의-account-종류">Ethereum의 Account 종류</h3>
<ul>
<li><p>Externally-owned account (EOA)</p>
<ul>
<li>누구나 개인키로 컨트롤을 할 수 있는 계정</li>
</ul>
</li>
<li><p>Contract account</p>
<ul>
<li>네트워크에 배포된 컨트랙트를 의미합니다. 소스코드를 기반으로 통제가 가능한 계정을 말합니다.</li>
</ul>
</li>
</ul>
<p>두 개의 Account는 모두 ETH 토큰을 소유하거나 보낼 수 있으며, 배포된 contract와 상호작용 하는 것이 가능합니다.</p>
<table>
<thead>
<tr>
<th></th>
<th>EOA</th>
<th>Contract</th>
</tr>
</thead>
<tbody><tr>
<td>생성 비용</td>
<td>생성 비용 없음</td>
<td>network 저장 공간 사용으로 비용 발생</td>
</tr>
<tr>
<td>트랜잭션</td>
<td>transaction 시작 가능</td>
<td>수신한 transaction에 대한 응답 가능</td>
</tr>
<tr>
<td>ETH 전송</td>
<td>다른 EOA와 통신만 가능</td>
<td>trigger code를 활용하여 다른 Contract, EOA와 통신</td>
</tr>
<tr>
<td>KeyPair 소유</td>
<td>Key Pair 소유</td>
<td>Key Pair를 소유하지 않음</td>
</tr>
</tbody></table>
<p><code>Ethereum</code>은 다음 4가지 종류의 필드 값을 가지고 있습니다.</p>
<ul>
<li>nonce<ul>
<li>외부에서 보낸 <code>transactions</code>를 구분하고 각 개수를 세기 위한 값</li>
</ul>
</li>
<li>balance<ul>
<li>계정이 소유하고 있는 wei의 양</li>
</ul>
</li>
<li>codeHash<ul>
<li>[[EVM]] 에 올라가 있는 account code의 hash값을 의미합니다.</li>
</ul>
</li>
<li>storageRoot<ul>
<li>storage hash 라고도 불리고 있습니다.</li>
<li><code>keccack256</code> 알고리즘을 기본으로 하여 인코딩하여 저장합니다.</li>
</ul>
</li>
</ul>
<p><img src="https://ethereum.org/content/developers/docs/accounts/accounts.png" alt="accounts"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[도파민네이션]]></title>
            <link>https://velog.io/@black_birds_beak/%EB%8F%84%ED%8C%8C%EB%AF%BC%EB%84%A4%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@black_birds_beak/%EB%8F%84%ED%8C%8C%EB%AF%BC%EB%84%A4%EC%9D%B4%EC%85%98</guid>
            <pubDate>Thu, 16 Nov 2023 08:54:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>나를 해하지 않는 것은 나를 더 강하게 만든다. -니체-</p>
</blockquote>
<p><code>나를 해하지 않는 것은 나를 더 강하게 만든다.</code> 이 책을 하나로 관통하는 문장이라고 생각한다.</p>
<p>우리는 살아오면서 여러가지 사건, 사고를 겪는다. 그러한 과정에서 우리는 도파민의 영향을 받으며 살아간다. 우리는 도파민의 영향을 받아 <code>보상 그 자체의 쾌락을 느끼는 과정</code> 보다 <code>보상을 얻기 위한 동기부여 과정</code>에 더 큰 영향을 받으며, 우리에게 행복한 느낌을 가져다 준다. 하지만 그런 과정이 과하게 될 경우, 어떠한 것에 <code>중독</code> 이라는 현상으로 나타날 수 있다.</p>
<p>도파민에 중독이 되면 우리는 삶에서 조금 더 쉬운 방법으로 쾌락을 얻고자 하며, 그러한 과정에서 고통을 피하게 된다. 공부를 하였을 때, 성적이 나오지 않을 것을 두려워하여 공부를 안 하기도 하고, 솔직한 모습을 보였을 때, 상대방이 나에게 실망하여 거리를 두는 것이 두려워 거짓말을 하기도 한다. 심한 경우, 자신을 학대하거나 마약에 중독이 되는 경우도 발생할 수 있다.</p>
<p>책 속에 나오는 환자들의 사례를 보면 우리의 삶이 지구 반대편에 있어도 다른 환경에 비슷한 고민을 할 수 있다는 것을 알게 된다. 나라도 거짓말을 했을 것이고 나라도 포기했을 것 같은 내용들이 나온다. </p>
<p>하지만, 책에 나오는 환자들은 각자의 상황을 솔직하게 받아들임으로써 현실을 극복해 나간다. 때로는 중독이 되는 대상을 없애버리고, 중독에서 벗어나기 위한 상징을 두기도 하고 나아가 다른 사람에게 도움을 요청하며 자신의 모습을 있는 그대로 드러내며 받아들인다.</p>
<p>살다 보면 취업에 실패하거나, 돈이 없어 자녀에게 지원을 못 해주는 상황, 알코올 중독에 빠진 상황 등 다양한 고통이 올 수 있다. 이러한 어려움이 나타났을 때 고통을 받아들이고 솔직함의 용기를 가지고 나의 본 모습을 마주할 때, 어려움을 극복할 수 있는 나아가 한 걸음 더 나아갈 수 있는 기회가 될 수 있을 것이라고 생각하게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hyper Ledger Query 기반 Ledger 조회]]></title>
            <link>https://velog.io/@black_birds_beak/Hyper-Ledger-Query-%EA%B8%B0%EB%B0%98-Ledger-%EC%A1%B0%ED%9A%8C</link>
            <guid>https://velog.io/@black_birds_beak/Hyper-Ledger-Query-%EA%B8%B0%EB%B0%98-Ledger-%EC%A1%B0%ED%9A%8C</guid>
            <pubDate>Thu, 16 Nov 2023 08:52:10 GMT</pubDate>
            <description><![CDATA[<p>Hyperledger fabric은 CouchDB에서 사용하는 Query문을 통해 데이터를 조회하는 것이 가능합니다.</p>
<h2 id="couchdb-selector-syntax">CouchDB Selector syntax</h2>
<p>selector 문법의 경우 기본적으로 다음과 같은 형태를 가진다. MongoDB의 문법과 유사한 점이 있지만 특정한 기능의 경우 다른 문법을 가지는 경우가 있습니다.</p>
<pre><code class="language-json">{
    &quot;selector&quot;: {
        &quot;year&quot;: {&quot;$gt&quot;: 2010}
    },
    &quot;fields&quot;: [&quot;_id&quot;, &quot;_rev&quot;, &quot;year&quot;, &quot;title&quot;],
    &quot;sort&quot;: [{&quot;year&quot;: &quot;asc&quot;}],
    &quot;limit&quot;: 2,
    &quot;skip&quot;: 0,
    &quot;execution_stats&quot;: true
}</code></pre>
<h2 id="selector-기본-문법">Selector 기본 문법</h2>
<h3 id="field-한-개-기반-조회">field 한 개 기반 조회</h3>
<p>기본적인 Selector 문법의 경우, 하나 이상의 조회하고자 하는 필드의 정보가 필요합니다.
특정한 필드의 값을 조회하는 경우, 다음과 같은 형태의 json 데이터로 조회하는 것이 가능합니다.</p>
<pre><code class="language-json">{
    &quot;director&quot;: &quot;Lars von Trier&quot;
}</code></pre>
<blockquote>
<p>위의 예시의 경우, <code>diredtor</code> 필드의 값이 <code>Lars von Trier</code> 인 경우를 찾아서 조회하는 문법입니다.</p>
</blockquote>
<p>조금 더 복잡한 selector 표현의 경우, 연산자<code>($regex, $eq, $gt, $gte, $lt, $lte 등)</code>의 조합으로서 다음과 같은 표현이 가능합니다.</p>
<pre><code class="language-json">{
    &quot;selector&quot;:{
        &quot;title&quot;: &quot;Live And Let Die&quot;
    },
    &quot;fields&quot;: [
        &quot;title&quot;,
        &quot;cast&quot;
    ]
}</code></pre>
<h3 id="field-2개-이상-조회">field 2개 이상 조회</h3>
<p>하나 이상의 필드를 조합한 조회를 하려면 다음과 같은 형태를 작성합니다.</p>
<pre><code class="language-json">{
    &quot;name&quot;:&quot;Paul&quot;,
    &quot;location&quot;: &quot;Boston&quot;
}</code></pre>
<blockquote>
<p>위 예시의 경우, <code>name</code> 필드의 값이 <code>Paul</code>, <code>location</code>의 값이 <code>Boston</code>인 경우를 조회합니다.</p>
</blockquote>
<h3 id="subfield-조회">Subfield 조회</h3>
<p>조금 더 복잡한 subfiled 조회는 다음과 같은 방법으로 조회하는 것이 가능합니다.
기본적으로는 json형태로 조회하며, 점 표기법을 사용하여 조회하는 것도 가능합니다.</p>
<pre><code class="language-json">{
    &quot;imdb&quot;:{
        &quot;rating&quot; : 8
    }
}
or
{
    &quot;imdb.rating&quot; : 8
}</code></pre>
<h2 id="operators">Operators</h2>
<p>연산자들은 달러 표시 <code>$</code>를 name 필드 앞 단에 붙여서 구분하여 사용할 수 있습니다.
연산자는 2 종류의 연산자 타입을 가지고 있습니다.</p>
<ul>
<li>Combination operators</li>
<li>Condition operators</li>
</ul>
<p>일반적으로 Combination 연산자의 경우는 최상급의 선택 단계에서 적용이 되며,
Condition 연산자의 경우에는 하나 이상의 <code>selector</code>로 조합하여 사용합니다.</p>
<h3 id="implicit-operators">Implicit Operators</h3>
<p>2 종류의 implicit operator가 있습니다.</p>
<ul>
<li>Equality</li>
<li>And</li>
</ul>
<p>두 개의 연산자는 선언하지 않더라도 선언이 된 것으로 간주가 됩니다.
예를 들어</p>
<pre><code class="language-json">{
    &quot;director&quot;: &quot;Lars von Trier&quot;
}

{
    &quot;director&quot;: {
        &quot;$eq&quot;: &quot;Lars von Trier&quot;
    }
}</code></pre>
<p>위의 2개의 json 데이터는 결과적으로 같은 결과를 가져옵니다.</p>
<h3 id="explicit-operators">Explicit Operators</h3>
<p><code>Equality</code>와 <code>And</code> 연산을 제외한 모든 연산자는 명확하게 명시해야 합니다.</p>
<h4 id="combination-operators">Combination Operators</h4>
<p>Combination Operators는 selectors를 조합하는데 사용합니다. 
대부분의 프로그래밍 언어가 제공하는 boolean 연산자처럼 <code>$all</code>, <code>$elemMatch</code>, <code>$allMatch</code> 등의 연산자는 Json 배열 또는 Json 데이터를 비교합니다.</p>
<p>Combination Operator는 하나의 인자를 받아서 사용합니다. 인자는 하나 혹은 배열 형태의 데이터를 사용합니다.</p>
<h5 id="combination-operators-list">Combination Operators List</h5>
<table>
<thead>
<tr>
<th align="left">Operator</th>
<th align="left">Argument</th>
<th align="left">Purpose</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>$and</code></td>
<td align="left">Array</td>
<td align="left">배열의 모든 Selector가 일치하는 경우를 조회</td>
</tr>
<tr>
<td align="left"><code>$or</code></td>
<td align="left">Array</td>
<td align="left">배열에 있는 Selector 중 하나라도 일치한다면 조회</td>
</tr>
<tr>
<td align="left"><code>$not</code></td>
<td align="left">Selector</td>
<td align="left">Selector와 일치하지 않는 데이터들을 조회</td>
</tr>
<tr>
<td align="left"><code>$nor</code></td>
<td align="left">Array</td>
<td align="left">배열의 Selector가 일치하지 않으면 조회</td>
</tr>
<tr>
<td align="left"><code>$all</code></td>
<td align="left">Array</td>
<td align="left">배열의 모든 요소를 포함하는 경우 조회</td>
</tr>
<tr>
<td align="left"><code>$elemMatch</code></td>
<td align="left">Selector</td>
<td align="left">Matches and returns all documents that contain an array field with at least one element that matches all the specified query criteria.</td>
</tr>
<tr>
<td align="left"><code>$allMatch</code></td>
<td align="left">Selector</td>
<td align="left">Matches and returns all documents that contain an array field with all its elements matching all the specified query criteria.</td>
</tr>
<tr>
<td align="left"><code>$keyMapMatch</code></td>
<td align="left">Selector</td>
<td align="left">Matches and returns all documents that contain a map that contains at least one key that matches all the specified query criteria.</td>
</tr>
</tbody></table>
<p><code>$and</code> 연산자는 2개의 필드를 사용한다.</p>
<pre><code class="language-json">{
  &quot;selector&quot;: {
    &quot;$and&quot;: [
      {
        &quot;title&quot;: &quot;Total Recall&quot;
      },
      {
        &quot;year&quot;: {
          &quot;$in&quot;: [1984, 1991]
        }
      }
    ]
  },
  &quot;fields&quot;: [
      &quot;year&quot;,
      &quot;title&quot;,
      &quot;cast&quot;
  ]
}</code></pre>
<h4 id="condition-operators">Condition Operators</h4>
<p>Condition Operators는 저장되어 있는 특정한 데이터의 값을 평가하는데 사용이 됩니다.
예를 들어서, <code>$eq</code> 연산자의 경우 저장된 데이터가 같은 값을 가지고 있는지 확인하는 기능을 합니다.</p>
<blockquote>
<p>Condition Operators는 반드시 문서에 조회하고자 하는 Selector가 존재해야 합니다.
<code>$ne</code> 연산자의 경우 데이터가 반드시 존재하고, 데이터 값이 같지 않음을 의미합니다.</p>
</blockquote>
<h5 id="condition-operators-list">Condition Operators List</h5>
<table>
<thead>
<tr>
<th>Operator type</th>
<th>Operator</th>
<th>Argument</th>
<th>Purpose</th>
</tr>
</thead>
<tbody><tr>
<td>(In)equality</td>
<td><code>$lt</code></td>
<td>Any JSON</td>
<td>The field is less than the argument.</td>
</tr>
<tr>
<td></td>
<td><code>$lte</code></td>
<td>Any JSON</td>
<td>The field is less than or equal to the argument.</td>
</tr>
<tr>
<td></td>
<td><code>$eq</code></td>
<td>Any JSON</td>
<td>The field is equal to the argument</td>
</tr>
<tr>
<td></td>
<td><code>$ne</code></td>
<td>Any JSON</td>
<td>The field is not equal to the argument.</td>
</tr>
<tr>
<td></td>
<td><code>$gte</code></td>
<td>Any JSON</td>
<td>The field is greater than or equal to the argument.</td>
</tr>
<tr>
<td></td>
<td><code>$gt</code></td>
<td>Any JSON</td>
<td>The field is greater than the to the argument.</td>
</tr>
<tr>
<td>Object</td>
<td><code>$exists</code></td>
<td>Boolean</td>
<td>Check whether the field exists or not, regardless of its value.</td>
</tr>
<tr>
<td></td>
<td><code>$type</code></td>
<td>String</td>
<td>Check the document field’s type. Valid values are <code>&quot;null&quot;</code>, <code>&quot;boolean&quot;</code>, <code>&quot;number&quot;</code>, <code>&quot;string&quot;</code>, <code>&quot;array&quot;</code>, and <code>&quot;object&quot;</code>.</td>
</tr>
<tr>
<td>Array</td>
<td><code>$in</code></td>
<td>Array of JSON values</td>
<td>The document field must exist in the list provided.</td>
</tr>
<tr>
<td></td>
<td><code>$nin</code></td>
<td>Array of JSON values</td>
<td>The document field not must exist in the list provided.</td>
</tr>
<tr>
<td></td>
<td><code>$size</code></td>
<td>Integer</td>
<td>Special condition to match the length of an array field in a document. Non-array fields cannot match this condition.</td>
</tr>
<tr>
<td>Miscellaneous</td>
<td><code>$mod</code></td>
<td>[Divisor, Remainder]</td>
<td>Divisor is a non-zero integer, Remainder is any integer. Non-integer values result in a 404. Matches documents where <code>field % Divisor == Remainder</code> is true, and only when the document field is an integer.</td>
</tr>
<tr>
<td></td>
<td><code>$regex</code></td>
<td>String</td>
<td>A regular expression pattern to match against the document field. Only matches when the field is a string value and matches the supplied regular expression. The matching algorithms are based on the Perl Compatible Regular Expression (PCRE) library. For more information about what is implemented, see the see the <a href="http://erlang.org/doc/man/re.html">Erlang Regular Expression</a>.</td>
</tr>
<tr>
<td></td>
<td><code>$beginsWith</code></td>
<td>String</td>
<td>Matches where the document field begins with the specified prefix (case-sensitive). If the document field contains a non-string value, the document is not matched.</td>
</tr>
</tbody></table>
<h3 id="sort-syntax">Sort Syntax</h3>
<p><code>sort</code> 필드는 필드의 이름, 정렬 방향이 기본적인 배열 형태로 표현이 됩니다.
첫 번째로 오는 필드의 이름과 방향이 가장 먼저 정렬이 되는 기준이 되며, 뒤에 오는 순서대로 정렬 규칙이 적용이 됩니다.</p>
<p><code>asc</code> 는 오름차순, <code>desc</code>는 내림차순 정렬을 하고자 할 때 사용합니다. 
특정한 방향을 입력하지 않는 경우 기본적으로 오름차순으로 정렬이 됩니다..</p>
<pre><code class="language-json">[{&quot;fieldName1&quot;: &quot;desc&quot;}, {&quot;fieldName2&quot;: &quot;desc&quot; }]</code></pre>
<p>정렬 기능을 사용하려면 다음과 같은 정보를 확인해야만 합니다.</p>
<ul>
<li>최소한 하나의 정렬 필드를 selector에 포함 되어야 한다.</li>
<li>모든 정렬 필드가 동일한 index 값이 적용이 되어 있어야 한다.</li>
<li>정렬 배열의 각 객체에는 single key가 하나 존재해야 한다.</li>
</ul>
<p>만약 single key를 가지고 있지 않는 경우, 정렬의 결과는 구현의 방향에 따라서 달라질 수 있습니다.
간단한 정렬 쿼리문의 예시는 다음과 같습니다.</p>
<pre><code class="language-json">{
    &quot;selector&quot;: {&quot;Actor_name&quot;: &quot;Robert De Niro&quot;},
    &quot;sort&quot;: [{&quot;Actor_name&quot;: &quot;asc&quot;}, {&quot;Movie_runtime&quot;: &quot;asc&quot;}]
}</code></pre>
<h3 id="filtering-fields">Filtering Fields</h3>
<p>필터를 통해서 가지고 오는 정보의 필드를 정확하게 지정하는 것이 가능합니다.
이러한 과정을 통해서, 다음과 같은 2가지 이점을 가져올 수 있습니다.</p>
<ul>
<li>Application에서 요구하는 특정한 데이터를 제한적으로 가져올 수 있다.</li>
<li>응답 데이터의 사이즈를 줄일 수 있다.</li>
</ul>
<p>특정한 필드를 포함한 응답 데이터의 경우, 자동적으로 <code>_id</code> 같은 metadata 필드 정보를 자동으로 포함하지 않고 반환합니다.</p>
<p>예시 형태는 다음과 같습니다.</p>
<pre><code class="language-json">{
    &quot;selector&quot;: { &quot;Actor_name&quot;: &quot;Robert De Niro&quot; },
    &quot;fields&quot;: [&quot;Actor_name&quot;, &quot;Movie_year&quot;, &quot;_id&quot;, &quot;_rev&quot;]
}</code></pre>
<h3 id="pagination">Pagination</h3>
<p><code>Mango query</code>는 bookmark 필드를 통해서 Pagination 기능을 지원합니다. 모든 <code>_fine</code> 응답은 bookmark 값을 포함합니다.
다음 query의 결과를 가져오기 위해서는 selector값을 동일하게 유지한 다음, 다음의 bookmark 값을 추가하여 가져올 수 있습니다.</p>
<p>Pagination의 결과가 마지막에 도달했는지 확인하는 법은 size_request - result의 결과가 &lt; limit 값을 비교하여 더 이상 없는 것을 확인할 수 있습니다.</p>
<h3 id="execution-statistics">Execution Statistics</h3>
<p>특정한 요청을 통해서 기본적인 통계 정보를 제공하고 있습니다. <code>_explain</code> endpoint 값을 포함하여 유용한 데이터 값을 제공 받을 수 있습니다.</p>
<table>
<thead>
<tr>
<th>필드 값</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>total_keys_examined</code></td>
<td>검사한 index의 데이터 수를 의미합니다.</td>
</tr>
<tr>
<td><code>total_docs_examined</code></td>
<td>Number of documents fetched from the database / index, equivalent to using <code>include_docs=true</code> in a view. These may then be filtered in-memory to further narrow down the result set based on the selector.</td>
</tr>
<tr>
<td><code>total_quorum_docs_examined</code></td>
<td>Number of documents fetched from the database using an out-of-band document fetch. This is only non-zero when read quorum &gt; 1 is specified in the query parameters.</td>
</tr>
<tr>
<td><code>results_returned</code></td>
<td>쿼리에서 나오는 결과 값을 의미합니다.</td>
</tr>
<tr>
<td><code>execution_time_ms</code></td>
<td>총 실행하는 시간을 의미합니다.</td>
</tr>
</tbody></table>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://docs.couchdb.org/en/stable/api/database/find.html#find-selectors">https://docs.couchdb.org/en/stable/api/database/find.html#find-selectors</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Local 환경에 Hyperledger fabric 구축하기]]></title>
            <link>https://velog.io/@black_birds_beak/Local-%ED%99%98%EA%B2%BD%EC%97%90-Hyperledger-fabric-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@black_birds_beak/Local-%ED%99%98%EA%B2%BD%EC%97%90-Hyperledger-fabric-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 01 Nov 2023 05:02:58 GMT</pubDate>
            <description><![CDATA[<h2 id="사전-준비-과정">사전 준비 과정</h2>
<h3 id="서버-환경">서버 환경</h3>
<ul>
<li>CPU: 1 cores (5 thread), 16 GB RAM </li>
<li>Operation system: Oracle Linux 8</li>
</ul>
<blockquote>
<p><strong>Type: Developer playground</strong></p>
<p>System Requirements: RaspberryPi(2/3) - Minimal Requirements</p>
<p>Setup - Single org - 2 Nodes - 1 channel - 1 orderer Node</p>
<p><strong>Type: Advanced Developer playground - Locally on a PC</strong></p>
<p>System Requirements: Ubuntu 16.04LTS, 4GB RAM, 20GB HDD</p>
<p>Setup - 3 orgs - 6 Nodes - 2 channel - 3 orderer Node</p>
<p><strong>Type: Developing an MVP for an organization</strong></p>
<p>System Requirements: Ubuntu 16.04LTS, 8GB RAM, 30GB HDD</p>
<p>Setup - Multi orgs - n Nodes - Multi channel Setup - 3 orderer Node</p>
</blockquote>
<h3 id="방화벽-설정-정보">방화벽 설정 정보</h3>
<table>
<thead>
<tr>
<th>목적지 IP</th>
<th>Port 정보</th>
</tr>
</thead>
<tbody><tr>
<td><a href="http://repo.zabbix.com">http://repo.zabbix.com</a></td>
<td>80, 20, 21</td>
</tr>
<tr>
<td><a href="https://download.docker.com">https://download.docker.com</a></td>
<td>443</td>
</tr>
<tr>
<td><a href="https://yum.oracle.com">https://yum.oracle.com</a></td>
<td>443</td>
</tr>
<tr>
<td><a href="https://www.github.com">https://www.github.com</a></td>
<td>443</td>
</tr>
<tr>
<td><a href="https://raw.githubusercontent.com">https://raw.githubusercontent.com</a></td>
<td>443</td>
</tr>
<tr>
<td><a href="https://registry-1.docker.io/v2/">https://registry-1.docker.io/v2/</a></td>
<td>443</td>
</tr>
</tbody></table>
<h3 id="사전-설치-프로그램">사전 설치 프로그램</h3>
<blockquote>
<p>AWS Blockchain manegement system에서 HyperLedger Fabric을 사용하고자 할 경우,
Go언어의 버전을 <strong>1.14</strong>로 해서 사용하여야만 Chaincode 업로드가 가능</p>
</blockquote>
<ul>
<li><a href="https://git-scm.com/downloads">Git</a></li>
<li><a href="https://docs.docker.com/get-docker/">Docker</a></li>
<li><a href="https://go.dev/doc/install">Go</a></li>
<li><a href="https://jqlang.github.io/jq/download/">JQ</a></li>
</ul>
<h2 id="설치-과정">설치 과정</h2>
<h3 id="테스트-네트워크-설치">테스트 네트워크 설치</h3>
<h4 id="테스트-네트워크-설치-script-download">테스트 네트워크 설치 script download</h4>
<pre><code class="language-sh">curl -sSLO https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh &amp;&amp; chmod +x install-fabric.sh</code></pre>
<h4 id="테스트-네트워크-실행-도움말">테스트 네트워크 실행 도움말</h4>
<pre><code class="language-sh">./install-fabric.sh -h</code></pre>
<h5 id="실행-예시">실행 예시</h5>
<pre><code class="language-bash">./install-fabric.sh -h
Usage: ./install-fabric.sh [-f|--fabric-version &lt;arg&gt;] [-c|--ca-version &lt;arg&gt;] &lt;comp-1&gt; [&lt;comp-2&gt;] ... [&lt;comp-n&gt;] ...
        &lt;comp&gt;: Component to install one or more of  d[ocker]|b[inary]|s[amples]. If none specified, all will be installed
        -f, --fabric-version: FabricVersion (default: &#39;2.5.4&#39;)
        -c, --ca-version: Fabric CA Version (default: &#39;1.5.6&#39;)</code></pre>
<h4 id="component를-선택해서-다운로드">Component를 선택해서 다운로드</h4>
<p>특정한 component를 선택해서 다운로드 하기 위해서는 다음의 인자를 추가해서 다운로드 진행</p>
<ul>
<li>docker<ul>
<li>Fabric container 이미지를 다운로드 받기 위해서 docker 사용</li>
</ul>
</li>
<li>podman<ul>
<li>Fabric container 이미지를 다운로드 받기 위해서 podman 사용</li>
</ul>
</li>
<li>binary<ul>
<li>Fabric binaries 다운로드 진행</li>
</ul>
</li>
<li>samples<ul>
<li>github repository에 있는 fabric-samples 코드를 현재 디렉토리에 다운로드</li>
</ul>
</li>
</ul>
<h5 id="실행-예시-1">실행 예시</h5>
<pre><code class="language-bash">./install-fabric.sh docker samples binary
or
./install-fabric.sh d s b</code></pre>
<h4 id="특정-버전의-fabric-다운로드">특정 버전의 Fabric 다운로드</h4>
<p>특정한 버전을 다운로드 하기 위해서는 <code>--fabric-version</code>과 <code>-ca-version</code> 옵션을 사용해서 다운로드가 가능</p>
<p>짧게 옵션을 준다면 <code>-f</code> , <code>-c</code>로 사용이 가능</p>
<h5 id="fabric-특정-버전-다운로드-예시">Fabric 특정 버전 다운로드 예시</h5>
<pre><code class="language-sh">./install-fabric.sh --fabric-version 2.5.4 binary</code></pre>
<h3 id="테스트-네트워크-실행">테스트 네트워크 실행</h3>
<h4 id="네트워크-실행-명령">네트워크 실행 명령</h4>
<pre><code class="language-sh">./network up</code></pre>
<h4 id="네트워크-실행-시-ca-channel-정보-db-정보-설정">네트워크 실행 시, ca, channel 정보, DB 정보 설정</h4>
<blockquote>
<p>예시 : ca를 설정하며 채널 이름은 <code>mychannel</code> 저장소는 <code>couchdb</code>를 사용하도록 설정 </p>
</blockquote>
<pre><code class="language-bash">./network.sh up createChannel -ca -c mychannel -s couchdb</code></pre>
<h4 id="네트워크-종료">네트워크 종료</h4>
<pre><code class="language-sh">./network down</code></pre>
<h3 id="chaincode-배포">Chaincode 배포</h3>
<h4 id="packaging">packaging</h4>
<p>Go언어로 개발이 된 chaincode 정보를 패키징을 진행</p>
<pre><code class="language-bash">peer lifecycle chaincode package chaincode.tar.gz --path ${O_CHAINCODE_PATH} --lang golang --label ${O_CHAINCODE_LABEL}</code></pre>
<ul>
<li><p>path : 프로젝트의 경로 설정</p>
</li>
<li><p>lang : chaincode가 개발된 언어 정보 설정 [Go, Java, Javascript]</p>
<blockquote>
<p>예시의 명령어의 경우, Go로 개발된 chaincode를 패키징</p>
</blockquote>
</li>
<li><p>label: chaincode의 라벨 정보를 추가</p>
</li>
</ul>
<h4 id="install">install</h4>
<p>패키징된 tar.gz 파일을 Hyperledger Fabric Peer에 설치하는 과정. Chaincode를 배포하기 위해서는 Channel에 속한 모든 Peer에 chaincode를 설치해줘야 한다.</p>
<p><code>peer</code> 명령어를 사용하기 전, 다음과 같은 환경설정을 먼저 진행한다.</p>
<h5 id="환경-설정">환경 설정</h5>
<pre><code class="language-bash">export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID=&quot;Org1MSP&quot;
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051</code></pre>
<p>각각 TLS에 대한 설정, Hyperledger Fabric의 MSP 설정, Peer의 주소 설정 정보를 의미한다.</p>
<h5 id="설치-명령어">설치 명령어</h5>
<pre><code class="language-bash">peer lifecycle chaincode install [패키징된 파일 이름]</code></pre>
<h4 id="approve">approve</h4>
<p>설치가 된 Chaincode에 대해서 각각의 Peer들은 배포의 여부를 증명하는 과정을 필요로 한다.</p>
<pre><code class="language-bash">peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name chaincode --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile &quot;${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem&quot;</code></pre>
<p>위와 같은 명령어를 통해 chaincode를 증명할 수 있는데, chaincode의 이름, 버전 정보, 시퀀스 넘버를 입력하여 증명하는 것이 가능하다.</p>
<h4 id="commit">commit</h4>
<p>일정한 비율만큼 증명이 되었다면 다음과 같은 명령어를 통해서 commit을 진행하는 것이 가능하다.</p>
<pre><code class="language-bash">peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name chaincode --version 1.0 --sequence 1 --tls --cafile &quot;${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem&quot; --peerAddresses localhost:7051 --tlsRootCertFiles &quot;${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt&quot; --peerAddresses localhost:9051 --tlsRootCertFiles &quot;${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt&quot;</code></pre>
<h4 id="참고">참고</h4>
<h5 id="배포하기-위해-작성했던-script-파일-내용">배포하기 위해 작성했던 script 파일 내용</h5>
<pre><code class="language-sh">#!/bin/bash

export PATH=${PWD}/../fabric-samples/bin:$PATH
export FABRIC_CFG_PATH=${PWD}/../fabric-samples/config/
export PATH=$PATH:/usr/local/go/bin

set_peer1() {
    export CORE_PEER_TLS_ENABLED=true
    export CORE_PEER_LOCALMSPID=&quot;Org1MSP&quot;
    export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
    export CORE_PEER_MSPCONFIGPATH=${PWD}/../fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
    export CORE_PEER_ADDRESS=localhost:7051
}

set_peer2() {
    export CORE_PEER_LOCALMSPID=&quot;Org2MSP&quot;
    export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
    export CORE_PEER_MSPCONFIGPATH=${PWD}/../fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
    export CORE_PEER_ADDRESS=localhost:9051
}

packaging() {
    PACKAGE_RESULT=$(peer lifecycle chaincode package omnione.tar.gz --path ${O_CHAINCODE_PATH} --lang golang --label ${O_CHAINCODE_LABEL})
    if [ $? -ne 0 ]; then
        echo &quot;PACKAGE ERROR!!&quot;
        exit 1
    fi
}

install_chaincode() {
    set_peer1
    PACKAGE_INSTALL_FIRST=$(peer lifecycle chaincode install omnione.tar.gz &gt;install_result.txt)
    if [ $? -ne 0 ]; then
        echo &quot;FIRST PEER CHAINCODE INSTALL ERROR&quot;
        exit 1
    fi

    set_peer2
    PACKAGE_INSTALL_SECOND=$(peer lifecycle chaincode install omnione.tar.gz)
    if [ $? -ne 0 ]; then
        echo &quot;SECOND PEER CHAINCODE INSTALL ERROR&quot;
        exit 1
    fi
}

approve_chaincode() {
    set_peer2
    $(peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name omnione --version ${O_CHAINCODE_VERSION} --package-id ${CC_PACKAGE_ID} --sequence 6 --tls --cafile &quot;${PWD}/../fabric-samples/test-network/organizations/ordererOrgan
    izations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem&quot;)

    set_peer1
    $(peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name omnione --version ${O_CHAINCODE_VERSION} --package-id ${CC_PACKAGE_ID} --sequence 6 --tls --cafile &quot;${PWD}/../fabric-samples/test-network/organizations/ordererOrgan
    izations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem&quot;)
}

commit_chaincode() {
    set_peer1
    $(peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name omnione --version 1.2 --sequence 6 --tls --cafile &quot;${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem&quot; --peerAddresses localhost:7051 --tlsRootCertFiles &quot;${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt&quot; --peerAddresses localhost:9051 --tlsRootCertFiles &quot;${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt&quot;)
}

print_help() {
    echo &quot;$0 -p PATH -v VERSION -l LABEL [-i ID]&quot;
    echo &quot;&lt;options&gt;&quot;
    echo &quot;    -p PATH: chaincode project path&quot;
    echo &quot;    -i CHAINCODE_ID: chaincode id that you want to deploy. default is &#39;omnione&#39;&quot;
    echo &quot;    -v VERSION: chaincode version&quot;
    echo &quot;    -l LABEL: label of chaincode&quot;
    echo &quot;    -h     : Show this message.&quot;
    echo &quot;&lt;exit code&gt;&quot;
    echo &quot;    0: Success.&quot;
    echo &quot;    1: Failure.&quot;
    exit 1
}

while getopts p:i:l:v: opts; do
    case $opts in
    i)
        O_CHAINCODE_ID=$OPTARG
        ;;
    p)
        O_CHAINCODE_PATH=$OPTARG
        ;;
    l)
        O_CHAINCODE_LABEL=$OPTARG
        ;;
    v)
        O_CHAINCODE_VERSION=$OPTARG
        ;;
    h)
        print_help
        ;;
    \?)
        print_help
        exit 1
        ;;
    :)
        print_help
        exit 1
        ;;
    *)
        print_help
        exit 1
        ;;
    esac
done

packaging
install_chaincode
approve_chaincode
</code></pre>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://www.quora.com/What-are-the-minimum-hardware-requirements-to-run-a-blockchain-hyperledger-fabric-node-or-validator">https://www.quora.com/What-are-the-minimum-hardware-requirements-to-run-a-blockchain-hyperledger-fabric-node-or-validator</a></li>
<li><a href="http://hyperledger-fabric.readthedocs.io/en/release-2.5/prereqs.html#">http://hyperledger-fabric.readthedocs.io/en/release-2.5/prereqs.html#</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NoSQL 데이터 모델링]]></title>
            <link>https://velog.io/@black_birds_beak/NoSQL-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81</link>
            <guid>https://velog.io/@black_birds_beak/NoSQL-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81</guid>
            <pubDate>Mon, 23 Oct 2023 02:41:41 GMT</pubDate>
            <description><![CDATA[<h2 id="nosql의-모델링-방법">NoSQL의 모델링 방법</h2>
<p> NoSQL 데이타 모델링이란, NoSQL에 저장할 데이터들의 구조를 설계하는 것을 의미한다.</p>
<p>기존의 RDBMS와는 근본적인 2가지 사상의 변경이 필요하다.</p>
<blockquote>
<ol>
<li><p>객체 모델 지향에서 쿼리 결과 지향 모델링</p>
<p>RDBMS 모델링 기법은 저장할 도메인 모델을 분석한 후, 관계를 식별하고 테이블을 이용하여 쿼리를 구현. 그에 따른 결과를 뽑아내는 방식이다.</p>
<p>하지만, NoSQL의 경우에는 이 방법을 역순으로 진행해야 한다.</p>
</li>
<li><p>정규화(Nomalization)에서 비정규화(Denormalization)</p>
<p>RDBMS 모델링에서는 데이터의 일관성과 도메인 모델과의 일치를 위해 데이터 모델을 정규화한다.</p>
<p>하지만, NoSQL의 경우 쿼리의 효율성을 위해서 데이터를 정규화하지 않고, 의도적으로 중복된 데이터를 저장하는 등의 데이터 모델 설계 방식으로 접근한다.</p>
</li>
</ol>
</blockquote>
<h2 id="nosql-데이터-모델링-절차">NoSQL 데이터 모델링 절차</h2>
<h3 id="1도메인-모델-파악">1.도메인 모델 파악</h3>
<p>저장하고자하는 도메인을 파악한다. NoSQL도 DB이고 저장할 데이터에 대한 명확한 이해가 없이는 제대로된 모델이 나올 수 없다.</p>
<h3 id="2쿼리-결과-디자인-데이터-출력형태-디자인">2.쿼리 결과 디자인 (데이터 출력형태 디자인)</h3>
<p>쿼리 결과를 디자인 한다. <code>도메인 모델</code>을 기반으로 Application에 의해서 쿼리되는 결과값을 먼저 정한다. 필요로 하는 출력 형식을 기반으로 필요로 하는 쿼리를 선정하고 내용을 정리한다.</p>
<h3 id="3패턴을-이용한-데이터-모델링">3.패턴을 이용한 데이터 모델링</h3>
<p>NoSQL은 RDBMS에서 흔히 사용하는 Sorting, Grouping, Join 등의 기능을 사용할 수 없다. 그렇기 때문에 Put / Get으로만 데이터를 가지고 올 수 있는 형태로 NoSQL의 데이터 모델을 재 정의를 해야한다.</p>
<p>데이터를 가급적으로 중복으로 저장(Demoralization)하여 한 번의 요청으로 데이터를 가급적 많은 정보를 가져와 데이터를 읽어오는 횟수를 줄이도록 한다.</p>
<p>특히 Key 값을 활용하여 여러가지 기능을 만들 수 있다. 각각의 userID나 postID 등의 값을 <code>:</code> 등의 구분자를 활용하여 Join, Orderring 기능으로 사용하는 것이 가능하다.</p>
<h3 id="4최적화를-위한-필요-기능들을-리스트화">4.최적화를 위한 필요 기능들을 리스트화</h3>
<p>각 저장할 데이터의 형태에 따라서 Document Store, Ordered Key 형태 등을 적용하여 저장한다.</p>
<h3 id="5후보-nosql을-선정-및-테스트">5.후보 NoSQL을 선정 및 테스트</h3>
<p>데이터의 성격과 업무의 목적에 맞는 다수의 NoSQL을 선정하여 사용</p>
<h3 id="6데이터-모델을-선정된-nosql에-최적화-및-하드웨어-디자인">6.데이터 모델을 선정된 NoSQL에 최적화 및 하드웨어 디자인</h3>
<p>선정된 NoSQL을 기반으로 데이터 모델을 최적화. 이에 맞는 Application 인터페이스 설계와 하드웨어에 대한 디자인을 진행.</p>
<h2 id="nosql-모델링-패턴">NoSQL 모델링 패턴</h2>
<p>기본적인 데이터 모델링</p>
<ol>
<li><p>Denormalization
같은 데이터를 중복해서 저장하는 방식. 테이블간의 Join을 없애 1 번의 IO로 데이터를 조회할 수 있게 만든다. Join 쿼리를 여러 번 수행하지 않기 때문에 성능의 향상과 쿼리 로직의 단순함 효과를 가져온다. 하지만 데이터를 중복해서 저장하기 때문에 일관성 문제가 발생할 수 있고 스토리지의 용량이 증가하게 된다.</p>
</li>
<li><p>Aggregation
NoSQL이 Key만 같다면, 각 Row의 값이 규격화되지 않은 것을 이용하여 각각 틀린 구조의 데이터를 저장한다. 하나의 데이터에 모든 정보를 저장하므로 Join의 수를 줄여 Query 성능을 높인다.</p>
</li>
<li><p>Application Side Join
어쩔 수 없이 join을 시도해야하는 경우, Application 단에서 Join이 필요한 테이블의 수 만큼 IO를 진행한다. IO가 발생하는 횟수가 많아 부담이 될 수 있지만, 스토리지 사용량을 절약할 수 있다.</p>
</li>
</ol>
<p>확장된 데이터 모델링</p>
<ol>
<li>Atomic aggregation
<img src="https://velog.velcdn.com/images/black_birds_beak/post/88d59b85-3d1a-4e4b-86a6-a27d66d077b4/image.png" alt=""></li>
</ol>
<p>여러 테이블에 업데이트를 시도할 경우, 데이터의 일관성에 문제가 발생할 수 있다. 이러한 문제를 방지하기 위해 테이블을 하나로 합쳐서 관리를 하는 모델링을 말한다. NoSQL도 하나의 테이블에 대해서는 Atomic operation을 보장하기 때문에 트랜잭션을 보장받을 수 있어, 하나의 트랜잭션에 여러 개의 테이블로 나눠서 데이터를 보관하고 있을 경우, 하나의 테이블로 관리하여 정보를 변경하도록 한다.</p>
<ol start="2">
<li>Index table
<img src="https://velog.velcdn.com/images/black_birds_beak/post/50dea158-33a7-4bf1-bf72-7a8eb6216f97/image.png" alt=""></li>
</ol>
<p>NoSQL은 RDBMS처럼 index가 없기 때문에, Key 이외의 속성값을 이용하여 조회를 하면, 전체 Table을 전부 조회하거나 아예 조회가 불가능하다.</p>
<p>이러한 문제를 해결하기 위한 방법으로 Index를 위한 별도의 Index Table을 만들어서 사용하는 경우를 말한다.</p>
<ol start="3">
<li>CompositeKey
<img src="https://velog.velcdn.com/images/black_birds_beak/post/81587ac8-325d-448a-b127-4d596b7ec0e0/image.png" alt=""></li>
</ol>
<p>하나 이상의 필드를 구분자를 이용하여 구분지어 사용하는 방법으로 RDBMS의 복합키 (Composite primary Key)와 같은 개념으로 볼 수 있다.</p>
<ol start="4">
<li>Inverted search index
<img src="https://velog.velcdn.com/images/black_birds_beak/post/433bf7ea-ab16-40e8-a9ae-b361d5991397/image.png" alt=""></li>
</ol>
<p>이 기법은 모델링이라기 보다는 데이터 처리 패턴에 조금 더 가깝다. 인덱스를 사용하여 특정한 기준에 맞춰 데이터를 찾도록 원본의 데이터의 표현 또는 전체 검색을 할 때 사용 된다.</p>
<h3 id="계층형-데이터-모델링">계층형 데이터 모델링</h3>
<h4 id="1-tree-aggregation">1. Tree Aggregation</h4>
<p><img src="https://highlyscalable.files.wordpress.com/2012/02/tree-aggregation.png?w=805" alt="Tree-aggregation 예시"></p>
<p>하나의 단일 Document 형태로 Tree구조 혹은 간단한 그래프 구조를 나타내도록 하는 방식을 말한다. 한 번에 블로그 글을 조회하거나 하는 방식에 매우 효과적이지만 내용을 자주 변경하거나 임의로 엑세스 하는 과정이 들어가는 경우 비효율적인 저장 방식이다.</p>
<blockquote>
<p>적용 가능 : Key-Value Stores, Document Databases</p>
</blockquote>
<h4 id="2-adjacency-lists">2. Adjacency Lists</h4>
<p>Adjancency List는 각 노드가 직접적으로 부모 노드, 또는 자식 노드의 배열을 포함하여 서로 의존적이지 않는 형태의 그래프 모델링입니다.</p>
<p>부모나 자식 노드의 식별자를 활용하여 검색할 수 있다. 하나의 노드로 전체 하위 노드의 정보를 가져오는데는 비효율적인 방법이다.</p>
<blockquote>
<p>적용 가능 : Key-Value Stores, Document Databases</p>
</blockquote>
<h4 id="3-materialized-paths">3. Materialized Paths</h4>
<p><img src="https://highlyscalable.files.wordpress.com/2012/02/materialized-paths2.png" alt="Materialized Path 예시"></p>
<p>Meterialized Paths는 트리 구조처럼 순회하는 것을 방지해주는 방법이다. 각 노드는 각자 부모 또는 자식노드를 특정지을 수 있도록 만들어져 있다. 이러한 방식의 구조는 이 방법은 계층 구조를 무계층 문서로 변환할 수 있기 때문에 전체 텍스트 검색 엔진에 특히 좋은 성능을 발휘합니다. </p>
<blockquote>
<p>적용 가능 : Key-Value Stores, Document Databases, Search Engines</p>
</blockquote>
<h4 id="4-nested-sets">4. Nested Sets</h4>
<p><img src="https://highlyscalable.files.wordpress.com/2012/02/nested-sets.png?w=805" alt="Nested Sets"></p>
<p>Nested Sets은 트리 같은 구조에서 기본적으로 사용이 되는 방법이다. 관계형 DB에서 넓게 사용이 되지만, Key-Value와 Document DB 에서도 완벽하게 사용하는 것이 가능하다. 이러한 구조는 그림과 같이 트리 구조의 리프를 배열에 저장하고 시작, 끝 인덱스를 사용하여 각 리프의 범위에 매핑하는 방식이다.</p>
<p>데이터의 양이 변하지 않는 상황일 경우 유용한 구조가 될 수 있지만 자주 삽입, 변경, 삭제가 일어나는 데이터의 경우에는 부적절한 방법이다.</p>
<blockquote>
<p>적용 가능 : Key-Value Stores, Document Databases</p>
</blockquote>
<h2 id="출처">출처</h2>
<ul>
<li><a href="https://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/">https://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/</a></li>
<li><a href="https://www.analyticsvidhya.com/blog/2022/07/nosql-data-modeling-technique/">https://www.analyticsvidhya.com/blog/2022/07/nosql-data-modeling-technique/</a></li>
<li><a href="https://bcho.tistory.com/666">https://bcho.tistory.com/666</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NoSQL에 대한 설명과 종류]]></title>
            <link>https://velog.io/@black_birds_beak/NoSQL%EC%97%90-%EB%8C%80%ED%95%9C-%EC%84%A4%EB%AA%85%EA%B3%BC-%EC%A2%85%EB%A5%98</link>
            <guid>https://velog.io/@black_birds_beak/NoSQL%EC%97%90-%EB%8C%80%ED%95%9C-%EC%84%A4%EB%AA%85%EA%B3%BC-%EC%A2%85%EB%A5%98</guid>
            <pubDate>Wed, 17 May 2023 06:46:43 GMT</pubDate>
            <description><![CDATA[<h2 id="nosql">NoSQL?</h2>
<p>사람에 따라 No SQL, Not Only SQL, Non-Relational Operational Database SQL로 엇갈리는 의견들이 있지만, 현재 Not Only SQL로 풀어 설명하는 것이 다수를 차지하고 있다.</p>
<p>기존의 RDBMS보다 더 융통성이 있는 모델을 사용하고 데이터의 저장 및 검색을 위한 특화된 매커니즘을 제공한다. 이를 통해서 NoSQL DB는 단순 검색 및 추가 작업에 있어서 매우 최적화된 Key-Value 저장 기법을 사용하여 응답속도, 처리 효율 등에 있어 뛰어난 성능을 보여준다.</p>
<h2 id="rdbms와-nosql의-차이">RDBMS와 NoSQL의 차이</h2>
<p>NoSQL이 DBMS라고 생각해서 RDBMS와 같은, 또는 떨어지지만 유사한 기능을 제공할것이라고 생각하면 큰 오산이다. <code>NoSQL은 데이타를 저장한다. 그리고 Key에 대한 Put/Get만 지원한다.</code> RDBMS로 치자면</p>
<blockquote>
<p>Put : Insert into TABLE VALUES(KEY,value1,value2,…,valuen)</p>
<p>Get : Select * from TABLE where KEY=<em>”key”</em></p>
</blockquote>
<p>만 딱 지원한다. 물론 제품에 따라서 기능에 대한 지원 범위는 다르기는 하지만, 공통적으로 고민해야 하는 기능은</p>
<ul>
<li>Sorting (SQL의 Order By)</li>
<li>Join (RDBMS에서 두개의 Table을 Foreign Key를 이용하여 join)</li>
<li>Grouping (SQL문의 group by)</li>
<li>Range Query (where key&gt;”start” and key&lt;”end” 와 같이 일정 범위내의 내용을 쿼리해오는 기능)</li>
<li>Index (RDBMS에 Index를 지정하여 select query 성능을 높이는 기능)</li>
</ul>
<p>이다. RDBMS에서는 너무나도 익숙하게 사용했던 기능들이기 때문에, 막상 이 기능들을 빼고 데이타를 다루고자 하면 매우불편하다. </p>
<h2 id="nosql-db의-종류">NoSQL DB의 종류</h2>
<p>NoSQL DB는 각 4가지 형태가 존재합니다. 각 DB는 형태에 따라 각자의 특성이 있어 상황에 맞게 DB를 선택을 해야한다.</p>
<blockquote>
<p>NoSQL DB의 종류</p>
<ul>
<li>Kye-value</li>
<li>Document based</li>
<li>Column based</li>
<li>Graph-based</li>
</ul>
</blockquote>
<h3 id="1-key-value-store">1. Key-value store</h3>
<p><img src="https://editor.analyticsvidhya.com/uploads/29281key-value-database.png" alt="Key-Value DB"></p>
<p>Key와 Value 값으로 이루어진 데이터를 저장하며, 저장과 조회라는 원칙에 가장 충실한 형태의 DB이다.</p>
<p>Key-Value DB의 Key값은 고유한 값을 유지하고 있어야 하며, Value에 모든 데이터 타입을 허용하므로, 개발자들은 데이터 입력 단계에서 검증로직 구현을 제대로 구현해야 한다.</p>
<h3 id="2-document-based-store">2. Document based store</h3>
<p><img src="https://editor.analyticsvidhya.com/uploads/32761document-database.png" alt="Document base"></p>
<p>Key-Value DB처럼 Key와 value 값을 저장하지만 Document는 Value값을 smi-structured entity (JSON, XML) 같은 표준 형식을 저장한다.</p>
<p>값을 저장하기 전에 schema를 별도로 정의하지 않으며, Document를 추가하면 그게 schema가 된다. </p>
<p>각 Document는 별도의 필드를 가질 수 있으며 개발자가 Application에서 데이터를 입력하는 단계에서 컬럼과 필드의 관리가 제대로 이루어지도록 보장하는 것이 중요하다. 예를 들면 필수 속성에 대한 관리도 Applicaiton 레벨에서 관리가 되어야 한다.</p>
<p>Document DB는 다음과 같은 목적으로 주로 사용된다.</p>
<blockquote>
<ol>
<li>대용량 데이터를 읽고 쓰는 웹 사이트용 백엔드</li>
<li>제품처럼 다양한 속성이 있는 데이터 관리</li>
<li>다양한 유형의 메타데이터 추적</li>
<li>JSON 데이터 구조를 사용하는 Application</li>
<li>비정규화된 중첩 구조의 데이터를 사용하는 Application</li>
</ol>
</blockquote>
<h3 id="3-column-based-store">3. Column based store</h3>
<p><img src="https://editor.analyticsvidhya.com/uploads/84509column-oriented-database.png" alt="Column based"></p>
<p>RDBMS 같이 행에 중점을 두는 것이 아닌 열에 중점을 둔 방식.</p>
<p>비슷한 속성 값, 같은 특성을 가진 대량의 데이터를 계산해야 하는 데이터 웨어하우스 등에 적합한 방식</p>
<h3 id="4-graph-based-store">4. Graph based store</h3>
<p><img src="https://editor.analyticsvidhya.com/uploads/90603graph-database-example.png" alt="Graph-based"></p>
<p>데이터를 노드로 표현하며 노드 사이의 관계를 엣지로 표현하는 방식의 DB.</p>
<p>RDBMS보다 퍼포먼스가 좋고 유연하며 유지보수가 용이함.</p>
<h2 id="참고자료">참고자료</h2>
<ul>
<li><a href="https://www.samsungsds.com/kr/insights/1232564_4627.html">https://www.samsungsds.com/kr/insights/1232564_4627.html</a></li>
<li><a href="https://www.analyticsvidhya.com/blog/2022/07/nosql-data-modeling-technique/">https://www.analyticsvidhya.com/blog/2022/07/nosql-data-modeling-technique/</a></li>
<li><a href="https://bcho.tistory.com/665">https://bcho.tistory.com/665</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[코딩테스트 연습 - 호텔 대실]]></title>
            <link>https://velog.io/@black_birds_beak/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%97%B0%EC%8A%B5-%ED%98%B8%ED%85%94-%EB%8C%80%EC%8B%A4</link>
            <guid>https://velog.io/@black_birds_beak/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%97%B0%EC%8A%B5-%ED%98%B8%ED%85%94-%EB%8C%80%EC%8B%A4</guid>
            <pubDate>Mon, 27 Mar 2023 17:37:57 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>호텔을 운영 중인 코니는 최소한의 객실만을 사용하여 예약 손님들을 받으려고 합니다. 한 번 사용한 객실은 퇴실 시간을 기준으로 10분간 청소를 하고 다음 손님들이 사용할 수 있습니다.
예약 시각이 문자열 형태로 담긴 2차원 배열 book_time이 매개변수로 주어질 때, 코니에게 필요한 최소 객실의 수를 return 하는 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<ul>
<li><p>1 ≤ book_time의 길이 ≤ 1,000</p>
</li>
<li><p>book_time[i]는 [&quot;HH:MM&quot;, &quot;HH:MM&quot;]의 형태로 이루어진 배열입니다.</p>
<ul>
<li>[대실 시작 시각, 대실 종료 시각] 형태입니다.</li>
</ul>
</li>
<li><p>시각은 HH:MM 형태로 24시간 표기법을 따르며, &quot;00:00&quot; 부터 &quot;23:59&quot; 까지로 주어집니다.</p>
<ul>
<li>예약 시각이 자정을 넘어가는 경우는 없습니다.</li>
<li>시작 시각은 항상 종료 시각보다 빠릅니다.</li>
</ul>
</li>
</ul>
<h3 id="풀이">풀이</h3>
<h4 id="전체-과정">전체 과정</h4>
<ol>
<li>문자열을 비교할 수 있는 형태로 변환한다.</li>
<li>입실 시간을 기준으로 정렬한다.</li>
<li>순서대로 투숙 입실 시간이 직전 퇴실 시간 + 10분 보다 넘어 날 경우, 
값을 하나씩 추가한다.</li>
</ol>
<h4 id="문자열-변환">문자열 변환</h4>
<pre><code class="language-cpp">int convertToInt(string s) {
  int hour = stoi(s.substr(0, 2));
  int minute = stoi(s.substr(3, 2));

  return hour * 100 + minute;
}

int convertToStratTime(string s) { return convertToInt(s); }

int convertToEndTime(string s) {
  int timeInfo = convertToInt(s);

  if (50 &lt;= timeInfo % 100) {
    timeInfo += 100;
    timeInfo -= 50;
  } else {
    timeInfo += 10;
  }

  return timeInfo;
}
</code></pre>
<p><code>convertToInt</code> 함수를 통해서 문자열을 비교할 수 있는 정수형으로 변환하였다.
투숙이 가능한 시간은 퇴실시간 + 10분이므로 종료시간의 경우, <code>convertToEndTime</code> 함수를 통해서 10분을 추가하여 반환하도록 한다.</p>
<h4 id="정렬">정렬</h4>
<pre><code class="language-cpp">sort(booking_info.begin(), booking_info.end());</code></pre>
<p>저장한 pair의 first 값을 기준으로 오름차순으로 정렬한다.</p>
<h4 id="비교-후-값-반환">비교 후 값 반환</h4>
<pre><code class="language-cpp">  vector&lt;pair&lt;int, int&gt;&gt; room;

  for(pair&lt;int, int&gt; booking_time : booking_info) {
    vector&lt;pair&lt;int, int&gt;&gt; temp;
    int start = booking_time.first;

    for(pair&lt;int, int&gt; using_room : room) {
      if(start &lt; using_room.second) {
        temp.push_back(using_room);
      }
    }

    room = temp;
    room.push_back(booking_time);
    answer = answer &lt;= room.size() ? room.size() : answer;
  }</code></pre>
<p>정렬한 vector를 순회하며 비교한다. 새로 들어오는 입실 시간과 기존에 저장이 되어있는 퇴실시간을 비교하여 입실이 가능한 정보만 저장하여 기존 정보를 저장하는 vector를 갱신한다.</p>
<p>최종적으로, vector의 사이즈를 결과로 반환한다.</p>
<h3 id="전체-코드">전체 코드</h3>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

using namespace std;


int convertToInt(string s) {
  int hour = stoi(s.substr(0, 2));
  int minute = stoi(s.substr(3, 2));

  return hour * 100 + minute;
}

int convertToStratTime(string s) { return convertToInt(s); }

int convertToEndTime(string s) {
  int timeInfo = convertToInt(s);

  if (50 &lt;= timeInfo % 100) {
    timeInfo += 100;
    timeInfo -= 50;
  } else {
    timeInfo += 10;
  }

  return timeInfo;
}

int solution(vector&lt;vector&lt;string&gt;&gt; book_time) {
  int answer = 0;

  vector&lt;pair&lt;int, int&gt;&gt; booking_info;

  for (vector&lt;string&gt; time_info : book_time) {
    int startTime = convertToStratTime(time_info[0]);
    int endTime = convertToEndTime(time_info[1]);

    booking_info.push_back(make_pair(startTime, endTime));
  }

  sort(booking_info.begin(), booking_info.end());

  vector&lt;pair&lt;int, int&gt;&gt; room;

  for(pair&lt;int, int&gt; booking_time : booking_info) {
    vector&lt;pair&lt;int, int&gt;&gt; temp;
    int start = booking_time.first;

    for(pair&lt;int, int&gt; using_room : room) {
      if(start &lt; using_room.second) {
        temp.push_back(using_room);
      }
    }

    room = temp;
    room.push_back(booking_time);
    answer = answer &lt;= room.size() ? room.size() : answer;
  }
  return answer;
}
</code></pre>
<h4 id="출처">출처</h4>
<ul>
<li><a href="https://school.programmers.co.kr/">프로그래머스</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Go 언어로 API 서버 만들기]]></title>
            <link>https://velog.io/@black_birds_beak/Go-%EC%96%B8%EC%96%B4%EB%A1%9C-API-%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@black_birds_beak/Go-%EC%96%B8%EC%96%B4%EB%A1%9C-API-%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 20 Mar 2023 07:59:17 GMT</pubDate>
            <description><![CDATA[<h2 id="발단">발단</h2>
<p>Hyperledger Fabric의 Chaincode를 개발할 상황이 생기면서 Go언어를 사용할 일이 생겼다. 
한 번쯤 배우고 싶었던 언어이고 필요성이 생긴 만큼 간단한 API 서버 개발을 통해 Go언어에 익숙해지기 위해서 간단한 API 서버를 개발해봤다.</p>
<h2 id="진행-과정">진행 과정</h2>
<h3 id="개발-환경-구축">개발 환경 구축</h3>
<h4 id="사전-준비">사전 준비</h4>
<ul>
<li><a href="https://code.visualstudio.com/download">VS Code</a></li>
<li><a href="https://docs.docker.com/engine/install/ubuntu/">Docker</a></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers">Remote Development</a></li>
<li><a href="https://www.postgresql.org/">Postgre SQL</a></li>
</ul>
<h3 id="개발-과정">개발 과정</h3>
<h4 id="model-설정">model 설정</h4>
<pre><code class="language-go">package model

type Message struct {
    ID  string `json:&quot;id&quot;`
    Msg string `json:&quot;msg&quot;`
}</code></pre>
<p>DB에 저장하기 위한 데이터 형태를 선언한 부분이다. 데이터는 ID 값과 메시지 값을 저장할 수 있게 선언했다. </p>
<h4 id="db-연결-부분">db 연결 부분</h4>
<pre><code class="language-go">package db

import (
    &quot;database/sql&quot;
    &quot;fmt&quot;

    model &quot;example.com/practice/model&quot;
    _ &quot;github.com/lib/pq&quot;
)

const (
    host     = &quot;localhost&quot;
    port     = 5432
    user     = &quot;postgres&quot;
    password = &quot;postgres&quot;
    dbname   = &quot;postgres&quot;
)

func connect() *sql.DB {
    psqlInfo := fmt.Sprintf(&quot;host=%s port=%d user=%s &quot;+
        &quot;password=%s dbname=%s sslmode=disable&quot;,
        host, port, user, password, dbname)

    dbcon, err := sql.Open(&quot;postgres&quot;, psqlInfo)

    if err != nil || dbcon.Ping() != nil {
        panic(err.Error())
    }

    return dbcon
}

func GetMessage(id string) []model.Message {
    db := connect()
    selectDynStmt := `SELECT id, msg FROM Message where &quot;id&quot;=$1`
    rows, err := db.Query(selectDynStmt, id)

    messages := []model.Message{}

    checkError(err)
    defer db.Close()
    defer rows.Close()

    for rows.Next() {
        var msg model.Message
        if err := rows.Scan(&amp;msg.ID, &amp;msg.Msg); err != nil {
            panic(err)
        }

        messages = append(messages, msg)
    }

    return messages
}

func SaveMessage(msg model.Message) sql.Result {
    db := connect()
    if db.Ping() != nil {
        panic(db.Ping().Error())
    }
    insertDynStmt := `insert into Message (&quot;id&quot;, &quot;msg&quot;) values($1, $2)`
    result, err := db.Exec(insertDynStmt, msg.ID, msg.Msg)
    checkError(err)
    defer db.Close()
    return result
}

func UpdateMessage(msg model.Message) sql.Result {
    db := connect()
    if db.Ping() != nil {
        panic(db.Ping().Error())
    }

    updateDynStmt := `update Message set &quot;msg&quot;=$1 where &quot;id&quot;=$2`
    result, err := db.Exec(updateDynStmt, msg.Msg, msg.ID)

    checkError(err)
    defer db.Close()

    return result
}

func DeleteMessage(id string) sql.Result {
    db := connect()
    if db.Ping() != nil {
        panic(db.Ping().Error())
    }

    deleteDynStmt := `delete from Message where &quot;id&quot;=$1`
    result, err := db.Exec(deleteDynStmt, id)
    checkError(err)

    defer db.Close()

    return result
}

func checkError(err error) {
    if err != nil {
        panic(err)
    }

}</code></pre>
<p>PostgreSQL과 연결하고 간단한 CRUD 작업을 하는 부분입니다.</p>
<h4 id="routing-설정">routing 설정</h4>
<pre><code class="language-go">package router

import (
    &quot;log&quot;
    &quot;net/http&quot;

    db &quot;example.com/practice/db&quot;
    model &quot;example.com/practice/model&quot;
    &quot;github.com/gin-gonic/gin&quot;
)

func StartRouter() {
    router := gin.Default()

    router.GET(&quot;/message/:id&quot;, func(c *gin.Context) {
        id := c.Param(&quot;id&quot;)
        result := db.GetMessage(id)
        c.IndentedJSON(http.StatusOK, result)
    })

    router.POST(&quot;/message&quot;, func(c *gin.Context) {
        var newMessage model.Message

        if err := c.BindJSON(&amp;newMessage); err != nil {
            log.Fatal(&quot;BIND JSON ERROR&quot;)
            return
        }

        result := db.SaveMessage(newMessage)
        c.IndentedJSON(http.StatusCreated, result)
    })

    router.PUT(&quot;/message&quot;, func(c *gin.Context) {
        var updateMessage model.Message

        if err := c.BindJSON(&amp;updateMessage); err != nil {
            log.Fatal(&quot;BIND JSON ERROR&quot;)
            return
        }

        result := db.UpdateMessage(updateMessage)
        c.IndentedJSON(http.StatusAccepted, result)
    })

    router.DELETE(&quot;/message&quot;, func(c *gin.Context) {
        var deleteMessage model.Message

        if err := c.BindJSON(&amp;deleteMessage); err != nil {
            log.Fatal(&quot;BIND JSON ERROR&quot;)
            return
        }

        result := db.DeleteMessage(deleteMessage.ID)
        c.IndentedJSON(http.StatusAccepted, result)
    })

    router.Run(&quot;localhost:8080&quot;)
}
</code></pre>
<p>간단한 REST API를 routing 하는 부분입니다.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://github.com/Apisapple/practice_project_golang">전체 소스 코드</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[HHH000327: Error performing load command 해결하기]]></title>
            <link>https://velog.io/@black_birds_beak/HHH000327-Error-performing-load-command-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@black_birds_beak/HHH000327-Error-performing-load-command-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 08 May 2022 11:56:04 GMT</pubDate>
            <description><![CDATA[<p>Spring을 활용하여 개인 프로젝트의 back end 서버를 개발하면서 조회 기능을 만들던 중에 다음과 같은 에러를 확인했다.</p>
<p><img src="https://velog.velcdn.com/images/black_birds_beak/post/fc91d3b8-e79d-4158-9390-b1d00385ad94/image.png" alt="에러 로그화면"></p>
<p>에러는 2개의 Entity가 <code>@OneToMany</code>로 연결이 되었을 때, 조회를 하는 과정에서 발생했던 문제였다. 에러가 발생했던 코드와 두개의 Entity는 다음과 같은 형태였다.</p>
<h4 id="error가-발생했던-함수">Error가 발생했던 함수</h4>
<pre><code class="language-java">  public ChartDataDto getChartDataById(Long id) throws ChartDataException {
    ChartData chartData = chartDataRepository.findById(id).orElseThrow(
        () -&gt; new ChartDataException(ChartDataErrorCode.CANNOT_FOUND_CHART_DATA.getErrorCode(),
            ChartDataErrorCode.CANNOT_FOUND_CHART_DATA.getMsg())
    );
    log.info(&quot;chart_data information {}&quot;, chartData.toString());
    for (var dataCategory : chartData.getDataCategories()) {
      log.info(&quot;Category information : {}&quot;, dataCategory.getCategory().toString());
    }
    return new ChartDataDto(chartData);
  }</code></pre>
<h4 id="chartdata-entity">ChartData Entity</h4>
<pre><code class="language-java">@Getter
@Entity
@NoArgsConstructor
public class ChartData {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @OneToMany(mappedBy = &quot;chartData&quot;)
  private List&lt;DataCategory&gt; dataCategories;

  private int money;

  private String itemName;

  private String memo;

  private LocalDateTime localDateTime;

  @Builder
  public ChartData(Long id, int money, String itemName, String memo, LocalDateTime localDateTime){
    this.id = id;
    this.money = money;
    this.itemName = itemName;
    this.memo = memo;
    this.localDateTime = localDateTime;
    this.dataCategories = new ArrayList&lt;&gt;();
  }

  public void setDataCategory(List&lt;DataCategory&gt; dataCategory) {
    this.dataCategories = dataCategory;
  }

  public void updateChartData(ChartDataDto chartDataDto) {
    this.money = chartDataDto.getMoney();
    this.itemName = chartDataDto.getItemName();
  }

  public void addDataCategory(DataCategory dataCategory) {
    this.dataCategories.add(dataCategory);
  }

  public ChartData(ChartDataDto chartDataDto) {
    this.money = chartDataDto.getMoney();
    this.localDateTime = chartDataDto.getLocalDateTime();
    this.memo = chartDataDto.getMemo();
    this.itemName = chartDataDto.getItemName();
  }

  @Override
  public String toString() {
    return getClass().getSimpleName() + &quot;(&quot; +
        &quot;id = &quot; + id + &quot;, &quot; +
        &quot;money = &quot; + money + &quot;, &quot; +
        &quot;itemName = &quot; + itemName + &quot;, &quot; +
        &quot;memo = &quot; + memo + &quot;, &quot; +
        &quot;localDateTime = &quot; + localDateTime + &quot;)&quot;;
  }
}</code></pre>
<h4 id="datacategory-entity">DataCategory Entity</h4>
<pre><code class="language-java">@Builder
@Getter
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class DataCategory {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @ManyToOne(fetch = FetchType.LAZY)
  private ChartData chartData;

  @ManyToOne(fetch = FetchType.LAZY)
  private Category category;

  public static DataCategory createDataCategory(ChartData chartData, Category category) {
    DataCategory dataCategory = new DataCategory();
    dataCategory.setCategory(category);
    dataCategory.setChartData(chartData);
    return dataCategory;
  }

  public static DataCategory createDataCategory(ChartData chartData) {
    DataCategory dataCategory = new DataCategory();
    dataCategory.setChartData(chartData);
    return dataCategory;
  }

  public static DataCategory createDataCategory(Category category) {
    DataCategory dataCategory = new DataCategory();
    dataCategory.setCategory(category);
    return dataCategory;
  }

  public void setChartData(ChartData chartData) {
    this.chartData = chartData;
    chartData.addDataCategory(this);
  }

  public void setCategory(Category category) {
    this.category = category;
    category.addDataCategory(this);
  }

  @Override
  public String toString() {
    return getClass().getSimpleName() + &quot;(&quot; +
        &quot;id = &quot; + id + &quot;)&quot;;
  }
}</code></pre>
<p>테스트를 하는 과정에서 <code>chartData.getDataCategories()</code> 함수를 실행하는 과정에서 발생했고 이런 문제가 발생했던 이유는 <code>@ManyToOne(fetch = FetchType.LAZY)</code> 때문이었다.
<code>ManyToOne(fetch = FetchType.LAZY)</code> 를 <code>ManyToOne(fetch = FetchType.EAGAR)</code> 변경함으로써 에러를 해결할 수 있었다.
여기서의 FetchType은 <code>LAZY</code>와 <code>EAGAR</code>이 있으며 각각 지연로딩과 즉시로딩을 의미한다.</p>
<h3 id="지연로딩lazy">지연로딩(LAZY)</h3>
<ul>
<li>데이터를 조회할 때, <code>필요할 때</code> 연관된 데이터를 불러오는 형태</li>
<li>연관된 엔티티를 프록시로 조회한다. 프록시를 실제로 사용할 때 초기화를 진행하면서 DB를 조회한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/black_birds_beak/post/c15329ca-d0a4-4634-b8a9-320f99a1a797/image.png" alt="지연로딩"></p>
<h3 id="즉시로딩eagar">즉시로딩(EAGAR)</h3>
<ul>
<li>데이터를 조회할 때, 연관된 데이터를 <code>한 번에 불러오는</code> 형태</li>
<li>Hibernate는 가능하면 <code>SQL JOIN</code>을 사용해서 한 번에 조회한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/black_birds_beak/post/07982986-6054-4802-b372-e9c3b59adf41/image.png" alt="즉시 로딩"></p>
]]></description>
        </item>
    </channel>
</rss>