블록체인2017. 7. 7. 17:49


토큰 기능 개선


암호화토큰은 코드를 만지지도 않고 바로 만들어 배포할 수 있다. 하지만 진짜 마술은 커스터마이징하면서 발생한다. 이번 장에서는 토큰에 추가 할 수있는 함수에 대한 제안을 제공하여 사용자의 필요에 더 적합하게 만든다.


중앙화된 관리자


dapp이 기본적으로 분산되어 있지만 원한다면 중앙화된 관리자를 가질수 없다는것을 의미하지는 않는다. 중앙에서 어떤 제약조건을 강제하고 싶을 수 있다. 이런 기능을 추가할 수 있으며 시작시에 추가할 수 있다 이는 모든 토큰 소유자는 게임을 소유하기로 결정하기전에 게임의 규칙을 정확하게 알 수 있다.


그러려면 화폐에 대한 중안컨트롤러가 필요하다. 이는 단순한 계정이 될수 있지만 또 한 컨트랙트로 가능하며 토큰을 생성하는데 이 컨트랙트에 따라서 결정이 되게 된다. 투표권을 부여할 수 있는 민주적단체이거나 토큰 주인에게 제한적인 능력만을 부여 할 수 있다.



contract owned {

    address public owner;


    function owned() {

        owner = msg.sender;

    }


    modifier onlyOwner {

        if (msg.sender != owner) throw;

        _;

    }


    function transferOwnership(address newOwner) onlyOwner {

        owner = newOwner;

    }

}


이것은 소유할 수 있는 계약에 대한 아주 기본적인 코드이가. 다음 단계로 컨트렉트에 추가한다.


contract MyToken is owned {

    /* the rest of the contract as usual */


이는 MyToken의 내부 어디에서든 owner와 modifier onlyOwner에 접근이 가능하다는 것을 의미한다. transferOwnership이라는 함수를 가지고 있다. 이 정보가 아주 중요하기 때문에 컨트랙트 시작시 셋업할 수 있다.


function MyToken(

    uint256 initialSupply,

    string tokenName,

    uint8 decimalUnits,

    string tokenSymbol,

    address centralMinter

    ) {

    if(centralMinter != 0 ) owner = centralMinter;


CENTERAL MINT


회전되고 있는 동전의 양이 바뀌길 원한다고 가정하자. 이것은 실제로 금괴인증서나 정부통화같은 블록체인이 아닌 자산(off blockchain asset)를 나타내며 가상 재고가 실제 재고를 반영하도록 하려는 경우이다.이는 통화보유자가 토큰 가격을 통제하기를 원하고 회전되고 있는 통화를 더 발행하거나 제거하려는 경우도 해당된다.


먼저 해야할것은 totalSuplly를 추가하고 초기화하는것이다.


contract MyToken {

    uint256 public totalSupply;


    function MyToken(...) {

        totalSupply = initialSupply;

        ...

    }

    ...

}


이제 새함수를 추가하여 소유자가 새로운 토큰을 만들수 있게 한다.


function mintToken(address target, uint256 mintedAmount) onlyOwner {

    balanceOf[target] += mintedAmount;

    totalSupply += mintedAmount;

    Transfer(0, owner, mintedAmount);

    Transfer(owner, target, mintedAmount);

}


함수명 뒤에 onlyOwner를 주의깊게 봐야한다. 이는 앞서 정의한 mofifier onlyOwner에서 상속되어 컴파일되어 재작성될것이라는것을 의미한다. 이 함수의 코드는 mofifier함수의 밑줄 부분에 삽입될것이다. 이는 이 함수가 해당 계정에 의해서만 호출되고 owner로 셋팅될것을 의미한다. 이 코드만 추가하면 더 많은 코인을 생성할 수 있다.


자산의 동결


토큰의 사용유형에 따라 사용할 수 있는 사람과 할수 없는 사람에 대한 규칙적인 제한이 필요할 수 있다. 그러려면 자산을 동결하거나 해재하는 파라메터를 컨트랙트에 추가해야 한다.


아래 변수와 함수를 아무데나 컨트랙트내에 추가한다. 아무데나 가능하지만 다른 이벤트나 매핑과 함께 배치하는게 좋다.


mapping (address => bool) public frozenAccount;

event FrozenFunds(address target, bool frozen);


function freezeAccount(address target, bool freeze) onlyOwner {

    frozenAccount[target] = freeze;

    FrozenFunds(target, freeze);

}


이 코드로 기본적으로 미동결상태인 모든 계좌를 Freeze Account를 호출함으로써 동결할 수 있다. 불행히도 동결은 아무런 효과가 없다. 왜냐하면 transfer함수에 아무것도 추가하지 않았기 때문에, 그래서 다음과 같이 해야 한다.


function transfer(address _to, uint256 _value) {

    if (frozenAccount[msg.sender]) throw;


이제 동결된 계좌는 자금은 있지만 송금은 할  수 없다. 디폴트는 미동결계좌이나 원한다면 화이트리스트를 관리하면서 원할 때 계좌를 동결할 수 있다. frozenAccount를 approvedAccount로 변경하여 적용이 가능하다.


if (!approvedAccount[msg.sender]) throw;


자동 구매 및 판매


지금까지 토큰을 평가하기 위해 유틸리티와 신뢰에 의존했다. 하지만 원한다면 자동으로 시장의 가치를 기준으로 구입하는 펀드를 만들어 토큰의 가치를 이더(또는 다른 토큰)로 뒷받침 할 수 있다.


먼저 구매와 판매가격을 셋팅한다.


uint256 public sellPrice;

uint256 public buyPrice;


function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {

    sellPrice = newSellPrice;

    buyPrice = newBuyPrice;

}


이것은 가격에 접근가능하게 하며 자주 변하지 않는다. 이를 변경하려면 트랜잭션을 싱행하면서 이더를 소비하게 되기 때문이다. 변동가격을 일정하게 유지하려면 표준데이터피드를 참고하라.


다음은 구매와 판매함수를 작성한다.


// 살때

// msg.sender => 구매자

function buy() payable returns (uint amount){

    amount = msg.value / buyPrice;                     // msg.value는 구매금액(eth), 구매금액을 토큰가격으로 나눠서 토큰개수 계산

    if (balanceOf[this] < amount) throw;               // 현재 토큰잔액 확인

    balanceOf[msg.sender] += amount;                   // 구매자 토큰개수 추가

    balanceOf[this] -= amount;                         // 판매자 토큰개수 차감

    Transfer(this, msg.sender, amount);                // 전송 수행

    return amount;                                     // 토큰개수 반환

}


// 팔때

// msg.sender => 판매자

function sell(uint amount) returns (uint revenue){

    if (balanceOf[msg.sender] < amount ) throw;   // 판매자 토큰 잔액 확인

    balanceOf[this] += amount;                              // 구매자 토큰잔액 추가

    balanceOf[msg.sender] -= amount;                   // 판매자 토큰 잔액 차감 subtracts the amount from seller's balance

    revenue = amount * sellPrice;                // 판매금액 계산(eth)

    if (!msg.sender.send(revenue)) {                   // 판매자에게 이더 전송, 아주 중요함

        throw;                                         // to do this last to prevent recursion attacks

    } else {

        Transfer(msg.sender, this, amount);             // 전송 수행

        return revenue;                                 // 판매금액 반환

    }

}


이게 새로운 토큰을 생성하지 않고 컨트렉트 주체의 잔액을 변경한다는것을 주의깊게 보아야 한다. 컨트랙트는 토큰과 이더 모두 그리고 컨트랙트 소유자를 보유할 수 있으나 가격을 설정하거나 유효한경우 일부 토큰을 생성한다. 이는 은행의 토큰이나 이더를 건드릴수는 없다. 이 컨트랙트에서 펀드를 이동시킬수 있는 유일한 방법은 구매나 판매을 하는것 뿐이다.


가치를 구매 및 판매하는것은 이더내에서 설정되지 않고 유로나 달라의 cent나 비트코인의 satoshi같은 단위인 wei가 있다. 1 이더는 1000000000000000000 wei(10경)이다. 따라서 이더로 토큰을 설정할때 18개의 0을 넣어야 한다. 무쟈게 많다. 10경이니..


컨트랙트를 생성할 때 시장에있는 모든 토큰을 다시 구매할 수 있도록 충분한 양의 이더를 보내야 한다.그렇지 않으면 컨트랙트가 파산하여 사용자가 토큰을 판매 할 수 없게 된니다.


위 샘플에서는 단일 중앙구매자와 판매자간의 컨트랙트를 설명했지만 좀더 흥미롭게 다른 사람이 다른 가격을 입찰하거나 외부에서 가격을 직접로드할 수 있습니다.


자동리필


이더리움에서 트랜잭셕이 일어날때마다 채굴자에게 일정비율의 요금을 지불하게 되는데 이 값은 컨트랙트에서 계산되어진다. 순간적으로 수수료는 이더로만 지불되므로 토큰의 모든 사용자는 이더를 필요로 한다. 소유자가 필요한 비용이 지불할 수 있을 때 까지 수수료보다 적은 잔액을 가진 계좌의 토큰은 정지(stick)된다. 하지만 어떤 경우에는 유저가 이더리움, 블록체인 또는 이더리움을 어떻게 얻을 수 있는지 몰라도 되기를 원할 수 있다. 이 경우 한가지 가능한 접근방법은 잔액이 너무 적게되면 자동으로 채워질 수 있게 하는것이다.


이렇게 하기 위해서는 먼저 임계치값을 변수로 생성해야 하며 변경할 수 있게 해야 한다. 5 finney(0.005 ether)로 설정해보자


uint minBalanceForAccounts;


function setMinBalance(uint minimumBalanceInFinney) onlyOwner {

     minBalanceForAccounts = minimumBalanceInFinney * 1 finney;

}


그리고 transfer함수를 수정한다.


/* Send coins */

function transfer(address _to, uint256 _value) {

    ...

    if(msg.sender.balance<minBalanceForAccounts)

        sell((minBalanceForAccounts-msg.sender.balance)/sellPrice);

}


송금인이 수취인에게 수수료를 지불할 수 있도록 변경할수도 있다.


/* Send coins */

function transfer(address _to, uint256 _value) {

    ...

    if(_to.balance<minBalanceForAccounts)

        _to.send(sell((minBalanceForAccounts-_to.balance)/sellPrice));

}


이러면 토큰을 받는 계좌가 수수료를 지불하는데 필요한 이더보다 적게 수수료를 지불하지 않습니다.


작업검증


수학공식을 코인과 엮는 몇가지 방안이 있다. 가장 간단한 방법은 이더로 "병합된 광산(채굴)"로 만드는것이다. 다시 말해 이더리움에서 블록을 발견한 사람은 누구나 그 불록에 대한 보상 기능을 호출하면 코인에서 보상을 받을것이다. 블록을 찾은 광부를 지칭하는 coinbase 키워드로 이를 수행할 수 있다.


function giveBlockReward() {

    balanceOf[block.coinbase] += 1;

}


이것은 또한 수식을 추가할 수 있는데 누구든 이를 수행하면 보상에서 승리할 수 있다. 다음 예에서 당신은 포인트를 얻는 현재 도전의 큐빅루트를 계산해야 하고 다음 도전을 설정할 수 있는 권한을 얻는다.


uint currentChallenge = 1; // Can you figure out the cubic root of this number?


function rewardMathGeniuses(uint answerToCurrentReward, uint nextChallenge) {

    if (answerToCurrentReward**3 != currentChallenge) throw; // If answer is wrong do not continue

    balanceOf[msg.sender] += 1;         // Reward the player

    currentChallenge = nextChallenge;   // Set the next challenge

}


물론 계산을 누군가의 머리로 하는것은 많이 어렵지만 계산기는 아주 쉽다.그래서 이 게임은 컴퓨터로는 금방 끝난다. 또한 마지막 승자가 다음 도전을 고를수 있기 때문에 다른 플레이어에게는 공정하지 않다. 인간에게는 쉽지만 기게로는 매우 어렵지만 스크립트로 코딩하는것을 매우 어렵다. 대신에 더 공정한 시스템은 기계가 할 수 있는것은 매우 어렵지만 기계가 검증하기가 그리 어렵지는 않다. 가장 적절한 후보는 도전자가 주어진 난이도보다 낮은 해시를 찾을때 까지 여러 숫자의 해시를 생성해야 하는 해시 도전을 만드는것이다.

이 프로세스는 1997년 Adam Back에 의해 hashcash로 처음 제안되었고 2008년에 satosi nakamoto의 비트코인에서 구현되었습니다. 이더리움은 보안모델을 위해 이러한 시스템을 사용하여 출시되었지만 작업증명보안모델에서 혼합된 모델(링크탭하여 더 알아보세요)로 이동하려고 계획하고 있습니다.

해싱을 코인의 랜덤발행형태로 하려는 경우 작업방행증명이 있는 자신의 이더기반 통화를 만들수 있다.


bytes32 public currentChallenge;                         // The coin starts with a challenge

uint public timeOfLastProof;                             // Variable to keep track of when rewards were given

uint public difficulty = 10**32;                         // Difficulty starts reasonably low


function proofOfWork(uint nonce){

    bytes8 n = bytes8(sha3(nonce, currentChallenge));    // Generate a random hash based on input

    if (n < bytes8(difficulty)) throw;                   // Check if it's under the difficulty


    uint timeSinceLastProof = (now - timeOfLastProof);  // Calculate time since last reward was given

    if (timeSinceLastProof <  5 seconds) throw;         // Rewards cannot be given too quickly

    balanceOf[msg.sender] += timeSinceLastProof / 60 seconds;  // The reward to the winner grows by the minute


    difficulty = difficulty * 10 minutes / timeSinceLastProof + 1;  // Adjusts the difficulty


    timeOfLastProof = now;                              // Reset the counter

    currentChallenge = sha3(nonce, currentChallenge, block.blockhash(block.number-1));  // Save a hash that will be used as the next proof

}


생성자함수도 변경해야 한다.


    timeOfLastProof = now;


컨트랙트가 온란인상태가 되면 "작업 증명"기능을 선택하고 nonce필드에 원하는 번호르 추가하여 실행해라. 확인 창이 "데이터를 실행할 수 없습니다"라는 빨간색 경고 메시지가 나타나면 거래가 진행될때까지 다른 번호를 선택하라. 이 프로세스는 임의적이다. 하나를 찾으면 마지막 보상이 주어지기 때문에 매 순간마다 1토큰을 받게되며 도전난이도는 보상당 평균 10분을 목표로 위아래로 조정된다.

보상을 제공하는 번호를 찾으려는 이 과정을 채굴이라고 부른다. 난이도가 올라가면 숫자를 찾기가 어려울 수 있지만 발견한것을 확인하는것은 항상 쉽다.



Posted by 삼스

댓글을 달아 주세요

블록체인2017. 7. 7. 13:34

https://www.facebook.com/groups/1429149120539506/



이 글은 https://ethereum.org/token 의 발번역입니다.

참고용으로만...


이글은 비트코인과 이더리움에 대한 이해가 있다는것을 가정합니다. 이더리움으로 만들 수 있는 여러가지 유형의 애플리케이션중에 토큰베이스에 대해서 설명합니다.


우리는 디지털토큰을 만들것이다. 이더리움 생태계에서 토큰은 모든 대체가능한 상품(동전, 누적포인트, 금인증서, 게임아이템등)을 나타낼수 있다. 모든 토큰들은 표준방식으로 몇가지 기본기능을 구현하기 때문에 이더리움 지갑, 다른 클라이언트 또는 동일한 표준을 사용하는 계약과 바로 호환될 수 있습니다.


아래 코드를 복붙해서 바로 사용 가능하다


pragma solidity ^0.4.8;

contract tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData); }


contract MyToken {

    /* Public variables of the token */

    string public standard = 'Token 0.1';

    string public name;

    string public symbol;

    uint8 public decimals;

    uint256 public totalSupply;


    /* This creates an array with all balances */

    mapping (address => uint256) public balanceOf;

    mapping (address => mapping (address => uint256)) public allowance;


    /* This generates a public event on the blockchain that will notify clients */

    event Transfer(address indexed from, address indexed to, uint256 value);


    /* This notifies clients about the amount burnt */

    event Burn(address indexed from, uint256 value);


    /* Initializes contract with initial supply tokens to the creator of the contract */

    function MyToken(

        uint256 initialSupply,

        string tokenName,

        uint8 decimalUnits,

        string tokenSymbol

        ) {

        balanceOf[msg.sender] = initialSupply;              // Give the creator all initial tokens

        totalSupply = initialSupply;                        // Update total supply

        name = tokenName;                                   // Set the name for display purposes

        symbol = tokenSymbol;                               // Set the symbol for display purposes

        decimals = decimalUnits;                            // Amount of decimals for display purposes

    }


    /* Send coins */

    function transfer(address _to, uint256 _value) {

        if (_to == 0x0) throw;                               // Prevent transfer to 0x0 address. Use burn() instead

        if (balanceOf[msg.sender] < _value) throw;           // Check if the sender has enough

        if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows

        balanceOf[msg.sender] -= _value;                     // Subtract from the sender

        balanceOf[_to] += _value;                            // Add the same to the recipient

        Transfer(msg.sender, _to, _value);                   // Notify anyone listening that this transfer took place

    }


    /* Allow another contract to spend some tokens in your behalf */

    function approve(address _spender, uint256 _value)

        returns (bool success) {

        allowance[msg.sender][_spender] = _value;

        return true;

    }


    /* Approve and then communicate the approved contract in a single tx */

    function approveAndCall(address _spender, uint256 _value, bytes _extraData)

        returns (bool success) {

        tokenRecipient spender = tokenRecipient(_spender);

        if (approve(_spender, _value)) {

            spender.receiveApproval(msg.sender, _value, this, _extraData);

            return true;

        }

    }        


    /* A contract attempts to get the coins */

    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {

        if (_to == 0x0) throw;                                // Prevent transfer to 0x0 address. Use burn() instead

        if (balanceOf[_from] < _value) throw;                 // Check if the sender has enough

        if (balanceOf[_to] + _value < balanceOf[_to]) throw;  // Check for overflows

        if (_value > allowance[_from][msg.sender]) throw;     // Check allowance

        balanceOf[_from] -= _value;                           // Subtract from the sender

        balanceOf[_to] += _value;                             // Add the same to the recipient

        allowance[_from][msg.sender] -= _value;

        Transfer(_from, _to, _value);

        return true;

    }


    function burn(uint256 _value) returns (bool success) {

        if (balanceOf[msg.sender] < _value) throw;            // Check if the sender has enough

        balanceOf[msg.sender] -= _value;                      // Subtract from the sender

        totalSupply -= _value;                                // Updates totalSupply

        Burn(msg.sender, _value);

        return true;

    }


    function burnFrom(address _from, uint256 _value) returns (bool success) {

        if (balanceOf[_from] < _value) throw;                // Check if the sender has enough

        if (_value > allowance[_from][msg.sender]) throw;    // Check allowance

        balanceOf[_from] -= _value;                          // Subtract from the sender

        totalSupply -= _value;                               // Updates totalSupply

        Burn(_from, _value);

        return true;

    }

}


토큰 컨트랙트는 상당히 복잡하다. 다음은 토큰을 구현하기 위한 최소의 코드이다.


contract MyToken {

    /* This creates an array with all balances */

    mapping (address => uint256) public balanceOf;


    /* Initializes contract with initial supply tokens to the creator of the contract */

    function MyToken(

        uint256 initialSupply

        ) {

        balanceOf[msg.sender] = initialSupply;              // Give the creator all initial tokens

    }


    /* Send coins */

    function transfer(address _to, uint256 _value) {

        if (balanceOf[msg.sender] < _value) throw;           // Check if the sender has enough

        if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows

        balanceOf[msg.sender] -= _value;                     // Subtract from the sender

        balanceOf[_to] += _value;                            // Add the same to the recipient

    }

}


코드를 이해해 보자



지갑앱을 실행 > Contract탭 > Deploy New Contract 선택

Solidity Contract Source code 입력필드에 아래 코드 입력


contract MyToken {

    /* This creates an array with all balances */

    mapping (address => uint256) public balanceOf;

}


매핑이란 주소를 잔액과 연결하는 연관 배열을 의미한다. 주소는 16진수 이더리움 포맷이며 잔액은 0에서 115*1027 범위의 정수이다 얼마나 큰수인지 상상이 안간다면 네가 계획산 전체 토큰수보다 많을거다. public 키워드는 블록체인에서 어디서든 접근 가능하게 한다. 즉 모든 잔액이 공개되어야 한다(클라이언트가 표시해야 하는 경우).


컨트랙트를 발행 했다면 효과가 있지만 유용하지는 않다. 즉 토큰의 잔액을 주소로 조회할 수 있는 컨트랙트가 될수 있으나 토큰 하나를 만들지 않았기 때문에 모두가 0을 반환한다. 그래서 시작할때 몇개의 토큰을 만들것이다. mapping... 바로 아래 코드를 추가하라.


contract MyToken {

    /* This creates an array with all balances */

    mapping (address => uint256) public balanceOf;

    

    function MyToken() {

        balanceOf[msg.sender] = 21000000;

    }

}


function MyToken과 contract MyToken이 동일한것을 눈여겨 보아야 한다. 하나를 변경하면 나머지 하나도 변경해야 한다. 생성자 역할을 하며 네트워크에 컨트랙트가 업로드될때 한번만 호출된다. 이 함수는 컨트랙트를 전개한 사용자인 msg.sender의 잔액을 2100만 잔고로 설정한다.

이 값은 이렇게 고정된 수가 아니라 파라메터로 초기화를 하는것이 좋다.


function MyToken(uint256 initialSupply) {

    balanceOf[msg.sender] = initialSupply;

}


컨트랙트 옆에 오른쪽에 보면 드롭다운에서 "My Token"을 선택하면 Constructor parameters를 볼수 있다. 변경가능한 파라메터로 같은코드를 재사용하여 변수를 변경할 수 있다.


지금은 토큰의 잔액을 만든 계약을 가지고 있지만 아무런 변화가 없기 때문에 이제 이동에 대해 구현하겠다.


/* Send coins */

function transfer(address _to, uint256 _value) {

    /* Add and subtract new balances */

    balanceOf[msg.sender] -= _value;

    balanceOf[_to] += _value;

}


이것은 매우 직관적인 기능이다.  수신자와 값을 파라메터로 갖고 호출될때마다 해당 값을 잔액에서 차감하고 수신자의 잔액은 증가시킨다. 여기에는 명백한 문제가 있다. 잔액이 부족할 경우 어떤일이 일어날까? 이 계약에 대해 부체를 처리할것이 아니기 때문에 빠르게 확인하고 송금자의 자금이 충분치 않으면 계약 실행이 중지된다. 또한 오버플로가 있는지 확인하여 0이 되게 하는 너무 큰숫자를 피한다.


계약의 실행을 중간에 정지하려면 return 이나 throw를 호출한다. 전자는 가스비용은 적은 반면 계약의 실행에 의한 변경사항이 적용되면서 더 머리가 아파질수 있다. 반면에 throw는 모든 계약실행을 취소하고 모든 변경을 원상복구하고 가스비용으로 사용된 이더를 잃게된다. 하지만 지갑이 throw를 예측하고 경고를 함으로써 더이상 이더를 소모하는것을 막아준다.


 이 글 작성 이후 2017년 8월 현재 throw는 deprecated되었다. revert(), require(), assert()의 사용이 권장된다.


function transfer(address _to, uint256 _value) {

    /* Check if sender has balance and for overflows */

    if (balanceOf[msg.sender] < _value || balanceOf[_to] + _value < balanceOf[_to])

        throw;


    /* Add and subtract new balances */

    balanceOf[msg.sender] -= _value;

    balanceOf[_to] += _value;

}


이제 남은것은 계약에 대한 기본정보에 대한 것이다. 이 정보는 추후 토큰의 등록으로 조작이 가능하겠지만 우리는 계약에 직접 추가할것이다


string public name;

string public symbol;

uint8 public decimals;


그리고 생성자 함수를 시작할 때 업데이트한다.


/* Initializes contract with initial supply tokens to the creator of the contract */

function MyToken(uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol) {

    balanceOf[msg.sender] = initialSupply;              // Give the creator all initial tokens

    name = tokenName;                                   // Set the name for display purposes

    symbol = tokenSymbol;                               // Set the symbol for display purposes

    decimals = decimalUnits;                            // Amount of decimals for display purposes

}


마지막으로 Events로 불리는 것이 필요하다. 이더이움 지갑같은 클라이언트가 계약에서 일어나는 활동을 추적할수 있도록 도와준다. Events는 대문자로 시작해야 하고 다음과 같이 이벤트를 선언하라.


event Transfer(address indexed from, address indexed to, uint256 value);


이제 transfer 함수내에서 호출해주면 된다.


    /* Notify anyone listening that this transfer took place */

    Transfer(msg.sender, _to, _value);


자 이제 토근이 준비가 다 되었다.



배포방법


이더리움지값을 열어서 컨트랙트탭의 "deploy new contract"를 선택하라.

Solidity source filed에 위에서 작성한 소스코드를 복사 및 붙여넣기를 하라. 코드 컴파일이 문제가 없다면 "pick a contract"를 볼수 있을것이고 "MyToken"를 드롭박스에서 선택하라. 그러면 파라메터를 입력할 수 있는 컬럼이 보일것이다.  원하는대로 조정할 수 있지만 튜터리얼의 목적에 따라 공급 매개변수 10000개 기호는 %, 소수자릿수 2개를 선택한다. 아래 처럼.

페이지 끝으로 스크롤하면 해당 계약의 계산 비용이 대략적으로 표시되며 이더비용을 지불할 의사가 있는 요금을 선택할 수 있다. 쓰지 않는 초과 이더는 돌려 보내질 것이므로 원한다면 기본 설정을 그대로 둘 수 있다. "배포"를 누르고 계정 암호를 입력 한 후 거래가 시작될 때까지 몇 초 기다린다.


그러면  첫페이지로 이동하여 거래 확인을 기다리는 것을 볼 수 있다. Etherbase(당신의 주계정)을 클릭하면 1분이 지나면 방금 만든 공유의 100%를 계정에 표시한다. 일부를 친구에게 보내려면 친구의 주소를 복사하여 "to"필드에 집어넣고 "send"를 눌러라.


친구에게 보내면 아직 지갑에는 아무것도 표시되지 않는다. 지갑이 알고 있는 토큰만 추적하기 때문에 지갑을 수동으로 추가해야 한다. 이제 '컨트랙트'탭으로 이동하면 새로 생성 된 컨트랙트 링크가 표시된다. 해당 페이지로 이동하려면 클릭한다. 이것은 매우 간단한 컨트랙트 페이지이므로 여기서 할 일이 많지 않다. "주소 복사"를 클릭하고 컨트랙트 주소를 텍스트 편집기에 붙여 넣는다.

확인하고자하는 토큰을 추가하려면 컨트랙트 페이지로 이동 한 다음 "Watch Token"을 클릭하라. 팝업 창이 나타나면 계약서 주소 만 붙여넣는다. 토큰 이름, 기호 및 십진수는 자동으로 채워 져야하지만 그렇지 않은 경우 원하는 것을 넣을 수 있다 (지갑에 표시되는 방식에만 영향을 미침). 이렇게하면 토큰의 잔액이 자동으로 표시되어 다른 사람에게 보낼 수 있다.


이제 당신만의 암호화된 토큰을 가지게 되었다. 토큰은 로컬커뮤니티내에서 가치교환에 유용하게 사용될수 있고, 근무시간 또는 다른 로열티프로그램을 추적할 수 있다. 하지만 통화를 만들어서 본질적인 가치를 만들 수 있는가?






Posted by 삼스

댓글을 달아 주세요

  1. donni

    ethereum token programming에 관심이 있어서 해보고 싶은데 ether는 어떻게 모아야 하나요ㅠ
    이더가 없어서 테스트를 해보지 못합니다ㅠ
    0xBc4221414406c6341f469ad556BaD9912A929CE5
    제 계정주소인데 이더좀 나눠주세요ㅠ

    2017.07.21 18:10 [ ADDR : EDIT/ DEL : REPLY ]
    • 저도 없어서 더 진행을 못하구 있어요.
      직접채굴은 권장을 못하겠고.
      거래소에서 사는 수밖에 없을듯 해요

      2017.08.14 14:06 신고 [ ADDR : EDIT/ DEL ]
    • 삼스

      https://www.youtube.com/watch?v=wKFz5c3TU4s
      참고하셔서 시도해보세요.
      또는 https://gitter.im/ethereum/go-ethereum 에서 달라고 졸라보세요. 저도 여기서 받았어요.

      2017.08.22 09:52 [ ADDR : EDIT/ DEL ]
  2. 글로벌 금융 위기 + 암호화폐 혁명
    = 이런 기회는 다시 오지 않는다.

    [제 닉네임을 클릭하시고]
    적어놓은 정보들을 활용하신다면,

    1500만원 ~ 1억 이상의
    가치를 한다고 확신합니다.

    1. 비트코인, 지금부터 시작해도 늦지 않았나?

    2. 100만원으로 비트코인 10개 매수 하는법

    3. 100만원으로 비트코인 10개 매수 하는법 (요약본)

    2020.05.15 04:09 신고 [ ADDR : EDIT/ DEL : REPLY ]

블록체인2017. 7. 4. 10:44


중앙서버 없이 분산시스템인 P2P기반의 비가역적 데이터베이스라고 정의할 수 있다.


P2P상의 노드(클라이언트)들은 블록을 생성하고 검증한다.


일정한 규칙에 따라 데이터를 담은 블록을 경쟁을 통해서 만들어낸다. 이렇게 만들어진 블록데이터는 되돌릴수 없게 된다.


P2P기반으로 중앙화 되어 있지 않기 때문에 중앙만 공격하면 뚤리게 되는 보안상의 취약성이 원천적으로 제거된다. 모든 참여자가 사용자들로만 이루어지게 되며 비가역적인데다 모든 사용자들이 데이터를 검증하기 때문에 조작이나 통제가 불가능한 시스템이 갖추어졌다.


최초에 생성된 블록을 탄생블록(Genesis Block)이라 하며 이 후 경쟁을 통해서 블록이 계속 생겨나게 된다. 경쟁에서 최초로 블록은 만들어낸 사용자의 블럭이 블록체인에 등록되고 나머지는 연결되지 못한다. 이긴 블록들로 이루어진 체인을 주체인이라하고 주체인에 들지 못한 블록을 탈락블록이라 칭한다. 주체인에 포함된 블록만이 유효한것으로 인정된다.


각 블록은 해시를 가지고 있고 이전블록의 개인키로 사인이 되어 있다. 따라서 이전블럭의 공개키가 있어야만 복호화가 가능하다. 이로써 해당 블록의 히스토리를 역산해나갈수 있다.


각 노드들이 하는 일은 작업증명 과 해시값 추적을 통한 블록을 생성하는 일이다.

해시값을 먼저 찾은 노드는 자신이 생성한 블록을 먼저 공표할 수 있다. 뒤늦게 하는 공표는 주체인이 될 수 었기에 탈락블록이 된다.

해시값을 빨리 찾기 위한 조건은 연산능력이 탁월한 하드웨어, 전기세, 관리비들이 되겠다. 따라서 돈이 많으면 더 많은 확보가 가능하다.


Posted by 삼스

댓글을 달아 주세요

Android2017. 6. 8. 13:49


https://android-developers.googleblog.com/2017/05/android-instant-apps-is-open-to-all.html


몇몇 선택된 개발자들에게만 허용되었던 안드로이드 인스탄트앱개발이 드디어 모든 개발자들에게 오픈되었다.


몇몇 선택된 개발자들에게만 허용되었던 안드로이드 인스탄트앱개발이 드디어 모든 개발자들에게 오픈되었다.


올초부터 안드로이드 인스탄트앱을 테스트하기 시작했다. 설치없이 실행가능한 앱이다. 우리 놀라운 개발자커뮤니티에 감사한다. 그들은 수많은 피드백을 우리에게 주었다. 그로 인해 end-to-end 제품경험을 개선하는데 큰 도움이 되었다.


오늘 우리는 모든 안드로이드 개발자들에게 오픈한다. 누구든 빌드하고 배포할 수 있다. 이미 50여개이상의 새로운 경험을 하는것이 가능하다. HotPads, Jet, 뉴욕타임즈, Vimeo그리고 One Football등이다. 이들은 아주 짧은 기간동안 서비스되고 있지만 아주 긍정적인 결과를 보여준다. 예를 들어 Jet과 HotPads는 두자리수의 주문이 증가했다.


초기파트너들로부터의 피드백은 오늘 제공하게 되는 개발툴을 직접 형성했다. 


인스탄트앱을 개발하려면 먼저 developer.android.com에 들어가서 Android Studio 3.0 preview와 Android Instant Apps SDK를 다운로드하라. 계속해서 단일 코드배이스를 사용하게 될것이다. Android Studio는 필요에 따라 기능을 다운로드 할 수 있도록 앱을 모듈화하는 데 필요한 도구를 제공합니다. 모든 앱들이 다르지만 우리는 초기파트너들과 최종 툴로 인스탄트앱을 개발하는데 보통 4~6주정도의 시간이 소요되는것을 보았다.


앱을 한번 개발해 놓으면 Play Console는 인스탄트앱의 배포에 대한 지원을 제공한다. 당신은 그저 설치가능한 APK와 함께 인스탄트앱 APK들을 업로드하면 된다.


40개국이상의 최신 안드로이드 디바이스에서 계속 인스탄트앱이 증가하고 있다. 그리고 Android O에서 인스탄트앱을 위한 더 향상퇸 런타임 샌드박스, 앱용량 감소를 위한 라이브러리의 공유, 런처통합 지원을 구축했다.


더 알고 싶으면 g.co/instantApps를 방문해라. 이미 "Introduction to Android Instant Apps" 세션을 올려두었고 Google I/O Youtube channel에서도 시청할 수 있다.


Posted by 삼스

댓글을 달아 주세요

HTML52017. 3. 3. 12:19


https://angular.io/docs/ts/latest/cli-quickstart.html


시작부터 정리 시작!


CLI로 프로젝트를 시작하는 방법에 대한 안내이다.


1. 개발환경 설정


NodeJs와 NPM설치

node는 6.9.x이상, npm은 3.x.x이상


angular cli는 npm install -g @angular/cli 로 설치


2. 프로젝트 생성


ng new my-app


3. Serve the app


cd my-app

ng serve --open


ng serve는 서버를 기동하고 파일을 감시하고 리빌드한다.


4. 이제 너의 콤포넌트들을 코딩하기 시작해라..


프로젝트 파일 리뷰


src 폴더


root 폴더 

Posted by 삼스

댓글을 달아 주세요

  1. 맨발

    항상 좋은 정보 감사합니다.
    개발중에 풀지못하는게 있어서 이렇게...
    지금까지 쓰신 글중에 블루투스를 화면이 꺼진 백그라운드 상태에서 스캔을 구동하여.틔정 블루투스 모듈과 소량의 데이터 통신이 가능하다는 글을 보았습니다.
    좀더 자세히 알수 있을까요?
    혹시 이메일 주소를 공유해 주실수 있을까요?
    부탁드립니다.
    manballu@naver.com
    조도영 입니다.
    부탁드립니다

    2017.05.19 17:20 [ ADDR : EDIT/ DEL : REPLY ]

HTML52017. 3. 3. 10:48


Crisis 예제에서 "Contact"버튼으로 메세지를 보낼 수 있는 팝업뷰를 띄우려고 한다.

팝업은 앱내에서 다른 페이지전환이 있어도 유지되기를 원한다. 사용자가 명시적으로 닫기전까지는


지금까지는 단일 아웃렛과 차일드아우트까지 사용하는 법에 대해 알아보았다. 라우터는 이름없는 아웃렛은 템플릿내에 하나만 허용한다.

하지만 템플릿은 이름을 부여(네임드아웃렛)하면 여러개의 아웃렛을 사용할 수 있다. 각 명명된 아웃렛은 자신면의 콤포넌트를 갖는 라우트셋을 가질수 있다. 여러개의 아웃렛이 서로다은 라우트를 가지고 동시에 컨텐츠를 표시할 수 있다.


AppComponent에 "popup"이라고 이름 붙인 아웃렛을 추가한다.


src/app/app.component.ts (outlets) <router-outlet></router-outlet> <router-outlet name="popup"></router-outlet>


팝업이 보여질 위치가 된다.


두번째 라우트


명명된 아웃렛은 두번째라우트의 타겟이다.


두번째 라우트도 첫번째 라우트와 유사하다. 몇가지점에서 다른데.

  • 서로 독립적이다.
  • 다른 라우트와 조화된다.
  • 명명된 아웃렛에 표시된다.

ComposeMessageComponent를 src/app/compose-message.component.ts에 추가한다.


아래와 같은 화면이다.



src/app/compose-message.component.ts
import { Component, HostBinding } from '@angular/core';
import { Router }                 from '@angular/router';
import { slideInDownAnimation }   from './animations';
@Component({
  moduleId: module.id,
  templateUrl: './compose-message.component.html',
  styles: [ ':host { position: relative; bottom: 10%; }' ],
  animations: [ slideInDownAnimation ]
})
export class ComposeMessageComponent {
  @HostBinding('@routeAnimation') routeAnimation = true;
  @HostBinding('style.display')   display = 'block';
  @HostBinding('style.position')  position = 'absolute';
  details: string;
  sending: boolean = false;
  constructor(private router: Router) {}
  send() {
    this.sending = true;
    this.details = 'Sending Message...';
    setTimeout(() => {
      this.sending = false;
      this.closePopup();
    }, 1000);
  }
  cancel() {
    this.closePopup();
  }
  closePopup() {
    // Providing a `null` value to the named outlet
    // clears the contents of the named outlet
    this.router.navigate([{ outlets: { popup: null }}]);
  }
}


src/app/compose-message.component.html <h3>Contact Crisis Center</h3> <div *ngIf="details"> {{ details }} </div> <div> <div> <label>Message: </label> </div> <div> <textarea [(ngModel)]="message" rows="10" cols="35" [disabled]="sending"></textarea> </div> </div> <p *ngIf="!sending"> <button (click)="send()">Send</button> <button (click)="cancel()">Cancel</button> </p>


다른 콤포넌트들과 비슷해 보인다. 다른게 두가지가 있는데. send 메서드가 1초동안 대기했다가 팝업을 닫고 있고 closePopup은 "popup"아웃렛을 null로 호출하고 있다.

다른 콤포넌트들과 마찬가지로 AppModule에 declareations에 ComposeMessageComponent를 추가한다.


두번째 라우트 추가


AppRoutingModule에 라우트를 추가한다.

src/app/app-routing.module.ts (compose route)
{
  path: 'compose',
  component: ComposeMessageComponent,
  outlet: 'popup'
},

outlet속성이 추가되었다. ComposeMessageComponent가 popup outlet에 표시될것이다.

html코드내에서는 아래와 같이 호출한다.

src/app/app.component.ts (contact-link) <a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a>

 compose 라우트가 popup 라우트에 고정되었도 RouterLink디렉티브에 라우트를 지정하기에는 충분하지 않다. 링크파라메터를 지정하고 속성바인딩으로 RouterLink에 바인딩해야 한다.

링크파라메터에는 outlets속성이 객체로 하나 존재하며 popup 아웃렉 속성과 값으로 다른 링크파라메터가  compose 라우트가 지정되어 있다.

이제 사용자가 링크를 클릭하면 compose 라우트가 popup아웃렛에 표시될것이다.


두번째 라우트 네비게이션 : 라우트머징


Crisis Center에서 Contact를 클릭하면 아래와 같은 주소가 찍히는 것을 확인할 수 있다.


http://.../crisis-center(popup:compose)


관심이 쏠리는것이 바로 ... 부분인데.


  • cirsis-center는 첫번째 네비게이션
  • 괄호부분은 두번째 네이게이션
  • 두번째 네비게이션은 popup 아웃랫과 라우트를 의미

 Heros링크를 누르면 이번에 주소가 


http://.../heros(popup:compose)


첫번째주소부분이 변경되었다. 두번째 라우트는 동일하다.

라우터는 첫번째와 두번째 브랜치를 유지한다. 그리고 URL를 표현해준다.

수많은 아웃렉과 라우트를 탑레벨이나 하위레벨로 만들어낼 수 있다. 라우터는 알어서 url을 만들어낸다.


두번째 라우트 정리


아웃렛의 콤포넌트는 새로운 콤포넌트로 이동하기전까지는 계속 유지된다. 두번째 아웃렛도 마찬가지이다. 

모든 두번째 아웃렛은 자체 네비게이션을 가지며 독립적으로 수행된다. 다른 아웃렛의 변경이 영향을 미치지 않는다는 것이다. 그래서 Crisis나 Heroes로 이동해도 Compose는 계속 보여질 수 있는 것이다.

위 예에서 보면 아래와 같이 popup을 제거하는 것을 알 수 있다.


src/app/compose-message.component.ts (closePopup)
closePopup() {
  // Providing a `null` value to the named outlet
  // clears the contents of the named outlet
  this.router.navigate([{ outlets: { popup: null }}]);
}




Posted by 삼스

댓글을 달아 주세요

HTML52017. 3. 3. 09:37


이번엔 라우트 안의 라우트를 알아볼것이다.


앱을 개발하다보면 화면단위 혹은 기능단위로 피쳐를 구성하게 된다.
보통 피쳐는 폴더에 대응되며 하나의 피쳐에는 아래 그림에서와 같이 여러개의 콤포넌트들을 포함할 수 있다.



위 그림에서 App Root Component는 Feature A, B, C를 가지며 Feature A는 A1, A2, A3을 가진다.

여기서 A1, A2, A3이  바로 차일드 라우트에 해당한다.


다음 코드는 앵귤러가이드문서의 예이다.


import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { CrisisCenterHomeComponent } from './crisis-center-home.component';
import { CrisisListComponent }       from './crisis-list.component';
import { CrisisCenterComponent }     from './crisis-center.component';
import { CrisisDetailComponent }     from './crisis-detail.component';

const crisisCenterRoutes: Routes = [
  {
    path: 'crisis-center',
    component: CrisisCenterComponent,
    children: [
      {
        path: '',
        component: CrisisListComponent,
        children: [
          {
            path: ':id',
            component: CrisisDetailComponent
          },
          {
            path: '',
            component: CrisisCenterHomeComponent
          }
        ]
      }
    ]
  }
];

@NgModule({
  imports: [
    RouterModule.forChild(crisisCenterRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class CrisisCenterRoutingModule { }


Feature에 해당하는게 CrisisCenter이고 그 안에 CrisisCenterComponent가 Feature의 루트콤포넌트이고 CrisisListComponent, CrisisDetailComponent, CrisisCenterHomeComponent가 있다.

RouterModule.forChild메서드를 사용한것을 눈여겨 보아야 한다. forRoot가 아니다.


이렇게 정의한 모듈은 AppModule즉 루트모듈에 추가해준다. 이 때 앞선 포스트에서도 언급한데로 import순서가 url pattern matching 의 순서와 동일하다는것에 유의해야 한다.


상대적인 네비게이션


crisis center feature를 빌드후 detail component를 보려면 /로 시작하는 절대 주소로 접근하게 된다. 

라우터는 라우트설정의 맨 위에서부터 절대주소를 매칭한다.

절대경로를 통해서 Crisis center feature에 접근이 가능하지만 상위라우팅구조에 링크가 고정된다. 만일 상위 경로(/crisis-center)를 변경하게 되면 링크파라메터를 반드시 변경해주어야 한다.

this.router.navigate(['/hero', hero.id]); // 이런코드가 존재한다면 '/hero'부분을 수정해주어야 한다는 의미로 보임.


이는 현재 URL segment에 상대경로를 정의함으로써 디펜던시로부터 자유로울수 있다. feature영역내에서의 네비게이션은 상위 라우트경로에 상관없이 유지될수 있다.


./ 또는 /가 없이 시작하면 현재 레벨을 의미

../ 는 한단게 상위 레벨을 의미


Router.navigate 메서드로 상대경로로 이동하려면 어떤 라우트트리에 있는지에 대한 정보가 필요하기 때문에 ActivatedRoute를 제공해야 한다.

링크파라메터배열을 링크한 후에 relativeTo속성에 ActivatedRoute를 셋해준다. 그러면 라우터는 현재 액티브된 라우트위치에 기반하여 URL을 계산해낸다.


항상 절대주소를 사용하려면 navigateByUrl메서드를 사용하면 된다.


다음은 Crisis detail을 상대주소로 접근하는 예이다.


src/app/crisis-center/crisis-list.component.ts
constructor(
  private service: CrisisService,
  private route: ActivatedRoute,
  private router: Router
) {}

onSelect(crisis: Crisis) {
  this.selectedId = crisis.id;

  // Navigate with relative link
  this.router.navigate([crisis.id], { relativeTo: this.route });
}


Router서비스가 아니고 RouterLink를 사용하게 된다면 동일한 링크파라메터를 사용한다. relativeTo속성이 보이지 않는데 이는 RouterLink가 내장하고 있다.


template: `

  <ul class="items">

    <li *ngfor="let crisis of crises | async">

      <a [routerlink]="[crisis.id]" [class.selected]="isSelected(crisis)">

        <span class="badge">{{ crisis.id }}</span>

        {{ crisis.name }}

      </a>

    </li>

  </ul>`


CrisisDetailComponent에서  뒤로가기를 하여 다시 리스트로 가고자 한다면 상대경로 이동으로 아래와 같이 시도할 수 있다.


src/app/crisis-center/crisis-detail.component.ts (relative navigation)
// Relative navigation back to the crises
this.router.navigate(['../', { id: crisisId, foo: 'foo' }], { relativeTo: this.route });


Posted by 삼스

댓글을 달아 주세요

HTML52017. 3. 2. 22:20


https://angular.io/docs/ts/latest/guide/router.html


라우트에 파라메터 적용하기


이전 예제에서 정의한 라우트정의를 다시 보자


{ path: 'hero/:id', component: HeroDetailComponent }


:id는 path에서 라우트파라메터의 슬롯을 생성한다. 이 경우 라우터는 이 슬롯에 hero의 id를 삽입한다.


heroid 15번의 상세화면으로 이동하고자 한다면 아래와 같이 접근하면 된다.


localhost:3000/hero/15



다음은 hero list를 표시하는 템플릿이다.


template: `

  <h2>HEROES</h2>

  <ul class="items">

    <li *ngFor="let hero of heroes | async"

      (click)="onSelect(hero)">

      <span class="badge">{{ hero.id }}</span> {{ hero.name }}

    </li>

  </ul>


  <button routerLink="/sidekicks">Go to sidekicks</button>

`


그리고 hero를 클릭했을 때 호출된 함수는 아래와 같다.


  onSelect(hero: Hero) {

    this.router.navigate(['/hero', hero.id]);

  }


this.router를 사용하기 위해서는 생성자에서 Router를 inject해야 한다.


constructor(

  private router: Router,

  private service: HeroService

) {}


위와 같이 하면 Hero를 선택하면 HeroDetailComponent에 id값이 파라메터로 전달된다. 코드를 보면 알겠지만 파라메터는  배열로 전달된다.

HeroDetailComponent에서는 id값에 어떻게 접근할 수 있을까?

ActivatedRoute서비스로 가능하다.

ActivatedRoute : 라우팅정보를 위한 원스탑서비스이다.
라우팅서비스에서 얻어지는 ActivatedRoute는 다음과 같은 정보를 제공한다.

url: 라우트된  path의 obserserable로 라우트경로의 배열료 구성된다.
data : 라두트에서 제공되는 데이터 객체를 포함하는 observerable
params : 옵셔널파라메터
queryParams : 모든 라우트에서 유효한 query parameter
fragment : 모들 라우트에서 유효한 URL fragment
outlet : 라우트를 렌더할때 사용되는 RouterOutlet
routeConfig : origin path를 포함하는 라우트에 사용되는 설정정보
parent : 자식 라우트의 경우 부모의 ActivatedRoute
firstChild : 자식 라우트가 있을 경우 첫번째 ACtivatedRoute
children : 현재 라우트 이하 모든 라우트들

다음과 같이 Router, ActivatedRoute, Params를 import 해야 한다.

import { Router, ActivatedRoute, Params } from '@angular/router';

switchMap연산자를 import하면 Observerable 라우트 파라메터들을 처리할 수 있다.

import 'rxjs/add/operator/switchMap';

해당 모듈을 사용하기 위해 inject를 구현한다.

constructor(
  private route: ActivatedRoute,
  private router: Router,
  private service: HeroService
) {}

이 후 ngOnInit에서 ActivatedRoute서비스를 통해서 파라메터에 접근이 가능하다.

ngOnInit() {
  this.route.params
    // (+) converts string 'id' to a number
    .switchMap((params: Params) => this.service.getHero(+params['id']))
    .subscribe((hero: Hero) => this.hero = hero);
}

파라메터가 Observerable로 제공되기 때문에 switchMap연산자를 id파라메터를 이름으로 접근하여 HeroService에 전달이 가능하다.
subscribe메서드로 id가 변경되면 감지해서 Hero 정보를 다시 셋할 수 있다.
이렇게 하면 id정보의 변경을 감지하여 화면은 유지한체 정보를 업데이트 할 수 있다.
shapshot의 경우는 non-observerable로 무조건 list에서만 detail로 진입한다면 사용이 가능하다.

ngOnInit() {
  // (+) converts string 'id' to a number
  let id = +this.route.snapshot.params['id'];

  this.service.getHero(id)
    .then((hero: Hero) => this.hero = hero);
}





Posted by 삼스

댓글을 달아 주세요

HTML52017. 3. 2. 21:49


다음으로는 세가지에 대해 알아보겠다.

  • App과 route들을 module을 사용해서 feature 영역에 배치하기
  • 콤포넌트에서 다른 콤포넌트로 네이게이트하기
  • 필수 또는 옵셔널한 정보를 라우트파라메터로 전달하기

대부분의 앱을 개발하다보면 업무별로 구분하기 위해서 폴더들을 생성하여 그 아래에 소스들을 배치하게 된다.

src/app/heroes |- hero-detail.component.ts |- hero-list.component.ts |- hero.service.ts |- heroes.module.ts


heros.module.ts는 아래와 같이 정의되어 있다.


import { NgModule }       from '@angular/core';
import { CommonModule }   from '@angular/common';
import { FormsModule }    from '@angular/forms';
import { HeroListComponent }    from './hero-list.component';
import { HeroDetailComponent }  from './hero-detail.component';
import { HeroService } from './hero.service';
@NgModule({
  imports: [
    CommonModule,
    FormsModule,
  ],
  declarations: [
    HeroListComponent,
    HeroDetailComponent
  ],
  providers: [ HeroService ]
})
export class HeroesModule {}


heros feature에는 두개의 콤포넌트가 있다. herolist와 herodetail. 리스트뷰는 조회된 결과를 표시하고 detail은 선택된 hero의 상세정보를 표시한다. list에서 detail로 넘어갈때는  hero id를 넘겨야 한다.


다음은 라우트 설정이다.


src/app/heroes/heroes-routing.module.ts

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroListComponent }    from './hero-list.component';
import { HeroDetailComponent }  from './hero-detail.component';
const heroesRoutes: Routes = [
  { path: 'heroes',  component: HeroListComponent },
  { path: 'hero/:id', component: HeroDetailComponent }
];
@NgModule({
  imports: [
    RouterModule.forChild(heroesRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class HeroRoutingModule { }


두개의 콤포넌트에 대한 라우팅설정을 완료하였다. 하나는 path에 파라메터를 전달할 수 있도록 정의하였다. 그리고 HeroRoutingModule을 export하였다.

그리고 RouterModule에 등록하고 있는데  AppRoutingModule과 다른점은 forRoot가 아니라 forChild를 사용한것이다. feature 모듈에서는 forChild를 사용해야 한다. forRoot는 앱레벨이다.

이제 HerosModule에 추가해야 한다.


import { NgModule }       from '@angular/core';
import { CommonModule }   from '@angular/common';
import { FormsModule }    from '@angular/forms';
import { HeroListComponent }    from './hero-list.component';
import { HeroDetailComponent }  from './hero-detail.component';
import { HeroService } from './hero.service';
import { HeroRoutingModule } from './heroes-routing.module';
@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    HeroRoutingModule
  ],
  declarations: [
    HeroListComponent,
    HeroDetailComponent
  ],
  providers: [ HeroService ]
})
export class HeroesModule {}

앱 메인라우터 설정과 중복이 있더라도 feature 라우터 우선으로 동작하는데는 문제가 없으나 중복은 좋지 않기 때문에 메인 라우터에 중복이 있다면 제거한다.


import { NgModule }              from '@angular/core';
import { RouterModule, Routes }  from '@angular/router';

import { CrisisListComponent }   from './crisis-list.component';
// import { HeroListComponent }  from './hero-list.component';  // <-- delete this line
import { PageNotFoundComponent } from './not-found.component';

const appRoutes: Routes = [
  { path: 'crisis-center', component: CrisisListComponent },
  // { path: 'heroes',     component: HeroListComponent }, // <-- delete this line
  { path: '',   redirectTo: '/heroes', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];

@NgModule({
  imports: [
    RouterModule.forRoot(appRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class AppRoutingModule {}


HorosModule로 별도로 분리된 모듈의 경우 AppModule에 추가해주어야 한다.

app.module.ts에 imports에 추가해준다.

app.module.ts에 import되는 순서에 따라 라우팅시 패턴매칭의 순서가 정해진다. 원하는대로 동작시키길 원한다면 순서에 유의해야 한다.



Posted by 삼스

댓글을 달아 주세요

HTML52017. 3. 2. 20:41


Angular routing & navigation


한뷰에서 다른 뷰로 넘어갈수 있도록 해준다.


개요


브라우저의 네비게이션모델은 다음과 같다.

- 주소바에 주소를 입력하면 해당 페이지로 이동한다.

- 페이지내의 링크를 누르면 해당 페이지로 이동한다.

- 뒤로가기/앞으로가기 버튼을 누르면 히스토리상의 앞/뒤 페이지로 이동한다.


앵귤러라우터는 이 모델을 빌어서 구현되었다. URL주소를 분석하여 클라이언트뷰 네비게이션 명령을 수행한다. 추가 파라메터를 전달하여 해당 페이지의 콘텐츠를 멀 표시할지 결정하는데 도움을 줄수 있다. 

라우터를 페이지에 링크할 수 있고 사용자가 클릭하면 해당 뷰로 네비게이트 할것이다. 


<base href>

대개의 라우팅애플리케이션은 index.html에 <head>내에 첫번째로 <base>엘리먼트를 추가한다. 이 값은 라우터가 네비게이션 url을 어떻게 조합할지 결정한다.


<baser href="/">


라우터는 옵셔널 서비스로 앵귤러 기본 콤포넌트는 아니다 따라서 아래와 같이 추가해주어야 한다.


import { RouterModule, Routes } from '@angular/router';


설정하기


라우팅 앵귤러 앱은 하나의 싱글톤 Router 서비스를 갖는다. 브라우저 URL이 변경되면 해당 표시할 콤포넌트를 결정하기 위해 해당 Route를 찾는다.

Router는 설정해주기전에는 Route를 갖지 않는다. 아래 예는 4개의 route를 설정, RouterModule.forRoot메소드롤 설정하고 AppModule의 imports 배열에 추가한다.


const appRoutes: Routes = [

  { path: 'crisis-center', component: CrisisListComponent },

  { path: 'hero/:id',      component: HeroDetailComponent },

  {

    path: 'heroes',

    component: HeroListComponent,

    data: { title: 'Heroes List' }

  },

  { path: '',

    redirectTo: '/heroes',

    pathMatch: 'full'

  },

  { path: '**', component: PageNotFoundComponent }

];


@NgModule({

  imports: [

    RouterModule.forRoot(appRoutes)

    // other imports here

  ],

  ...

})

export class AppModule { }


appRoutes는 라우팅방법에 대해 기술한다. 그리고 imports에 RouterModule.forRoot네 파라메터로 넘기게 된다.


각 Route는 하나의 콤포넌트에 URL path하나를 맵핑한다. path는 '/'로 시작하지 않는다. 뷰간의 네비게이션시 상대적/절대적 경로를 만드는데 사용된다.


:id는 route파라메터의 토큰으로 /hero/42같은 URL에서 42가 id 파라메터에 해당하게 된다. HeroDetailComponent는  hero정보를 표시하기 위해 id값을 취해서 정보를 표시한다.


data 속성은 해당 route와 연관된 데이터를 위치시키는데 사용된다. 활성화된 각 route에서 접근이 가능하다. 페이지타이틀이나 로그텍스트(방문페이지표시), 또는 읽기전용의 스테틱데이터를 저장하는데 사용하는게 권장된다.


빈 path의 경우 URL이 빈경우에 해당하며 일반적으로 시작화면이 된다. 위 예의 경우 디폴트 라우드는 /heros URL로 redirect되고 HeroListComponent가 표시되게 된다.


** path는 와일드카드로 라우터는 요청된 URL이 매칭되는 경로가 없는 경우 표시된다. 404-Not Found에 대해 특정 페이지로 이동시켜서 변경이 가능하다.


route설정의 순서는 라우터가 매칭되면 이 후의 것들은 무시가 되기 때문에 전략적으로 설정해야 한다.



Router outlet

Router설정에 따라서 표시할 콤포넌트가 결정되면 해당 내용은 RouterOutlet에 표시된다.


<router-outlet></router-outlet>



Router links

라우팅된 페이지가 표시된 후 다른 페이지로 이동은 어떻게 할까? 브라우저 주소바에 URL을 입력하여 이동이 가능하나 대부분 페이지내에서 클릭을 통해서 이동하게 된다.


template: `

  <h1>Angular Router</h1>

  <nav>

    <a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>

    <a routerLink="/heroes" routerLinkActive="active">Heroes</a>

  </nav>

  <router-outlet></router-outlet>

`


anchore 태그의 RouterLink 디렉티브는 라우팅을 제어할 수 있게 해준다. URL은 미리 결정되기 때문에 하드코딩으로 routerLink를 지정할 수 있다.


RouterLinkActive 디렉티브는 현재 선택된 라우터를 구별할 수 있게 해준다. active CSS 클랙스가 액티브 되었을 때 사용되게 된다.



Router state

네이게이션 라이프사이클이 완료된 후에 라우터는 현재 상태를 반영하는 ActivatedRoute객체의 트리를 만들어낸다.  Router서비스와 routerState속성을 사용하는 곳 어느곳에서나 RouterState에 접근이 가능하다.


요약

앱은 설정된 라우터를 하나 갖게된다. 라우터가 생성하는 뷰는 RouterOutlet에 표시되게 되고 RouterLink로 특정 라우트로 이동할 수 있다.


Router Part 

 Meaning

 Router

유효한 URL의 콤포넌트를 표시하고 한 콤포넌트에서 다른 콤포넌트로의 네비게이션을 관리한다. 

 RouterModule

앵귤러에서 분리된 모듈로 필요한 서비스프로바이더와 디렉티브들을 제공한다. 

 Routes

Route들의 배열로 URL경로와 콤포넌트간의 맵핑테이블이다. 

 Route

라우터가 URL패턴에 따라 콤포넌트를 어떻게 다룰지에 대해 정의한다. 대부분의 라우트는 경로와 콤포넌트로 구성된다. 

 RouterOutlet

라우터가 뷰를 표시할 directive(<router-outlet>)이다. 

 RouterLink

디렉티브로 클릭가능한 라우트 엘리먼트(routerLink)이다.  

 RouterLinkActive

css class로 routerLink가 active/inactive될때 적용된다. 

 ActivatedRoute

서비스로 각 라우트콤포넌트에 라우트파라메터, 스테틱데이터, resolve data, global query params그리고 global fragment정보를 제공한다.

 RouterState

route tree로부터 router의 상태정보를 제공한다. 

 Link parameter 배열

라우팅 명령으로 처리되는 배열로 RouterLink에 바인드하거나 Router.navigate메서드로 배열을 전달할 수 있다. 

 Routing component

RouterOutlet과 함께 앵귤러 콤포넌트로 라우터네비게이션에 기반하여 뷰를 표시한다. 



위에서 설명한것으로 라우팅을 적용한 앱을 만들어 볼수 있다.

자세한 설명이 필요하다면 링크 참조(https://angular.io/docs/ts/latest/guide/router.html)



Posted by 삼스

댓글을 달아 주세요