[Truffle, React.js] 과일 가게 dApp 만들기 (1/2)

in #ethereum6 years ago (edited)

할 것


  1. 이더리움을 이용해 과일을 구매하고 판매하는 dApp 을 만듭니다.

  2. React.js 로 프론트엔드를 구성합니다.

완성물 미리보기 (Youtube)


Project_Preview

클릭하면 유뷰트로 이동합니다.

실제 완성물은 조금 더 못생겼습니다. css 를 다루기 귀찮아서...



준비물


Node.js

홈페이지에 접속해 LTS 버전을 설치해주세요. Truffle 을 설치하는데 사용합니다. (Node.js 5.0 상위 버전을 추천합니다.)

Truffle

npm install -g truffle



Truffle 은 이더리움 dApp 개발 프레임워크입니다. 스마트 컨트랙트 작성, 빌드 및 배포를 제공하며 유닛 테스트에 용이합니다.

Ganache

홈페이지에 접속하면 쉽게 다운로드 및 설치 할 수 있습니다.

Ganache 는 가상의 이더리움 클라이언트를 만들어줍니다. 마음껏 테스트 가능한 지갑, 계정, 화폐를 제공합니다. 이번 예제에서 Ganache 에 있는 이더리움을 사용합니다.

MetaMask

MetaMask 는 크롬 익스텐션입니다. 웹브라우저에 이더리움 트랜잭션 기능을 추가합니다. 꼭 설치해주세요.



프로젝트 생성



Truffle_React

Truffle 은 dApp 을 손쉽게 개발 할 수 있도록 미리 세팅해둔 프로젝트(보일러플레이트)를 제공합니다. 이를 Truffle Boxes 라고 부릅니다.

우리는 그 안에 기능만 추가하면 됩니다.

Truffle Boxes 중 react 박스를 사용합니다.

react 박스는 Truffle 이 제공하는 Official Box 이며 292 개 이상의 Star 를 보유하고 있습니다.

디렉토리를 생성하고 reactunbox 합니다:

> mkdir fruitshop
> cd fruitshop
> truffle unbox react



프로젝트를 생성하는데 몇 분 걸립니다.

프로젝트 구조



디렉토리를 조사합니다:

> tree -L 1
.
├── box-img-lg.png
├── box-img-sm.png
├── config
├── contracts
├── migrations
├── node_modules
├── package-lock.json
├── package.json
├── public
├── scripts
├── src
├── test
├── truffle-config.js
└── truffle.js



다양한 폴더가 존재하지만 우리는 일부만 사용합니다:

  • /contracts: 스마트 컨트랙트를 작성합니다.

  • /migrations: Truffle 마이그레이션 파일을 작성합니다.

  • /scripts: build, start, test 환경을 설정합니다.

  • /src: React.js 폴더입니다.

  • truffle.js: Truffle 환경 설정 파일입니다.



불필요 코드 제거하기


  1. react box 가 미리 작성해 놓은 /contracts/SimpleStorage.sol, /build/contracts/SimpleStorage.json 를 삭제합니다.

  2. 이에 해당하는 /migrations/2_deploy_contracts.js 마이그레이션도 함께 지워줍니다. 추후에 우리가 작성한 컨트랙트에 해당하는 마이그레이션 파일을 생성합니다.

Truffle 콘픽 설정하기



truffle.js 를 열어서 다음과 같이 입력합니다:

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "*" // Match any network id
    }
  }
};



우리는 로컬 환경에서 실습하기 때문에 host127.0.0.1 로 설정합니다. portGanache 의 포트를 적어줍니다:

Ganache_Port

컨트랙트 생성하기



과일 가게를 운영할 Shop.sol 컨트랙트가 필요합니다.

컨트랙트를 생성하는 방법은 두 가지가 있습니다:

> truffle create contract Shop



이렇게 커맨드를 입력하거나 직접 Shop.sol 파일을 /contracts 폴더에 생성합니다.

이제 /contracts 폴더는 다음과 같습니다:

> tree .
.
├── Migrations.sol
└── Shop.sol


  • Migrations.sol 은 프로젝트 생성시 포함되는 컨트랙트입니다. 이는 여러분이 Deploy, Migrate 한 모든 컨트랙트의 상태 및 변화를 추적합니다. 자세한 내용은 여기를 참고해주세요.

컨트랙트 작성하기



우리는 과일을 구매하고 판매 할 수 있는 하나의 시장을 구축해야 합니다.

앞서 보여드린 동영상에서 사과, 바나나 그리고 키위를 판매했지만 포스팅에선 사과만 다루겠습니다. 바나나와 키위는 단순히 기능 추가만 하면 되므로 생략하겠습니다.

첫째로, 계정의 사과 보유량을 기록하는 mapping 을 선언합니다:

pragma solidity ^0.4.18;

contract Shop {
  mapping (address=>uint16) myApple;
}


  • myApple 은 계정에 몇 개의 사과가 남았는지 addressuint16 을 맵핑합니다.

예를 들어 특정한 두 계정이 사과를 4개, 3개 씩 구매 했다면 myApple 은 다음과 같습니다:

0xdaF72FcEE99c3ED561b5F91a83B69c6F3D6B02e8 => 4
0x5De494B59aa0a1c3B7a4f2E45929Bcd341599613 => 3


만약 0xdaF72FcEE99c3ED561b5F91a83B69c6F3D6B02e8 가 사과를 한 개 더 구매한다면 myApple 은 이렇게 변하겠죠?:

0xdaF72FcEE99c3ED561b5F91a83B69c6F3D6B02e8 => 5
0x5De494B59aa0a1c3B7a4f2E45929Bcd341599613 => 3


다음으로 사과 구매 함수를 작성합니다:

function buyApple() payable  external {
        myApple[msg.sender]++;
}


  • buyApple 은 외부에서 트랜잭션을 실행하기 위해 payable external 입니다.

  • 함수가 실행되면 msg.sendermyApple 을 증가시킵니다.

보유한 사과 개수를 반환하는 함수를 작성합니다:

function getMyApples() view external returns(uint16) {
        return myApple[msg.sender];
}


  • getMyApples 는 컨트랙트의 상태를 변경하지 않고 값을 반환하므로 view 함수입니다. 또한 외부에서 호출 가능하도록 external 로 선언했습니다.

  • msg.sender 의 사과 개수를 uint16 형으로 반환합니다.

보유한 모든 사과를 시장에 다시 판매하는 함수를 작성합니다:

function sellMyApple(uint _applePrice) payable external {
        uint refund = (myApple[msg.sender] * _applePrice);
        myApple[msg.sender] = 0;
        msg.sender.transfer(refund);
}


  • sellMyApple 은 트랜잭션 가능하고 외부에서 호출해야하므로 payable external 함수입니다.

  • 사과의 시세(_applePrice)를 서버에서 받아와 현재 계정이 보유한 개수과 곱해 총 가격을 산출합니다.

  • msg.sendermyApple0 으로 초기화합니다.

  • 계산한 총 가격만큼 msg.senderETH 를 전송합니다.

이제 컨트랙트를 완성했습니다. Shop.sol 을 다시 한 번 확인하세요:

pragma solidity ^0.4.18;

contract Shop {

  mapping (address=>uint16) myApple;

  function buyApple() payable  external {
          myApple[msg.sender]++;
  }

  function getMyApples() view external returns(uint16) {
          return myApple[msg.sender];
  }

  function sellMyApple(uint _applePrice) payable external {
        uint refund = (myApple[msg.sender] * _applePrice);
        myApple[msg.sender] = 0;
        msg.sender.transfer(refund);
  }

}




컨트랙트 배포 준비



Truffle 에서 컨트랙트를 배포(Deploy)하려면 migration 을 작성해야합니다.

/migrations 폴더에 2_deploy_contracts.js 를 직접 생성해주세요.

혹은 truffle console 을 사용합니다.

> truffle create migration shop


커맨드로 생성한 migration 은 파일 이름이 1526449185_shop.js 처럼 생성됩니다. 이를 2_deploy_contracts.js 로 변경해주세요. 파일 이름이 직접적으로 영향을 끼치지 않지만 x_script_name.js 를 사용하는게 관습입니다.

2_deploy_contracts.js 에 Truffle 이 우리가 작성한 컨트랙트를 어떻게 배포할지 지시합니다.

이제 코드를 작성합시다:

// 2_deploy_contracts.js
var Shop = artifacts.require("./Shop.sol");

module.exports = function(deployer) {
  deployer.deploy(Shop);
};


  • 우리가 작성한 Shop.sol 을 불러오고 이를 배포합니다. 현재는 별다른 기능이 없지만 여러개의 컨트랙트를 작성한 경우 순서를 설정하거나 관계를 정의 할 수 있습니다.

컨트랙트 배포



Shop.sol 컨트랙트를 자바스크립트에서 접근 가능하도록 json 파일로 변환 작업이 필요합니다:

> truffle compile
Compiling ./contracts/Shop.sol...
Writing artifacts to ./build/contracts



/build/contracts/ 폴더에 Shop.json 파일이 생성되었습니다. 컨트랙트를 다루는데 필요한 정보가 들어있습니다.

Ganache 가 실행된 상태에서 migration 을 진행합니다:

> truffle migrate --reset
Using network 'development'.

Running migration: 1_initial_migration.js
  Replacing Migrations...
  ... 0xcea701639e031a7211bbe43038b4fb3fb710a539161e6f36249efb1520c8c653
  Migrations: 0x302c323f97a9d01fabeb57d7746865afd9a94ec4
Saving successful migration to network...
  ... 0x5225d65039b9f92eed19d76abb52c7ebd1173dd9001d5a6181868f09db3d4ae0
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying Shop...
  ... 0x720118601d7250e764a4da707489abfefe8c1a98ab8259b24a860102fe234c05
  Shop: 0x5648e270176f70be090d07ccfaaada2e5d4e1da2
Saving successful migration to network...
  ... 0x3106e54ca62502c0eb0eca0aaf804b969b12f04f6561796b33c28b13ba082fe6
Saving artifacts...


--reset 옵션을 사용하면 이전에 로컬에서 작업한 내용들을 지우고 다시 migrate 합니다. 만약 Ganache 를 처음 설치하셨다면 해당 옵션 없이 진행하셔도 괜찮습니다.

방금 입력한 커맨드로 여러분이 작성한 스마트 컨트랙트를 로컬 이더리움 네트워크에 배포했습니다. 축하합니다!!

이어서...



다음 포스팅은 React.js 와 Web3.js 를 통해 컨트랙트와 통신합니다.

감사합니다.

Sort:  

짱짱맨 호출에 출동했습니다!!

안녕하세요. 리액트와 dapp에 대해서 둘다 공부 중이였는데 마침 좋은 예제 만들어주셔서 잘보고갑니다. 자주들를게요😊

감사합니다. 저도 처음 시도해보는거라 많이 허접하지만 잘 부탁드립니다 😃😃

좋은 글 감사합니다.