[Truffle, React.js] 과일 가게 dApp 만들기 (1/2)
할 것
이더리움을 이용해 과일을 구매하고 판매하는
dApp
을 만듭니다.React.js
로 프론트엔드를 구성합니다.
완성물 미리보기 (Youtube)
실제 완성물은 조금 더 못생겼습니다. css 를 다루기 귀찮아서...
준비물
Node.js
홈페이지에 접속해 LTS 버전을 설치해주세요. Truffle
을 설치하는데 사용합니다. (Node.js 5.0 상위 버전을 추천합니다.)
Truffle
npm install -g truffle
Truffle
은 이더리움 dApp 개발 프레임워크입니다. 스마트 컨트랙트 작성, 빌드 및 배포를 제공하며 유닛 테스트에 용이합니다.
Ganache
홈페이지에 접속하면 쉽게 다운로드 및 설치 할 수 있습니다.
Ganache
는 가상의 이더리움 클라이언트를 만들어줍니다. 마음껏 테스트 가능한 지갑, 계정, 화폐를 제공합니다. 이번 예제에서 Ganache
에 있는 이더리움을 사용합니다.
MetaMask
MetaMask
는 크롬 익스텐션입니다. 웹브라우저에 이더리움 트랜잭션 기능을 추가합니다. 꼭 설치해주세요.
프로젝트 생성
Truffle 은 dApp 을 손쉽게 개발 할 수 있도록 미리 세팅해둔 프로젝트(보일러플레이트
)를 제공합니다. 이를 Truffle Boxes
라고 부릅니다.
우리는 그 안에 기능만 추가하면 됩니다.
Truffle Boxes 중 react 박스를 사용합니다.
react 박스는 Truffle 이 제공하는 Official Box 이며 292 개 이상의 Star 를 보유하고 있습니다.
디렉토리를 생성하고 react
를 unbox
합니다:
> 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 환경 설정 파일입니다.
불필요 코드 제거하기
react box
가 미리 작성해 놓은/contracts/SimpleStorage.sol
,/build/contracts/SimpleStorage.json
를 삭제합니다.이에 해당하는
/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
}
}
};
우리는 로컬 환경에서 실습하기 때문에 host
를 127.0.0.1
로 설정합니다. port
는 Ganache
의 포트를 적어줍니다:
컨트랙트 생성하기
과일 가게를 운영할 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
은 계정에 몇 개의 사과가 남았는지address
에uint16
을 맵핑합니다.
예를 들어 특정한 두 계정이 사과를 4개, 3개 씩 구매 했다면 myApple
은 다음과 같습니다:
0xdaF72FcEE99c3ED561b5F91a83B69c6F3D6B02e8 => 4
0x5De494B59aa0a1c3B7a4f2E45929Bcd341599613 => 3
만약 0xdaF72FcEE99c3ED561b5F91a83B69c6F3D6B02e8
가 사과를 한 개 더 구매한다면 myApple
은 이렇게 변하겠죠?:
0xdaF72FcEE99c3ED561b5F91a83B69c6F3D6B02e8 => 5
0x5De494B59aa0a1c3B7a4f2E45929Bcd341599613 => 3
다음으로 사과 구매 함수를 작성합니다:
function buyApple() payable external {
myApple[msg.sender]++;
}
buyApple
은 외부에서 트랜잭션을 실행하기 위해payable external
입니다.함수가 실행되면
msg.sender
의myApple
을 증가시킵니다.
보유한 사과 개수를 반환하는 함수를 작성합니다:
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.sender
의myApple
을0
으로 초기화합니다.계산한 총 가격만큼
msg.sender
에ETH
를 전송합니다.
이제 컨트랙트를 완성했습니다. 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 를 통해 컨트랙트와 통신합니다.
감사합니다.
짱짱맨 호출에 출동했습니다!!
안녕하세요. 리액트와 dapp에 대해서 둘다 공부 중이였는데 마침 좋은 예제 만들어주셔서 잘보고갑니다. 자주들를게요😊
감사합니다. 저도 처음 시도해보는거라 많이 허접하지만 잘 부탁드립니다 😃😃
좋은 글 감사합니다.