<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>cat_dev</title>
        <link>https://velog.io/</link>
        <description>devlog</description>
        <lastBuildDate>Wed, 04 May 2022 00:23:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>cat_dev</title>
            <url>https://images.velog.io/images/dandev_sw/profile/9361fa6e-f64a-44a6-89ce-1d39eaf14aca/meow.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. cat_dev. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dandev_sw" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[캡스톤디자인B] 비상장주식거래 플랫폼에 필요한 Rust 컨트랙트 작성하기!]]></title>
            <link>https://velog.io/@dandev_sw/%EC%BA%A1%EC%8A%A4%ED%86%A4%EB%94%94%EC%9E%90%EC%9D%B8B-%EB%B9%84%EC%83%81%EC%9E%A5%EC%A3%BC%EC%8B%9D%EA%B1%B0%EB%9E%98-%ED%94%8C%EB%9E%AB%ED%8F%BC%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-Rust-%EC%BB%A8%ED%8A%B8%EB%9E%99%ED%8A%B8-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dandev_sw/%EC%BA%A1%EC%8A%A4%ED%86%A4%EB%94%94%EC%9E%90%EC%9D%B8B-%EB%B9%84%EC%83%81%EC%9E%A5%EC%A3%BC%EC%8B%9D%EA%B1%B0%EB%9E%98-%ED%94%8C%EB%9E%AB%ED%8F%BC%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-Rust-%EC%BB%A8%ED%8A%B8%EB%9E%99%ED%8A%B8-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 04 May 2022 00:23:24 GMT</pubDate>
            <description><![CDATA[<p>내가 느낀 블록체인 개발의 가장 큰 장벽은 자세한/정확한 레퍼런스를 찾기 어렵다는 것이었다. 특히 한국어로 적혀있는 튜토리얼은 거의 없다고 보면 되어서, 적응하기 쉽지 않았다.
나는 졸업 프로젝트에서 블록체인 개발을 하며 NEAR 프로토콜의 NEAR University의 튜토리얼과 NEAR discord에 직접 질문하는 방식을 이용하여 막힌 부분들을 풀었다.</p>
<p>이 글이 하나의 좋은 한국어 개발 튜토리얼이 되길 바라며! 내가 사용한 Rust 스마트 컨트랙트 작성과 NEAR 테스트넷에 배포하는 방식을 적어보겠다.</p>
<h1 id="필요-사항">필요 사항</h1>
<ol>
<li>NEAR 테스트넷 계정</li>
<li>near-cli 설치</li>
<li>Rust tool chain 설치</li>
</ol>
<h1 id="컨트랙트-배포">컨트랙트 배포</h1>
<h2 id="사용-기술-설명">사용 기술 설명</h2>
<ol>
<li>Cargo 이용</li>
</ol>
<ul>
<li>rust는 node.js의 npm처럼 cargo라는 관리 툴을 이용한다.</li>
</ul>
<ol start="2">
<li>Rust는 어셈블리어인 wasm파일을 만든 후, 이를 블록체인에 배포하는 형식으로 진행한다.</li>
</ol>
<ul>
<li>rust로 작성된 컨트랙트를 wasm파일로 컴파일 한 후, 원하는 블록체인 네트워크에 배포하는 방식을 이용한다.<h2 id="절차">절차</h2>
<h3 id="step-1-레포지토리-만들기">STEP 1 레포지토리 만들기</h3>
<pre><code class="language-shell">$ cargo new &lt;나의 레포지토리 이름&gt;
$ cd &lt;레포지토리&gt;</code></pre>
</li>
<li>cargo의 new 명령어를 이용해 cargo 환경이 세팅된 레포지토리를 생성한다.<pre><code class="language-shell">.
├── Cargo.toml
└── src
 └── main.rs</code></pre>
</li>
<li>생성된 레포지토리의 구조는 다음과 같다.</li>
<li>src폴더 내부에 .rs 확장자로 되어있는 컨트랙트를 작성하고 배포하는 구조이다.<h3 id="step-2-스마트-컨트랙트-작성하기">STEP 2 스마트 컨트랙트 작성하기</h3>
스마트 컨트랙트를 계약 내용을 직접 명시하는 것이나 현실의 계약서와 같은 개념이라고 보면 개발할 때 많은 혼란이 있을 것이다. 스마트 컨트랙트는 하나의 인터페이스, 블록체인에 올라가는 데이터 틀을 만든다고 생각하면 더 편하게 이해할 수 있다. </li>
</ul>
<p>나는 NFT를 발행하고 거래하는 플랫폼을 개발 중이어서 NEAR의 nft표준인 <code>NEP-171</code> 표준을 따르는 스마트 컨트랙트를 작성했다. <code>NEP-171</code> 표준을 사용하여 컨트랙트를 작성하면 해당 표준을 공유하는 NFT간 공유가 가능하다.</p>
<ul>
<li>내가 만들고 있는 nft 플랫폼의 경우 nft를 발행하는 mint, nft의 개수를 세는 기능인 enumerable, nft 내부 정보를 관리하는 internal, nft에 이미지 등의 정보를 넣을 수 있는 metadata, NEP-171 표준을 명시하는 nft_core의 기능이 필요했다.</li>
<li>많은 기능을 한 컨트랙트에 넣을 수 있지만, 분리하면 frontend와의 상호작용에 매우 편리하므로, 분리해서 넣었다.</li>
</ul>
<h4 id="핵심기능-1-mintrs">핵심기능 1 mint.rs</h4>
<ul>
<li>nft를 발행하는 기능이 들어가는 컨트랙트이다.</li>
<li><code>nft_mint</code> 함수를 구현한다.</li>
<li>발행 시 유저가 정한 토큰 아이디와, 메타데이터, 로열티, 받는 사람과 토큰 타입을 저장한다.</li>
</ul>
<h4 id="전체-코드">전체 코드</h4>
<pre><code class="language-rust">use crate::*;

#[near_bindgen]
impl Contract {
    #[payable]
    pub fn nft_mint(
        &amp;mut self,
        token_id: Option&lt;TokenId&gt;,
        metadata: TokenMetadata,
        perpetual_royalties: Option&lt;HashMap&lt;AccountId, u32&gt;&gt;,
        receiver_id: Option&lt;ValidAccountId&gt;,
        token_type: Option&lt;TokenType&gt;,
    ) {

        let mut final_token_id = format!(&quot;{}&quot;, self.token_metadata_by_id.len() + 1);
        if let Some(token_id) = token_id {
            final_token_id = token_id
        }

        let initial_storage_usage = env::storage_usage();
        let mut owner_id = env::predecessor_account_id();
        if let Some(receiver_id) = receiver_id {
            owner_id = receiver_id.into();
        }

        // CUSTOM - create royalty map
        let mut royalty = HashMap::new();
        let mut total_perpetual = 0;
        // user added perpetual_royalties (percentage paid with every transfer)
        if let Some(perpetual_royalties) = perpetual_royalties {
            assert!(perpetual_royalties.len() &lt; 7, &quot;Cannot add more than 6 perpetual royalty amounts&quot;);
            for (account, amount) in perpetual_royalties {
                royalty.insert(account, amount);
                total_perpetual += amount;
            }
        }
        // royalty limit for minter capped at 20%
        assert!(total_perpetual &lt;= MINTER_ROYALTY_CAP, &quot;Perpetual royalties cannot be more than 20%&quot;);

        // CUSTOM - enforce minting caps by token_type 
        if token_type.is_some() {
            let token_type = token_type.clone().unwrap();
            let cap = u64::from(*self.supply_cap_by_type.get(&amp;token_type).expect(&quot;Token type must have supply cap.&quot;));
            let supply = u64::from(self.nft_supply_for_type(&amp;token_type));
            assert!(supply &lt; cap, &quot;Cannot mint anymore of token type.&quot;);
            let mut tokens_per_type = self
                .tokens_per_type
                .get(&amp;token_type)
                .unwrap_or_else(|| {
                    UnorderedSet::new(
                        StorageKey::TokensPerTypeInner {
                            token_type_hash: hash_account_id(&amp;token_type),
                        }
                        .try_to_vec()
                        .unwrap(),
                    )
                });
            tokens_per_type.insert(&amp;final_token_id);
            self.tokens_per_type.insert(&amp;token_type, &amp;tokens_per_type);
        }
        // END CUSTOM

        let token = Token {
            owner_id,
            approved_account_ids: Default::default(),
            next_approval_id: 0,
            royalty,
            token_type,
        };
        assert!(
            self.tokens_by_id.insert(&amp;final_token_id, &amp;token).is_none(),
            &quot;Token already exists&quot;
        );
        self.token_metadata_by_id.insert(&amp;final_token_id, &amp;metadata);
        self.internal_add_token_to_owner(&amp;token.owner_id, &amp;final_token_id);

        let new_token_size_in_bytes = env::storage_usage() - initial_storage_usage;
        let required_storage_in_bytes =
            self.extra_storage_in_bytes_per_token + new_token_size_in_bytes;

        refund_deposit(required_storage_in_bytes);
    }
}</code></pre>
<h4 id="핵심기능-2-nft_corers">핵심기능 2 nft_core.rs</h4>
<ul>
<li><code>NEP-171</code> 표준을 구현한다.</li>
<li>nft_transfer, nft_approve, nft_revoke등 nft를 거래하기 위해 필요한 함수를 구현한다.</li>
<li>코어의 경우 커스텀이 필요한 부분이 적어 <a href="https://github.com/near/NEPs/blob/master/specs/Standards/NonFungibleToken/Core.md">NEP-171 github</a>를 참고하여 작성하였다.<h4 id="전체-코드-1">전체 코드</h4>
<pre><code class="language-rust">use crate::*;
use near_sdk::json_types::{ValidAccountId};
use near_sdk::{ext_contract, log, Gas, PromiseResult};
</code></pre>
</li>
</ul>
<p>const GAS_FOR_NFT_APPROVE: Gas = 10_000_000_000_000;
const GAS_FOR_RESOLVE_TRANSFER: Gas = 10_000_000_000_000;
const GAS_FOR_NFT_TRANSFER_CALL: Gas = 25_000_000_000_000 + GAS_FOR_RESOLVE_TRANSFER;
const NO_DEPOSIT: Balance = 0;</p>
<p>pub trait NonFungibleTokenCore {
    fn nft_transfer(
        &amp;mut self,
        receiver_id: ValidAccountId,
        token_id: TokenId,
        approval_id: u64,
        memo: Option<String>,
    );</p>
<pre><code>  fn nft_payout(&amp;self, token_id: String, balance: U128, max_len_payout: u32) -&gt; Payout;

fn nft_transfer_payout(
    &amp;mut self,
    receiver_id: ValidAccountId,
    token_id: TokenId,
    approval_id: u64,
    memo: String,
    balance: U128,
    max_len_payout: u32,
) -&gt; Payout;

/// Returns `true` if the token was transferred from the sender&#39;s account.
fn nft_transfer_call(
    &amp;mut self,
    receiver_id: ValidAccountId,
    token_id: TokenId,
    approval_id: u64,
    memo: Option&lt;String&gt;,
    msg: String,
) -&gt; PromiseOrValue&lt;bool&gt;;

fn nft_approve(&amp;mut self, token_id: TokenId, account_id: ValidAccountId, msg: Option&lt;String&gt;);

fn nft_is_approved(
    &amp;self,
    token_id: TokenId,
    approved_account_id: AccountId,
    approval_id: Option&lt;u64&gt;,
) -&gt; bool;

fn nft_revoke(&amp;mut self, token_id: TokenId, account_id: ValidAccountId);

fn nft_revoke_all(&amp;mut self, token_id: TokenId);

fn nft_total_supply(&amp;self) -&gt; U128;

fn nft_token(&amp;self, token_id: TokenId) -&gt; Option&lt;JsonToken&gt;;</code></pre><p>}</p>
<p>#[ext_contract(ext_non_fungible_token_receiver)]
trait NonFungibleTokenReceiver {
    /// Returns <code>true</code> if the token should be returned back to the sender.
    fn nft_on_transfer(
        &amp;mut self,
        sender_id: AccountId,
        previous_owner_id: AccountId,
        token_id: TokenId,
        msg: String,
    ) -&gt; Promise;
}</p>
<p>#[ext_contract(ext_non_fungible_approval_receiver)]
trait NonFungibleTokenApprovalsReceiver {
    fn nft_on_approve(
        &amp;mut self,
        token_id: TokenId,
        owner_id: AccountId,
        approval_id: u64,
        msg: String,
    );
}</p>
<p>// TODO: create nft_on_revoke</p>
<p>#[ext_contract(ext_self)]
trait NonFungibleTokenResolver {
    fn nft_resolve_transfer(
        &amp;mut self,
        owner_id: AccountId,
        receiver_id: AccountId,
        token_id: TokenId,
        approved_account_ids: HashMap&lt;AccountId, u64&gt;,
    ) -&gt; bool;
}</p>
<p>trait NonFungibleTokenResolver {
    fn nft_resolve_transfer(
        &amp;mut self,
        owner_id: AccountId,
        receiver_id: AccountId,
        token_id: TokenId,
        approved_account_ids: HashMap&lt;AccountId, u64&gt;,
    ) -&gt; bool;
}</p>
<p>#[near_bindgen]
impl NonFungibleTokenCore for Contract {</p>
<pre><code>#[payable]
fn nft_transfer(
    &amp;mut self,
    receiver_id: ValidAccountId,
    token_id: TokenId,
    approval_id: u64,
    memo: Option&lt;String&gt;,
) {
    assert_one_yocto();
    let sender_id = env::predecessor_account_id();
    let previous_token = self.internal_transfer(
        &amp;sender_id,
        receiver_id.as_ref(),
        &amp;token_id,
        Some(approval_id),
        memo,
    );
    refund_approved_account_ids(
        previous_token.owner_id.clone(),
        &amp;previous_token.approved_account_ids,
    );
}

fn nft_payout(&amp;self, token_id: String, balance: U128, max_len_payout: u32) -&gt; Payout {
    let token = self.tokens_by_id.get(&amp;token_id).expect(&quot;No token&quot;);

    // compute payouts based on balance option
    // adds in contract_royalty and computes previous owner royalty from remainder
    let owner_id = token.owner_id;
    let mut total_perpetual = 0;
    let balance_u128 = u128::from(balance);
    let mut payout: Payout = HashMap::new();
    let royalty = token.royalty;

    assert!(royalty.len() as u32 &lt;= max_len_payout, &quot;Market cannot payout to that many receivers&quot;);

    for (k, v) in royalty.iter() {
        let key = k.clone();
        if key != owner_id {
            payout.insert(key, royalty_to_payout(*v, balance_u128));
            total_perpetual += *v;
        }
    }

    // payout to contract owner - may be previous token owner, they get remainder of balance
    if self.contract_royalty &gt; 0 &amp;&amp; self.owner_id != owner_id {
        payout.insert(self.owner_id.clone(), royalty_to_payout(self.contract_royalty, balance_u128));
        total_perpetual += self.contract_royalty;
    }
    assert!(total_perpetual &lt;= MINTER_ROYALTY_CAP + CONTRACT_ROYALTY_CAP, &quot;Royalties should not be more than caps&quot;);
    // payout to previous owner
    payout.insert(owner_id, royalty_to_payout(10000 - total_perpetual, balance_u128));

    payout
}

#[payable]
fn nft_transfer_payout(
    &amp;mut self,
    receiver_id: ValidAccountId,
    token_id: TokenId,
    approval_id: u64,
    memo: String,
    balance: U128,
    max_len_payout: u32,
) -&gt; Payout {
    assert_one_yocto();
    let sender_id = env::predecessor_account_id();
    let previous_token = self.internal_transfer(
        &amp;sender_id,
        receiver_id.as_ref(),
        &amp;token_id,
        Some(approval_id),
        Some(memo),
    );
    refund_approved_account_ids(
        previous_token.owner_id.clone(),
        &amp;previous_token.approved_account_ids,
    );

    // compute payouts based on balance option
    // adds in contract_royalty and computes previous owner royalty from remainder
    let owner_id = previous_token.owner_id;
    let mut total_perpetual = 0;
    let balance_u128 = u128::from(balance);
    let mut payout: Payout = HashMap::new();
    let royalty = self.tokens_by_id.get(&amp;token_id).expect(&quot;No token&quot;).royalty;

    assert!(royalty.len() as u32 &lt;= max_len_payout, &quot;Market cannot payout to that many receivers&quot;);

    for (k, v) in royalty.iter() {
        let key = k.clone();
        if key != owner_id {
            payout.insert(key, royalty_to_payout(*v, balance_u128));
            total_perpetual += *v;
        }
    }

    // payout to contract owner - may be previous token owner, they get remainder of balance
    if self.contract_royalty &gt; 0 &amp;&amp; self.owner_id != owner_id {
        payout.insert(self.owner_id.clone(), royalty_to_payout(self.contract_royalty, balance_u128));
        total_perpetual += self.contract_royalty;
    }
    assert!(total_perpetual &lt;= MINTER_ROYALTY_CAP + CONTRACT_ROYALTY_CAP, &quot;Royalties should not be more than caps&quot;);
    // payout to previous owner
    payout.insert(owner_id, royalty_to_payout(10000 - total_perpetual, balance_u128));

    payout
}

#[payable]
fn nft_transfer_call(
    &amp;mut self,
    receiver_id: ValidAccountId,
    token_id: TokenId,
    approval_id: u64,
    memo: Option&lt;String&gt;,
    msg: String,
) -&gt; PromiseOrValue&lt;bool&gt; {
    assert_one_yocto();
    let sender_id = env::predecessor_account_id();
    let previous_token = self.internal_transfer(
        &amp;sender_id,
        receiver_id.as_ref(),
        &amp;token_id,
        Some(approval_id),
        memo,
    );
    // Initiating receiver&#39;s call and the callback
    ext_non_fungible_token_receiver::nft_on_transfer(
        sender_id,
        previous_token.owner_id.clone(),
        token_id.clone(),
        msg,
        receiver_id.as_ref(),
        NO_DEPOSIT,
        env::prepaid_gas() - GAS_FOR_NFT_TRANSFER_CALL,
    )
    .then(ext_self::nft_resolve_transfer(
        previous_token.owner_id,
        receiver_id.into(),
        token_id,
        previous_token.approved_account_ids,
        &amp;env::current_account_id(),
        NO_DEPOSIT,
        GAS_FOR_RESOLVE_TRANSFER,
    )).into()
}

#[payable]
fn nft_approve(&amp;mut self, token_id: TokenId, account_id: ValidAccountId, msg: Option&lt;String&gt;) {
    assert_at_least_one_yocto();
    let account_id: AccountId = account_id.into();

    let mut token = self.tokens_by_id.get(&amp;token_id).expect(&quot;No token&quot;);

    assert_eq!(
        &amp;env::predecessor_account_id(),
        &amp;token.owner_id,
        &quot;Predecessor must be the token owner.&quot;
    );

    let approval_id: u64 = token.next_approval_id;
    let is_new_approval = token
        .approved_account_ids
        .insert(account_id.clone(), approval_id)
        .is_none();

    let storage_used = if is_new_approval {
        bytes_for_approved_account_id(&amp;account_id)
    } else {
        0
    };

    token.next_approval_id += 1;
    self.tokens_by_id.insert(&amp;token_id, &amp;token);

    refund_deposit(storage_used);

    if let Some(msg) = msg {

        // CUSTOM - add token_type to msg
        let mut final_msg = msg;
        let token_type = token.token_type;
        if let Some(token_type) = token_type {
            final_msg.insert_str(final_msg.len() - 1, &amp;format!(&quot;,\&quot;token_type\&quot;:\&quot;{}\&quot;&quot;, token_type));
        }

        ext_non_fungible_approval_receiver::nft_on_approve(
            token_id,
            token.owner_id,
            approval_id,
            final_msg,
            &amp;account_id,
            NO_DEPOSIT,
            env::prepaid_gas() - GAS_FOR_NFT_APPROVE,
        )
        .as_return(); // Returning this promise
    }
}

fn nft_is_approved(
    &amp;self,
    token_id: TokenId,
    approved_account_id: AccountId,
    approval_id: Option&lt;u64&gt;,
) -&gt; bool {
    let token = self.tokens_by_id.get(&amp;token_id).expect(&quot;No token&quot;);
    let approval = token.approved_account_ids.get(&amp;approved_account_id);
    if let Some(approval) = approval {
        if let Some(approval_id) = approval_id {
            approval_id == *approval
        } else {
            false
        }
    } else {
        false
    }
}

#[payable]
fn nft_revoke(&amp;mut self, token_id: TokenId, account_id: ValidAccountId) {
    assert_one_yocto();
    let mut token = self.tokens_by_id.get(&amp;token_id).expect(&quot;No token&quot;);
    let predecessor_account_id = env::predecessor_account_id();
    assert_eq!(&amp;predecessor_account_id, &amp;token.owner_id);
    if token
        .approved_account_ids
        .remove(account_id.as_ref())
        .is_some()
    {
        refund_approved_account_ids_iter(predecessor_account_id, [account_id.into()].iter());
        self.tokens_by_id.insert(&amp;token_id, &amp;token);
    }
}

#[payable]
fn nft_revoke_all(&amp;mut self, token_id: TokenId) {
    assert_one_yocto();
    let mut token = self.tokens_by_id.get(&amp;token_id).expect(&quot;No token&quot;);
    let predecessor_account_id = env::predecessor_account_id();
    assert_eq!(&amp;predecessor_account_id, &amp;token.owner_id);
    if !token.approved_account_ids.is_empty() {
        refund_approved_account_ids(predecessor_account_id, &amp;token.approved_account_ids);
        token.approved_account_ids.clear();
        self.tokens_by_id.insert(&amp;token_id, &amp;token);
    }
}

fn nft_total_supply(&amp;self) -&gt; U128 {
    U128(self.token_metadata_by_id.len() as u128)
}

fn nft_token(&amp;self, token_id: TokenId) -&gt; Option&lt;JsonToken&gt; {
    if let Some(token) = self.tokens_by_id.get(&amp;token_id) {
        let metadata = self.token_metadata_by_id.get(&amp;token_id).unwrap();
        Some(JsonToken {
            token_id,
            owner_id: token.owner_id,
            metadata,
            royalty: token.royalty,
            approved_account_ids: token.approved_account_ids,
            token_type: token.token_type,
        })
    } else {
        None
    }
}</code></pre><p>}</p>
<p>#[near_bindgen]
impl NonFungibleTokenResolver for Contract {
    #[private]
    fn nft_resolve_transfer(
        &amp;mut self,
        owner_id: AccountId,
        receiver_id: AccountId,
        token_id: TokenId,
        approved_account_ids: HashMap&lt;AccountId, u64&gt;,
    ) -&gt; bool {
        // Whether receiver wants to return token back to the sender, based on <code>nft_on_transfer</code>
        // call result.
        if let PromiseResult::Successful(value) = env::promise_result(0) {
            if let Ok(return_token) = near_sdk::serde_json::from_slice::<bool>(&amp;value) {
                if !return_token {
                    // Token was successfully received.
                    refund_approved_account_ids(owner_id, &amp;approved_account_ids);
                    return true;
                }
            }
        }</p>
<pre><code>    let mut token = if let Some(token) = self.tokens_by_id.get(&amp;token_id) {
        if token.owner_id != receiver_id {
            // The token is not owner by the receiver anymore. Can&#39;t return it.
            refund_approved_account_ids(owner_id, &amp;approved_account_ids);
            return true;
        }
        token
    } else {
        // The token was burned and doesn&#39;t exist anymore.
        refund_approved_account_ids(owner_id, &amp;approved_account_ids);
        return true;
    };

    log!(&quot;Return {} from @{} to @{}&quot;, token_id, receiver_id, owner_id);

    self.internal_remove_token_from_owner(&amp;receiver_id, &amp;token_id);
    self.internal_add_token_to_owner(&amp;owner_id, &amp;token_id);
    token.owner_id = owner_id;
    refund_approved_account_ids(receiver_id, &amp;token.approved_account_ids);
    token.approved_account_ids = approved_account_ids;
    self.tokens_by_id.insert(&amp;token_id, &amp;token);

    false
}</code></pre><p>}</p>
<pre><code>

#### 핵심기능 3 metadata.rs
- 발행하고자 하는 nft의 특징이 가장 잘 드러나는 부분이 metadata이다.
- 토큰의 필요한 정보들을 저장한다.
- 나의 플랫폼에 필요한 메타 데이터는 제목, 설명, 그림, 그림해시, nft 총 발행 개수, 발행처, 체인의 블록 생성 주기, 레퍼런스 URL이었다.

#### 전체 코드
```rust
use crate::*;

#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone)]
#[serde(crate = &quot;near_sdk::serde&quot;)]
pub struct NFTMetadata {
    pub spec: String,              // required, essentially a version like &quot;nft-1.0.0&quot;
    pub name: String,              // required, ex. &quot;Mosaics&quot;
    pub symbol: String,            // required, ex. &quot;MOSIAC&quot;
    pub icon: Option&lt;String&gt;,      // Data URL
    pub base_uri: Option&lt;String&gt;, // Centralized gateway known to have reliable access to decentralized storage assets referenced by `reference` or `media` URLs
    pub reference: Option&lt;String&gt;, // URL to a JSON file with more info
    pub reference_hash: Option&lt;Base64VecU8&gt;, // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included.
}

#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
#[serde(crate = &quot;near_sdk::serde&quot;)]
pub struct TokenMetadata {
    pub title: Option&lt;String&gt;, // ex. &quot;Arch Nemesis: Mail Carrier&quot; or &quot;Parcel #5055&quot;
    pub description: Option&lt;String&gt;, // free-form description
    pub media: Option&lt;String&gt;, // URL to associated media, preferably to decentralized, content-addressed storage
    pub media_hash: Option&lt;Base64VecU8&gt;, // Base64-encoded sha256 hash of content referenced by the `media` field. Required if `media` is included.
    pub copies: Option&lt;u64&gt;, // number of copies of this set of metadata in existence when token was minted.
    pub issued_at: Option&lt;u64&gt;, // When token was issued or minted, Unix epoch in milliseconds
    pub expires_at: Option&lt;u64&gt;, // When token expires, Unix epoch in milliseconds
    pub starts_at: Option&lt;u64&gt;, // When token starts being valid, Unix epoch in milliseconds
    pub updated_at: Option&lt;u64&gt;, // When token was last updated, Unix epoch in milliseconds
    pub extra: Option&lt;String&gt;, // anything extra the NFT wants to store on-chain. Can be stringified JSON.
    pub reference: Option&lt;String&gt;, // URL to an off-chain JSON file with more info.
    pub reference_hash: Option&lt;Base64VecU8&gt;, // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included.
}

pub trait NonFungibleTokenMetadata {
    fn nft_metadata(&amp;self) -&gt; NFTMetadata;
}

#[near_bindgen]
impl NonFungibleTokenMetadata for Contract {
    fn nft_metadata(&amp;self) -&gt; NFTMetadata {
        self.metadata.get().unwrap()
    }
}
</code></pre><h3 id="step-3-wasm-파일로-컴파일하기">STEP 3 wasm 파일로 컴파일하기</h3>
<pre><code class="language-shell">cargo build --target wasm32-unknown-unknown --release</code></pre>
<ul>
<li>cargo의 build 명령어를 사용해 rust컨트랙트를 기계어로 컴파일한 wasm파일을 만든다.<pre><code>.
├── Cargo.lock  ⟵ dependency들을 저장해놓는 Lock파일, 자동 생성된다.
├── Cargo.toml
├── src
│  └── 나의스마트컨트랙트.rs
└── target      ⟵ target 레포지토리 내부에 wasm 파일이 자동으로 저장된다.
</code></pre></li>
</ul>
<pre><code>- build 이후 레포지토리에 Cargo.lock 파일과 target 폴더가 자동 생성된다.
- target 폴더 내부에 컴파일된 wasm 파일을 확인할 수 있다.
- target에 해당 파일이 생성된 것을 확인할 수 있다.

### STEP 4 컨트랙트 블록체인에 배포하기
#### near cli 로그인

</code></pre><p>near login</p>
<pre><code>- near의 login 명령어를 이용해 near계정에 로그인한다.
- near의 wallet 웹이 자동으로 실행되며, 웹에서 로그인한 결과가 near cli에 반영된다.
- default 설정은 testnet이며, 메인넷이나 베타넷 배포를 희망할 경우 network 옵션을 추가하여 명령어를 실행하면 된다.
#### 컨트랙트 블록체인에 배포하기
</code></pre><p>near deploy --wasmFile target/wasm32-unknown-unknown/release/&lt;내파일이름&gt;.wasm --accountId &lt;나의accountID (0000.testnet)&gt;</p>
<p>```</p>
<ul>
<li>near의 deploy 명령어를 이용해 STEP 3에서 만들었던 wasm 파일을 배포한다.</li>
<li>near는 자신의 account와 컨트랙트 아이디가 매칭되는 방식을 이용한다. <code>--accountId</code> 옵션에 정확한 자신의 account 아이디를 적자!</li>
</ul>
<h4 id="배포-확인하기">배포 확인하기</h4>
<ul>
<li>NEAR explorer에서 자신의 어카운트 아이디를 검색하여 자신의 컨트랙트가 정상적으로 배포되었는지 확인해볼 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[EPPER] 후위표기법]]></title>
            <link>https://velog.io/@dandev_sw/EPPER-%ED%9B%84%EC%9C%84%ED%91%9C%EA%B8%B0%EB%B2%95</link>
            <guid>https://velog.io/@dandev_sw/EPPER-%ED%9B%84%EC%9C%84%ED%91%9C%EA%B8%B0%EB%B2%95</guid>
            <pubDate>Wed, 12 May 2021 01:42:43 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-정의">문제 정의</h1>
<p><img src="https://images.velog.io/images/dandev_sw/post/34883666-78e0-4c62-9255-b37c234603d3/image.png" alt="">
<img src="https://images.velog.io/images/dandev_sw/post/4b5da1cc-62b4-4fdc-9fb1-fe4824465ffe/image.png" alt=""></p>
<h1 id="문제-풀이">문제 풀이</h1>
<h2 id="사용-개념">사용 개념</h2>
<ul>
<li>문제를 보면 앞에서부터 element를 뽑고, 다시 계산한 값을 앞에 넣어야 해서 앞 뒤에서 넣고 빼는게 자유로운 <code>dequeue</code>가 적절하다고 생각했다.</li>
<li>또한, 연산자마다 <code>if</code>문 걸기에는 너무 코드가 길어질 것 같아서 <code>switch</code> 문을 오랜만에 써봤다.<h2 id="핵심-코드">핵심 코드</h2>
<pre><code class="language-java">//두 숫자 연산을 하니까 두 개를 뽑아서 저장하려고 선언 먼저
      String e1 = null;
      String e2 = null;
      //스택에 요소가 있을 때 계속 반복
      while (!dequeue.isEmpty()) {
          //맨 앞 요소를 뽑아서 검사
          String cur = dequeue.removeFirst();
          //switch문으로 검사, 우선 연산자일 경우 각각 계산하도록 처리한다
          switch (cur) {
              case &quot;*&quot;:
                  //먼저 string으로 넣어놨으니까 Integer.parseInt로 숫자로 바꿈
                  int temp = Integer.parseInt(e1) * Integer.parseInt(e2);
                  //바꾼 다음 연산한 값을 덱의 맨 처음에 넣는다.
                  dequeue.addFirst(String.valueOf(temp));
                  e1 = e2 = null;
                  break;
              case &quot;/&quot;:
                  temp = Integer.parseInt(e1) / Integer.parseInt(e2);
                  dequeue.addFirst(String.valueOf(temp));
                  e1 = e2 = null;
                  break;
              case &quot;+&quot;:
                  temp = Integer.parseInt(e1) + Integer.parseInt(e2);
                  dequeue.addFirst(String.valueOf(temp));
                  e1 = e2 = null;
                  break;
              case &quot;-&quot;:
                  temp = Integer.parseInt(e1) - Integer.parseInt(e2);
                  dequeue.addFirst(String.valueOf(temp));
                  e1 = e2 = null;
                  break;
              default:
                  if (e1 == null) {
                      e1 = cur;
                  }
                  else if (e2 == null) {
                      e2 = cur;
                  }
                  if (dequeue.size() == 0) {
                      System.out.println(cur);
                      break;
                  }
                  break;
          }
      }</code></pre>
<ul>
<li>코드가 또 구구절절이라서.. 이거 그냥 <code>dequeue</code> 말고 <code>stack</code>으로 풀어봐야겠다는 생각이 들었다.</li>
<li><code>dequeue</code> 나와있는 자료 구조를 이용했고, 처음에 넣고 빼기 위해서 <code>removeFirst</code>, <code>addFirst</code> 를 썼다.</li>
</ul>
</li>
<li>맨 마지막에 숫자면서 하나밖에 안남아있는 경우 완결된 답이므로 <code>default</code>의 <code>dequeue</code> 사이즈 검사를 이용했다.<h1 id="최종-코드">최종 코드</h1>
<pre><code class="language-java">package EPPER;
</code></pre>
</li>
</ul>
<p>import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Scanner;</p>
<p>public class EPPER_15_6 {
    public static void main(String[] args) {
        Deque<String> dequeue = new ArrayDeque&lt;&gt;();
        Scanner s = new Scanner(System.in);
        int m = s.nextInt();
        for (int i = 0; i &lt; m; i++) {
            dequeue.add(s.next());
        }</p>
<pre><code>    //두 숫자 연산을 하니까 두 개를 뽑아서 저장하려고 선언 먼저
    String e1 = null;
    String e2 = null;
    //스택에 요소가 있을 때 계속 반복
    while (!dequeue.isEmpty()) {
        //맨 앞 요소를 뽑아서 검사
        String cur = dequeue.removeFirst();
        //switch문으로 검사, 우선 연산자일 경우 각각 계산하도록 처리한다
        switch (cur) {
            case &quot;*&quot;:
                //먼저 string으로 넣어놨으니까 Integer.parseInt로 숫자로 바꿈
                int temp = Integer.parseInt(e1) * Integer.parseInt(e2);
                //바꾼 다음 연산한 값을 덱의 맨 처음에 넣는다.
                dequeue.addFirst(String.valueOf(temp));
                e1 = e2 = null;
                break;
            case &quot;/&quot;:
                temp = Integer.parseInt(e1) / Integer.parseInt(e2);
                dequeue.addFirst(String.valueOf(temp));
                e1 = e2 = null;
                break;
            case &quot;+&quot;:
                temp = Integer.parseInt(e1) + Integer.parseInt(e2);
                dequeue.addFirst(String.valueOf(temp));
                e1 = e2 = null;
                break;
            case &quot;-&quot;:
                temp = Integer.parseInt(e1) - Integer.parseInt(e2);
                dequeue.addFirst(String.valueOf(temp));
                e1 = e2 = null;
                break;
            default:
                if (e1 == null) {
                    e1 = cur;
                }
                else if (e2 == null) {
                    e2 = cur;
                }
                if (dequeue.size() == 0) {
                    System.out.println(cur);
                    break;
                }
                break;
        }
    }
}</code></pre><p>}</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 1715 카드 정렬하기]]></title>
            <link>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-1715-%EC%B9%B4%EB%93%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-1715-%EC%B9%B4%EB%93%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 07 May 2021 11:15:19 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-정의">문제 정의</h1>
<h2 id="문제">문제</h2>
<p>정렬된 두 묶음의 숫자 카드가 있다고 하자. 각 묶음의 카드의 수를 A, B라 하면 보통 두 묶음을 합쳐서 하나로 만드는 데에는 A+B 번의 비교를 해야 한다. 이를테면, 20장의 숫자 카드 묶음과 30장의 숫자 카드 묶음을 합치려면 50번의 비교가 필요하다.</p>
<p>매우 많은 숫자 카드 묶음이 책상 위에 놓여 있다. 이들을 두 묶음씩 골라 서로 합쳐나간다면, 고르는 순서에 따라서 비교 횟수가 매우 달라진다. 예를 들어 10장, 20장, 40장의 묶음이 있다면 10장과 20장을 합친 뒤, 합친 30장 묶음과 40장을 합친다면 (10 + 20) + (30 + 40) = 100번의 비교가 필요하다. 그러나 10장과 40장을 합친 뒤, 합친 50장 묶음과 20장을 합친다면 (10 + 40) + (50 + 20) = 120 번의 비교가 필요하므로 덜 효율적인 방법이다.</p>
<p>N개의 숫자 카드 묶음의 각각의 크기가 주어질 때, 최소한 몇 번의 비교가 필요한지를 구하는 프로그램을 작성하시오.</p>
<h2 id="입력">입력</h2>
<p>첫째 줄에 N이 주어진다. (1 ≤ N ≤ 100,000) 이어서 N개의 줄에 걸쳐 숫자 카드 묶음의 각각의 크기가 주어진다. 숫자 카드 묶음의 크기는 1,000보다 작거나 같은 양의 정수이다.</p>
<h2 id="출력">출력</h2>
<p>첫째 줄에 최소 비교 횟수를 출력한다.</p>
<h1 id="문제-풀이">문제 풀이</h1>
<h2 id="문제-풀이-논리">문제 풀이 논리</h2>
<ul>
<li>비교 횟수가 점점 누적되어 더해지는 형태니까 적은 숫자 카드먼저 더하는게 유리하다!</li>
<li>처음에는 그래서 그냥 <code>Array</code> 로 정렬을 하고 앞에서부터 누적 덧셈을 해줬었다.. 근데 틀림! 
만약에 맨 처음 20 25 30 35 50 이렇게 카드가 있다고 치면 중간에 20+25 = 45로 뒤에 더해지는 값인 35보다 커져서 이 경우에 20+25한 다음 30+35하고 나서 20+25인 45를 더하는게 더 비교 횟수가 적어진다.</li>
<li>따라서, <code>Priority Queue</code>를 사용해서 그때그때 가장 작은 숫자 두개를 먼저 더하는 형식으로 코드를 다시 짰다.</li>
</ul>
<h2 id="큐에서-숫자-뽑기-알고리즘">큐에서 숫자 뽑기 알고리즘</h2>
<pre><code class="language-java">        //큐에서 가장 작은 숫자부터 뽑아서 계산
        int temp_sum = 0;
        int result = 0;
        while (pq.size() &gt; 1) {
            temp_sum = pq.poll() + pq.poll();
            result = result + temp_sum;
            pq.add(temp_sum);
        }
        System.out.println(result);</code></pre>
<ul>
<li><code>queue</code>에 숫자가 하나밖에 없으면 비교할 게 더이상 없는 거니까 <code>while</code>문을 멈추고, 숫자가 남아 있으면 계속 연산을 진행한다.</li>
<li><code>temp_sum</code>에 맨 앞 두 개 숫자, 그니까 가장 작은 숫자 두 개를 뽑아서 더한 값을 넣는다.</li>
<li><code>result</code>는 모든 비교 횟수가 누적된 값이어야 하므로 한 번 루프가 돌 때마다 <code>temp_sum</code>을 계속해서 더해준다.</li>
<li><code>pq</code>에 현재 더한 값인 <code>temp_sum</code>을 넣어 위 논리에 적은, 앞의 두 값을 더한게 뒤 값보다 커지는 경우를 방지하고 비교할 수 있게 한다.</li>
<li>마지막에 <code>result</code>를 프린트하면 끝!</li>
</ul>
<h1 id="전체-코드">전체 코드</h1>
<pre><code class="language-java">package Greedy;

import java.util.PriorityQueue;
import java.util.Scanner;

public class BOJ1715 {
    public static void main(String[] args) {
        //작은 숫자 뽑기 위해서 Integer형 priority queue 사용
        PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;();
        //초기화 및 입력 받아 배열 채우기
        Scanner s = new Scanner(System.in);
        int N = s.nextInt();
        for (int i = 0; i &lt; N; i++) {
            pq.add(s.nextInt());
        }

        //큐에서 가장 작은 숫자부터 뽑아서 계산
        int temp_sum = 0;
        int result = 0;
        while (pq.size() &gt; 1) {
            temp_sum = pq.poll() + pq.poll();
            result = result + temp_sum;
            pq.add(temp_sum);
        }
        System.out.println(result);
    }
}</code></pre>
<p><img src="https://images.velog.io/images/dandev_sw/post/e43e89a9-bd67-421e-a7db-49d2dfaf1828/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-05-07%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.14.48.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] BOJ 11399 ATM]]></title>
            <link>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-11399-ATM</link>
            <guid>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-11399-ATM</guid>
            <pubDate>Wed, 05 May 2021 06:39:39 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-정의">문제 정의</h1>
<h2 id="문제">문제</h2>
<p>인하은행에는 ATM이 1대밖에 없다. 지금 이 ATM앞에 N명의 사람들이 줄을 서있다. 사람은 1번부터 N번까지 번호가 매겨져 있으며, i번 사람이 돈을 인출하는데 걸리는 시간은 Pi분이다.</p>
<p>사람들이 줄을 서는 순서에 따라서, 돈을 인출하는데 필요한 시간의 합이 달라지게 된다. 예를 들어, 총 5명이 있고, P1 = 3, P2 = 1, P3 = 4, P4 = 3, P5 = 2 인 경우를 생각해보자. [1, 2, 3, 4, 5] 순서로 줄을 선다면, 1번 사람은 3분만에 돈을 뽑을 수 있다. 2번 사람은 1번 사람이 돈을 뽑을 때 까지 기다려야 하기 때문에, 3+1 = 4분이 걸리게 된다. 3번 사람은 1번, 2번 사람이 돈을 뽑을 때까지 기다려야 하기 때문에, 총 3+1+4 = 8분이 필요하게 된다. 4번 사람은 3+1+4+3 = 11분, 5번 사람은 3+1+4+3+2 = 13분이 걸리게 된다. 이 경우에 각 사람이 돈을 인출하는데 필요한 시간의 합은 3+4+8+11+13 = 39분이 된다.</p>
<p>줄을 [2, 5, 1, 4, 3] 순서로 줄을 서면, 2번 사람은 1분만에, 5번 사람은 1+2 = 3분, 1번 사람은 1+2+3 = 6분, 4번 사람은 1+2+3+3 = 9분, 3번 사람은 1+2+3+3+4 = 13분이 걸리게 된다. 각 사람이 돈을 인출하는데 필요한 시간의 합은 1+3+6+9+13 = 32분이다. 이 방법보다 더 필요한 시간의 합을 최소로 만들 수는 없다.</p>
<p>줄을 서 있는 사람의 수 N과 각 사람이 돈을 인출하는데 걸리는 시간 Pi가 주어졌을 때, 각 사람이 돈을 인출하는데 필요한 시간의 합의 최솟값을 구하는 프로그램을 작성하시오.</p>
<h2 id="입력">입력</h2>
<p>첫째 줄에 사람의 수 N(1 ≤ N ≤ 1,000)이 주어진다. 둘째 줄에는 각 사람이 돈을 인출하는데 걸리는 시간 Pi가 주어진다. (1 ≤ Pi ≤ 1,000)</p>
<h2 id="출력">출력</h2>
<p>첫째 줄에 각 사람이 돈을 인출하는데 필요한 시간의 합의 최솟값을 출력한다.</p>
<h1 id="문제-풀이">문제 풀이</h1>
<h2 id="그리디-알고리즘-이용">그리디 알고리즘 이용</h2>
<ul>
<li>그리디 알고리즘이란, YOLO 같이 미래 생각 안하고 그때그때 최상인 것을 선택하는 알고리즘!</li>
<li>이 문제는 그리디가 자주 사용되는 스케줄링 문제!</li>
<li>시간이 짧은 순서대로 하면 최소 대기시간이 걸릴 것이라는게 문제에도 쓰여있다.<h2 id="그리디-알고리즘-작동-증명">그리디 알고리즘 작동 증명</h2>
<blockquote>
<p>💡 그리디 알고리즘은 제대로 작동하는 경우가 정말 별로 없다..ㅎ.. 그래서 탐욕적 선택 속성, 최적 부분 구조를 만족하는지 항상 검토해야 한다. 
💡 손으로 테스트 케이스 몇개 풀어보면 그리디의 감이 온다.. 스케줄링은 일단 그리디를 한번 부벼보자. 안되면 DP로 코드를 변경하면 됨!</p>
</blockquote>
</li>
</ul>
<h3 id="1-탐욕적-선택-속성-증명">1. 탐욕적 선택 속성 증명</h3>
<blockquote>
<p>💡 그리디 알고리즘을 선택했을 시, 손해가 발생하는지 점검한다.</p>
</blockquote>
<ol>
<li>가장 적은 이용 시간을 가진 사람이 빠진 줄이 있다고 가정한다.</li>
<li>맨 앞의 사람을 빼고 이 가장 적은 이용 시간을 가진 사람을 세운다.</li>
<li>총 대기 시간이 더 짧아졌으면 짧아졌지 더 길어지지는 않는다.</li>
</ol>
<ul>
<li>그리디 알고리즘을 선택하는 것은 손해가 아님!</li>
</ul>
<h3 id="2-최적-부분-구조-증명">2. 최적 부분 구조 증명</h3>
<blockquote>
<p>💡 그리디한 답을 고르는 것이 부분 문제 해결 뿐만 아니라 최종 문제 해결까지 최적인지 검사한다.</p>
</blockquote>
<ol>
<li>가장 이용 시간이 짧은 사람이 먼저 일을 처리한다고 가정한다.</li>
<li>두 번째 대기하고 있는 사람은 가장 짧게 대기하고 일을 처리할 수 있다.</li>
<li>대기 시간은 앞에서부터 계속 누적되는 구조니까 다 총 대기 시간은 짧아질 수밖에 없다.<h2 id="대기-시간-계산-로직">대기 시간 계산 로직</h2>
<h3 id="소스-코드">소스 코드</h3>
<pre><code class="language-java">  int temp = 0;
     int result = 0;
     for (int i = 1; i &lt;= N; i++) {
         temp += people[i - 1];
         result += temp;
     }</code></pre>
</li>
</ol>
<ul>
<li>맨 첫번째 사람은 대기하지 않으므로, <code>i</code>는 <code>1</code>부터 시작한다.</li>
<li>맨 마지막 사람도 대기 시간 계산에 포함해야 하므로, i는 N과 같아질 때도 연산에 포함한다.</li>
<li>대기 시간은 누적되는 구조이며, 앞 사람이 대기한 시간 + 바로 앞 사람이 업무 본 시간이 현재 사람이 대기하는 시간이며 <code>temp</code>에 저장한다.</li>
<li>총 대기시간인 <code>result</code>는 이 <code>temp</code>들을 모두 더한 값이므로 <code>result += temp;</code> 로 표기한다.<h1 id="최종-코드">최종 코드</h1>
<pre><code class="language-java">package Greedy;
</code></pre>
</li>
</ul>
<p>import java.util.Arrays;
import java.util.Scanner;</p>
<p>public class BOJ11399 {
    public static void main(String[] args) {
        //변수 선언 및 배열 값 넣기
        Scanner s = new Scanner(System.in);
        int N = s.nextInt();
        int[] people = new int[N];
        for (int i = 0; i &lt; N; i++) {
            people[i] = s.nextInt();
        }</p>
<pre><code>    //배열 작은 값부터 순서대로 정렬
    Arrays.sort(people);

    //기다린 시간 계산 - 누적으로 계산됨!
    int temp = 0;
    int result = 0;
    for (int i = 1; i &lt;= N; i++) {
        temp += people[i - 1];
        result += temp;
    }
    System.out.println(result);
}</code></pre><p>}</p>
<p>```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] BOJ 1806 부분합]]></title>
            <link>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-1806-%EB%B6%80%EB%B6%84%ED%95%A9</link>
            <guid>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-1806-%EB%B6%80%EB%B6%84%ED%95%A9</guid>
            <pubDate>Tue, 04 May 2021 03:07:28 GMT</pubDate>
            <description><![CDATA[<h1 id="📄-문제-설명">📄 문제 설명</h1>
<h2 id="문제">문제</h2>
<p>10,000 이하의 자연수로 이루어진 길이 N짜리 수열이 주어진다. 이 수열에서 연속된 수들의 부분합 중에 그 합이 S 이상이 되는 것 중, 가장 짧은 것의 길이를 구하는 프로그램을 작성하시오.</p>
<h2 id="입력">입력</h2>
<p>첫째 줄에 N (10 ≤ N &lt; 100,000)과 S (0 &lt; S ≤ 100,000,000)가 주어진다. 둘째 줄에는 수열이 주어진다. 수열의 각 원소는 공백으로 구분되어져 있으며, 10,000이하의 자연수이다.</p>
<h2 id="출력">출력</h2>
<p>첫째 줄에 구하고자 하는 최소의 길이를 출력한다. 만일 그러한 합을 만드는 것이 불가능하다면 0을 출력하면 된다.</p>
<h3 id="예제-입력-1">예제 입력 1</h3>
<p>10 15
5 1 3 5 10 7 4 9 2 8</p>
<h3 id="예제-출력-1">예제 출력 1</h3>
<p>2</p>
<h1 id="✍🏻-문제-풀이">✍🏻 문제 풀이</h1>
<h2 id="적용-개념">적용 개념</h2>
<p>시간 복잡도를 <code>O(N)</code>으로 줄이기 위해 투 포인터를 이용해서 풀었다.</p>
<h3 id="투-포인터-개념">투 포인터 개념</h3>
<ul>
<li>투 포인터는 부분합을 구할 때 유용하게 사용할 수 있는 개념인데, 배열의 시작 지점인 <code>start</code>와 끝 지점인 <code>end</code>를 명시해 합을 구하고 범위를 조절할 수 있도록 하는 방법이다.<h3 id="투-포인터-적용">투 포인터 적용</h3>
</li>
<li><code>start</code>를 증가시키는 경우 : 자연수의 배열이므로, 구해야하는 합보다 현재 구간 합이 크거나 같을 경우 <code>start</code>를 증가시켜서 다음 합을 찾음</li>
<li><code>end</code>를 증가시키는 경우 : 자연수의 배열이므로, 구해야하는 합보다 현재 구간 합이 작을 경우 증가시켜서 더하는 수를 늘림<h2 id="문제-적용">문제 적용</h2>
</li>
</ul>
<ol>
<li>이 문제에서는 <code>S</code>와 정확히 같은 경우가 아니라 <code>S</code>보다 크거나 같은 경우를 카운트 한다.</li>
<li>따라서 <code>S</code>보다 크거나 같을 경우 구간의 길이를 재고, 다음 영역으로 옮기기 위해 <code>start</code>를 조정한다.</li>
<li>어차피 길이 최소값을 구하는 거니까 <code>S</code>를 넘기만 하면 다음 구간으로 옮기면 됨!</li>
</ol>
<h1 id="💡-전체-코드">💡 전체 코드</h1>
<pre><code class="language-java">package TwoPointer;

import java.util.Scanner;

public class BOJ1806 {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int start = 0;
        int end = 0;
        int N = s.nextInt();
        int S = s.nextInt();
        //초기화
        int[] numbers = new int[N];
        for (int i = 0; i &lt; N; i++) {
            numbers[i] = s.nextInt();
        }
        //투 포인터 돌리기
        int length = Integer.MAX_VALUE;
        while (start &lt; N &amp;&amp; end &lt; N) {
            int sum = 0;
            for (int i = start; i &lt;= end; i++) {
                sum += numbers[i];
            }
            if (sum &lt; S) {
                end++;
            } else {
                length = Math.min(length, end - start + 1);
                start++;
                if (end &lt; start) {
                    end++;
                }
            }
        }
        System.out.println(length);
    }
}</code></pre>
<h1 id="시간복잡도가-안맞음-다시-바꿨다">시간복잡도가 안맞음,, 다시 바꿨다!</h1>
<pre><code class="language-java">package TwoPointer;

import java.util.Scanner;

public class BOJ1806 {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int start = 0;
        int end = 0;
        int N = s.nextInt();
        int S = s.nextInt();

        int[] numbers = new int[N];
        for (int i = 0; i &lt; N; i++) {
            numbers[i] = s.nextInt();
        }

        int length = Integer.MAX_VALUE;
        int sum = 0;
        while(true){
            if (sum &gt;= S) {
                length = Math.min(length, (end - start));
                sum -= numbers[start++];
            }
            else if (end == N) {
                break;
            }
            else {
                sum += numbers[end++];
            }
        }
        if (length == Integer.MAX_VALUE) {
            System.out.println(0);
        }
        else System.out.println(length);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[#9 Crypto Zombies] Oracle 빌드하기]]></title>
            <link>https://velog.io/@dandev_sw/9-Crypto-Zombies-%EC%B2%B4%EC%9D%B8%EC%97%90%EC%84%9C-Oracle-%EC%9D%B4%EC%9A%A9</link>
            <guid>https://velog.io/@dandev_sw/9-Crypto-Zombies-%EC%B2%B4%EC%9D%B8%EC%97%90%EC%84%9C-Oracle-%EC%9D%B4%EC%9A%A9</guid>
            <pubDate>Sun, 07 Mar 2021 13:07:42 GMT</pubDate>
            <description><![CDATA[<h1 id="oracle이란">Oracle이란?</h1>
<h2 id="oracle을-사용하는-이유">Oracle을 사용하는 이유</h2>
<blockquote>
<p>블록체인에서 데이터를 바로 끌어오는 것이 보안상 불가능하기때문에 오라클을 이용해 데이터를 끌어온다.</p>
</blockquote>
<h2 id="oracle-통신-방식">Oracle 통신 방식</h2>
<p><img src="https://images.velog.io/images/dandev_sw/post/8f4761ef-8779-4ff1-930c-63522bcd63b7/image.png" alt=""></p>
<h2 id="oracle-사용-구조-만들기">Oracle 사용 구조 만들기</h2>
<pre><code class="language-shell">├── caller
│   ├── contracts
│   ├── migrations
│   ├── test
│   └── truffle-config.js
├── oracle
│   ├── contracts
│   ├── migrations
│   ├── test
│   └── truffle-config.js
└── package.json</code></pre>
<h1 id="오라클-통신">오라클 통신</h1>
<h2 id="오라클-컨트랙트-부르기">오라클 컨트랙트 부르기</h2>
<blockquote>
<p>caller 컨트랙트가 오라클과 통신하기 위해서는 인터페이스를 정의해야 한다.</p>
</blockquote>
<h3 id="인터페이스가-필요한-이유">인터페이스가 필요한 이유</h3>
<ul>
<li>인터페이스는 함수 선언만 한다</li>
<li>오라클과 통신하기 위해 반드시 정의 필요<h3 id="call-하기">call 하기</h3>
</li>
</ul>
<ol>
<li><code>FastFood</code>라는 contract가 있다고 가정<pre><code class="language-js">pragma solidity 0.5.0;
</code></pre>
</li>
</ol>
<p>contract FastFood {
  function makeSandwich(string calldata _fillingA, string calldata _fillingB) external {
    //Make the sandwich
  }
}</p>
<pre><code>2. `FastFood` contract 부르는 `PrepareLunch` contract 만들고 인터페이스 정의하기
```js
pragma solidity 0.5.0;
//인터페이스 정의하기
//FastFood안에 있는 makeSandwich라는 함수가 컨트랙트를 부른다
interface FastFoodInterface {
   function makeSandwich(string calldata _fillingA, string calldata _fillingB) external;
}</code></pre><ol start="3">
<li>인터페이스를 인스턴스화하고 함수를 <code>call</code> 한다.<pre><code class="language-js">pragma solidity 0.5.0;
import &quot;./FastFoodInterface.sol&quot;;
</code></pre>
</li>
</ol>
<p>contract PrepareLunch {</p>
<p>  FastFoodInterface private fastFoodInstance;</p>
<p>  function instantiateFastFoodContract (address _address) public {
    fastFoodInstance = FastFoodInterface(_address);
    fastFoodInstance.makeSandwich(&quot;sliced ham&quot;, &quot;pickled veggies&quot;);
  }
}</p>
<pre><code># Mapping을 req 기록으로 사용하기
## 이더 가격 업데이트 하기
- 오라클에서 이더리움 가격 끌어오려면 `getLatestEthPrice()` 를 작동시켜야 한다. 
- 하지만 이 작업이 비동기적으로 일어나서 이건 유니크한 `id`만 리턴한다.
- 대신에 `callback`함수가 이더 가격을 업데이트해준다.
## map으로 callback 검사하기
```js
//맵핑 정의
mapping(address =&gt; uint) public balances;
//msg.sender == address
balances[msg.sender] = someNewValue</code></pre><ul>
<li><code>uint</code> 값은 맨 처음에 <code>0</code> 으로 초기화된다.</li>
<li>새로 받은 값을 이렇게 맵으로 저장!<h2 id="callback-함수의-역할">callback 함수의 역할</h2>
<h3 id="callback-함수-작동-순서">callback 함수 작동 순서</h3>
</li>
</ul>
<ol>
<li>함수가 인증된 <code>id</code> 로 부터 호출되었는지 <code>require</code>써서 검사</li>
<li>인증된 <code>id</code>일 경우 <code>myRequests</code> 맵핑에서 제거</li>
<li>프론트엔드에게 이더 가격이 변동되었다고 알림 날림<h1 id="오라클-인증">오라클 인증</h1>
<h2 id="인증된-주소인지-검사하기">인증된 주소인지 검사하기</h2>
</li>
</ol>
<ul>
<li><code>오라클의 주소</code>를 담은 변수를 이용해서 확인한다.</li>
<li>함수를 부른 곳의 주소가 이 <code>오라클 주소</code>인지를 확인하면 된다.</li>
<li>솔리디티의 <code>msg.sender</code> 를 이용해서 검사한다.<pre><code class="language-js">  modifier onlyOracle() {
    require(msg.sender==oracleAddress, &quot;You are not authorized to call this function.&quot;);
    _;
  }</code></pre>
</li>
<li>뒤의 구문은 만약 아닐 경우 나는 오류 메세지<h1 id="오라클-컨트랙트">오라클 컨트랙트</h1>
<h2 id="오라클-컨트랙트의-목적">오라클 컨트랙트의 목적</h2>
</li>
<li>caller contract가 이더 가격 피드에 접근할 수 있게 해주는 다리같은 역할을 한다.</li>
<li><code>getLatestEthPrice()</code> 와 <code>setLatestEthPrice()</code> 두 개를 <code>implement</code> 해서 사용한다.<h3 id="getlatestethprice"><code>getLatestEthPrice()</code></h3>
</li>
<li>오라클은 맨 처음 <code>request id</code> 를 계산한다.</li>
<li>이 <code>request id</code> 는 보안상의 이유로 계산하기 힘들게 만들어야 한다.<pre><code class="language-js">uint randNonce = 0;
uint modulus = 1000;
uint randomNumber = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
</code></pre>
</li>
</ul>
<pre><code>- 위 코드처럼 `keccak256`으로 랜덤 넘버를 생성한다.
```js
function getLatestEthPrice() public returns (uint256) {
    randNonce++;
    uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
  //리퀘스트 트래킹
    pendingRequests[id] = true;
  //이벤트가 발생했음을 알려줌
    emit GetLatestEthPriceEvent(msg.sender, id);
  //id 리턴
    return id;
  }</code></pre><ul>
<li>리퀘스트 트래킹하는 시스템을 매핑으로 만들 수 있다.</li>
<li>마지막에 이벤트가 발생했음을 알려줘야 함</li>
<li><code>request id</code> 를 <code>return</code>해야 함<h3 id="setlatestethprice"><code>setLatestEthPrice()</code></h3>
<h3 id="함수-설명">함수 설명</h3>
</li>
<li>위에서 이더 가격을 받고 <code>setLatestEthPrice()</code>함수를 불러서 아래 세 가지를 인자로 넘겨준다</li>
</ul>
<ol>
<li>이더 가격</li>
<li>리퀘스트를 시작한 컨트랙트의 주소</li>
<li>리퀘스트의 아이디<pre><code class="language-js">function setLatestEthPrice(uint256 _ethPrice, address _callerAddress,   uint256 _id) public onlyOwner {
 require(pendingRequests[_id], &quot;This request is not in my pending list.&quot;);
 delete pendingRequests[_id];
}</code></pre>
</li>
</ol>
<ul>
<li>리퀘스트 아이디가 <code>true</code> 인지 확인힌다.(<code>==true</code> 안해도 저 자체가 <code>boolean</code>이라 <code>pendingRequests[_id]</code>만 사용)</li>
<li>넘겨받으면 <code>pendingRequests</code>에서 해당 아이디 기록을 지워야 한다.<h3 id="인터페이스를-이용해-callback-작동시킴">인터페이스를 이용해 callback 작동시킴</h3>
<pre><code class="language-js">function setLatestEthPrice(
      uint256 _ethPrice,
      address _callerAddress,
      uint256 _id
  ) public onlyOwner {
      require(
          pendingRequests[_id],
          &quot;This request is not in my pending list.&quot;
      );
      delete pendingRequests[_id];
        //컨트랙트 인스턴스화
      CallerContracInterface callerContractInstance;
      callerContractInstance = CallerContracInterface(_callerAddress);
        //콜백 함수 작동시킴
      callerContractInstance.callback(_ethPrice, _id);
        //이벤트 트리거
      emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
  }</code></pre>
</li>
<li>컨트랙트의 인터페이스를 인스턴스화시킴</li>
<li>인스턴스화된 컨트랙트의 <code>callback</code>함수를 작동시켜 이더리움 가격과 리퀘스트의 아이디를 넘긴다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[#8 Crypto Zombies] DApp Deploy하기]]></title>
            <link>https://velog.io/@dandev_sw/8-Crypto-Zombies-DApp-Deploy%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dandev_sw/8-Crypto-Zombies-DApp-Deploy%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 06 Mar 2021 06:51:35 GMT</pubDate>
            <description><![CDATA[<h1 id="truffle---배포용-도구">Truffle - 배포용 도구</h1>
<h2 id="트러플을-사용하는-이유">트러플을 사용하는 이유</h2>
<ol>
<li>쉬운 스마트 컨트랙트 연결</li>
<li>자동화된 <code>ABI</code> 만들기</li>
<li>스마트 컨트랙트 테스팅이 합쳐져 있음(<code>chai</code>랑 <code>Mocha</code>도 지원)</li>
<li>여러 네트워크 지원<h2 id="트러플-기본-구조">트러플 기본 구조</h2>
<h3 id="기본으로-설치되는-tree-구조">기본으로 설치되는 tree 구조</h3>
<pre><code class="language-shell">├── contracts
 ├── Migrations.sol
├── migrations
 ├── 1_initial_migration.js
└── test
truffle-config.js
truffle.js</code></pre>
</li>
</ol>
<ul>
<li><p><code>truffle init</code> 명령어를 실행하면 위와 같은 기본 구조가 설치된다.</p>
<h3 id="구조-설명">구조 설명</h3>
</li>
<li><p><code>contracts</code> : 트러플이 내가 만든 컨트랙트를 찾는 곳. 보통 <code>contracts/tokens</code> 처럼 <code>nested</code>로 정리한다.</p>
</li>
<li><p><code>migrations</code> : 트러플에게 어떻게 스마트 컨트랙트를 배포할지 말하는 자바스크립트 파일이 위치</p>
</li>
<li><p><code>test</code> : 유닛 테스트 파일을 넣는 곳</p>
</li>
<li><p><code>truffle.js</code> : 네트워크 세팅을 저장하는 곳</p>
</li>
<li><p><code>truffle.config.js</code> : 윈도우 쓰면 <code>truffle.js</code> 지우고 이걸 <code>config</code> 파일로 사용해야 한다.</p>
<h2 id="truffle-hdwallet-provider">truffle-hdwallet-provider</h2>
<blockquote>
<p>트랜잭션 sign을 하기 위해 설치하는 프로바이더! 
보통 <code>truffle init</code> 하고 바로 설치함!</p>
</blockquote>
</li>
<li><p>우리는 <code>Infura</code> 를 사용해서 이더리움에 코드를 배포할 것이다.</p>
</li>
<li><p><code>Infura</code> 를 쓰면 이더리움 노드나 지갑을 직접 <code>run</code> 할 필요가 없다.</p>
</li>
<li><p>하지만 인퓨라의 경우 프라이빗 키를 제공하지 않아서 트랜잭션을 직접 시작할 수 없다. </p>
</li>
<li><p>그래서 트랜잭션을 시작할 수 있는 <code>truffle-hdwallet-provider</code>를 따로 설치해서 이용한다.</p>
<h1 id="compiler">Compiler</h1>
<h2 id="컴파일러를-사용하는-이유">컴파일러를 사용하는 이유</h2>
</li>
<li><p>이더리움 가상 머신은 솔리디티로 작성된 코드를 바로 이해하지 못한다.</p>
</li>
<li><p>따라서 머신이 읽을 수 있도록 컴파일 하는 것이 필요하다.</p>
<h2 id="스마트-컨트랙트-컴파일-하기">스마트 컨트랙트 컴파일 하기</h2>
<pre><code class="language-shell">├── contracts
  ├── Migrations.sol
  ├── CryptoZombies.sol
  ├── erc721.sol
  ├── ownable.sol
  ├── safemath.sol
  ├── zombieattack.sol
  ├── zombiefactory.sol
  ├── zombiefeeding.sol
  ├── zombiehelper.sol
  ├── zombieownership.sol
├── migrations
└── test</code></pre>
</li>
<li><p>스마트 컨트랙트 코드를 <code>contracts</code> 안에 넣고 컴파일 진행</p>
</li>
</ul>
<h1 id="로컬-테스트">로컬 테스트</h1>
<h2 id="ganache-이용-로컬-테스트---migrations">Ganache 이용 로컬 테스트 - migrations</h2>
<h3 id="새-migration-만들기">새 migration 만들기</h3>
<pre><code class="language-js">var Migrations = artifacts.require(&quot;./Migrations.sol&quot;);
module.exports = function(deployer) {
  deployer.deploy(Migrations);
};</code></pre>
<ol>
<li>이 스크립트는 트러플에게 우리가 <code>migration</code> 컨트랙트와 통신하기를 원한다는 것을 알려준다</li>
<li>이 스크립트는 <code>deployer</code>라는 객체를 받는 함수를 <code>export</code>한다</li>
</ol>
<ul>
<li>여기서 <code>deployer</code>라는 객체는 개발자와 프러플의 개발 엔진 간의 인터렉션을 담당한다.<h3 id="내-앱에-맞게-migrations-수정하기">내 앱에 맞게 migrations 수정하기</h3>
<pre><code class="language-js">var CryptoZombies = artifacts.require(&quot;./CryptoZombies.sol&quot;);
module.exports = function(deployer) {
deployer.deploy(CryptoZombies);
};</code></pre>
</li>
<li>내 앱에 맞게 변수 이름 수정하기!<h1 id="testnet-이용---rinkeby">Testnet 이용 - Rinkeby</h1>
<h2 id="config-파일-수정으로-네트워크-이용-설정하기">Config 파일 수정으로 네트워크 이용 설정하기</h2>
<blockquote>
<p>아래 코드는 메인넷과 rinkeby 의 config 객체 두개에 대한 설정을 담고있다.</p>
</blockquote>
</li>
</ul>
<pre><code class="language-js">networks: {
  // Configuration for mainnet
  mainnet: {
    provider: function () {
      // Setting the provider with the Infura Rinkeby address and Token
      return new HDWalletProvider(mnemonic, &quot;https://mainnet.infura.io/v3/YOUR_TOKEN&quot;)
    },
    network_id: &quot;1&quot;
  },
  // Configuration for rinkeby network
  rinkeby: {
    // Special function to setup the provider
    provider: function () {
      // Setting the provider with the Infura Rinkeby address and Token
      return new HDWalletProvider(mnemonic, &quot;https://rinkeby.infura.io/v3/YOUR_TOKEN&quot;)
    },
    // Network id is 4 for Rinkeby
    network_id: 4
  }</code></pre>
<ul>
<li>트러플에게 메인넷을 알려준다.</li>
<li>트러플에게 <code>rinkeby</code> 를 사용할거라고 알려준다.<h2 id="config-파일-전체-모습">config 파일 전체 모습</h2>
<pre><code class="language-js">// Initialize HDWalletProvider
const HDWalletProvider = require(&quot;truffle-hdwallet-provider&quot;);
</code></pre>
</li>
</ul>
<p>// Set your own mnemonic here
const mnemonic = &quot;YOUR_MNEMONIC&quot;;</p>
<p>// Module exports to make this configuration available to Truffle itself
module.exports = {
  // Object with configuration for each network
  networks: {
    // Configuration for mainnet
    mainnet: {
      provider: function () {
        // Setting the provider with the Infura Rinkeby address and Token
        return new HDWalletProvider(mnemonic, &quot;<a href="https://mainnet.infura.io/v3/YOUR_TOKEN&quot;">https://mainnet.infura.io/v3/YOUR_TOKEN&quot;</a>)
      },
      network_id: &quot;1&quot;
    },
    // Configuration for rinkeby network
    rinkeby: {
      // Special function to setup the provider
      provider: function () {
        // Setting the provider with the Infura Rinkeby address and Token
        return new HDWalletProvider(mnemonic, &quot;<a href="https://rinkeby.infura.io/v3/YOUR_TOKEN&quot;">https://rinkeby.infura.io/v3/YOUR_TOKEN&quot;</a>)
      },
      // Network id is 4 for Rinkeby
      network_id: 4
    }
  }
};</p>
<pre><code>## Faucet으로 이더 받기
https://faucet.rinkeby.io/
- 위 링크에서 주소로 이더 받기 가능!
## migrate 실행
![](https://images.velog.io/images/dandev_sw/post/c56f5a7d-d1e5-4af5-ad84-45c7bc7a9bca/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-03-06%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.42.32.png)
- 위 처럼 `truffle migrate --network rinkeby` 명령어로 실행
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[#7 Crypto Zombies] Truffle로 스마트 컨트랙트 테스트하기]]></title>
            <link>https://velog.io/@dandev_sw/7-Crypto-Zombies-Truffle%EB%A1%9C-%EC%8A%A4%EB%A7%88%ED%8A%B8-%EC%BB%A8%ED%8A%B8%EB%9E%99%ED%8A%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dandev_sw/7-Crypto-Zombies-Truffle%EB%A1%9C-%EC%8A%A4%EB%A7%88%ED%8A%B8-%EC%BB%A8%ED%8A%B8%EB%9E%99%ED%8A%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 05 Mar 2021 00:33:46 GMT</pubDate>
            <description><![CDATA[<h1 id="트러플-이용">트러플 이용</h1>
<h2 id="프로젝트-구조">프로젝트 구조</h2>
<p>트러플을 사용해 빌드한 프로젝트 구조는 다음과 같이 된다.</p>
<pre><code class="language-tree">├── build
  ├── contracts
      ├── Migrations.json
      ├── CryptoZombies.json
      ├── erc721.json
      ├── ownable.json
      ├── safemath.json
      ├── zombieattack.json
      ├── zombiefactory.json
      ├── zombiefeeding.json
      ├── zombiehelper.json
      ├── zombieownership.json
├── contracts
  ├── Migrations.sol
  ├── CryptoZombies.sol
  ├── erc721.sol
  ├── ownable.sol
  ├── safemath.sol
  ├── zombieattack.sol
  ├── zombiefactory.sol
  ├── zombiefeeding.sol
  ├── zombiehelper.sol
  ├── zombieownership.sol
├── migrations
└── test
. package-lock.json
. truffle-config.js
. truffle.js</code></pre>
<ul>
<li><code>test</code> 폴더에 주목!<h2 id="트러플-특징">트러플 특징</h2>
</li>
<li>언어는 <code>javascript</code>와 <code>solidity</code>를 지원한다.</li>
<li>이 튜토리얼에서는 간편한 작성을 위해 <code>javascript</code>로 코드를 짤 예정</li>
</ul>
<h2 id="테스트-코드-주의할-점">테스트 코드 주의할 점</h2>
<ul>
<li>테스트 코드를 짤 때에는 한 컨트랙트 당 하나의 테스트를 짜야 유지 보수에 좋다!</li>
</ul>
<h1 id="빌드">빌드</h1>
<h2 id="빌드-생성물">빌드 생성물</h2>
<ul>
<li>스마트 컨트랙트를 빌드할 때 솔리디티 컴파일러는 자동으로 <code>JSON</code> 형식의 파일을 생성한다.</li>
<li>이 파일 속에는 컨트랙트의 <code>binary representation</code>이 들어있다.</li>
<li>빌드하면서 나온 <code>JSON</code> 파일은 <code>build/contracts</code> 폴더에 저장된다.<h2 id="빌드-생성물-가져오기">빌드 생성물 가져오기</h2>
<pre><code class="language-js">const myAwesomeContract = artifacts.require(“myAwesomeContract”);</code></pre>
</li>
<li>이 코드는 <code>컨트랙트 abstraction</code>을 리턴한다.</li>
<li>말 그대로 <code>abstraction</code> 이라서 뒤에서 자바스크립트 객체를 생성해서 그거로 컨트랙트와 통신해야한다.<h1 id="contract-함수">Contract 함수</h1>
테스트를 단순하게 만들기 위해 <code>Truffle</code>은 <code>Mocha</code>안에 얇은 <code>wrapper</code>를 만든다.<h2 id="그룹-테스트">그룹 테스트</h2>
</li>
<li><code>contract()</code>함수를 불러서 실행하고, 이건 <code>mocha</code>의 <code>describe</code>함수에 테스팅 어카운트 리스트를 제공하고 정리 좀 해주고 <code>extends</code> 한다.</li>
<li><code>contract()</code>함수는 두개의 인자를 받는다. 어떤 것을 테스트할지 보여주는 <code>string</code>과 테스트를 <code>write</code>하는 장소를 보여주는 <code>callback()</code><h2 id="it으로-테스트-실행하기">it()으로 테스트 실행하기</h2>
</li>
<li><code>it()</code>이라는 함수를 불러서 테스트를 실행한다. </li>
<li>이것도 맨 처음에 어떤 것을 테스트할지 나타내는 <code>string</code>과 테스트 장소를 나타내는<code>callback()</code>으로 이루어져있다.<h2 id="최종-테스트-코드">최종 테스트 코드</h2>
<pre><code class="language-js">contract(&quot;MyAwesomeContract&quot;, (accounts) =&gt; {
 it(&quot;should be able to receive Ethers&quot;, await () =&gt; {
 })
})</code></pre>
</li>
<li>이건 <code>contract</code>의 <code>callback()</code>에서 <code>it()</code>을 부르는 형태</li>
<li>자바스크립트 비동기적이니까 <code>await</code>써서 <code>it()</code>함수 사용<h1 id="로컬-테스트">로컬 테스트</h1>
<h2 id="ganache">Ganache</h2>
</li>
<li>이더리움에 배포하기 전에 로컬 환경에서 스마트 컨트랙트를 테스트 해봐야 한다. <code>Ganache</code>라는 툴은 로컬 이더리움 네트워크를 셋업해준다.</li>
<li><code>Ganache</code>는 시작할 때마다 10개의 테스트 어카운트를 만들고 각각 100이더를 지급한다.</li>
<li><code>Ganache</code>와 <code>Truffle</code>은 잘 연동되어 있어서 우리는 <code>accounts</code>라는 배열로 어카운트에 접근할 수 있다.<h2 id="접근하기">접근하기</h2>
</li>
<li><code>account[0]</code>, <code>account[1]</code>같은 것은 테스트 시 읽기가 매우 불편하다. 그래서 <code>contract()</code> 함수 안에서 아래처럼 <code>initialize</code> 해서 쓴다.<pre><code class="language-js">let [alice, bob] = accounts;</code></pre>
<h1 id="테스트-순서">테스트 순서</h1>
<h2 id="set-up">Set up</h2>
</li>
<li><code>initial state</code>와 <code>input</code>을 <code>define</code>한다.<h3 id="js-인스턴스와-abstraction-만들어서-테스트하기">js 인스턴스와 abstraction 만들어서 테스트하기</h3>
<pre><code class="language-js">const contractInstance = await myAwesomeContract.new();</code></pre>
</li>
<li>위에서 만든 <code>abstraction</code>인 <code>myAwesomeContract</code>을 이용하여 실제 컨트랙트 통신에 이용할 수 있는 자바스크립트 인스턴스를 만든다.</li>
</ul>
<pre><code class="language-js">//좀비 array를 글로벌로 선언하고
const zombieNames = [&quot;Zombie #1&quot;, &quot;Zombie #2&quot;];

//아래처럼 좀비를 이용해 컨트랙트의 메소드를 실행할 수 있다.
//createRandomZombie는 좀비 객체를 받는 함수였을 것..
contractInstance.createRandomZombie(zombieNames[0]);</code></pre>
<ul>
<li>자바스크립트 객체를 만든 후에 메소드를 이용하는 형식!<h2 id="act">act</h2>
</li>
<li>실제로 코드를 테스트 하는 공간!</li>
<li>항상 하나의 로직만 테스트하도록 해야한다<h3 id="truffle-내장-기능-이용해서-address지정하기">Truffle 내장 기능 이용해서 address지정하기</h3>
<pre><code class="language-js">const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice});</code></pre>
</li>
<li><code>Setup</code> 부분 코드의 문제점은 좀비를 보내는 주소를 지정하지 못한다는 것이었다.</li>
<li>Truffle에서는 솔리디티의 주소 체계를 가져와서 이용할 수 있다. 따라서 <code>createRandomZombie</code> 함수에 <code>{from: alice}</code>를 넣어 <code>msg.sender</code>가 <code>address</code>를 <code>set</code>할 수 있도록 한다.<h3 id="artifactsrequire-이용">artifacts.require 이용</h3>
</li>
<li>트러플에서 제공하는 <code>artifacts.require</code>을 사용하면 <code>result.logs[0].args.name</code> 같이 이용할 수 있다.<h3 id="result에는-무엇이-저장될까">result에는 무엇이 저장될까?</h3>
</li>
<li><code>result.tx</code>: 트랜잭션 해시값</li>
<li><code>result.receipt</code>: 트랜잭션 영수증을 포함하는 <code>object</code></li>
<li><code>result.receipt.status</code>: <code>true</code>일 경우 트랜잭션 성공, 아닐경우 <code>false</code>를 리턴<h2 id="assert">assert</h2>
</li>
<li>결과물을 체크하는 공간<h3 id="내장된-라이브러리-사용">내장된 라이브러리 사용</h3>
</li>
<li><code>equal()</code>, <code>deepEqual()</code> 같이 빌트인된 <code>assertion</code> 을 이용한다.</li>
<li><code>assert.equal()</code> 이런 식으로 사용!<h2 id="최종-코드">최종 코드</h2>
<pre><code class="language-js">it(&quot;should be able to create a new zombie&quot;, async () =&gt; {
 //앞에 만든 abstraction으로 instance를 생성해서 사용
      const contractInstance = await CryptoZombies.new();
 //await으로 result에 반드시 값이 들어오도록 설정
      const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice});
 //영수증 받은거 맞는지 확인
      assert.equal(result.receipt.status, true);
 //좀비 이름이 같은지 확인
      assert.equal(result.logs[0].args.name,zombieNames[0]);
  })</code></pre>
<h1 id="hooks">Hooks</h1>
<h2 id="test-환경-자동설정">test 환경 자동설정</h2>
</li>
<li>테스트를 할 때마다 새롭게 인스턴스를 생성해야 하는데, Truffle에서는 이걸 <code>beforeEach()</code> 함수로 자동으로 설정해준다.<pre><code class="language-js">beforeEach(async () =&gt; {
// let&#39;s put here the code that creates a new contract instance
});
</code></pre>
</li>
</ul>
<pre><code>- `contract.new()` 대신에 `beforeEach()` 한 번이면 뚝딱 해결

## beforEach()로 매번 생성? 매번 삭제는 afterEach()
```js
//컨트랙트 삭제하는 kill 함수 정의
function kill() public onlyOwner {
   selfdestruct(owner());
}

//매번 실행이 끝날 때마다 컨트랙트를 kill
afterEach(async () =&gt; {
   await contractInstance.kill();
});</code></pre><ul>
<li>블록체인 테스트서버 과부하를 막기 위해 테스트가 끝난 후 kill해서 컨트랙트를 <code>destruct</code>한다!<h1 id="error-잡기">error 잡기</h1>
<pre><code class="language-js">try {
  //try to create the second zombie
  await contractInstance.createRandomZombie(zombieNames[1], {from: alice});
  assert(true);
}
catch (err) {
  return;
}
assert(false, &quot;The contract did not throw.&quot;);</code></pre>
</li>
<li><code>try-catch</code> 사용해서 에러가 났을 때 에러 잡음!</li>
<li>여기서 에러 나는 이유는 앨리스가 이미 좀비를 가지고 있는데 또 다른 좀비를 <code>create</code> 했기 때문이다.</li>
<li>보통 이 에러 잡는 구문은 따로 분리해 <code>helpers/utils</code>에 적고 <code>import</code> 해온다.<h1 id="account-간-통신-test">Account 간 통신 test</h1>
<h2 id="erc721-토큰-통신-방식">ERC721 토큰 통신 방식</h2>
</li>
</ul>
<ol>
<li><code>owner</code> 가 <code>transferFrom()</code>을 불러 <code>receiver</code>에게 토큰 전달<pre><code class="language-js">function transferFrom(address _from, address _to, uint256 _tokenId) external payable;</code></pre>
</li>
<li><code>owner</code> 가 <code>approve</code>를 부르고 <code>owner</code>나 <code>receiver</code>가 <code>transferForm()</code>을 보내는 형식<pre><code class="language-js">//approve 부르기
function approve(address _approved, uint256 _tokenId) external payable;
</code></pre>
</li>
</ol>
<p>//transferForm 부르기
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;</p>
<pre><code>- `approve`를 부르면 컨트랙트가 트랜잭션 정보(address)를 저장한다.
- `transferForm`을 부르면 자동으로 `msg.sender`가 `owner`나 `receiver`의 주소와 일치하는지를 검사한다. 
- 일치한다면 토큰을 보낸다.
## Truffle이 제공하는 context() 이용
### 1번 케이스 테스트 코드
```js
context(&quot;with the single-step transfer scenario&quot;, async () =&gt; {
    it(&quot;should transfer a zombie&quot;, async () =&gt; {
      // TODO: Test the single-step transfer scenario.
      //랜덤 좀비 생성 코드
      const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice});
      //좀비 아이디가 로그에 찍힌 좀비 아이디와 같은지 확인
            const zombieId = result.logs[0].args.zombieId.toNumber();
            await contractInstance.transferFrom(alice, bob, zombieId, {from: alice});
      //새로운 주인이 bob이 되었는지 확인
            const newOwner = await contractInstance.ownerOf(zombieId);
            assert.equal(newOwner, bob);
    })
})</code></pre><h3 id="2번-케이스-테스트-코드">2번 케이스 테스트 코드</h3>
<pre><code class="language-js"> context(&quot;with the two-step transfer scenario&quot;, async () =&gt; {
   //alice가 transferFrom을 부름
        it(&quot;should approve and then transfer a zombie when the approved address calls transferFrom&quot;, async () =&gt; {
            const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice});
            const zombieId = result.logs[0].args.zombieId.toNumber();
            await contractInstance.approve(bob, zombieId, {from: alice});
            await contractInstance.transferFrom(alice, bob, zombieId, {from: bob});
            const newOwner = await contractInstance.ownerOf(zombieId);
            assert.equal(newOwner,bob);
        })
   //bob이 transferForm을 부름
        it(&quot;should approve and then transfer a zombie when the owner calls transferFrom&quot;, async () =&gt; {
            const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice});
            const zombieId = result.logs[0].args.zombieId.toNumber();
            await contractInstance.approve(bob, zombieId, {from: alice});
            await contractInstance.transferFrom(alice, bob, zombieId, {from: alice});
            const newOwner = await contractInstance.ownerOf(zombieId);
            assert.equal(newOwner,bob);
         })
    })</code></pre>
<ul>
<li>위 테스트코드는 항상 모든 테스트를 검사함..</li>
<li>따라서 아래처럼 <code>x</code>를 붙여 트러플이 검사를 스킵할 수 있도록 조정</li>
</ul>
<pre><code class="language-js">//context 앞에 x
xcontext(&quot;with the single-step transfer scenario&quot;, async () =&gt; {
    it(&quot;should transfer a zombie&quot;, async () =&gt; {
      // TODO: Test the single-step transfer scenario.
    })
})
//it 앞에 x
context(&quot;with the single-step transfer scenario&quot;, async () =&gt; {
    xit(&quot;should transfer a zombie&quot;, async () =&gt; {
      // TODO: Test the single-step transfer scenario.
    })
})</code></pre>
<h2 id="테스트-결과">테스트 결과</h2>
<pre><code class="language-shell">Contract: CryptoZombies
    ✓ should be able to create a new zombie (199ms)
    ✓ should not allow two zombies (175ms)
    with the single-step transfer scenario
      - should transfer a zombie
    with the two-step transfer scenario
      - should approve and then transfer a zombie when the owner calls transferFrom
      - should approve and then transfer a zombie when the approved address calls transferFrom


  2 passing (827ms)
  3 pending</code></pre>
<h1 id="time-traveling">Time traveling</h1>
<h3 id="이전-좀비-게임-test-문제점---cooldown">이전 좀비 게임 test 문제점 - coolDown</h3>
<pre><code class="language-js">function _createZombie(string _name, uint _dna) internal {
  uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1;
  zombieToOwner[id] = msg.sender;
  ownerZombieCount[msg.sender] = ownerZombieCount[msg.sender].add(1);
  emit NewZombie(id, _name, _dna);
}</code></pre>
<ul>
<li><code>coolDown</code> 시간 설정 때문에 한 번 좀비를 생성하면 하루동안 생성이 안됨.. 그래서 테스트 코드를 여러번 돌려볼 수 없음</li>
<li>그래서 time traveling을 적용!<h2 id="ganache의-시간-여행-함수">Ganache의 시간 여행 함수</h2>
<h3 id="시간-조정-함수-두-가지">시간 조정 함수 두 가지</h3>
</li>
</ul>
<ol>
<li><code>evm_increaseTime</code>: 다음 블럭의 시간을 <code>increase</code></li>
<li><code>evm_mine</code>: 새로운 블록을 <code>mine</code><h3 id="함수가-작동하는-원리">함수가 작동하는 원리</h3>
</li>
</ol>
<ul>
<li>새로운 블록이 채굴될 때마다 채굴자는 타임스탬프를 블록에 넣는다. </li>
<li><code>evm_increaseTime</code>을 작동시킨다. 블록체인은 이미 기록된 블록은 변할 수가 없으니 당장 시간에는 아무 변화가 없고, 대신 다음 채굴되는 블록의 시간을 변화시킨다.</li>
<li><code>evm_mine</code>을 통해 블록을 채굴하면 시간이 앞으로 당겨져있는 것을 볼 수 있다.<h3 id="time-travel-코드">time travel 코드</h3>
<pre><code class="language-js">await web3.currentProvider.sendAsync({
jsonrpc: &quot;2.0&quot;,
method: &quot;evm_increaseTime&quot;,
params: [86400],  // there are 86400 seconds in a day
id: new Date().getTime()
}, () =&gt; { });
</code></pre>
</li>
</ul>
<p>web3.currentProvider.send({
    jsonrpc: &#39;2.0&#39;,
    method: &#39;evm_mine&#39;,
    params: [],
    id: new Date().getTime()
});</p>
<pre><code>- 이건 `helper` 쪽으로 빼서 작성해놓는다.

### 가져와서 이용하는 코드
```js
await time.increase(time.duration.days(1))</code></pre><ul>
<li><code>time</code> 임포트해와서 가져다가 사용!<h3 id="트러플에서-돌려보면">트러플에서 돌려보면?</h3>
<pre><code class="language-shell">Contract: CryptoZombies
  ✓ should be able to create a new zombie (119ms)
  ✓ should not allow two zombies (112ms)
  ✓ should return the correct owner (109ms)
  ✓ zombies should be able to attack another zombie (475ms)
  with the single-step transfer scenario
    ✓ should transfer a zombie (235ms)
  with the two-step transfer scenario
    ✓ should approve and then transfer a zombie when the owner calls transferFrom (181ms)
    ✓ should approve and then transfer a zombie when the approved address calls transferFrom (152ms)</code></pre>
<h1 id="chai를-이용한-assertion">Chai를 이용한 assertion</h1>
<blockquote>
<p>위의 assertion은 읽기가 힘들어서 chai의 assertion 모듈을 많이 따다가 쓴다!</p>
</blockquote>
</li>
</ul>
<h2 id="chai의-세-가지-assertion-type">Chai의 세 가지 Assertion type</h2>
<p>That said, let&#39;s take a look at the three kinds of assertion styles bundled into Chai:</p>
<ol>
<li><p><code>expect</code>: 체인의 <code>natural language</code> <code>assertions</code> 을 다음과 같이 제공한다.</p>
<pre><code class="language-js">let lessonTitle = &quot;Testing Smart Contracts with Truffle&quot;;
expect(lessonTitle).to.be.a(&quot;string&quot;);</code></pre>
</li>
<li><p><code>should</code>: <code>should</code> 로 시작하고 인터페이스와 비슷한 <code>assertion</code>을 제공한다.</p>
<pre><code class="language-js">let lessonTitle = &quot;Testing Smart Contracts with Truffle&quot;;
lessonTitle.should.be.a(&quot;string&quot;);</code></pre>
</li>
<li><p><code>assert</code>: <code>notation</code> 을 제공하고 브라우저에서 작동할 수 있는 추가적인 테스트를 제공한다. </p>
<pre><code class="language-js">let lessonTitle = &quot;Testing Smart Contracts with Truffle&quot;;
assert.typeOf(lessonTitle, &quot;string&quot;);</code></pre>
<h2 id="expecttoequal">expect().to.equal()</h2>
</li>
</ol>
<pre><code class="language-js">let zombieName = &#39;My Awesome Zombie&#39;;
expect(zombieName).to.equal(&#39;My Awesome Zombie&#39;);</code></pre>
<ul>
<li><code>assertion</code>들을 위 코드처럼 <code>chai</code> 문법으로 바꾸어 사용한다!</li>
</ul>
<h1 id="loom에서-테스트하기">Loom에서 테스트하기</h1>
<ul>
<li>위 코드를 고칠 필요 없이 바로 사용할 수 있음<h2 id="loom-network-object">Loom network object</h2>
<pre><code class="language-js">  loom_testnet: {
    provider: function() {
      const privateKey = &#39;YOUR_PRIVATE_KEY&#39;;
      const chainId = &#39;extdev-plasma-us1&#39;;
      const writeUrl = &#39;wss://extdev-basechain-us1.dappchains.com/websocket&#39;;
      const readUrl = &#39;wss://extdev-basechain-us1.dappchains.com/queryws&#39;;
      return new LoomTruffleProvider(chainId, writeUrl, readUrl, privateKey);
    },
    network_id: &#39;extdev&#39;
  }</code></pre>
<h2 id="트러플과-loom-통신">트러플과 Loom 통신</h2>
<h3 id="trufflejs">truffle.js</h3>
<pre><code class="language-js">const HDWalletProvider = require(&quot;truffle-hdwallet-provider&quot;);
const LoomTruffleProvider = require(&#39;loom-truffle-provider&#39;);
const mnemonic = &quot;YOUR MNEMONIC HERE&quot;;
module.exports = {
  // Object with configuration for each network
  networks: {
      //development
      development: {
          host: &quot;127.0.0.1&quot;,
          port: 7545,
          network_id: &quot;*&quot;,
          gas: 9500000
      },
      // Configuration for Ethereum Mainnet
      mainnet: {
          provider: function() {
              return new HDWalletProvider(mnemonic, &quot;https://mainnet.infura.io/v3/&lt;YOUR_INFURA_API_KEY&gt;&quot;)
          },
          network_id: &quot;1&quot; // Match any network id
      },
      // Configuration for Rinkeby Metwork
      rinkeby: {
          provider: function() {
              // Setting the provider with the Infura Rinkeby address and Token
              return new HDWalletProvider(mnemonic, &quot;https://rinkeby.infura.io/v3/&lt;YOUR_INFURA_API_KEY&gt;&quot;)
          },
          network_id: 4
      },
      // Configuration for Loom Testnet
      loom_testnet: {
          provider: function() {
              const privateKey = &#39;YOUR_PRIVATE_KEY&#39;;
              const chainId = &#39;extdev-plasma-us1&#39;;
              const writeUrl = &#39;wss://extdev-basechain-us1.dappchains.com/websocket&#39;;
              const readUrl = &#39;wss://extdev-basechain-us1.dappchains.com/queryws&#39;;
              // TODO: Replace the line below
              return new LoomTruffleProvider(chainId, writeUrl, readUrl, privateKey);
          },
          network_id: &#39;9545242630824&#39;
      }
  },
  compilers: {
      solc: {
          version: &quot;0.4.25&quot;
      }
  }
};</code></pre>
<h3 id="loomprovider-이용">LoomProvider 이용</h3>
</li>
<li>현재 디폴트로 사용하고 있는 <code>HDWallet</code>대신 <code>LoomProvider</code>를 이용하고, 프로바이더로 <code>account</code> 정보를 보내야 한다.<pre><code class="language-js">return new LoomTruffleProvider(chainId, writeUrl, readUrl, privateKey)</code></pre>
</li>
<li>이 코드를 아래처럼 바꾸어야 돌아감!<pre><code class="language-js">const loomTruffleProvider = new LoomTruffleProvider(chainId, writeUrl, readUrl, privateKey);
loomTruffleProvider.createExtraAccountsFromMnemonic(mnemonic, 10);
return loomTruffleProvider;</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] BOJ 2042 구간 합 구하기]]></title>
            <link>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-2042-%EA%B5%AC%EA%B0%84-%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-2042-%EA%B5%AC%EA%B0%84-%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 22 Feb 2021 11:08:11 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/2042">https://www.acmicpc.net/problem/2042</a></p>
<h1 id="문제-해석">문제 해석</h1>
<h2 id="문제">문제</h2>
<p>어떤 N개의 수가 주어져 있다. 그런데 중간에 수의 변경이 빈번히 일어나고 그 중간에 어떤 부분의 합을 구하려 한다. 만약에 1,2,3,4,5 라는 수가 있고, 3번째 수를 6으로 바꾸고 2번째부터 5번째까지 합을 구하라고 한다면 17을 출력하면 되는 것이다. 그리고 그 상태에서 다섯 번째 수를 2로 바꾸고 3번째부터 5번째까지 합을 구하라고 한다면 12가 될 것이다.</p>
<h2 id="입력">입력</h2>
<p>첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)과 M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000) 가 주어진다. M은 수의 변경이 일어나는 횟수이고, K는 구간의 합을 구하는 횟수이다. 그리고 둘째 줄부터 N+1번째 줄까지 N개의 수가 주어진다. 그리고 N+2번째 줄부터 N+M+K+1번째 줄까지 세 개의 정수 a, b, c가 주어지는데, a가 1인 경우 b(1 ≤ b ≤ N)번째 수를 c로 바꾸고 a가 2인 경우에는 b(1 ≤ b ≤ N)번째 수부터 c(b ≤ c ≤ N)번째 수까지의 합을 구하여 출력하면 된다.</p>
<p>입력으로 주어지는 모든 수는 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.</p>
<h2 id="출력">출력</h2>
<p>첫째 줄부터 K줄에 걸쳐 구한 구간의 합을 출력한다. 단, 정답은 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.</p>
<h1 id="문제-풀이">문제 풀이</h1>
<h2 id="👊🏻-무식하게-풀기">👊🏻 무식하게 풀기</h2>
<h3 id="풀이-과정">풀이 과정</h3>
<p>문제 자체는 단순한 편이나, <code>입력</code>이 엄청 큰 문제였다.
일단 무식하게 풀기는 그냥 문제에서 하라는대로 풀었고, 특별한 자료구조를 사용하지 않고 그냥 <code>배열</code>을 이용했다.</p>
<ol>
<li>입력을 받아 숫자를 배열에 저장한다.</li>
<li><code>a</code>가 <code>1</code>일 경우 문제에서 하라는 대로 배열의 위치를 바꾼다.</li>
<li><code>a</code>가 <code>2</code>일 경우 문제에서 하라는 대로 구간합을 구해 출력한다.<h3 id="풀이-코드">풀이 코드</h3>
<pre><code class="language-java">package Tree;
</code></pre>
</li>
</ol>
<p>import java.util.Scanner;</p>
<p>public class BOJ2042 {
    static int[] numbers;
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int N = s.nextInt();
        int M = s.nextInt();
        int K = s.nextInt();
        numbers = new int[N+1];
        for (int i = 1; i &lt; N+1; i++) {
            numbers[i] = s.nextInt();
        }
        for (int i = 0; i &lt; M+K; i++) {
            int a = s.nextInt();
            int b = s.nextInt();
            int c = s.nextInt();
            sum(a, b, c);
        }
    }</p>
<pre><code>static void sum(int a, int b, int c) {
    if(a==1){
        numbers[b] = c;
    }
    else if(a==2){
        int result = 0;
        for (int i = b; i &lt;= c; i++) {
            result = result + numbers[i];
        }
        System.out.println(result);
    }
}</code></pre><p>}</p>
<pre><code>### 무식한 풀이 결과
![](https://images.velog.io/images/dandev_sw/post/2d0378fd-c4c3-43b3-bb31-54db09bad35d/image.png)
- 결과는 예상대로 `시간 초과`.. 
- 코드 뚝딱 2분컷 해서 억울하지는 않아요..아..
- 담담한척 하지만 사실 많이 슬펐다.. 진짜 `트리`를 써야하나..?! `DP`로 풀어볼까..? 고민했다.. 근데 트리 문제 야매로 푸는거 자꾸 버릇들어서 버릇을 고쳐보기로함..!
## 🌲 세그먼트 트리로 풀기</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[#6 Crypto Zombies] 앱 프론트엔드 & Web3.js]]></title>
            <link>https://velog.io/@dandev_sw/6-Crypto-Zombies-%EC%95%B1-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-Web3.js</link>
            <guid>https://velog.io/@dandev_sw/6-Crypto-Zombies-%EC%95%B1-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-Web3.js</guid>
            <pubDate>Sun, 21 Feb 2021 04:04:15 GMT</pubDate>
            <description><![CDATA[<h1 id="web3js">Web3.js</h1>
<h2 id="web3js란">Web3.js란?</h2>
<p>이더리움 네트워크는 노드로 구성되어 있고, 각 노드는 블록체인의 복사본을 가지고 있다. 스마트 컨트랙트의 함수를 실행하고자 한다면, 이 노드들 중 하나에 질의를 보내 아래 내용을 전달해야 한다.</p>
<ol>
<li>스마트 컨트랙트의 주소</li>
<li>실행하고자 하는 함수</li>
<li>그 함수에 전달하고자 하는 변수</li>
</ol>
<p>이더리움 노드들은 <code>JSON-RPC</code>라고 불리는 언어로만 소통한다. </p>
<p>퀴리는 아래와 같이 생겼다.</p>
<pre><code class="language-json">{&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;eth_sendTransaction&quot;,&quot;params&quot;:[{&quot;from&quot;:&quot;0xb60e8dd61c5d32be8058bb8eb970870f07233155&quot;,&quot;to&quot;:&quot;0xd46e8dd67c5d32be8058bb8eb970870f07244567&quot;,&quot;gas&quot;:&quot;0x76c0&quot;,&quot;gasPrice&quot;:&quot;0x9184e72a000&quot;,&quot;value&quot;:&quot;0x9184e72a&quot;,&quot;data&quot;:&quot;0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675&quot;}],&quot;id&quot;:1}</code></pre>
<p>다행히도, <code>Web3.js</code> 편리하고 쉽게 읽을 수 있는 자바스크립트 인터페이스로 이를 전달할 수 있게 해준다.</p>
<p>위의 쿼리 대신 아래와 같이 코드에서 함수를 호출할 수 있다.</p>
<pre><code class="language-javascript">
CryptoZombies.methods.createRandomZombie(&quot;Vitalik Nakamoto 🤔&quot;)
  .send({ from: &quot;0xb60e8dd61c5d32be8058bb8eb970870f07233155&quot;, gas: &quot;3000000&quot; })</code></pre>
<h2 id="web3-프로바이더">Web3 프로바이더</h2>
<p>우리가 처음 필요로 하는 것은 Web3 프로바이더(Provider)이다.</p>
<ul>
<li>이더리움은 똑같은 데이터의 복사본을 공유하는 <code>노드</code>들로 구성되어 있다. <code>Web3.js</code>에서 <code>Web3 프로바이더</code>를 설정하는 것은 코드에 읽기와 쓰기를 처리하려면 <code>어떤 노드</code>와 통신을 해야 하는지를 설정하는 과정이다.</li>
<li>이는 전통적인 웹 앱에서 <code>API 호출</code>을 위해 원격 웹 <code>서버의 URL</code>을 설정하는 것과 같다.</li>
</ul>
<h2 id="infura---간편한-프로바이더">Infura - 간편한 프로바이더</h2>
<p><code>Infura</code>는 빠른 읽기를 위한 캐시 계층을 포함하는 다수의 이더리움 노드를 운영하는 서비스이다. <code>Infura</code>를 프로바이더로 사용하면, 자네만의 이더리움을 설치하고 계속 유지할 필요 없이 이더리움 블록체인과 메세지를 확실히 주고받을 수 있다.</p>
<p>다음과 같이 Web3에 Web3 프로바이더로 Infura를 쓰도록 설정할 수 있다.</p>
<pre><code class="language-javascript">var web3 = new Web3(new Web3.providers.WebsocketProvider(&quot;wss://mainnet.infura.io/ws&quot;));</code></pre>
<p>하지만, 많은 사용자들이 DApp에 읽고 쓰기 연산을 할 것이니, 이 사용자들이 개인 키를 사용해 트랜잭션에 서명을 하는 과정을 추가해야 한다.</p>
<blockquote>
<p>💊 참고: 이더리움(그리고 일반적으로 블록체인)은 트랜잭션에 전자 서명을 하기 위해 공개/개인 키 쌍을 사용한다. 만약 블록체인에서 어떤 데이터를 변경하면, 나의 공개 키를 통해 내가 거기 서명을 한 사람이라고 증명할 수 있다 - 하지만 아무도 내 개인 키를 모르기 때문에, 내 트랜잭션을 누구도 위조할 수 없다.</p>
</blockquote>
<h2 id="메타마스크metamask---사용자-개인키-관리">메타마스크(Metamask) - 사용자 개인키 관리</h2>
<h3 id="메타마스크의-정의">메타마스크의 정의</h3>
<p>메타마스크는 사용자들이 <code>이더리움 계정</code>과 <code>개인 키</code>를 안전하게 관리할 수 있게 해주는 크롬과 파이어폭스의 브라우저 확장 프로그램이다. 그리고 해당 계정들을 써서 <code>Web3.js</code>를 사용하는 웹사이트들과 상호작용을 할 수 있도록 해준다.</p>
<p>디앱을 메타마스크와 호환한다면, 디앱 사용자들은 웹에서도  자유롭게 디앱 서비스를 이용할 수 있다.</p>
<blockquote>
<p>💊 참고: 메타마스크는 내부적으로 <code>Infura</code>의 서버를 <code>Web3</code> 프로바이더로 사용한다. 하지만 사용자들에게 그들만의 Web3 프로바이더를 선택할 수 있는 옵션을 주기도 한다. 즉 메타마스크의 Web3 프로바이더를 사용하면, 사용자에게 선택권을 주는 것이기도 하면서 앱의 개인 키 문제도 해결할 수 있다.</p>
</blockquote>
<h3 id="메타마스크의-web3-프로바이더-사용하기">메타마스크의 Web3 프로바이더 사용하기</h3>
<p>메타마스크는 <code>web3</code>라는 전역 <code>자바스크립트 객체</code>를 통해 브라우저에 <code>Web3</code> 프로바이더를 주입하네. 그러므로 앱에서는 <code>web3</code>가 존재하는지 확인하고, 만약 존재한다면 <code>web3.currentProvider</code>를 프로바이더로서 사용하면 된다.</p>
<p>아래는 메타마스크에서 제공하는 코드이다. 사용자가 메타마스크를 설치했는지 확인하고 설치가 안 된 경우 우리 앱을 사용하려면 메타마스크를 설치해야 한다고 알려주는 코드!</p>
<pre><code class="language-javascript">window.addEventListener(&#39;load&#39;, function() {

  // Web3가 브라우저에 주입되었는지 확인(Mist/MetaMask)
  if (typeof web3 !== &#39;undefined&#39;) {
    // Mist/MetaMask의 프로바이더 사용
    web3js = new Web3(web3.currentProvider);
  } else {
    // 사용자가 Metamask를 설치하지 않은 경우에 대해 처리
    // 사용자들에게 Metamask를 설치하라는 등의 메세지를 보여줄 것
  }

  // 이제 앱을 시작하고 web3에 자유롭게 접근할 수 있다
  startApp()

})</code></pre>
<blockquote>
<p>참고: 메타마스크 말고도 사용자들이 쓸 수 있는 다른 개인 키 관리 프로그램도 있다. <code>미스트(Mist)</code> 웹 브라우저 같은 것들이지. 하지만, 그것들도 모두 web3 변수를 주입하는 동일한 형태를 사용한다. 그러니 사용자들이 다른 프로그램을 쓰더라도 여기서 설명하는 방식으로 사용자의 Web3 프로바이더를 인식해야 한다.</p>
</blockquote>
<h1 id="컨트랙트와-대화하기">컨트랙트와 대화하기</h1>
<p>이제 메타마스크의 Web3 프로바이더로 Web3.js를 초기화했으니, 우리의 스마트 컨트랙트와 통신을 할 수 있도록 만들자.</p>
<p><code>Web3.js</code>는 스마트 컨트랙트와 통신을 위해 <code>2가지</code>를 필요로 한다.</p>
<ol>
<li>컨트랙트의 <code>주소</code></li>
<li><code>ABI</code></li>
</ol>
<h2 id="컨트랙트-주소란">컨트랙트 주소란?</h2>
<p>스마트 컨트랙트를 모두 작성한 후, 컨트랙트를 컴파일한 후 이더리움에 배포한다.</p>
<p>컨트랙트를 배포한 후, 해당 컨트랙트는 영원히 존재하는, 이더리움 상에서 <code>고정된 주소</code>를 얻을 것이네. 레슨2를 상기해보면, 이더리움 메인넷에서 크립토키티의 주소는 <code>0x06012c8cf97BEaD5deAe237070F9587f8E7A266d</code>였다.</p>
<p>스마트 컨트랙트와 통신을 하기 위해서는 컨트랙트 배포 후 이 주소를 복사해 사용해야 한다.</p>
<h2 id="컨트랙트-abi란">컨트랙트 ABI란?</h2>
<h3 id="컨트랙트-abi의-정의">컨트랙트 ABI의 정의</h3>
<p><code>ABI</code>는 <code>Application Binary Interface</code>의 줄임말이다. 기본적으로 <code>JSON</code> 형태로 <strong>컨트랙트의 메소드를 표현</strong>한다. 컨트랙트가 이해할 수 있도록 하려면 <code>Web3.js</code>가 어떤 형태로 함수 호출을 해야 하는지 알려주는 것이다.</p>
<h3 id="abi-얻는-방법">ABI 얻는 방법?</h3>
<p>이더리움에 배포하기 위해 컨트랙트를 컴파일할 때 솔리디티 컴파일러는<code>ABI</code>를 반환한다. 그러니 컨트랙트 주소와 함께 이를 복사하여 저장해놓고 사용하면 된다.</p>
<h2 id="web3js-컨트랙트-인스턴스화하기">Web3.js 컨트랙트 인스턴스화하기</h2>
<p>컨트랙트의 주소와 ABI를 얻고 나면, 다음과 같이 Web3에서 인스턴스화할 수 있다.</p>
<pre><code class="language-javascript">// myContract 인스턴스화
var myContract = new web3js.eth.Contract(myABI, myContractAddress);</code></pre>
<h2 id="컨트랙트-함수-호출하기">컨트랙트 함수 호출하기</h2>
<p><code>Web3.js</code>는 컨트랙트의 함수를 호출하기 위해 우리가 사용할 두 개의 메소드를 가지고 있다. - <code>call</code>과 <code>send</code></p>
<ul>
<li>둘의 차이점은 <code>call</code>은 <code>view</code>와 <code>pure</code>함수에서만 사용 가능하고, <code>send</code>는 그 이외 함수에서 사용된다는 점이다.</li>
<li>따라서 <code>call</code>은 <code>가스</code>를 소모하지 않는다.</li>
</ul>
<h3 id="call-정의">Call 정의</h3>
<p><code>call</code>은 <code>view</code>와 <code>pure</code> 함수에서 사용된다. 로컬 노드에서만 실행되고, 블록체인에 트랜잭션을 만들지 않는다.</p>
<blockquote>
<p>📝 복습: <code>view</code>와 <code>pure</code> 함수는 읽기 전용이고 블록체인에서 상태를 변경한다. 가스를 전혀 소모하지 않고, 메타마스크에서 트랜잭션에 서명하라고 사용자에게 창을 띄우지도 않는다.</p>
</blockquote>
<h3 id="call-사용하기">call 사용하기</h3>
<p><code>Web3.js</code> 를 사용하여, 다음과 같이 <code>123</code>을 매개 변수로 <code>myMethod</code>라는 이름의 함수를 <code>call</code>할 수 있다.</p>
<pre><code class="language-javascript">myContract.methods.myMethod(123).call()</code></pre>
<h3 id="send-정의">Send 정의</h3>
<p><code>send</code>는 트랜잭션을 만들고 블록체인 상의 데이터를 변경한다. <code>view</code>와 <code>pure</code>가 아닌 모든 함수에 대해 <code>send</code>를 사용해야 한다.</p>
<blockquote>
<p>참고: 트랜잭션을 <code>send</code>하는 것은 사용자에게 <code>가스</code>를 지불하도록 하고, 메타마스크에서 트랜잭션에 서명하라고 창을 띄울 것이다. Web3 프로바이더로 메타마스크를 사용할 때, <code>send()</code>를 호출하면 자동으로 이 모든 것이 이루어지고, 우리의 코드에 어떤 특별한 것도 추가할 필요가 없다. 꽤나 훌륭하지!?</p>
</blockquote>
<h3 id="send-사용하기">send 사용하기</h3>
<p><code>Web3.js</code>를 사용하여, 다음과 같이 <code>123</code>을 매개 변수로 <code>myMethod</code>라는 이름의 함수를 호출하는 트랜잭션을 <code>send</code>할 수 있다.</p>
<pre><code class="language-javascript">myContract.methods.myMethod(123).send()</code></pre>
<p>구문은 <code>call()</code>과 거의 똑같다.</p>
<h3 id="call과-다른-send만의-특징">call과 다른 send만의 특징</h3>
<ol>
<li>트랜잭션을 전송(send)하려면 함수를 호출한 사람의 from 주소가 필요하다(솔리디티 코드에서는 msg.sender). </li>
</ol>
<ul>
<li>따라서, 메타마스크가 나타나 디앱 사용자들에게 서명을 하도록 한다.</li>
</ul>
<ol start="2">
<li><p>트랜잭션 전송(send)은 가스를 소모한다.</p>
</li>
<li><p>사용자가 트랜잭션 전송을 하고 난 후 실제로 블록체인에 적용될 때까지는 상당한 지연이 발생한다. </p>
</li>
</ol>
<ul>
<li>트랜잭션이 블록에 포함될 때까지 기다려야 하는데, 이더리움의 평균 블록 시간은 <code>15초</code>이다. </li>
<li>만약 이더리움에 보류 중인 거래가 많거나 사용자가 가스 가격을 지나치게 낮게 보낼 경우, 우리 트랜잭션이 블록에 포함되길 기다려야 하고, 이는 몇 분씩 시간이 소요될 수 있다.<h2 id="데이터-받기">데이터 받기</h2>
<h3 id="컨트랙트-데이터-접근---call함수-이용">컨트랙트 데이터 접근 - call함수 이용</h3>
</li>
</ul>
<p>좀비 배열을 public으로 만들었었다.</p>
<pre><code class="language-js">Zombie[] public zombies;</code></pre>
<p>솔리디티에서, <code>public</code>으로 변수를 선언하면 자동으로 같은 이름의 퍼블릭 <code>getter</code> 함수를 만들어 낸다. 그러니 ID 15인 좀비를 찾길 원한다면, 변수를 함수인 것처럼 호출할 수 있다 : <code>zombies(15)</code>.</p>
<p>프론트엔드에서 좀비 ID를 받아 해당 좀비에 대해 컨트랙트에 질의를 보내고, 결과를 반환하는 자바스크립트 함수를 작성하는 방법은 다음과 같다.</p>
<pre><code class="language-js">function getZombieDetails(id) {
  return cryptoZombies.methods.zombies(id).call()
}

// 함수를 호출하고 결과를 가지고 무언가를 처리:
getZombieDetails(15)
.then(function(result) {
  console.log(&quot;Zombie 15: &quot; + JSON.stringify(result));
});</code></pre>
<p><code>cryptoZombies.methods.zombies(id).call()</code>는 Web3 프로바이더와 통신하여 컨트랙트의 <code>Zombie[] public zombies</code>에서 인덱스가 <code>id</code>인 좀비를 반환하도록 할 것이다.</p>
<ul>
<li>이 과정은 외부 서버로 API 호출을 하는 것처럼 비동기적으로 일어난다. 즉 Web3는 여기서 <code>Promise</code>를 반환한다.</li>
</ul>
<p><code>Promise</code>가 만들어지면(이는 Web3 프로바이더로부터 응답을 받았다는 것을 의미하지) 우리 예제 코드는 <code>then</code> 문장을 실행하고, 여기서 <code>result</code>를 콘솔에 로그로 기록한다.</p>
<p><code>result</code>는 다음과 같이 생긴 자바스크립트 객체이다.</p>
<pre><code class="language-js">
{
  &quot;name&quot;: &quot;H4XF13LD MORRIS&#39;S COOLER OLDER BROTHER&quot;,
  &quot;dna&quot;: &quot;1337133713371337&quot;,
  &quot;level&quot;: &quot;9999&quot;,
  &quot;readyTime&quot;: &quot;1522498671&quot;,
  &quot;winCount&quot;: &quot;999999999&quot;,
  &quot;lossCount&quot;: &quot;0&quot; // Obviously.
}</code></pre>
<h3 id="메타마스크에서-사용자-계정-가져오기">메타마스크에서 사용자 계정 가져오기</h3>
<p>메타마스크는 확장 프로그램 안에서 사용자들이 다수의 계정을 관리할 수 있도록 해준다.</p>
<p>우리는 주입되어 있는 <code>web3</code> 변수에 현재 활성화된 계정이 무엇인지 다음처럼 확인할 수 있다.</p>
<pre><code class="language-js">var userAccount = web3.eth.accounts[0]</code></pre>
<ul>
<li>사용자가 언제든지 메타마스크에서 활성화된 계정을 바꿀 수 있다.</li>
<li>따라서, 이 변수의 값이 바뀌었는지 확인하기 위해 계속 감시를 하고 값이 바뀌면 그에 따라 UI를 업데이트해야 한다. </li>
</ul>
<p>이를 위해 다음과 같이 <code>setInterval</code>을 쓸 수 있다.</p>
<pre><code class="language-js">var accountInterval = setInterval(function() {
  // 계정이 바뀌었는지 확인
  if (web3.eth.accounts[0] !== userAccount) {
    userAccount = web3.eth.accounts[0];
    // 새 계정에 대한 UI로 업데이트하기 위한 함수 호출
    updateInterface();
  }
}, 100);</code></pre>
<ul>
<li>여기서는 <code>userAccount</code>가 여전히 <code>web3.eth.accounts[0]</code>과 같은지 확인하기 위해 100밀리초마다 확인한다.</li>
<li>그렇지 않다면, <code>userAccount</code>에 현재 활성화된 계정을 다시 할당하고, 화면을 업데이트하기 위한 함수를 호출한다.</li>
</ul>
<h3 id="좀비-데이터-보여주기---간단한-예제">좀비 데이터 보여주기 - 간단한 예제</h3>
<p>jQuery 이용 정보 출력</p>
<pre><code class="language-html">// 우리 컨트랙트에서 좀비 상세 정보를 찾아, `zombie` 객체 반환
getZombieDetails(id)
.then(function(zombie) {
  // HTML에 변수를 넣기 위해 ES6의 &quot;template literal&quot; 사용
  // 각각을 #zombies div에 붙여넣기
  $(&quot;#zombies&quot;).append(`&lt;div class=&quot;zombie&quot;&gt;
    &lt;ul&gt;
      &lt;li&gt;Name: ${zombie.name}&lt;/li&gt;
      &lt;li&gt;DNA: ${zombie.dna}&lt;/li&gt;
      &lt;li&gt;Level: ${zombie.level}&lt;/li&gt;
      &lt;li&gt;Wins: ${zombie.winCount}&lt;/li&gt;
      &lt;li&gt;Losses: ${zombie.lossCount}&lt;/li&gt;
      &lt;li&gt;Ready Time: ${zombie.readyTime}&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;`);
});</code></pre>
<h3 id="메타마스크-이용해-webjs에서-함수-호출하기">메타마스크 이용해 web.js에서 함수 호출하기</h3>
<p>사용자가 호출할 함수 컨트랙트 <code>createRandomZombie</code>.</p>
<p>여기서 우리 컨트랙트의 솔리디티 코드는 아래와 같다.</p>
<pre><code class="language-js">function createRandomZombie(string _name) public {
  require(ownerZombieCount[msg.sender] == 0);
  uint randDna = _generateRandomDna(_name);
  randDna = randDna - randDna % 100;
  _createZombie(_name, randDna);
}</code></pre>
<p>아래 코드는 메타마스크를 사용해 <code>Web3.js</code>에서 위 함수를 호출하는 방법이다.</p>
<pre><code class="language-js">function createRandomZombie(name) {
  // 시간이 꽤 걸릴 수 있으니, 트랜잭션이 보내졌다는 것을
  // 유저가 알 수 있도록 UI를 업데이트해야 함
  $(&quot;#txStatus&quot;).text(&quot;Creating new zombie on the blockchain. This may take a while...&quot;);
  // 우리 컨트랙트에 전송하기:
  return CryptoZombies.methods.createRandomZombie(name)
  .send({ from: userAccount })
  .on(&quot;receipt&quot;, function(receipt) {
    $(&quot;#txStatus&quot;).text(&quot;Successfully created &quot; + name + &quot;!&quot;);
    // 블록체인에 트랜잭션이 반영되었으며, UI를 다시 그려야 함
    getZombiesByOwner(userAccount).then(displayZombies);
  })
  .on(&quot;error&quot;, function(error) {
    // 사용자들에게 트랜잭션이 실패했음을 알려주기 위한 처리
    $(&quot;#txStatus&quot;).text(error);
  });
}</code></pre>
<ul>
<li><p>위 함수는 Web3 프로바이더에게 트랜잭션을 전송(send)하고, 몇 가지 이벤트 리스너들을 연결한다.</p>
</li>
<li><p><code>receipt</code> : 트랜잭션이 이더리움의 블록에 <code>포함</code>될 때, 즉 좀비가 생성되고 우리의 컨트랙트에 저장되었을 때 발생한다.</p>
</li>
<li><p><code>error</code> : 트랜잭션이 블럭에 <code>포함되지 못했</code>을 때, 예를 들어 사용자가 충분한 가스를 전송하지 않았을 때 발생하게 된다. 이 경우 프론트엔드의 UI를 통해 사용자에게 트랜잭션이 전송되지 않았음을 알리고, 다시 시도할 수 있도록 해야 한다.</p>
<blockquote>
<p>참고: <code>send</code>를 호출할 때 <code>gas</code>와 <code>gasPrice</code>를 선택적으로 지정할 수 있다. <code>.send({ from: userAccount, gas: 3000000 })</code>와 같이 작성한다. 만약 지정하지 않는다면, 메타마스크는 사용자가 이 값들을 선택할 수 있도록 한다.</p>
</blockquote>
</li>
</ul>
<h1 id="payable-처리">Payable 처리</h1>
<p><code>payable</code>함수는 <code>web3.js</code>에서 특별한 처리가 필요하다.</p>
<h2 id="payable함수-추가-방법">payable함수 추가 방법</h2>
<p><code>ZombieHelper</code>를 다시 생각해보면, 우린 사용자가 레벨업할 수 있는 곳에 <code>payable</code> 함수를 추가했었다.</p>
<pre><code class="language-js">function levelUp(uint _zombieId) external payable {
  require(msg.value == levelUpFee);
  zombies[_zombieId].level++;
}</code></pre>
<p>함수를 이용해 이더를 보내는 방법은 간단하지만, 이더가 아니라 <code>wei</code>로 얼마를 보낼지 정하는 것에는 제한이 있다.</p>
<h2 id="wei란">Wei란?</h2>
<p><code>wei</code>는 이더의 가장 작은 하위 단위이다.</p>
<ul>
<li>하나의 이더는 <code>10^18</code>개의 <code>wei</code>이다.</li>
<li><code>Web3.js</code>에는 이러한 작업을 해주는 변환 유틸리티가 있다.</li>
</ul>
<pre><code class="language-js">// 이렇게 하면 1 ETH를 Wei로 바꿀 것이네
web3js.utils.toWei(&quot;1&quot;);</code></pre>
<ul>
<li>괄호 안에 있는 금액을 <code>wei</code>로 보낸다는 의미!</li>
</ul>
<p>DApp에서, <code>levelUpFee = 0.001 ether</code>로 설정했었다. 그러니 <code>levelup</code> 함수를 호출할 때, 아래의 코드를 써서 사용자가 <code>0.001</code> 이더를 보내게 할 수 있다.</p>
<pre><code class="language-js">CryptoZombies.methods.levelUp(zombieId)
.send({ from: userAccount, value: web3js.utils.toWei(&quot;0.001&quot;) })</code></pre>
<h1 id="이벤트-구독하기">이벤트 구독하기</h1>
<h2 id="새로운-업데이트-구독하기">새로운 업데이트 구독하기</h2>
<p><code>zombiefactory.sol</code>을 다시 생각해보면, 새로운 좀비가 생성될 때마다 매번 호출되던 <code>NewZombie</code>라는 이벤트가 있었다.</p>
<pre><code class="language-js">event NewZombie(uint zombieId, string name, uint dna);</code></pre>
<p><code>Web3.js</code>에서 이벤트를 구독하여 해당 이벤트가 발생할 때마다 <code>Web3 프로바이더</code>가 코드 내의 어떠한 로직을 실행시키도록 할 수 있다.</p>
<pre><code class="language-js">cryptoZombies.events.NewZombie()
.on(&quot;data&quot;, function(event) {
  let zombie = event.returnValues;
  // `event.returnValue` 객체에서 이 이벤트의 세 가지 반환 값에 접근할 수 있다.
  console.log(&quot;새로운 좀비가 태어났습니다!&quot;, zombie.zombieId, zombie.name, zombie.dna);
}).on(&quot;error&quot;, console.error);</code></pre>
<ul>
<li>이 코드는 현재 사용자의 좀비만이 아니라 DApp에서 생성된 모든 좀비에 대한 알람을 보낸다. </li>
</ul>
<h2 id="indexed-사용하기---선택적-이벤트-수신">indexed 사용하기 - 선택적 이벤트 수신</h2>
<p>이벤트를 필터링하고 현재 사용자와 연관된 변경만을 수신하기 위해서 <code>ERC721</code>을 구현할 때 <code>Transfer</code> 이벤트에서 했던 것처럼 컨트랙트에 <code>indexed</code> 키워드를 사용해야 한다.</p>
<pre><code class="language-js">event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);</code></pre>
<ul>
<li>경우, <code>_from</code>과 <code>_to</code>가 <code>indexed</code> 되어 있기 때문에, 우리 프론트엔드의 이벤트 리스너에서 이들을 필터링할 수 있다.</li>
</ul>
<pre><code class="language-js">// `filter`를 사용해 `_to`가 `userAccount`와 같을 때만 코드를 실행
cryptoZombies.events.Transfer({ filter: { _to: userAccount } })
.on(&quot;data&quot;, function(event) {
  let data = event.returnValues;
  // 현재 사용자가 방금 좀비를 받았네!
  // 해당 좀비를 보여줄 수 있도록 UI를 업데이트할 수 있도록 여기에 추가
}).on(&quot;error&quot;, console.error);</code></pre>
<ul>
<li><code>event</code>와 <code>indexed</code> 영역을 사용하는 것은 컨트랙트에서 변화를 감지하고 프론트엔드에 반영할 수 있게 하는 유용한 방법이다.</li>
</ul>
<h2 id="지난-이벤트에-대해-질의하기">지난 이벤트에 대해 질의하기</h2>
<p><code>getPastEvents</code>를 이용해 지난 이벤트들에 대해 질의를 하고, <code>fromBlock</code>과 <code>toBlock</code> 필터들을 이용해 이벤트 로그에 대한 시간 범위를 솔리디티에 전달할 수 있다. (여기서 <code>block</code>은 이더리움 블록 번호를 나타낸다)</p>
<pre><code class="language-js">cryptoZombies.getPastEvents(&quot;NewZombie&quot;, { fromBlock: 0, toBlock: &quot;latest&quot; })
.then(function(events) {
  // `events`는 우리가 위에서 했던 것처럼 반복 접근할 `event` 객체들의 배열이네.
  // 이 코드는 생성된 모든 좀비의 목록을 우리가 받을 수 있게 할 것이네.
});</code></pre>
<ul>
<li><p>위 메소드를 사용해서 시작 시간부터의 이벤트 로그들에 대해 질의를 할 수 있기 때문에, 이벤트를 저렴한 형태의 <code>storage</code>로 사용할 수 있다.</p>
<h3 id="이벤트를-저렴한-형태의-storage로-사용하기">이벤트를 저렴한 형태의 <code>storage</code>로 사용하기</h3>
</li>
<li><p>데이터를 블록체인에 기록하는 것은 솔리디티에서 가장 비싼 비용을 지불하는 작업 중 하나였다 하지만 이벤트를 이용하는 것은 가스 측면에서 훨씬 더 저렴하다.</p>
</li>
<li><p>단점이 되는 부분은 스마트 컨트랙트 자체 안에서는 이벤트를 읽을 수 없다는 것이다. 하지만 히스토리로 블록체인에 기록하여 앱의 프론트엔드에서 읽기를 원하는 데이터가 있다면, 이벤트 히스토리를 유용하게 사용할 수 있다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[#5 Crypto Zombies] ERC721 & 크립토 수집품]]></title>
            <link>https://velog.io/@dandev_sw/5-Crypto-Zombies-ERC721-%ED%81%AC%EB%A6%BD%ED%86%A0-%EC%88%98%EC%A7%91%ED%92%88</link>
            <guid>https://velog.io/@dandev_sw/5-Crypto-Zombies-ERC721-%ED%81%AC%EB%A6%BD%ED%86%A0-%EC%88%98%EC%A7%91%ED%92%88</guid>
            <pubDate>Sun, 21 Feb 2021 02:11:24 GMT</pubDate>
            <description><![CDATA[<p><a href="https://cryptozombies.io/ko/lesson/5/chapter/1">실습 링크</a></p>
<h1 id="이더리움-상의-토큰">이더리움 상의 토큰</h1>
<h2 id="토큰이란">토큰이란?</h2>
<ul>
<li>이더리움에서 <code>토큰</code>은 기본적으로 몇몇 공통 규약을 따르는 <code>스마트 컨트랙트</code>이다.컨트랙트 안에서 누가 얼마나 많은 토큰을 가지고 있는지 기록하고, 몇몇 함수를 가지고 사용자들이 그들의 토큰을 다른 주소로 전송할 수 있게 한다.</li>
<li>즉, 다른 모든 토큰 컨트랙트가 사용하는 표준 함수 집합을 구현한게 <code>토큰</code>예를 들면 <code>transfer(address _to, uint256 _value)</code>나 <code>balanceOf(address _owner)</code> 같은 함수!</li>
</ul>
<blockquote>
<p>내부적으로 스마트 컨트랙트는 보통 mapping(address =&gt; uint256) balances와 같은 매핑을 가지고 있다. 각각의 주소에 잔액이 얼마나 있는지 기록하는 것. 토큰도 스마트 컨트랙트의 일종이므로 저 매핑을 가지고 있다.</p>
</blockquote>
<p>즉 기본적으로 토큰은 그냥 하나의 컨트랙트!</p>
<h2 id="erc20-토큰-상호작용">ERC20 토큰 상호작용</h2>
<blockquote>
<p>💵 ERC20 토큰은 화폐처럼 사용되는 토큰으로 적절하다.</p>
</blockquote>
<p>모든 ERC20 토큰들이 똑같은 이름의 <strong>동일한 함수 집합</strong>을 공유하기 때문에, 이 토큰들에 똑같은 방식으로 상호작용이 가능하다.</p>
<ul>
<li>즉 하나의 ERC20 토큰과 상호작용할 수 있는 애플리케이션 하나를 만들면, 이 앱이 다른 어떤 ERC20 토큰과도 상호작용이 가능하다.</li>
<li>한 거래소에서 새로운 ERC20 토큰을 상장할 때, 실제로는 이 거래소에서 통신이 가능한 또 하나의 <strong>스마트 컨트랙트를 추가</strong>하는 것이다.</li>
<li>사용자들은 이 컨트랙트에 거래소의 지갑 주소에 토큰을 보내라고 할 수 있고, 거래소에서는 이 컨트랙트에 사용자들이 출금을 신청하면 토큰을 다시 돌려보내라고 할 수 있게 만드는 것!</li>
</ul>
<p>따라서, <code>로직</code>은 한번 구현하고, 새로운 토큰을 추가하는 것은 <code>DB</code>에 <code>새 컨트랙트 주소</code>를 추가하기만 하면 된다. (토큰이 스마트 컨트랙트 그 자체이므로)</p>
<h2 id="다른-토큰-표준">다른 토큰 표준</h2>
<h3 id="다른-종류의-토큰이-필요한-이유">다른 종류의 토큰이 필요한 이유</h3>
<p>지금 만들고 있는 좀비 게임에서는 아래와 같은 이유로 ERC20을 못쓴다..!</p>
<ol>
<li>좀비는 화폐처럼 분할할 수가 없다.</li>
</ol>
<ul>
<li>다른 사람에게 0.237ETH를 보낼 수 있지만, 0.237개의 좀비를 보내는 것은 불가능하다.</li>
</ul>
<ol>
<li>모든 좀비가 똑같지 않다.</li>
</ol>
<ul>
<li>다른 사람의 레벨2 좀비 <code>Steve</code>는 내 레벨732 좀비 <code>H4XF13LD MORRIS 💯💯😎💯💯</code>와는 완전히 다르다.</li>
</ul>
<h3 id="erc721-토큰">ERC721 토큰</h3>
<blockquote>
<p>☄️ 크립토좀비와 같은 크립토 수집품을 위해 더 적절한 토큰 표준은 ERC721 토큰!</p>
</blockquote>
<p>ERC712 토큰을 사용하는 이유</p>
<ol>
<li>ERC721 토큰은 교체가 불가하다.</li>
</ol>
<ul>
<li>각각의 토큰이 유일하고 분할이 불가하다.</li>
<li>각 토큰을 하나의 전체 단위로만 거래할 수 있다.</li>
<li>각각의 토큰은 유일한 ID를 가지고 있다.</li>
</ul>
<ol>
<li>경매나 중계 로직을 직접 구현하지 않아도 된다.</li>
</ol>
<ul>
<li>ERC721과 같은 표준을 사용하면 컨트랙트에서 사용자들이 우리의 좀비를 거래/판매할 수 있도록 하는 로직이 이미 구현되어 있다.</li>
</ul>
<h1 id="erc721-표준-다중-상속">ERC721 표준, 다중 상속</h1>
<h2 id="erc721-표준">ERC721 표준</h2>
<pre><code>contract ERC721 {
  event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
  event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);

  function balanceOf(address _owner) public view returns (uint256 _balance);
  function ownerOf(uint256 _tokenId) public view returns (address _owner);
  function transfer(address _to, uint256 _tokenId) public;
  function approve(address _to, uint256 _tokenId) public;
  function takeOwnership(uint256 _tokenId) public;
}</code></pre><ul>
<li>위의 메소드를 다 구현해서 표준을 맞춰야 ERC721 토큰을 사용할 수 있다.</li>
</ul>
<h2 id="다중-상속">다중 상속</h2>
<pre><code>contract SatoshiNakamoto is NickSzabo, HalFinney {
  // , 로 간단히 다중 상속 가능
}</code></pre><ul>
<li>다중 상속은 그냥 <code>,</code> 로 구분해서 나란히 쓴다.</li>
</ul>
<h1 id="erc721-구현">ERC721 구현</h1>
<h2 id="메소드-구현">메소드 구현</h2>
<h3 id="balanceof">balanceOf</h3>
<pre><code>  function balanceOf(address _owner) public view returns (uint256 _balance);</code></pre><ul>
<li>이 함수는 단순히 <code>address</code>를 받아, 해당 <code>address</code>가 <code>토큰</code>을 얼마나 가지고 있는지 반환한다.</li>
<li>현재 만들고 있는 게임의 경우, <code>토큰</code>은 <code>좀비</code>들이 된다.</li>
</ul>
<h3 id="ownerof">ownerOf</h3>
<pre><code>  function ownerOf(uint256 _tokenId) public view returns (address _owner);</code></pre><ul>
<li>이 함수에서는 <code>토큰 ID</code>를 받아, 이를 소유하고 있는 사람의 <code>address</code>를 반환한다.</li>
</ul>
<h2 id="전송-로직">전송 로직</h2>
<h3 id="첫번째-방법---transter">첫번째 방법 - transter</h3>
<pre><code>function transfer(address _to, uint256 _tokenId) public;</code></pre><ul>
<li>토큰을 보내는 사람이 함수 호출</li>
<li>토큰의 소유자가 전송 상대의 <code>address</code>, 전송하고자 하는 <code>_tokenId</code>와 함께 <code>transfer</code> 함수를 호출하는 것이다.</li>
</ul>
<h3 id="두번째-방법---approve">두번째 방법 - approve</h3>
<pre><code>function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;</code></pre><ul>
<li>토큰을 받는 사람이 함수 호출</li>
<li>토큰의 소유자가 먼저 위에서 본 정보들을 가지고 <code>approve</code>를 호출하는 형식이다.</li>
<li>그 후, 컨트랙트에 누가 해당 토큰을 가질 수 있도록 허가를 받았는지 저장한다.</li>
<li>보통 <code>mapping (uint256 =&gt; address)</code>를 써서 이를 확인한다.</li>
<li>누군가 <code>takeOwnership</code>을 호출하면, 해당 컨트랙트는 이 <code>msg.sender</code>가 소유자로부터 토큰을 받을 수 있게 허가를 받았는지 확인한다. 그리고 허가를 받았다면 해당 토큰을 그에게 전송한다.</li>
</ul>
<p><code>transfer</code>와 <code>takeOwnership</code> 모두 동일한 전송 로직을 가지고 있다. 순서만 반대인 것!(전자는 토큰을 보내는 사람이 함수를 호출, 후자는 토큰을 받는 사람이 호출).</p>
<p>그러니 이 로직만의 프라이빗 함수, <code>_transfer</code>를 만들어 추상화하면 두 함수에서 모두에서 효율적으로 사용할 수 있다.</p>
<pre><code>  function _transfer(address _from, address _to, uint256 _tokenId) private {
      ownerZombieCount[_to]++;
      ownerZombieCount[_from]--;
      zombieToOwner[_tokenId] = _to;
      Transfer(_from, _to, _tokenId);
  }</code></pre><h3 id="approve-구현">approve 구현</h3>
<p><strong>작동 방식 요약</strong></p>
<ol>
<li>소유자인 자네가 새로운 소유자의 <code>address</code>와 그에게 보내고 싶은 <code>_tokenId</code>를 사용하여 <code>approve</code>를 호출한다.</li>
<li>새로운 소유자가 <code>_tokenId</code>를 사용하여 <code>takeOwnership</code> 함수를 호출하면, 컨트랙트는 그가 승인된 자인지 확인하고 그에게 토큰을 전송한다.</li>
</ol>
<ul>
<li>함수 호출 사이에 누가 무엇에 대해 승인이 되었는지 저장할 데이터 구조가 필요하다.</li>
</ul>
<h1 id="컨트랙트-보안">컨트랙트 보안</h1>
<blockquote>
<p>🌝 컨트랙트 보안 고려 요소: 오버플로우, 언더플로우</p>
</blockquote>
<h2 id="오버플로우">오버플로우</h2>
<h3 id="오버플로우란-무엇인가">오버플로우란 무엇인가?</h3>
<p>우리가 8비트 데이터를 저장할 수 있는 <code>uint8</code> 하나를 가지고 있다고 해보지. 이 말인즉 우리가 저장할 수 있는 가장 큰 수는 이진수로 <code>11111111</code>(또는 십진수로 2^8 - 1 = 255)가 된다.</p>
<pre><code>uint8 number = 255;
number++;</code></pre><p>이 예시에서, 우리는 이 변수에 오버플로우를 만들었다.</p>
<ul>
<li>위에서 <code>number</code>는 직관과는 다르게 <code>0</code>이 된다.이진수 <code>11111111</code>에 <code>1</code>을 더하면, 이 수는 <code>00000000</code>으로 돌아간다.</li>
</ul>
<h2 id="언더플로우">언더플로우</h2>
<h3 id="언더플로우란">언더플로우란?</h3>
<p>언더플로우는 오버플로우와 유사하게 <code>0</code> 값을 가진 <code>uint8</code>에서 <code>1</code>을 빼면, <code>255</code>와 같아지는 것을 말한다. <strong><em>(uint에 부호가 없어, 음수가 될 수 없기 때문에!)</em></strong></p>
<p>따라서, 미래에 우리의 DApp에 예상치 못한 문제가 발생하지 않도록 컨트랙트에 보호 장치를 두어야 한다! <del>(컨트랙트 한번 올리면 수정도 못함..)</del></p>
<h1 id="safemath-사용하기">SafeMath 사용하기</h1>
<h2 id="safemath란">SafeMath란?</h2>
<p>오버플로우와 언더플로우를 막기 위해, <code>OpenZeppelin</code>에서 기본적으로 이런 문제를 막아주는 <code>SafeMath</code>라고 하는 라이브러리를 만들었다.</p>
<h3 id="솔리디티에서-라이브러리란">솔리디티에서 라이브러리란?</h3>
<p>라이브러리(Library)는 솔리디티에서 <strong>특별한 종류의 컨트랙트</strong>이다. 이게 유용하게 사용되는 경우 중 하나는 기본(native) 데이터 타입에 함수를 붙일 때!</p>
<p>예를 들어, <code>SafeMath</code> 라이브러리를 쓸 때는 <code>using SafeMath for uint256</code>이라는 구문을 사용한다. <code>SafeMath</code> 라이브러리는 4개의 함수를 가지고 있다. - <code>add</code>, <code>sub</code>, <code>mul</code>,<code>div</code> . 함수 접근은 아래와 같이 이루어진다.</p>
<pre><code>using SafeMath for uint256;

uint256 a = 5;
uint256 b = a.add(3); // 5 + 3 = 8
uint256 c = a.mul(2); // 5 * 2 = 10</code></pre><h2 id="safemath-내부의-코드">SafeMath 내부의 코드</h2>
<h3 id="safemath-라이브러리-코드">safemath 라이브러리 코드</h3>
<pre><code>
library SafeMath {

  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b &gt; 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn&#39;t hold
    return c;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b &lt;= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c &gt;= a);
    return c;
  }
}</code></pre><p>라이브러리는 <code>using</code> 키워드를 사용할 수 있게 해준다. 이를 통해 라이브러리의 메소드들을 다른 데이터 타입에 적용할 수 있다.</p>
<h3 id="safemath-적용-예시">safemath 적용 예시</h3>
<pre><code>using SafeMath for uint;
// 우리는 이제 이 메소드들을 아무 uint에서나 쓸 수 있다.
uint test = 2;
test = test.mul(3); // test는 이제 6이 된다
test = test.add(5); // test는 이제 11이 된다</code></pre><ul>
<li><code>mul</code>과 <code>add</code> 함수는 각각 2개의 인수를 필요로 한다.하지만 우리가 <code>using SafeMath for uint</code>를 선언할 때, 우리가 함수를 적용하는 <code>uint(test)</code>는 첫 번째 인수로 <strong>자동으로 전달</strong>된다.</li>
</ul>
<h3 id="safemath-작동-방식---add함수">safemath 작동 방식 - add함수</h3>
<pre><code>function add(uint256 a, uint256 b) internal pure returns (uint256) {
  uint256 c = a + b;
  //여기가 오버플로우를 막는 부분
  assert(c &gt;= a);
  return c;
}</code></pre><ul>
<li>기본적으로 <code>add</code>는 그저 2개의 <code>uint</code>를 +처럼 더한다.</li>
<li>하지만 그 안에 <code>assert</code> 구문을 써서 그 합이 a보다 크도록 보장하고, 이것이 오버플로우를 막아준다.</li>
</ul>
<h3 id="assert의-역할">assert의 역할</h3>
<ul>
<li><code>assert</code>는 조건을 만족하지 않으면 에러를 발생시킨다는 점에서 <code>require</code>와 비슷하다.</li>
<li><code>assert</code>와 <code>require</code>의 차이점은, <code>require</code>는 함수 실행이 실패하면 남은 가스를 사용자에게 되돌려 주지만, <code>assert</code>는 그렇지 않다는 것이다.</li>
<li>따라서, 대부분 코드에 <code>require</code>를 쓰고, <code>assert</code>는 일반적으로 코드가 심각하게 잘못 실행될 때 사용한다.</li>
</ul>
<h2 id="코드에-safemath-사용하기">코드에 SafeMath 사용하기</h2>
<p>오버플로우나 언더플로우를 막기 위해, 우리의 코드에서 <code>+</code>, <code>-</code>, <code>*</code> 또는 <code>/</code>을 쓰는 곳을 찾아 <code>add</code>, <code>sub</code>, <code>mul</code>, <code>div</code>로 교체한다.</p>
<p>예를 들어, 아래처럼 하는 대신</p>
<pre><code>myUint++;</code></pre><p>이렇게 사용해 오버플로우와 언더플로우를 방지한다.</p>
<pre><code>myUint = myUint.add(1);</code></pre><h1 id="솔리디티-주석---natspec">솔리디티 주석 - natspec</h1>
<pre><code>/// @title 기본적인 산수를 위한 컨트랙트
/// @author H4XF13LD MORRIS 💯💯😎💯💯
/// @notice 지금은 곱하기 함수만 추가한다.
contract Math {
  /// @notice 2개의 숫자를 곱한다.
  /// @param x 첫 번쨰 uint.
  /// @param y 두 번째 uint.
  /// @return z (x * y) 곱의 값
  /// @dev 이 함수는 현재 오버플로우를 확인하지 않는다.
  function multiply(uint x, uint y) returns (uint z) {
    // 이것은 일반적인 주석으로, natspec에 포함되지 않는다.
    z = x * y;
  }
}</code></pre><ul>
<li><code>@title</code>과 <code>@author</code> : 말 그대로 제목, 쓴사람</li>
<li><code>@notice</code> : 사용자에게 컨트랙트/함수가 무엇을 하는지 설명한다.</li>
<li><code>@dev</code> : 개발자에게 추가적인 상세 정보를 설명한다.</li>
<li><code>@param</code>과 <code>@return</code> : 함수에서 어떤 매개 변수와 반환값을 가지는지 설명한다.</li>
<li>모든 태그가 필수는 아니지만, <code>@dev</code>는 대부분 남겨야 함!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[#4 Crypto Zombies] 좀비 전투 시스템]]></title>
            <link>https://velog.io/@dandev_sw/4-Crypto-Zombies-%EC%A2%80%EB%B9%84-%EC%A0%84%ED%88%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C</link>
            <guid>https://velog.io/@dandev_sw/4-Crypto-Zombies-%EC%A2%80%EB%B9%84-%EC%A0%84%ED%88%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C</guid>
            <pubDate>Fri, 19 Feb 2021 03:01:36 GMT</pubDate>
            <description><![CDATA[<h1 id="✍🏻-솔리디티-개념">✍🏻 솔리디티 개념</h1>
<h2 id="함수-제어자">함수 제어자</h2>
<h3 id="접근-제어자">접근 제어자</h3>
<ul>
<li><code>private</code> : 컨트랙트 내부의 다른 함수들에서만 호출될 수 있음</li>
<li><code>internal</code> : private과 비슷하지만, 해당 컨트랙트를 상속하는 컨트랙트에서도 호출될 수 있음</li>
<li><code>external</code> : 오직 컨트랙트 외부에서만 호출</li>
<li><code>public</code> : 내외부 모두에서, 어디서든 호출</li>
</ul>
<h3 id="상태-제어자">상태 제어자</h3>
<ul>
<li><code>view</code> : 해당 함수를 실행해도 어떤 데이터도 저장/변경되지 않음</li>
<li><code>pure</code> : 해당 함수가 어떤 데이터도 블록체인에 저장하지 않을 뿐만 아니라, 블록체인으로부터 어떤 데이터도 읽지 않음</li>
</ul>
<p>둘 다 컨트랙트 외부에서 호출됐을 경우, 가스를 전혀 소모하지 않고 다른 함수에 의해 내부 호출되었을 경우에는 가스를 소모한다.</p>
<h3 id="사용자-정의-제어자">사용자 정의 제어자</h3>
<ul>
<li><code>onlyOwner</code>와 <code>aboveLevel</code> 같은 것!</li>
<li>함수에 이 제어자들이 어떻게 영향을 줄지를 결정하는 로직을 구성할 수 있네.</li>
</ul>
<p>이런 제어자들은 함수 하나에 다음처럼 함께 사용할 수 있다.</p>
<pre><code class="language-javascript">function test() external view onlyOwner anotherModifier { /* ... */ }</code></pre>
<h3 id="payable-제어자-함수-제어자">payable 제어자 (함수 제어자)</h3>
<blockquote>
<p>✨ payable은 이더를 받을 수 있는 특별한 함수 유형!</p>
</blockquote>
<ul>
<li>payable은 이더를 받을 수 있는 특별한 함수 유형이다.</li>
<li>이더리움에서는 돈, 데이터, 컨트랙트 코드 자체가 모두 이더리움 위에 존재하기 때문에 함수를 실행하는 동시에 컨트랙트에 돈을 지불하는 것이 가능하다.</li>
</ul>
<p>이를 통해 함수를 실행하기 위해 컨트랙트에 일정 금액을 지불하게 하는 것 같은 구조를 아래와 같이 만들어낼 수 있다.</p>
<pre><code class="language-javascript">contract OnlineStore {
  function buySomething() external payable {
    // 함수 실행에 0.001이더가 보내졌는지 확실히 하기 위해 확인
    require(msg.value == 0.001 ether);
    // 보내졌다면, 함수를 호출한 자에게 디지털 아이템을 전달하기 위한 내용 구성
    transferThing(msg.sender);
  }
}</code></pre>
<p>여기서, <code>msg.value</code>는 컨트랙트로 이더가 얼마나 보내졌는지 확인하는 방법이고, <code>ether</code>는 기본적으로 포함된 단위이다.</p>
<p>여기서 일어나는 일은 누군가 <code>web3.js</code>(DApp의 자바스크립트 프론트엔드)에서 다음과 같이 함수를 실행할 때 발생한다.</p>
<pre><code class="language-javascript">// `OnlineStore`는 이더리움 상의 컨트랙트를 가리킨다고 가정한다.
OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)})</code></pre>
<p><code>value</code> 필드를 보면, 자바스크립트 함수 호출에서 이 필드를 통해 <code>ether</code>를 얼마나 보낼지 결정한다.</p>
<p>참고: 만약 함수가 <code>payable</code>로 표시되지 않았는데 위에서 본 것처럼 이더를 보내려 한다면, 함수에서 트랜잭션을 거부할 것이다.</p>
<h1 id="💸-트랜젝션">💸 트랜젝션</h1>
<p>컨트랙트로 이더를 보내면, 해당 컨트랙트의 이더리움 계좌에 이더가 저장되고 거기에 갇히게 된다. 따라서 컨트랙트로부터 이더를 인출하는 함수를 만들어야 한다.</p>
<p>다음과 같이 컨트랙트에서 이더를 인출하는 함수를 작성할 수 있다.</p>
<pre><code class="language-javascript">contract GetPaid is Ownable {
  function withdraw() external onlyOwner {
    owner.transfer(this.balance);
  }
}</code></pre>
<blockquote>
<p>💡 <code>Ownable</code> 컨트랙트를 <code>import</code> 했다고 가정하고 <code>owner</code>와 <code>onlyOwner</code>를 사용하고 있다는 것을 참고!</p>
</blockquote>
<h3 id="transfer-함수-사용">transfer 함수 사용</h3>
<ol>
<li><code>transfer</code> 함수를 사용해서 이더를 특정 주소로 전달할 수 있다. </li>
</ol>
<ul>
<li>그리고 <code>this.balance</code>는 컨트랙트에 저장돼있는 전체 <code>잔액</code>을 반환한다. 에를 들어, 100명의 사용자가 나의 컨트랙트에 1이더를 지불했다면, <code>this.balance</code>는 100이더가 될 것이다.</li>
</ul>
<ol start="2">
<li><code>transfer</code> 함수를 써서 특정한 이더리움 주소에 돈을 보낼 수 있다. </li>
</ol>
<ul>
<li>예를 들어, 만약 누군가 한 아이템에 대해 초과 지불을 했다면, 이더를 <code>msg.sender</code>로 되돌려주는 함수를 만들 수도 있다.<pre><code class="language-javascript">uint itemFee = 0.001 ether;
msg.sender.transfer(msg.value - itemFee);</code></pre>
</li>
<li>또는 구매자와 판매자가 존재하는 컨트랙트에서, 판매자의 주소를 <code>storage</code>에 저장하고, 누군가 판매자의 아이템을 구매하면 구매자로부터 받은 요금을 그에게 전달할 수도 있다. <code>seller.transfer(msg.value)</code></li>
</ul>
<p>이런 식으로 누구에게도 제어되지 않는 분산 장터를 만들 수 있다!</p>
<h1 id="🪅-이더리움에서-난수-생성하기">🪅 이더리움에서 난수 생성하기</h1>
<blockquote>
<p>😂 솔리디티에서는 난수를 안전하게 생성할 수 없다.....!</p>
</blockquote>
<h2 id="keccak256을-통한-난수-생성못씀">keccak256을 통한 난수 생성(못씀..)</h2>
<h3 id="keccak256을-이용한-난수-생성-방법">keccak256을 이용한 난수 생성 방법</h3>
<ul>
<li>솔리디티에서 난수를 만들기에 가장 좋은 방법은 keccak256 해시 함수를 쓰는 것이다.</li>
</ul>
<p>다음과 같은 방식으로 난수를 만들어낼 수 있다</p>
<pre><code class="language-javascript">// Generate a random number between 1 and 100:
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;</code></pre>
<p>이 예시에서는 <code>now</code>의 타임스탬프 값, <code>msg.sender</code>, 증가하는 <code>nonce</code>(딱 한 번만 사용되는 숫자, 즉 똑같은 입력으로 두 번 이상 동일한 해시 함수를 실행할 수 없게 함)를 받고 있다.</p>
<p>그리고서 <code>keccak</code>을 사용하여 이 입력들을 임의의 해시 값으로 변환하고, 변환한 해시 값을 <code>uint</code>로 바꾼 후, <code>% 100</code>을 써서 마지막 2자리 숫자만 받도록 했네. 이를 통해 <code>0과 99</code> 사이의 완전한 난수를 얻을 수 있다.</p>
<h3 id="keccak을-이용한-난수를-못쓰는-이유">keccak을 이용한 난수를 못쓰는 이유</h3>
<blockquote>
<p>이 메소드는 정직하지 않은 노드의 공격에 취약하다.</p>
</blockquote>
<p><strong>이더리움 검증 과정</strong></p>
<ol>
<li><p>이더리움에서는 컨트랙트의 함수를 실행하면 <code>transaction</code>으로서 네트워크의 노드 <code>하나</code> 혹은 <code>여러</code> 노드에 실행을 알리게 된다. </p>
</li>
<li><p>그 후 네트워크의 노드들은 여러 개의 트랜잭션을 모으고, <code>작업 증명</code>으로 알려진 계산이 매우 복잡한 수학적 문제를 먼저 풀기 위한 시도를 하게 된다.</p>
</li>
<li><p>그리고 해당 트랜잭션 그룹을 그들의 <code>작업 증명</code>(PoW)과 함께 <code>_블록_</code>으로 네트워크에 배포하게 된다.</p>
</li>
<li><p>한 노드가 어떤 PoW를 풀면, 다른 노드들은 그 PoW를 풀려는 시도를 멈추고 해당 노드가 보낸 트랜잭션 목록이 유효한 것인지 검증한다. </p>
</li>
<li><p>유효하다면 해당 블록을 받아들이고 다음 블록을 풀기 시작한다.</p>
</li>
</ol>
<p>이것이 우리의 <strong>난수 함수</strong>를 취약하게 만든다.</p>
<p><strong>트랜잭션 조작 가능</strong></p>
<ol>
<li><p>동전 던지기 컨트랙트를 사용한다고 가정 - 앞면이 나오면 돈이 두 배가 되고, 뒷면이 나오면 모두 다 잃는 것이다. 앞뒷면을 결정할 때 위에서 본 난수 함수를 사용한다고 가정한다. (random &gt;= 50은 앞면, random &lt; 50은 뒷면이네).</p>
</li>
<li><p>내가 만약 노드를 실행하고 있다면, 나는 오직 <code>나의 노드</code>에만 트랜잭션을 알리고 이것을 공유하지 않을 수 있다. (하나 또는 여러 노드에 실행을 알리면 되니까)</p>
</li>
<li><p>그 후 내가 이기는지 확인하기 위해 동전 던지기 함수를 실행할 수 있다.</p>
</li>
</ol>
<ul>
<li>그리고 만약 내가 진다면, 내가 풀고 있는 다음 블록에 <strong>해당 트랜잭션을 포함하지 않는 것을 선택</strong>한다. </li>
<li>난 이것을 내가 결국 동전 던지기에서 이기고 다음 블록을 풀 때까지 무한대로 반복할 수 있고, 이득을 볼 수 있다.</li>
</ul>
<h2 id="그럼-이더리움에서는-어떻게-난수를-안전하게-만들어낼-수-있을까">그럼 이더리움에서는 어떻게 난수를 안전하게 만들어낼 수 있을까?</h2>
<p>블록체인의 전체 내용은 모든 참여자에게 공개되므로, 이건 풀기 어려운 문제이고 그 해결 방법은 여러가지가 있다.<a href="https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract">StackOverflow</a></p>
<ul>
<li><p>하나의 방법은 이더리움 블록체인 <strong>외부의 난수 함수</strong>에 접근할 수 있도록 <code>오라클</code>을 사용하는 것이다. (내부에서 만들지 말고 외부 난수 함수를 가져온다!)</p>
</li>
<li><p><code>oracle</code>: 이더리움 외부에서 데이터를 받아오는 안전한 방법 중 하나</p>
</li>
<li><p>오라클을 사용해서 블록체인 밖에서 안전한 난수를 만들어 온체인에서 사용</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[#3 Crypto Zombies] 고급 솔리디티 개념]]></title>
            <link>https://velog.io/@dandev_sw/3-Crypto-Zombies-%EA%B3%A0%EA%B8%89-%EC%86%94%EB%A6%AC%EB%94%94%ED%8B%B0-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@dandev_sw/3-Crypto-Zombies-%EA%B3%A0%EA%B8%89-%EC%86%94%EB%A6%AC%EB%94%94%ED%8B%B0-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Thu, 18 Feb 2021 05:40:14 GMT</pubDate>
            <description><![CDATA[<p><a href="https://cryptozombies.io/ko/lesson/3">실습 링크</a></p>
<h1 id="솔리디티-특징">솔리디티 특징</h1>
<h2 id="컨트랙트의-불변성">컨트랙트의 불변성</h2>
<p>이더리움에 컨트랙트를 배포하고 나면, 컨트랙트는 변하지 않는다(Immutable). 다시 말하자면 컨트랙트를 수정하거나 업데이트할 수 없다는 것!</p>
<ul>
<li>코드를 이후에 고칠 수 있는 방법이 없기 때문에 <code>보안</code>이 굉장히 큰 이슈가 됨</li>
<li>함수를 호출할 때마다, 코드에 쓰여진 그대로 함수가 실행될 것이라고 확신할 수 있다. 그 누구도 배포 이후에 함수를 <code>수정</code>하거나 <code>예상치 못한 결과</code>를 발생시키지 못한다.</li>
</ul>
<h2 id="외부-의존성">외부 의존성</h2>
<p>블록체인 외부에서 끌어온 데이터에 문제가 생겼을 경우, 나의 DApp이 작동하지 못할 수 있다. 따라서 외부에서 끌어오는 데이터는 <code>가변 데이터</code>로 저장한다.</p>
<pre><code class="language-javascript">//인터페이스 변수 선언만 한다.
KittyInterface kittyContract;

//함수 내부에서 address대입!
  function setKittyContractAddress(address _address) external {
    kittyContract = KittyInterface(_address);
  }</code></pre>
<h2 id="소유-가능한-콘트랙트">소유 가능한 콘트랙트</h2>
<h3 id="ownable-contract">Ownable contract</h3>
<p>위의 코드에서 함수를 external로 선언했기 때문에, 누구든 이 함수를 호출해 주소를 변경할 수 있다. 따라서 컨트랙트를 <code>소유 가능</code>하게 만들어 특별한 권리를 가진 소유자만 주소 변경이 가능하도록 설정한다.</p>
<p>아래에 나와있는 것은 <code>OpenZeppelin</code> 솔리디티 라이브러리에서 가져온 <code>Ownable</code> 컨트랙트이다. OpenZeppelin은 DApp에서 사용할 수 있는, 안전하고 커뮤니티에서 검증받은 <code>스마트 컨트랙트</code>의 라이브러리이다.</p>
<pre><code class="language-javascript">/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of &quot;user permissions&quot;.
 */
contract Ownable {
  address public owner;
  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  function Ownable() public {
    owner = msg.sender;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }
}</code></pre>
<h4 id="해당-코드-내부-요소-설명">해당 코드 내부 요소 설명</h4>
<ul>
<li><code>생성자</code>(Constructor): <code>function Ownable()</code>는 생성자이다. 자바에서처럼 컨트랙트와 동일한 이름을 가진, 생략할 수 있는 특별한 함수. 이 함수는 컨트랙트가 생성될 때 딱 한 번만 실행된다.</li>
<li><code>함수 제어자</code>(Function Modifier): <code>modifier onlyOwner()</code>. 제어자는 다른 함수들에 대한 접근을 제어하기 위해 사용되는 일종의 <code>유사 함수</code>이다. 보통 함수 실행 전의 요구사항 <code>충족 여부를 확인</code>하는 데에 사용한다. <code>onlyOwner</code>의 경우에는 접근을 제한해서 오직 컨트랙트의 소유자만 해당 함수를 실행할 수 있도록 하기 위해 사용될 수 있다.</li>
</ul>
<h4 id="ownable-컨트랙트의-기본-역할">Ownable 컨트랙트의 기본 역할</h4>
<ol>
<li><p>컨트랙트가 생성되면 컨트랙트의 생성자가 <code>owner</code>에 <code>msg.sender</code>(컨트랙트를 배포한 사람)를 대입한다.</p>
</li>
<li><p>특정한 함수들에 대해서 <code>오직 소유자만 접근</code>할 수 있도록 제한 가능한 <code>onlyOwner</code> 제어자를 추가한다.</p>
</li>
<li><p><code>새로운 소유자</code>에게 해당 컨트랙트의 <code>소유권</code>을 옮길 수 있도록 한다.</p>
</li>
</ol>
<p>대부분의 솔리디티 DApp들은 <code>Ownable</code> 컨트랙트를 복사/붙여넣기 하면서 시작한다.</p>
<h2 id="함수-제어자">함수 제어자</h2>
<h3 id="제어자란">제어자란?</h3>
<p>함수 제어자는 함수처럼 보이지만, <code>function</code> 키워드 대신 <code>modifier</code> 키워드를 사용한다. 그리고 함수를 호출하듯이 직접 호출할 수는 없다. 대신에 함수 정의부 끝에 해당 함수의 작동 방식을 바꾸도록 제어자의 이름을 붙일 수 있다.</p>
<p><code>onlyOwner</code>를 살펴보면서 더 자세히 알아보도록 하자.</p>
<pre><code class="language-javascript">/**
 * @dev Throws if called by any account other than the owner.
 */
modifier onlyOwner() {
  require(msg.sender == owner);
  //원래 함수로 되돌아가는 코드는 아래쪽!
  _;
}</code></pre>
<p>우리는 이 제어자를 다음과 같이 사용할 것이다.</p>
<pre><code class="language-javascript">
contract MyContract is Ownable {
  event LaughManiacally(string laughter);

  // 아래 `onlyOwner`의 사용 방법을 잘 보자.
  function likeABoss() external onlyOwner {
    LaughManiacally(&quot;Muahahahaha&quot;);
  }
}</code></pre>
<p><code>likeABoss</code> 함수의 <code>onlyOwner</code> 제어자 부분을 보자. <code>likeABoss</code> 함수를 호출하면, <code>onlyOwner</code>의 코드가 먼저 실행된다. 그리고 <code>onlyOwner</code>의 <code>_;</code> 부분은 <code>likeABoss</code> 함수로 되돌아가 해당 코드를 실행하게 한다.</p>
<h3 id="제어자-사용하기">제어자 사용하기</h3>
<p>제어자를 사용할 수 있는 다양한 방법이 있지만, 가장 일반적으로 쓰는 예시 중 하나는 함수 실행 전에 <code>require</code> 체크를 넣는 것이다.</p>
<p><code>onlyOwner</code>의 경우에는, 함수에 이 제어자를 추가하면 오직 컨트랙트의 소유자만이 해당 함수를 호출할 수 있다.</p>
<blockquote>
<p>🔎 참고: 이렇게 소유자가 컨트랙트에 특별한 권한을 갖도록 하는 것은 자주 필요하지만, 이게 악용될 수도 있다. 예를 들어, 소유자가 다른 사람의 좀비를 뺏어올 수 있도록 하는 백도어 함수를 추가할 수도 있음!</p>
</blockquote>
<h3 id="개발-시-주의사항">개발 시 주의사항</h3>
<ul>
<li>이더리움에서 돌아가는 DApp이라고 해서 그것만으로 분산화되어 있다고 할 수는 없다.</li>
<li>소유자에 의한 특별한 제어가 불가능한 상태인지 확인이 반드시 필요하다.</li>
<li>잠재적인 버그를 수정하고 DApp을 안정적으로 유지하도록 하는 것과 사용자들이 그들의 데이터를 믿고 저장할 수 있는 소유자가 없는 플랫폼을 만드는 것 사이에서 균형을 잘 잡는 것이 중요하다.<h3 id="인수를-가지는-함수-제어자">인수를 가지는 함수 제어자</h3>
함수 제어자는 인수를 가질 수 있음!<pre><code class="language-javascript">// 사용자의 나이를 저장하기 위한 매핑
mapping (uint =&gt; uint) public age;
</code></pre>
</li>
</ul>
<p>// 사용자가 특정 나이 이상인지 확인하는 제어자
modifier olderThan(uint _age, uint _userId) {
  require (age[_userId] &gt;= _age);
  _;
}</p>
<p>// 차를 운전하기 위햐서는 16살 이상이어야 하네(적어도 미국에서는).
// <code>olderThan</code> 제어자를 인수와 함께 호출하려면 이렇게 하면 된다.
function driveCar(uint _userId) public olderThan(16, _userId) {
  // 필요한 함수 내용들
}</p>
<pre><code>
여기서 `olderthan` 제어자가 함수와 비슷하게 인수를 받는 것을 볼 수 있다. 그리고 `driveCar` 함수는 받은 인수를 제어자로 전달하고 있다.


## Gas
### 가스란? - 이더리움 DApp이 사용하는 연료
&gt;솔리디티에서는 사용자들이 DApp의 함수를 실행할 때마다 _가스_라고 불리는 화폐를 지불해야 한다. 사용자는 이더(ETH, 이더리움의 화폐)를 이용해서 가스를 사기 때문에, DApp 함수를 실행하려면 사용자들은 ETH를 소모해야만 한다.

함수를 실행하는 데에 얼마나 많은 가스가 필요한지는 그 `함수의 로직`(논리 구조)이 얼마나 복잡한지에 따라 달라진다. 각각의 연산은 소모되는 가스 비용(gas cost)이 있고, 그 연산을 수행하는 데에 소모되는 `컴퓨팅 자원`의 양이 이 비용을 결정한다. 예를 들어, `storage`에 값을 쓰는 것은 두 개의 정수를 더하는 것보다 훨씬 비용이 높다. 함수의 `전체 가스 비용`은 그 함수를 구성하는 `개별 연산`들의 가스 비용을 모두 합친 것과 같다.

함수를 실행하는 것은 사용자들에게 실제 돈을 쓰게 하기 때문에, 이더리움에서 `코드 최적화`는 다른 프로그래밍 언어들에 비해 훨씬 더 중요하다. 만약 코드가 엉망이라면, 사용자들은 함수를 실행하기 위해 일종의 할증료를 더 내야 한다. 그리고 수천 명의 사용자가 이런 불필요한 비용을 낸다면 할증료가 수십 억 원까지 쌓일 수 있다.

### 가스는 왜 필요한가?
이더리움은 크고 느린, 하지만 굉장히 안전한 컴퓨터와 같다. 어떤 함수를 실행할 때, 네트워크상의 모든 개별 노드가 함수의 출력값을 `검증`하기 위해 그 함수를 실행해야 한다. 모든 함수의 실행을 검증하는 수천 개의 노드가 바로 이더리움을 `분산화`하고, 데이터를 보존하며 누군가 `검열`할 수 없도록 하는 요소가 된다.

이더리움을 만든 사람들은 누군가가 무한 반복문을 써서 네트워크를 방해하거나, 자원 소모가 큰 연산을 써서 네트워크 자원을 모두 사용하지 못하도록 만들길 원했다. 그래서 그들은 `연산 처리`에 `비용`이 들도록 만들었고, 사용자들은 `저장 공간` 뿐만 아니라 연산 `사용 시간`에 따라서도 비용을 지불해야 한다.

&gt;참고: 사이드체인에서는 반드시 이렇지는 않다. 크립토좀비를 만든 사람들이 Loom Network에서 만들고 있는 것들이 좋은 예시. 이더리움 메인넷에서 월드 오브 워크래프트 같은 게임을 직접적으로 돌리는 것은 절대 말이 되지 않는다. 가스 비용이 엄청나게 높을 것이기 때문이다. 하지만 다른 합의 알고리즘을 가진 사이드체인에서는 가능할 수 있다. 

### 가스를 아끼기 위한 구조체 압축
앞에서 우리는 `uint`에 다른 타입들이 있다는 것을 배웠다. `uint8`, `uint16`, `uint32`, 기타 등등..

기본적으로는 이런 하위 타입들을 쓰는 것은 아무런 이득이 없다. 왜냐하면 솔리디티에서는 `uint`의 크기에 상관없이 `256비트`의 `저장 공간`을 미리 잡아놓기 때문이지. 예를 들자면, `uint(uint256)`대신에 `uint8`을 쓰는 것은 가스 소모를 줄이는 데에 아무 영향이 없다.

하지만 여기에 예외가 하나 있다. 바로 `struct`의 안에서!!

만약 구조체 안에 여러 개의 `uint`를 만든다면, 가능한 더 작은 크기의 `uint`를 쓰도록 해야 한다. 솔리디티에서 그 변수들을 더 적은 공간을 차지하도록 `압축`된다. 

```javascript
struct NormalStruct {
  uint a;
  uint b;
  uint c;
}

struct MiniMe {
  uint32 a;
  uint32 b;
  uint c;
}

// `mini`는 구조체 압축을 했기 때문에 `normal`보다 가스를 조금 사용할 것이네.
NormalStruct normal = NormalStruct(10, 20, 30);
MiniMe mini = MiniMe(10, 20, 30); </code></pre><p>이런 이유로, 구조체 안에서는 가능한 한 작은 크기의 정수 타입을 쓰는 것이 좋다.</p>
<p>또한 동일한 데이터 타입은 하나로 묶어놓는 것이 좋다. 즉, 구조체에서 서로 옆에 있도록 선언하면 솔리디티에서 사용하는 저장 공간을 최소화한다. 예를 들면, <code>uint c</code>; <code>uint32 a</code>; <code>uint32 b</code>;라는 필드로 구성된 구조체가 <code>uint32 a</code>; <code>uint c</code>; <code>uint32 b</code>; 필드로 구성된 구조체보다 가스를 덜 소모한다. <code>uint32</code> 필드가 묶여있기 때문이다.</p>
<p>정리하면 가스 비용을 줄이기 위해서</p>
<ol>
<li>구조체 안에서 가능한 한 <code>작은 크기의 정수</code> 타입을 쓰도록 한다.</li>
<li><code>동일한 데이터</code> 타입을 하나로 묶어서 쓴다.</li>
</ol>
<h2 id="시간-단위">시간 단위</h2>
<p>솔리디티는 시간을 다룰 수 있는 단위계를 기본적으로 제공한다.</p>
<p><code>now</code> 변수를 쓰면 현재의 유닉스 타임스탬프(1970년 1월 1일부터 지금까지의 초 단위 합) 값을 얻을 수 있다.</p>
<blockquote>
<p>참고: 유닉스 타임은 전통적으로 32비트 숫자로 저장된다. 이는 유닉스 타임스탬프 값이 32비트로 표시가 되지 않을 만큼 커졌을 때 많은 구형 시스템에 문제가 발생할 &quot;Year 2038&quot; 문제를 일으킬 것이다. 그러니 만약 DApp이 지금부터 20년 이상 운영되길 원한다면, 우리는 64비트 숫자를 써야 할 것이다. 하지만 우리 유저들은 그동안 더 많은 가스를 소모해야 하니까, 설계를 보고 결정을 해야 함!</p>
</blockquote>
<p>솔리디티는 또한 <code>seconds</code>, <code>minutes</code>, <code>hours</code>, <code>days</code>, <code>weeks</code>, <code>years</code> 같은 시간 단위 또한 포함하고 있다. 이들은 그에 해당하는 길이 만큼의 <strong>초 단위 <code>uint</code> 숫자로 변환</strong>된다. 즉 1 minutes는 60, 1 hours는 3600(60초 x 60 분), 1 days는 86400(24시간 x 60분 x 60초) 같이 변환된다.</p>
<pre><code class="language-javascript">uint lastUpdated;

// `lastUpdated`를 `now`로 설정
function updateTimestamp() public {
  lastUpdated = now;
}

// 마지막으로 `updateTimestamp`가 호출된 뒤 5분이 지났으면 `true`를, 5분이 아직 지나지 않았으면 `false`를 반환
function fiveMinutesHavePassed() public view returns (bool) {
  return (now &gt;= (lastUpdated + 5 minutes));
}</code></pre>
<h2 id="view-함수-이용해-가스-절약하기">View 함수 이용해 가스 절약하기</h2>
<h3 id="view-함수는-가스를-소모하지-않는다">View 함수는 가스를 소모하지 않는다.</h3>
<p>view 함수는 사용자에 의해 외부에서 호출되었을 때 가스를 전혀 소모하지 않는다.</p>
<ul>
<li>이건 <code>view</code> 함수가 블록체인 상에서 데이터를 읽기만 하지 실제로 어떤 것도 수정하지 않기 때문이다. </li>
<li>함수에 <code>view</code> 표시를 하는 것은 <code>web3.js</code>에 로컬 이더리움 노드에 <code>질의</code>만 날리면 되고, 블록체인에 어떤 <code>트랜잭션</code>도 만들지 않는다는걸 알려주는 것과 같다.</li>
<li>가능한 모든 곳에 읽기 전용의 <code>external view</code> 함수를 쓰는 것은 사용자들을 위해 DApp의 가스 사용을 최적화하는 비결!</li>
</ul>
<blockquote>
<p>참고: 만약 <code>view</code> 함수가 동일 컨트랙트 내에 있는, view 함수가 아닌 <code>다른 함수</code>에서 내부적으로 호출될 경우, 여전히 가스를 소모한다. 
이것은 다른 함수가 이더리움에 트랜잭션을 생성하고, 이는 모든 개별 노드에서 <code>검증</code>되어야 하기 때문이다. 그러니 view 함수는 외부에서 호출됐을 때에만 무료!</p>
</blockquote>
<h2 id="storage는-비싸다">Storage는 비싸다</h2>
<h3 id="storage-쓰기는-비싼-연산">storage 쓰기는 비싼 연산!</h3>
<ul>
<li><p>데이터의 일부를 쓰거나 바꿀 때마다, 블록체인에 영구적으로 기록되기 때문에 이 연산이 굉장히 비싸다.</p>
</li>
<li><p>비용을 최소화하기 위해서, 진짜 필요한 경우가 아니면 storage에 데이터를 쓰지 않는 것이 좋다. </p>
</li>
<li><p>이를 위해 때때로는 겉보기에 비효율적으로 보이는 프로그래밍 구성을 할 필요가 있다. </p>
</li>
<li><p>어떤 배열에서 내용을 빠르게 찾기 위해, 단순히 변수에 저장하는 것 대신 함수가 호출될 때마다 배열을 memory에 다시 만드는 것처럼!</p>
</li>
</ul>
<p>대부분의 프로그래밍 언어에서는, 큰 데이터 집합의 개별 데이터에 모두 접근하는 것은 비용이 비싸다. 하지만 솔리디티에서는 그 접근이 <code>external view</code> 함수라면 <code>storage</code>를 사용하는 것보다 더 저렴한 방법이네. <code>view</code> 함수는 사용자들의 가스를 소모하지 않기 때문이다.</p>
<h3 id="메모리에-배열-선언하기">메모리에 배열 선언하기</h3>
<p><code>Storage</code>에 아무것도 쓰지 않고도 함수 안에 새로운 배열을 만들려면 배열에 <code>memory</code> 키워드를 쓰면 된다. 이 배열은 함수가 끝날 때까지만 존재할 것이고, 이는 <code>storage</code>의 배열을 직접 업데이트하는 것보다 가스 소모 측면에서 훨씬 저렴하다. 이게 외부에서 호출되는 <code>view</code> 함수라면 무료!</p>
<p>메모리에 배열을 선언하는 방법은 다음과 같다.</p>
<pre><code class="language-javascript">function getArray() external pure returns(uint[]) {
  // 메모리에 길이 3의 새로운 배열을 생성한다.
  uint[] memory values = new uint[](3);
  // 여기에 특정한 값들을 넣는다.
  values.push(1);
  values.push(2);
  values.push(3);
  // 해당 배열을 반환한다.
  return values;
}</code></pre>
<blockquote>
<p>참고: 메모리 배열은 반드시 <code>길이 인수</code>와 함께 생성되어야 한다(이 예시에서는, 3). 메모리 배열은 현재로서는 <code>storage</code> 배열처럼 <code>array.push()</code>로 크기가 조절되지는 않는다.</p>
</blockquote>
<h2 id="for-반복문">for 반복문</h2>
<p>배열이 수정될 때마다 <code>storage</code>를 갈아치우는건 너무 많은 가스 비용을 소진한다. 따라서 배열 안에 있는 원소를 for문을 이용해 가져와 <code>Memory</code>에 저장해서 아용하는 연산을 이용한다.</p>
<pre><code class="language-javascript">function getEvens() pure external returns(uint[]) {
  uint[] memory evens = new uint[](5);
  // 새로운 배열의 인덱스를 추적하는 변수
  uint counter = 0;
  // for 반복문에서 1부터 10까지 반복함
  for (uint i = 1; i &lt;= 10; i++) {
    // `i`가 짝수라면...
    if (i % 2 == 0) {
      // 배열에 i를 추가함
      evens[counter] = i;
      // `evens`의 다음 빈 인덱스 값으로 counter를 증가시킴
      counter++;
    }
  }
  return evens;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] BOJ 1931 회의실 배정]]></title>
            <link>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-1931-%ED%9A%8C%EC%9D%98%EC%8B%A4-%EB%B0%B0%EC%A0%95</link>
            <guid>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-1931-%ED%9A%8C%EC%9D%98%EC%8B%A4-%EB%B0%B0%EC%A0%95</guid>
            <pubDate>Thu, 18 Feb 2021 03:09:53 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1931">문제 링크</a></p>
<h2 id="문제-해석">문제 해석</h2>
<h3 id="문제">문제</h3>
<p>한 개의 회의실이 있는데 이를 사용하고자 하는 N개의 회의에 대하여 회의실 사용표를 만들려고 한다. 각 회의 I에 대해 시작시간과 끝나는 시간이 주어져 있고, 각 회의가 겹치지 않게 하면서 회의실을 사용할 수 있는 회의의 최대 개수를 찾아보자. 단, 회의는 한번 시작하면 중간에 중단될 수 없으며 한 회의가 끝나는 것과 동시에 다음 회의가 시작될 수 있다. 회의의 시작시간과 끝나는 시간이 같을 수도 있다. 이 경우에는 시작하자마자 끝나는 것으로 생각하면 된다.</p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 회의의 수 N(1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N+1 줄까지 각 회의의 정보가 주어지는데 이것은 공백을 사이에 두고 회의의 시작시간과 끝나는 시간이 주어진다. 시작 시간과 끝나는 시간은 231-1보다 작거나 같은 자연수 또는 0이다.</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 최대 사용할 수 있는 회의의 최대 개수를 출력한다.</p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="무식하게-풀-수-있을까">무식하게 풀 수 있을까?</h3>
<p>무식하게 풀기 - 회의실 사용 가능한 모든 경우의 수 중 가장 많은 경우를 출력한다.
회의 수가 10만이나 입력되기 때문에 아마 모든 경우를 구하기는 메모리제한이 있어서 어려울 것 같다.,</p>
<h3 id="💰그리디-알고리즘-이용">💰그리디 알고리즘 이용</h3>
<blockquote>
<p>💰 그리디 알고리즘이란?
답을 여러 조각으로 쪼개고, 각 단계마다 지금 당장 가장 좋은 방법만을 선택하는 알고리즘</p>
</blockquote>
<p><strong>많은 경우 해를 찾아낼 수 없음, 제한된 경우에서만 사용된다.</strong></p>
<p>그럼에도 그리디 알고리즘을 사용하는 이유?
동적 계획법이나 완전 탐색법보다 훨씬 빠르기 때문에!
속도가 빨라서 그냥 이거 쓸 수 있으면 쓰는게 좋다..</p>
<p>그리디 알고리즘 사용되는 경우</p>
<ol>
<li><p>탐욕법을 사용해도 <code>항상 최적해</code>를 구할 수 있는 문제를 만난 경우 </p>
</li>
<li><p>시간이나 공간적 제약으로 인해 <code>최적해를 찾기 너무 어려우면</code> 그냥 대충 탐욕법으로 괜찮은 답을 찾음</p>
</li>
</ol>
<h3 id="✨-그리디-알고리즘의-정당성-증명">✨ 그리디 알고리즘의 정당성 증명</h3>
<blockquote>
<p>탐욕적인 알고리즘이 항상 최적해를 찾아낼 수 있다는 것을 아래 두가지 속성을 증명함으로써 보일 수 있다!</p>
</blockquote>
<p>1. 탐욕적 선택 속성</p>
<p>각 단계에서 탐욕적으로 내리는 선택이 항상 최적해로 가는 길 중 하나이며,
탐욕적 선택이 <code>손해가 없다!</code>를 증명</p>
<pre><code class="language-html">가장 종료 시간이 빠른 회의를 포함하는 최적해가 반드시 존재한다.</code></pre>
<ul>
<li>가장 일찍 끝나는 회의를 선택해서 최적해를 얻는 것이 불가능해지는 경우는 없음!</li>
</ul>
<p>2. 최적 부분 구조</p>
<ul>
<li>부분 문제의 최적해에서 전체 문제의 최적해를 만들 수 있음을 증명
대개 자명해서 따로 증명할 필요가 없음!</li>
</ul>
<h3 id="그리디-알고리즘을-이용한-풀이-구성">그리디 알고리즘을 이용한 풀이 구성</h3>
<h4 id="구상하기">구상하기</h4>
<p>회의실 배정이 가장 많은 경우는 회의시간이 짧은 회의부터 착착 배정하면 가장 많은 회의를 찾을 수 있을 것 같음</p>
<ol>
<li><p><code>제일 가까운 시간</code>이면서</p>
</li>
<li><p><code>회의시간</code>이 <code>짧은</code> 회의</p>
</li>
</ol>
<p>따라서, <code>가장 가까운 시간</code>에 시작하는 회의 중 <code>끝나는 시간</code>이 제일 빠른 회의를 선택!
시간만으로 회의실을 배정하는 경우는 아래처럼 되어 해답을 찾을 수 없다.</p>
<p><img src="https://images.velog.io/images/dandev_sw/post/87f05f8a-81e7-4549-82be-d4ddc330ffab/image.png" alt=""></p>
<ul>
<li>짧은 하나의 회의를 긴 두회의 대신 잡아버림,,</li>
<li>그럴듯하다고 해서 정답이 아니라 쓰기가 까다로움.. 0.ㅠ 항상 정당성 증명이 필요하다!</li>
</ul>
<h4 id="구현하기">구현하기</h4>
<p>✨ 한 회의를 선택할 때마다 겹치는 회의 모두 제거하는 방법</p>
<ol>
<li>모든 회의를 종료시간 오름차순으로 정렬</li>
<li>정렬된 회의를 순회하면서 앞 회의와 겹치지 않는 회의 찾기</li>
</ol>
<h3 id="💡-오름차순-정렬---java-arrayssort-고치기">💡 오름차순 정렬 - Java Arrays.sort 고치기!</h3>
<h4 id="sort-방식-커스텀하는-방법은-크게-두가지">sort 방식 커스텀하는 방법은 크게 두가지!</h4>
<ol>
<li><p><code>Comparable</code> 을 class에 implememt 한 후, <code>compateTo</code> 메소드를 override</p>
</li>
<li><p>sort내부에서 <code>comparator</code>를 사용하고, <code>compare</code>메소드를 override</p>
</li>
</ol>
<p>나는 2번이 편해서 2번을 선택해서 풀이를 진행했다.</p>
<h4 id="comparator-정의"><strong>Comparator 정의</strong></h4>
<ul>
<li><p>정렬 가능한 클래스들의 <code>기본 정렬 기준</code>과 다르게 정렬하고 싶을 때 사용하는 <code>인터페이스</code></p>
</li>
<li><p>package: java.util.Comparator</p>
</li>
<li><p>주로 <strong>익명 클래스</strong>로 사용됨</p>
</li>
<li><p>기본 정렬 방법인 오름차순 정렬을 내림차순으로 정렬할 때 많이 사용</p>
</li>
</ul>
<h4 id="comparator-구현-방법"><strong>Comparator 구현 방법</strong></h4>
<ol>
<li><p>comparator interface를 <code>implements</code></p>
</li>
<li><p><code>compare()</code> 메서드 오버라이드 한 <code>myComparator</code> class 작성</p>
<p>--&gt; <code>익명 클래스로</code>도 작성 가능, 아래 코드에서는 익명 클래스로 작성하였음</p>
</li>
</ol>
<h4 id="compare-메소드-작성법"><strong>compare() 메소드 작성법</strong></h4>
<ol>
<li><p>첫번째 파라미터로 넘어온 객체 &lt; 두번째 파라미터로 넘어온 객체: <code>음수</code> 리턴</p>
</li>
<li><p>첫번째 파라미터로 넘어온 객체 == 두번째 파라미터로 넘어온 객체: <code>0</code>리턴</p>
</li>
<li><p>첫번째 파라미터로 넘어온 객체 &gt; 두번째 파라미터로 넘어온 객체: <code>양수</code> 리턴</p>
</li>
</ol>
<blockquote>
<p>✨  음수 또는 0이면 객체 자리 유지되며, <code>양수</code>인 경우 두 <strong>객체의 자리가 변경</strong>된다.</p>
</blockquote>
<pre><code class="language-java">    Arrays.sort(timetable, new Comparator&lt;int[]&gt;() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[1] == o2[1]) {
                    return o1[0] - o2[0];
                }

                return o1[1] - o2[1];
            }
        });</code></pre>
<ul>
<li><p>o1[1]==o2[1] : 두개 끝나는 시간이 같을 경우</p>
</li>
<li><p>return o1[0] - o2[0] 
1) o1의 시작하는 시간이 더 빠르면 <code>음수</code> --&gt; 둘의 위치 바뀌지 않음, 
2) o1의 시작하는 시간이 더 크면 <code>양수</code> --&gt; 둘의 위치 바뀜 
--&gt; 더 시작시간이 빠른 게 앞으로 정렬됨</p>
</li>
<li><p>return o1[1] - o2[1]
두개 끝나는 시간 비교해서 o1이 더 빨리 끝날 경우 자리 변경 없고, 더 늦게 끝날 경우 o2와 자리 바뀜</p>
</li>
</ul>
<h2 id="문제-풀이-코드">문제 풀이 코드</h2>
<pre><code class="language-java">package Greedy;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

public class BOJ1931 {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int N = s.nextInt();
        int[][] timetable = new int[N][2];
        for (int i = 0; i &lt; N; i++) {
            timetable[i][0] = s.nextInt();
            timetable[i][1] = s.nextInt();
        }

        //timetable 정렬 다시 정의
        Arrays.sort(timetable, new Comparator&lt;int[]&gt;() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[1] == o2[1]) {
                    return o1[0] - o2[0];
                }

                return o1[1] - o2[1];
            }
        });

        int result = 0;
        int end_time = 0;
        for (int i = 0; i &lt; N; i++) {
            if (timetable[i][0] &gt;= end_time) {
                end_time = timetable[i][1];
                result++;
            }
        }
        System.out.println(result);


    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] BOJ 11047 동전 0 ]]></title>
            <link>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-11047-%EB%8F%99%EC%A0%84-0</link>
            <guid>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-11047-%EB%8F%99%EC%A0%84-0</guid>
            <pubDate>Thu, 18 Feb 2021 02:58:09 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/11047">문제 링크</a></p>
<h2 id="문제-해석">문제 해석</h2>
<h3 id="문제">문제</h3>
<p>준규가 가지고 있는 동전은 총 N종류이고, 각각의 동전을 매우 많이 가지고 있다.</p>
<p>동전을 적절히 사용해서 그 가치의 합을 K로 만들려고 한다. 이때 필요한 동전 개수의 최솟값을 구하는 프로그램을 작성하시오.</p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 N과 K가 주어진다. (1 ≤ N ≤ 10, 1 ≤ K ≤ 100,000,000)</p>
<p>둘째 줄부터 N개의 줄에 동전의 가치 Ai가 오름차순으로 주어진다. (1 ≤ Ai≤ 1,000,000, A1= 1, i ≥ 2인 경우에 Ai는 Ai-1의 배수)</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 K원을 만드는데 필요한 동전 개수의 최솟값을 출력한다.</p>
<h2 id="문제-풀이">문제 풀이</h2>
<h4 id="무식하게-풀-수-있을까">무식하게 풀 수 있을까?</h4>
<p>무식하게 풀 수 있을 것 같다!
가장 적게 동전을 사용하면 되는 문제이므로,</p>
<ol>
<li>입력 중 가장 큰 값부터 검사해서 만들어야 하는 값보다 화폐가 작으면 마이너스, 아니면 인덱스를 줄임!</li>
<li>마이너스 할 때마다 동전 개수 count 올리기</li>
<li>0 될때까지 반복 후 마지막에 count 출력</li>
</ol>
<p>무식하게 풀었음!</p>
<h4 id="💰-그리디-알고리즘-이용">💰 그리디 알고리즘 이용</h4>
<blockquote>
<p>🍕 Greedy Algorithm은 문제를 해결하는 과정에서 그 순간순간마다 최적이라고 생각되는 결정을 하는 방식으로 진행하여 최종 해답에 도달하는 문제 해결 방식이다.</p>
</blockquote>
<p>그리디 알고리즘은 아래 두 조건이 충족되었을 때 잘 작동한다.</p>
<ol>
<li>탐욕스러운 선택 조건</li>
</ol>
<ul>
<li>앞의 선택이 이후의 선택에 영향을 주지 않는다.</li>
</ul>
<ol start="2">
<li>최적 부분 구조 조건</li>
</ol>
<ul>
<li>문제에 대한 최종 해결 방법이 부분 문제에 대해서도 또한 최적 문제 해결 방법이다.</li>
</ul>
<p>풀이는 아래 두가지 방법을 이용!</p>
<ol>
<li><code>가장 큰 액수</code>부터 계산!</li>
<li>나누는 돈 / 현재 지폐 에서 <code>몫은 지폐 사용량</code>, <code>나머지는 다음 화폐로 나눌 금액</code>이 된다.</li>
</ol>
<pre><code class="language-java">import java.io.FileInputStream;
import java.util.*;
import java.util.stream.*;

class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(new FileInputStream(&quot;input.txt&quot;));
        // Scanner sc = new Scanner(System.in);

        int N = sc.nextInt();
        int M = sc.nextInt();
        int count =0;
        int[] arr=  new int[N];
        for(int i=0; i&lt;N; i++){
            arr[i] = sc.nextInt();
        }

        for(int i = N-1; i&gt;=0; i--){
            if(M&gt;=arr[i]){
                count += M/arr[i];
                M = M%arr[i];
            }

        }

        System.out.println(count);


    }

}
</code></pre>
<h2 id="문제-풀이-코드">문제 풀이 코드</h2>
<pre><code class="language-java">package Greedy;

import java.util.Scanner;

public class BOJ11047 {
    static int[] wallet;
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int N = s.nextInt();
        int K = s.nextInt();

        wallet = new int[N];
        for (int i = 0; i &lt; N; i++) {
            wallet[i] = s.nextInt();
        }

        int index = N-1;
        int count = 0;
        while (true) {
            if(K==0) break;
            if (K &lt; wallet[index]) {
                index = index - 1;
            }
            else {
                K = K - wallet[index];
                count = count + 1;
            }
        }
        System.out.println(count);
    }
}

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 자료 구조 종류 정리]]></title>
            <link>https://velog.io/@dandev_sw/%EC%9E%90%EB%B0%94-%EC%9E%90%EB%A3%8C-%EA%B5%AC%EC%A1%B0-%EC%A2%85%EB%A5%98-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dandev_sw/%EC%9E%90%EB%B0%94-%EC%9E%90%EB%A3%8C-%EA%B5%AC%EC%A1%B0-%EC%A2%85%EB%A5%98-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 18 Feb 2021 02:54:15 GMT</pubDate>
            <description><![CDATA[<h2 id="🧶-collection-interface---순서나-집합적인-저장공간">🧶 Collection interface - 순서나 집합적인 저장공간</h2>
<h3 id="📝-list">📝 List</h3>
<blockquote>
<p><strong>순서가 있는</strong> 데이터의 집합, 데이터의 <strong>중복을 허용</strong>함 (순서가 있으니까 중복 상관없음!)</p>
</blockquote>
<h4 id="linkedlist"><strong>LinkedList</strong></h4>
<ul>
<li><p>get(index), set(index, element), add(index, elelment), remove(index), peek(), peekLast(), push(e), pop(), size(), clear()</p>
</li>
<li><p>removeFirst(), removeLast(), addFirst(element), addLast(element)</p>
</li>
</ul>
<h4 id="stack"><strong>Stack</strong></h4>
<ul>
<li><p>LIFO 구조</p>
</li>
<li><p>push(), pop(), peek(), search()</p>
</li>
</ul>
<h4 id="vector-동기화-보장"><strong>Vector: 동기화 보장</strong></h4>
<ul>
<li><p>addElement()</p>
</li>
<li><p>elementAt()</p>
</li>
<li><p>size()</p>
</li>
<li><p>insertElementAt()</p>
</li>
<li><p>setElementAt()</p>
</li>
<li><p>indexOf() : 데이터 검사해서 index 반환</p>
</li>
<li><p>contains() : 데이터 존재 유무 검사</p>
</li>
</ul>
<h4 id="arraylist-동기화-보장되지-않음"><strong>ArrayList: 동기화 보장되지 않음</strong></h4>
<ul>
<li>add(), get(), size, toArray(), contains(), size()</li>
</ul>
<h3 id="📦-set">📦 Set</h3>
<blockquote>
<p><strong>순서를 유지하지 않는</strong> 데이터의 집합, 데이터의 중복을 허용하지 않음 (순서 없으니까 중복 허용하면 구분을 못함!)</p>
</blockquote>
<h4 id="hashset"><strong>HashSet</strong></h4>
<ul>
<li>add(), next(), remove(), contains(), size()</li>
</ul>
<h4 id="treeset-정렬을-위한-set계열-클래스sortedset-인터페이스-구현"><strong>TreeSet: 정렬을 위한 Set계열 클래스(SortedSet 인터페이스 구현)</strong></h4>
<ul>
<li><p>사용자가 직접 정렬 방식 지정</p>
</li>
<li><p>정렬을 위한 comparator 인터페이스 구현하면 정렬 방식 지정 가능</p>
</li>
<li><p>아무 설정 하지 않고 데이터를 넣으면 기본적으로 오름차순 정렬 --&gt; comparator 인터페이스 구현으로 수정해야 함</p>
</li>
</ul>
<h2 id="🗂-map-interface---키와-값으로-데이터-핸들">🗂 Map interface - 키와 값으로 데이터 핸들</h2>
<h3 id="🔖-hashtable">🔖 Hashtable</h3>
<blockquote>
<p><strong>key와 value의 쌍</strong>으로 이루어진 데이터의 집합, 순서 유지 X, 키 중복 X, <strong>값 중복은 가능</strong></p>
</blockquote>
<h4 id="hashmap"><strong>HashMap</strong></h4>
<ul>
<li>put(), get(key)</li>
</ul>
<h4 id="treemap--sortedmap"><strong>TreeMap:  SortedMap</strong></h4>
<ul>
<li>사용자가 직접 정렬 방식을 지정해서 구현</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] BOJ 14501 퇴사 ]]></title>
            <link>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-14501-%ED%87%B4%EC%82%AC</link>
            <guid>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-14501-%ED%87%B4%EC%82%AC</guid>
            <pubDate>Thu, 18 Feb 2021 02:49:39 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/14501">문제 링크</a></p>
<h2 id="문제-해석">문제 해석</h2>
<p>상담원으로 일하고 있는 백준이는 퇴사를 하려고 한다.</p>
<p>오늘부터 N+1일째 되는 날 퇴사를 하기 위해서, 남은 N일 동안 최대한 많은 상담을 하려고 한다.
백준이는 비서에게 최대한 많은 상담을 잡으라고 부탁을 했고,
비서는 하루에 하나씩 서로 다른 사람의 상담을 잡아놓았다.</p>
<p>각각의 상담은 상담을 완료하는데 걸리는 기간 Ti와 상담을 했을 때 받을 수 있는 금액 Pi로 이루어져 있다.</p>
<p>N = 7인 경우에 다음과 같은 상담 일정표를 보자.</p>
<table style="border-collapse: collapse; width: 100%; height: 57px;" border="1"><tbody><tr style="height: 19px;"><td style="width: 12.5%; height: 19px;">&nbsp;</td><td style="width: 12.5%; height: 19px;">1일</td><td style="width: 12.5%; height: 19px;">2일</td><td style="width: 12.5%; height: 19px;">3일</td><td style="width: 12.5%; height: 19px;">4일</td><td style="width: 12.5%; height: 19px;">5일</td><td style="width: 12.5%; height: 19px;">6일</td><td style="width: 12.5%; height: 19px;">7일</td></tr><tr style="height: 19px;"><td style="width: 12.5%; height: 19px;"><span style="color: #333333;">Ti</span></td><td style="width: 12.5%; height: 19px;">3</td><td style="width: 12.5%; height: 19px;">5</td><td style="width: 12.5%; height: 19px;">1</td><td style="width: 12.5%; height: 19px;">1</td><td style="width: 12.5%; height: 19px;">2</td><td style="width: 12.5%; height: 19px;">4</td><td style="width: 12.5%; height: 19px;">2</td></tr><tr style="height: 19px;"><td style="width: 12.5%; height: 19px;"><span style="color: #333333;">Pi</span></td><td style="width: 12.5%; height: 19px;">10</td><td style="width: 12.5%; height: 19px;">20</td><td style="width: 12.5%; height: 19px;">10</td><td style="width: 12.5%; height: 19px;">20</td><td style="width: 12.5%; height: 19px;">15</td><td style="width: 12.5%; height: 19px;">40</td><td style="width: 12.5%; height: 19px;">200</td></tr></tbody></table>

<p>1일에 잡혀있는 상담은 총 3일이 걸리며, 상담했을 때 받을 수 있는 금액은 10이다.
5일에 잡혀있는 상담은 총 2일이 걸리며, 받을 수 있는 금액은 15이다.</p>
<p>상담을 하는데 필요한 기간은 1일보다 클 수 있기 때문에, 모든 상담을 할 수는 없다. 예를 들어서 1일에 상담을 하게 되면, 2일, 3일에 있는 상담은 할 수 없게 된다. 2일에 있는 상담을 하게 되면, 3, 4, 5, 6일에 잡혀있는 상담은 할 수 없다.</p>
<p>또한, N+1일째에는 회사에 없기 때문에, 6, 7일에 있는 상담을 할 수 없다.
퇴사 전에 할 수 있는 상담의 최대 이익은 1일, 4일, 5일에 있는 상담을 하는 것이며, 이때의 이익은 10+20+15=45이다.</p>
<p>상담을 적절히 했을 때, 백준이가 얻을 수 있는 최대 수익을 구하는 프로그램을 작성하시오.</p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="무식하게-풀-수-있는가">무식하게 풀 수 있는가?</h3>
<p>무식하면 못푸는 문제 같은데 항상 무식하게 풀기부터 도전..
스택을 어찌어찌 잘 이용해서 max값 찾으면 될지도 모르겠다.
근데 그게 dp로 짜는거보다 쉬운지도 모르겠다..
그냥 dp로 풀기로 했음</p>
<h3 id="다이나믹-프로그래밍으로-풀기">다이나믹 프로그래밍으로 풀기!</h3>
<p>💡다이나믹 프로그래밍은 메모이제이션이 핵심!</p>
<blockquote>
<p>✨ 메모이제이션이란?<br>메모이제이션(memoization)은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다.</p>
</blockquote>
<p>1️⃣  무엇을 메모하면 좋을까?</p>
<p>마감 기한을 고려하는게 핵심인 알고리즘이니까, 마감 날짜를 메모한다!
아래와 같이 기록!</p>
<pre><code class="language-html">memo[1] = 10
memo[2] = 30
memo[3] = 40
.
.
.
memo[8] = 90</code></pre>
<p>memo[8]에는 8일까지 일했을 때의 최대 액수를 기록한다!</p>
<p>2️⃣  마감 기한에 따른 최대 액수를 어떤 식으로 저장할 수 있을까?</p>
<p>아래 두가지 조건을 고려!</p>
<ul>
<li>마감된 날짜가 같을 수는 없다.</li>
<li>하루도 안쉬고 일하는 것보다 띄엄띄엄 일하는 경우의 수익이 더 높을 수도 있다.</li>
</ul>
<p>1. 마감된 날짜가 같을 수는 없다.</p>
<pre><code class="language-java">memo[pay[i][0]+i] = max(memo[pay[i][0]+i],pay[i][1]+memo[i]);</code></pre>
<p>마감된 날짜가 같을 수는 없으니 해당 날짜의 페이는 아래 두가지 경우이다.</p>
<p> 1) <code>해당 마감일에 원래 저장되어있던 pay</code>
 2) <code>오늘 pay, 오늘 마감인 과제 pay더한 값</code></p>
<p>더 큰 값을 저장해야 하므로, max를 써서 둘 중 더 큰 값을 출력</p>
<p>2. 하루도 안쉬고 일하는 것보다 띄엄띄엄 일하는 경우의 수익이 더 높을 수 있다.</p>
<pre><code class="language-java">memo[i] = max(memo[i], max);
max = max(max, memo[i]);</code></pre>
<p>i일 까지 일했을 때 최대값은 다음 두가지 경우 중 하나이다.</p>
<p> 1) <code>i일 까지 꽉 채워서 일한 경우</code>
 2) <code>i일에 일하지 않은 경우</code></p>
<p>따라서 memo[i]나 이전 저장된 값인 max 중 더 큰 값을 memo한다.</p>
<h2 id="문제-풀이-코드">문제 풀이 코드</h2>
<pre><code class="language-java">package DP;

import java.util.Scanner;

import static java.lang.Math.max;

public class BOJ14501 {
    static int[][] pay;
    static int day;
    static int[] memo;
    static int max = 0;
    public static void main(String[] args) {
        //입력 배열로 정돈해서 받기
        Scanner s = new Scanner(System.in);
        day = s.nextInt();
        pay = new int[day + 10][2];
        for (int i = 1; i &lt; day+1; i++) {
            pay[i][0] = s.nextInt();
            pay[i][1] = s.nextInt();
        }
        //메모 선언
        memo = new int[day + 10];
        //DP돌림
        DP(pay);
    }
    static void DP(int[][] pay) {
        for (int i = 1; i &lt;=day+1; i++) {
            //이전까지의 최대 수입을 비교해서 최대 수입을 현재에도 저장해준다.
            //이전에 최대수입이 났을 수 있으므로
            //ex) 3,7,(5 예상) 이라고 하면 5의 값은 7로 바꿔주는게 최대수입을 얻는데 맞다.
            memo[i] = max(memo[i], max);
            //이전에 저장된 최대수익 vs 이번 움직임으로 생긴 최대 수익
            memo[pay[i][0]+i] = max(memo[pay[i][0]+i],pay[i][1]+memo[i]);
            //출력될 최대 수입
            max = max(max, memo[i]);
        }
        System.out.println(max);
    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] BOJ 2839 설탕 배달]]></title>
            <link>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-2839-%EC%84%A4%ED%83%95-%EB%B0%B0%EB%8B%AC</link>
            <guid>https://velog.io/@dandev_sw/%EB%B0%B1%EC%A4%80-BOJ-2839-%EC%84%A4%ED%83%95-%EB%B0%B0%EB%8B%AC</guid>
            <pubDate>Thu, 18 Feb 2021 02:43:27 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/2839">문제 링크</a></p>
<h2 id="문제-해석">문제 해석</h2>
<p>상근이는 요즘 설탕공장에서 설탕을 배달하고 있다. 상근이는 지금 사탕가게에 설탕을 정확하게 N킬로그램을 배달해야 한다. 설탕공장에서 만드는 설탕은 봉지에 담겨져 있다. 봉지는 3킬로그램 봉지와 5킬로그램 봉지가 있다.</p>
<p>상근이는 귀찮기 때문에, 최대한 적은 봉지를 들고 가려고 한다. 예를 들어, 18킬로그램 설탕을 배달해야 할 때, 3킬로그램 봉지 6개를 가져가도 되지만, 5킬로그램 3개와 3킬로그램 1개를 배달하면, 더 적은 개수의 봉지를 배달할 수 있다.</p>
<p>상근이가 설탕을 정확하게 N킬로그램 배달해야 할 때, 봉지 몇 개를 가져가면 되는지 그 수를 구하는 프로그램을 작성하시오.</p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="무식하게-풀-수-있는가">무식하게 풀 수 있는가?</h3>
<blockquote>
<p>💡 완전 탐색으로 풀기! 설탕 봉지 개수를 기록하며 풀었다.</p>
</blockquote>
<p>1️⃣ 어떤 것을 메모하면 좋을까?</p>
<p>문제에서 &quot;최대한 적은 봉지&quot; 수를 찾아야 한다고 했으므로
한번에 최대한 많은 용량의 봉지, 즉 5킬로짜리 봉지를 담아야 한다.
따라서 5킬로짜리 봉지 개수 메모를 배열로 만들고, 3킬로짜리 봉지 개수를 메모하기로 했다.</p>
<p>2️⃣ 메모 형태는 어떻게?</p>
<p>5킬로짜리 봉지의 개수를 배열의 index로!
배열의 값은 3킬로짜리 봉지의 값으로 설정했다.</p>
<pre><code class="language-html">5[0] = 3 //5킬로 봉지 0개, 3킬로 봉지 3개
5[1] = 2 //5킬로 봉지 1개, 3킬로 봉지 2개
5[2] = -1 //5킬로 봉지 2개를 써서 해당 설탕 무게를 맞출 수 없음</code></pre>
<p>문제에서 5킬로와 3킬로짜리 봉지로 해당 설탕 크기를 나를 수 없다면 -1을 출력하라고 했기 때문에
배열의 초기값을 -1로 설정했다.</p>
<p>3️⃣ 적당한 메모장 크기 찾기</p>
<p>11킬로짜리 설탕을 나르는 데 5킬로 봉지는 최대 2개 필요하므로
입력 최대 크기인 5000/5 = 1000, 길이 1000의 배열을 만들 필요가 없다.
따라서 입력받은 수를 5로 나눈 몫보다 하나 큰 크기의 배열을 생성해 -1로 초기화하도록 했다.</p>
<p>4️⃣ 설탕 봉지 개수 구하기</p>
<ol>
<li>우선 5킬로 봉지를 입력값에서 뺀다.</li>
<li>남은 입력값을 3으로 나눠 나머지가 0이면 몫을 메모</li>
<li>나머지가 0이 아닐 경우 continue</li>
</ol>
<h2 id="문제-풀이-코드">문제 풀이 코드</h2>
<pre><code class="language-java">package DP;

import java.util.Scanner;

import static java.lang.Math.min;

public class BOJ2839 {
    static int sugar;
    static int memo[];

    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        sugar = s.nextInt();
        //배열 크기 정하기
        int length = sugar / 5;

        //메모 배열 초기화
        memo = new int[length+1];
        for (int i = 0; i &lt;= length; i++) {
            memo[i] = -1;
        }

        //메모하기
        for (int i = 0; i &lt;= length; i++) {
            int temp = sugar - (5 * i);
            if (temp % 3 == 0) {
                memo[i] = temp / 3;
            }
        }

        //배열 최소값 찾기
        int min = 1001;
        int index = 0;
        for (int i = 0; i &lt;= length; i++) {
            if (memo[i] != -1) {
                int lastmin = min;
                min = min(min, memo[i]);
                if (lastmin != min) {
                    index = i;
                }
            }
        }
        //최소 값 출력
        if (min == 1001) {
            System.out.println(-1);
        }
        else{
            System.out.println(min + index);
        }

    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[#2 Crypto Zombies] 좀비가 희생물을 공격하다]]></title>
            <link>https://velog.io/@dandev_sw/2-Crypto-Zombies-%EC%A2%80%EB%B9%84%EA%B0%80-%ED%9D%AC%EC%83%9D%EB%AC%BC%EC%9D%84-%EA%B3%B5%EA%B2%A9%ED%95%98%EB%8B%A4</link>
            <guid>https://velog.io/@dandev_sw/2-Crypto-Zombies-%EC%A2%80%EB%B9%84%EA%B0%80-%ED%9D%AC%EC%83%9D%EB%AC%BC%EC%9D%84-%EA%B3%B5%EA%B2%A9%ED%95%98%EB%8B%A4</guid>
            <pubDate>Thu, 18 Feb 2021 02:20:41 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/dandev_sw/post/4bbf9ca0-381a-4aa8-ae3c-1b4aa1b420df/image.png" alt=""></p>
<h1 id="✍🏻-코드-분석">✍🏻 코드 분석</h1>
<h2 id="매핑과-주소">매핑과 주소</h2>
<h3 id="주소">주소</h3>
<p>이더리움 블록체인은 은행 계좌와 같은 <strong>계정들</strong>로 이루어져 있다. 계정은 이더리움 블록체인상의 통화인 <strong>_이더_</strong>의 잔액을 가진다.
은행 계좌에서 다른 계좌로 돈을 송금할 수 있듯이, 계정을 통해 다른 계정과 이더를 주고 받을 수 있다.
각 계정은 은행 계좌 번호와 같은주소를 가지고 있다. 주소는 특정 계정을 가리키는 고유 식별자로, 아래와 같이 표현된다.</p>
<pre><code class="language-javascript">0x0cE446255506E92DF41614C46F1d6df9Cc969183</code></pre>
<p><strong>&quot;주소는 특정 유저(혹은 스마트 컨트랙트)가 소유한다&quot;</strong>라는 점이 핵심!
따라서, 주소를 자원에 대한 소유권을 나타내는 고유 ID로 활용할 수 있다.</p>
<h3 id="매핑">매핑</h3>
<p>솔리디티에서 <strong>구조화된 데이터를 저장</strong>하는 방법이다.
매핑은 다음과 같이 사용할 수 있다.</p>
<pre><code class="language-javascript">//기본형
mapping(key =&gt; value) mapping_name;

// 금융 앱용으로, 유저의 계좌 잔액을 보유하는 uint를 저장한다.
mapping (address =&gt; uint) public accountBalance; 

// 혹은 userID로 유저 이름을 저장/검색하는 데 매핑을 쓸 수도 있다.
mapping (uint =&gt; string) userIdToName;</code></pre>
<p>매핑은 기본적으로 키-값 (key-value) 저장소로, 데이터를 저장하고 검색하는 데 이용된다.
첫번째 예시에서 키는address이고 값은uint이다. 두번째 예시에서 키는uint이고 값은string이다.</p>
<h2 id="msgsender">Msg.sender</h2>
<p>솔리디티에는 모든 함수에서 이용 가능한 특정 전역 변수들이 있다.
그 중의 하나가 <strong>현재 함수를 호출한 사람 (혹은 스마트 컨트랙트)의 주소</strong>를 가리키는msg.sender!</p>
<blockquote>
<p>참고: 솔리디티에서 함수 실행은 항상 외부 호출자가 시작한다. 컨트랙트는 누군가가 컨트랙트의 함수를 호출할 때까지 블록체인 상에서 아무 것도 안 하고 있을 것이니 항상msg.sender가 있어야 한다.</p>
</blockquote>
<p>Mapping을 아래와 같이 msg.sender가 이용할 수 있다.</p>
<pre><code class="language-javascript">mapping (address =&gt; uint) favoriteNumber;

function setMyNumber(uint _myNumber) public {
  // `msg.sender`에 대해 `_myNumber`가 저장되도록 `favoriteNumber` 매핑을 업데이트한다 `
  favoriteNumber[msg.sender] = _myNumber;
  // ^ 데이터를 저장하는 구문은 배열로 데이터를 저장할 떄와 동일하다 
}

function whatIsMyNumber() public view returns (uint) {
  // sender의 주소에 저장된 값을 불러온다 
  // sender가 `setMyNumber`을 아직 호출하지 않았다면 반환값은 `0`이 될 것이다
  return favoriteNumber[msg.sender];
}</code></pre>
<p>Key값을 []안에 넣고, value를 할당하는 형식으로 작동한다!
msg.sender는 현재 함수를 호출한 사람의 주소를 담고 있으므로 mapping의 Key인 address형이 된다.</p>
<h2 id="require">require</h2>
<p>특정 조건이 참이 아닐 때 함수가 에러 메세지를 발생하고 실행을 멈추게 한다.
보통 한 노드가 함수를 한 번 호출하는 데에 쓰임!</p>
<pre><code class="language-javascript">function sayHiToVitalik(string _name) public returns (string) {
  // _name이 &quot;Vitalik&quot;인지 비교한다. 참이 아닐 경우 에러 메시지를 발생하고 함수를 벗어난다
  // (참고: 솔리디티는 고유의 스트링 비교 기능을 가지고 있지 않기 때문에 
  // 스트링의 keccak256 해시값을 비교하여 스트링 값이 같은지 판단한다)
  require(keccak256(_name) == keccak256(&quot;Vitalik&quot;));
  // 참이면 함수 실행을 진행한다:
  return &quot;Hi!&quot;;
}</code></pre>
<pre><code class="language-javascript"> require(ownerZombieCount[msg.sender]==0);</code></pre>
<p>이런 구문이 있으면 저 ownerZombieCount의 값이 0이 아닐 경우 에러 메세지가 출력되고, 함수가 작동되지 않는다.</p>
<h2 id="상속">상속</h2>
<pre><code class="language-javascript">contract Doge {
  function catchphrase() public returns (string) {
    return &quot;So Wow CryptoDoge&quot;;
  }
}

contract BabyDoge is Doge {
  function anotherCatchphrase() public returns (string) {
    return &quot;Such Moon BabyDoge&quot;;
  }
}</code></pre>
<p>문법 진짜 이상하다.. 저 <strong>is 가 implements</strong>와 같은 기능을 함!
위 코드에 따르면 baby doge는 <strong>doge의 모든 public 함수 접근</strong> 가능!</p>
<h2 id="저장-공간">저장 공간</h2>
<blockquote>
<p>storage == HDD</p>
</blockquote>
<p>블록체인 상에 영구적으로 저장되는 변수
상태 변수(함수 외부 선언 변수)는 초기 설정상 storage로 선언됨
포인터로 사용되는 변수를 선언한다!</p>
<blockquote>
<p>memory == RAM</p>
</blockquote>
<p>임시적으로 저장되는 변수, 컨트랙트 함수에 대한 외부 호출들이 일어나는 사이에 지워짐
함수 내에 선언된 변수는 자동으로 Memory로 선언됨 
단순히 temp 변수로 사용된다!</p>
<blockquote>
<p>💡  두개 지정은 구조체와 배열을 처리할 때 사용! 평소에는 솔리디티가 자동으로 해준다.</p>
</blockquote>
<pre><code class="language-javascript">contract SandwichFactory {
  struct Sandwich {
    string name;
    string status;
  }

  Sandwich[] sandwiches;

  function eatSandwich(uint _index) public {
    // Sandwich mySandwich = sandwiches[_index];

    // ^ 꽤 간단해 보이나, 솔리디티는 여기서 
    // `storage`나 `memory`를 명시적으로 선언해야 한다는 경고 메시지를 발생한다. 
    // 그러므로 `storage` 키워드를 활용하여 다음과 같이 선언해야 한다:
    Sandwich storage mySandwich = sandwiches[_index];
    // ...이 경우, `mySandwich`는 저장된 `sandwiches[_index]`를 가리키는 포인터이다.
    // 그리고 
    mySandwich.status = &quot;Eaten!&quot;;
    // ...이 코드는 블록체인 상에서 `sandwiches[_index]`을 영구적으로 변경한다. 

    // 단순히 복사를 하고자 한다면 `memory`를 이용하면 된다: 
    Sandwich memory anotherSandwich = sandwiches[_index + 1];
    // ...이 경우, `anotherSandwich`는 단순히 메모리에 데이터를 복사하는 것이 된다. 
    // 그리고 
    anotherSandwich.status = &quot;Eaten!&quot;;
    // ...이 코드는 임시 변수인 `anotherSandwich`를 변경하는 것으로 
    // `sandwiches[_index + 1]`에는 아무런 영향을 끼치지 않는다. 그러나 다음과 같이 코드를 작성할 수 있다: 
    sandwiches[_index + 1] = anotherSandwich;
    // ...이는 임시 변경한 내용을 블록체인 저장소에 저장하고자 하는 경우이다.
  }
}</code></pre>
<h3 id="상속-관계-함수-접근">상속 관계 함수 접근</h3>
<blockquote>
<p>private를 <strong>internal</strong>로 바꾸어 선언하면 함수가 정의된 컨트랙트를 <strong>상속하는 컨트랙트에서도 접근</strong> 가능하다!</p>
</blockquote>
<h2 id="다른-컨트랙트와-상호작용하기---interface-이용">다른 컨트랙트와 상호작용하기 - interface 이용</h2>
<h3 id="인터페이스-정의">인터페이스 정의</h3>
<p>블록체인 상에 있으면서 우리가 소유하지 않은 컨트랙트와 우리 컨트랙트가 상호작용을 하려면 우선 <strong>인터페이스</strong>를 정의해야 한다.</p>
<p>다음과 같은 블록체인 컨트랙트가 있다고 가정해보자.</p>
<pre><code class="language-javascript">contract LuckyNumber {
  mapping(address =&gt; uint) numbers;

  function setNum(uint _num) public {
    numbers[msg.sender] = _num;
  }

  function getNum(address _myAddress) public view returns (uint) {
    return numbers[_myAddress];
  }
}</code></pre>
<p>이 컨트랙트는 아무나 자신의 행운의 수를 저장할 수 있는 간단한 컨트랙트이고, 각자의 이더리움 주소와 연관이 있을 것이다. 이 주소를 이용해서 누구나 그 사람의 행운의 수를 찾아 볼 수 있게 public으로 모든 함수가 정의되어있다.</p>
<p>이제getNum함수를 이용하여 이 컨트랙트에 있는 데이터를 읽고자 하는 external 함수가 있다고 해 보자. 먼저,LuckyNumber 컨트랙트의 <strong>인터페이스</strong>를 정의할 필요가 있다.</p>
<pre><code class="language-javascript">contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}</code></pre>
<p>인터페이스는 자바 인터페이스 정의하듯이 하면 된다! <strong>시작이 interface가 아니라 contract!!!!!</strong>
여기서는 인터페이스를 정의하는 것이 컨트랙트를 정의하는 것과 유사하다는 걸 참고해야 한다.
먼저, 다른 컨트랙트와 <strong>상호작용하고자 하는 함수만을 선언</strong>할 뿐 <strong>다른 함수나 상태 변수를 언급하지 않는다.</strong>
다음으로, 함수 몸체를 정의하지 않는다. 중괄호{,}를 쓰지 않고 함수 선언을 세미콜론(;)으로 간단하게 끝낸다.
그러니 인터페이스는 컨트랙트 뼈대처럼 보인다고 할 수 있다. 
우리의 dapp 코드에 이런 인터페이스를 포함하면 컨트랙트는 다른 컨트랙트에 정의된 함수의 특성, 호출 방법, 예상되는 응답 내용에 대해 알 수 있게 된다.</p>
<h3 id="다수의-반환값-처리">다수의 반환값 처리</h3>
<p>또 참고하면 좋은게, 다른 프밍 언어들과 다르게 <strong>솔리디티는 함수가 하나 이상의 값을 반환</strong>할 수 있다!</p>
<pre><code class="language-javascript">function multipleReturns() internal returns(uint a, uint b, uint c) {
  return (1, 2, 3);
}

function processMultipleReturns() external {
  uint a;
  uint b;
  uint c;
  // 다음과 같이 다수 값을 할당한다:
  (a, b, c) = multipleReturns();
}

// 혹은 단 하나의 값에만 관심이 있을 경우: 
function getLastReturnValue() external {
  uint c;
  // 다른 필드는 빈칸으로 놓기만 하면 된다: 
  (,,c) = multipleReturns();
}</code></pre>
<p>아래처럼 다수 값을 할당해서 처리! 하나의 값만 가져오는 것도 흥미로움.,,,네네</p>
<h3 id="인터페이스-이용">인터페이스 이용</h3>
<pre><code class="language-javascript">//인터페이스 정의
contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

//인터페이스 이용
contract MyContract {
  address NumberInterfaceAddress = 0xab38...
  // ^ 이더리움상의 FavoriteNumber 컨트랙트 주소이다
  NumberInterface numberContract = NumberInterface(NumberInterfaceAddress)
  // 이제 `numberContract`는 다른 컨트랙트를 가리키고 있다.

  function someFunction() public {
    // 이제 `numberContract`가 가리키고 있는 컨트랙트에서 `getNum` 함수를 호출할 수 있다:
    uint num = numberContract.getNum(msg.sender);
    // ...그리고 여기서 `num`으로 무언가를 할 수 있다
  }
}</code></pre>
<p>코드 가운데쪽에 보면 인터페이스 numberContract를 선언해 사용한다는걸 알 수 있음! 초기값은 오른쪽 코드처럼 설정한다.</p>
<pre><code class="language-javascript">//getkitty는 10개 값을 반환하고, 맨 마지막에 있는 값을 kittyDna에 저장하는 코드

function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna);
  }</code></pre>
<p>getkitty는 10개 값을 반환하고, 맨 마지막에 있는 값을 kittyDna에 저장하는 코드이다. 쉼표 진짜 어이없음..</p>
<h2 id="string-동일-여부-판단">string 동일 여부 판단</h2>
<pre><code class="language-javascript"> if(keccak256(_species)==keccak256(&quot;kitty&quot;)){
        newDna = newDna - newDna % 100 + 99;
    }</code></pre>
<p>hash값 비교로 스트링이 한글자한글자 일치하는지 판단 가능! 해시는 입력 값이 조금만 바뀌어도 확 바뀌니까..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[#1 Crypto Zombies] 좀비 공장 만들기]]></title>
            <link>https://velog.io/@dandev_sw/1-Crypto-Zombies-%EC%A2%80%EB%B9%84-%EA%B3%B5%EC%9E%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@dandev_sw/1-Crypto-Zombies-%EC%A2%80%EB%B9%84-%EA%B3%B5%EC%9E%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 18 Feb 2021 02:08:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/dandev_sw/post/40407785-66d6-4961-a545-8f476bdf1363/cat2.jpeg" alt="좀비 고양이"></p>
<p><a href="https://cryptozombies.io/ko/lesson/1/chapter/1">크립토 좀비 튜토리얼</a></p>
<p>Solidity 튜토리얼로는 크립토 좀비가 굉장히 유명하다.
블록체인은 배울 수 있는 곳이 정말정말 적어서 이런게 너무 귀하고 그렇다..네네
튜토리얼 하면서 솔리디티 문법 하나하나 차근차근 배울 수 있어서 좋았다. 솔리디티 문법 자체는 좀 기괴했지만...
어쨌든 크립토 좀비를 하면서 솔리디티 문법 배운 것들을 착착 적어보려 한다.</p>
<p><img src="https://images.velog.io/images/dandev_sw/post/f1654928-cc9c-4f81-8f28-b8eca8cecb4d/image.png" alt=""></p>
<h2 id="✍🏻-코드-분석">✍🏻 코드 분석</h2>
<h3 id="1-기본-구조">1. 기본 구조</h3>
<pre><code class="language-javascript">pragma solidity ^0.4.19;

contract HelloWorld {

}</code></pre>
<ul>
<li>모든 솔리디티 소스 코드는 &quot;version pragma&quot;로 시작해야 하는데, 이는 해당 코드가 이용해야 하는 솔리디티 버전을 선언하는 것이다.</li>
<li>솔리디티 코드는 <strong>컨트랙트</strong>안에 싸여 있다.컨트랙트는 이더리움 애플리케이션의 기본적인 구성 요소로, 모든 변수와 함수는 어느 한 컨트랙트에 속한다. 컨트랙트는 모든 프로젝트의 시작점!</li>
</ul>
<h3 id="2-새로운-변수-uint형과-상태-변수">2. 새로운 변수 uint형과 상태 변수</h3>
<pre><code class="language-javascript">contract Example {
  // 이 변수는 블록체인에 영구적으로 저장된다
  uint myUnsignedInteger = 100;
}</code></pre>
<ul>
<li>컨트랙트 안에서 선언하는 변수는 블록체인에 영구적으로 저장된다. 그래서 조심해야함..!</li>
<li>uint자료형은 부호 없는 정수로, <strong>값이 음수가 아니어야 한다는</strong> 의미. 부호 있는 정수는 int자료형!</li>
<li>솔리디티에서 uint는 실제로 uint256, 즉 256비트 부호 없는 정수의 다른 표현이다.uint8, uint16, uint32등과 같이 uint를 더 적은 비트로 선언할 수도 있다. 하지만 특별한 경우 아니면 그냥 uint로 선언!</li>
</ul>
<h3 id="3-구조체-선언-배열-선언">3. 구조체 선언, 배열 선언</h3>
<pre><code class="language-javascript">struct Person {
  uint age;
  string name;
}

// 2개의 원소를 담을 수 있는 고정 길이의 배열:
uint[2] fixedArray;
// 또다른 고정 배열으로 5개의 스트링을 담을 수 있다:
string[5] stringArray;
// 동적 배열은 고정된 크기가 없으며 계속 크기가 커질 수 있다:
uint[] dynamicArray;

//구조체 배열
Person[] people; // 이는 동적 배열로, 원소를 계속 추가할 수 있다.</code></pre>
<ul>
<li>자바처럼 구조체, 배열 선언이 가능하다.</li>
<li>고정 길이 배열과 동적 배열 둘 다 선언 가능하다.</li>
<li>구조체 배열도 선언 가능하다.</li>
</ul>
<pre><code class="language-javascript">Person[] public people;</code></pre>
<ul>
<li>다른 앱에서 접근 가능하도록 하려면 public 배열을 선언해야 한다.</li>
</ul>
<pre><code class="language-javascript">//구조체 배열에 구조체 생성해서 넣기
people.push(Person(16, &quot;Vitalik&quot;));

//배열에 push로 원소 넣기
uint[] numbers;
numbers.push(5);
numbers.push(10);
numbers.push(15);
// numbers 배열은 [5, 10, 15]과 같다.</code></pre>
<p>-array.push()는 무언가를 배열의<strong>끝</strong>에 추가해서 모든 원소가 순서를 유지하도록 한다.</p>
<h3 id="4-함수-선언">4. 함수 선언</h3>
<pre><code class="language-javascript">string greeting = &quot;What&#39;s up dog&quot;;

//public 함수
function sayHello() public returns (string) {
  return greeting;
}

//pure 함수
function _multiply(uint a, uint b) private pure returns (uint) {
  return a * b;
}

//private 함수
  function _createZombie(string _name, uint _dna) private {
        zombies.push(Zombie(_name, _dna));
}</code></pre>
<ul>
<li>pure는 함수가 앱에서 어떤 데이터도 접근하지 않는 것을 의미</li>
<li>return 타입을 앞에 명시함.. 개이상해</li>
<li>public, privte은 자바와 똑같음! 외부 앱에 접근 권한을 주냐 마냐 하는 것</li>
</ul>
<h3 id="5-keccak256과-형-변환">5. Keccak256과 형 변환</h3>
<pre><code class="language-javascript">//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256(&quot;aaaab&quot;);
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256(&quot;aaaac&quot;);</code></pre>
<ul>
<li>keccak256: 솔리디티가 내장하고 있는 해시 함수. 주석처리된 값으로 변환시켜줌</li>
<li>해시 함수는 기본적으로 입력 스트링을 랜덤 256비트 16진수로 매핑</li>
</ul>
<pre><code class="language-javascript">uint8 a = 5;
uint b = 6;
// a * b가 uint8이 아닌 uint를 반환하기 때문에 에러 메시지가 난다:
uint8 c = a * b; 
// b를 uint8으로 형 변환해서 코드가 제대로 작동하도록 해야 한다:
uint8 c = a * uint8(b); </code></pre>
<ul>
<li>uint 기본은 256비트! uint8은 8비트여서 저 둘이 호환이 안된다.</li>
</ul>
<h3 id="6-이벤트-작성">6. 이벤트 작성</h3>
<pre><code class="language-javascript">// 이벤트를 선언한다
event IntegersAdded(uint x, uint y, uint result);

function add(uint _x, uint _y) public {
  uint result = _x + _y;
  // 이벤트를 실행하여 앱에게 add 함수가 실행되었음을 알린다:
  IntegersAdded(_x, _y, result);
  return result;
}</code></pre>
<ul>
<li><strong>이벤트</strong>는 컨트랙트가 블록체인 상에서 자네 앱의 사용자 단에서 무언가 액션이 발생했을 때 의사소통하는 방법이다. 컨트랙트는 특정 이벤트가 일어나는지 &quot;귀를 기울이고&quot; 그 이벤트가 발생하면 행동을 취하게 된다.</li>
</ul>
<h3 id="7-web3js">7. web3.js</h3>
<pre><code class="language-javascript">// 여기에 우리가 만든 컨트랙트에 접근하는 방법을 제시한다:
var abi = /* abi generated by the compiler */
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress = /* our contract address on Ethereum after deploying */
var ZombieFactory = ZombieFactoryContract.at(contractAddress)
// `ZombieFactory`는 우리 컨트랙트의 public 함수와 이벤트에 접근할 수 있다.

// 일종의 이벤트 리스너가 텍스트 입력값을 취한다:
$(&quot;#ourButton&quot;).click(function(e) {
  var name = $(&quot;#nameInput&quot;).val()
  // 우리 컨트랙트의 `createRandomZombie`함수를 호출한다:
  ZombieFactory.createRandomZombie(name)
})

// `NewZombie` 이벤트가 발생하면 사용자 인터페이스를 업데이트한다
var event = ZombieFactory.NewZombie(function(error, result) {
  if (error) return
  generateZombie(result.zombieId, result.name, result.dna)
})

// 좀비 DNA 값을 받아서 이미지를 업데이트한다
function generateZombie(id, name, dna) {
  let dnaStr = String(dna)
  // DNA 값이 16자리 수보다 작은 경우 앞 자리를 0으로 채운다
  while (dnaStr.length &lt; 16)
    dnaStr = &quot;0&quot; + dnaStr

  let zombieDetails = {
    // 첫 2자리는 머리의 타입을 결정한다. 머리 타입에는 7가지가 있다. 그래서 모듈로(%) 7 연산을 하여
    // 0에서 6 중 하나의 값을 얻고 여기에 1을 더해서 1에서 7까지의 숫자를 만든다. 
    // 이를 기초로 &quot;head1.png&quot;에서 &quot;head7.png&quot; 중 하나의 이미지를 불러온다:
    headChoice: dnaStr.substring(0, 2) % 7 + 1,
    // 두번째 2자리는 눈 모양을 결정한다. 눈 모양에는 11가지가 있다:
    eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
    // 셔츠 타입에는 6가지가 있다:
    shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
    // 마지막 6자리는 색깔을 결정하며, 360도(degree)까지 지원하는 CSS의 &quot;filter: hue-rotate&quot;를 이용하여 아래와 같이 업데이트된다:
    skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
    eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
    clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
    zombieName: name,
    zombieDescription: &quot;A Level 1 CryptoZombie&quot;,
  }
  return zombieDetails
}</code></pre>
<ul>
<li>이더리움은<strong>Web3.js</strong>라고 하는 자바스크립트 라이브러리를 가지고 있다.</li>
<li>직접 작성한 자바스크립트로 위의zombieDetails에서 생성된 값을 받아 웹 브라우저 기반 자바스크립트의 마법과 같은 기능(우리는 Vue.js를 이용함)을 활용하여 이미지를 변경하고 CSS 필터를 적용할 수 있다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>