블록체인 Block-Chain - 이더리움 DApp - Counter 만들기 ( Ganache ) (1)

dev_swan·2022년 7월 13일
0

블록체인

목록 보기
21/36
post-thumbnail
post-custom-banner

DApp - Counter 만들기

개발 환경 세팅

  • Truffle : mkdir truffle -> cd truffle -> npx truffle init
  • Front : create-react-app- front
  • 블록체인 네트워크 : npx ganache-cli

MetaMask에 Ganache 네트워크 추가 및 지갑 불러오기

  • Ganache로 실행한 지갑 주소와 개인키를 미리 복사해놓습니다.
  • 지갑 1 : 0xe75E29893E906eD60BF47FfDA91dCFCf3035069d
  • 개인키 1 : 0x6f73aae168847459a07b139b18f0eddc2459c33720ecc705fabd0bd62974963d


  • 메타마스크에 계정 가져오기


솔리디티 코드 작성

Counter.sol 파일 생성

pragma solidity ^0.8.15;

contract Counter {
	uint256 private _count;
	
	// public이 아닌 private기 때문에 _count 상태변수를 가져오는 함수를 만들어주었습니다.
	function current() public view returns(uint256){
		return _count
	}
	
	function increment() public {
		_count += 1;
	}
	
	function decrement() public {
		_count -= 1;
	}
}

Solidity Code로 상태변수 count를 가져올 getter 함수와 상태변수를 변경할 setter 함수들을 작성합니다.


Smart Contract 배포 ( Feat. Truffle )

  • truffle 디렉토리로 이동 및 npx truffle init

  • truffle config.js 환경설정 세팅 ( development 주석 풀기 )

  • truffle complie로 컴파일링 실행 => build 디렉토리에 솔리디티 코드를 컴파일링한 json 파일을 확인합니다.

  • migrations 디렉토리에 2_deploy_Counter.js 파일 생성 => migration 디렉토리 안에 불러온 코드들을 배포할것입니다.

  • truffle migration으로 배포 -> 메타마스크의 지갑에서 Transaction을 발생하여 가스비가 빠져나간것을 확인할 수 있습니다.

  • truffle console 이동 => Counter 입력하면 abibytecode 내용이 담겨져 있는 객체를 보여줍니다.

  • Counter.deployed().then(instance => it = instance) then에 있던 값을 전역에서 사용하도록 변수에 담아줍니다.
    it 인스턴스안에 methods에 배포한 smart contract가 담겨있는 것을 확인할 수 있습니다.

  • it.current.call()을 하면 BN { negative : 0, words: [ 0, <1 empty item> ] ~~~ } 이런 값이 나오는데 words의 첫번째 index의 있는 값이 count 값입니다.

  • it.increment() -> 상태변수를 바꾸는 함수이기 때문에 Tx을 발생시킵니다.
    Ganache에서는 Transaction 발생시 자동으로 mining이 되기 때문에 mining을 해주지 않아도 됩니다.

  • 다시 it.current.call()을 해보면 값이 0 -> 1로 바뀐것을 확인할 수 있습니다.

Truffle Test Code 작성

const Counter = artifacts.require('Counter');

describe('Counter Test', () => {
	let counter;

	it('Counter deployed', async () => {
		counter = await Counter.deployed(); // 배포된 결과물을 가져옴
		console.log(counter);
	});

	it('get current', async () => {
		console.log(await counter.current.call()); //return _count
	});

	it('increment', async () => {
		await counter.increment(); // TxHash값과 TxReceipt값이 나옴
		const result = await counter.current.call(); // return _count
		console.log(result.toNumber()); // 1
	});

	it('decrement', async () => {
		await counter.decrement(); // TxHash값과 TxReceipt값이 나옴
		const result = await counter.current.call(); // return _count
		console.log(result.toNumber()); // 0
	});
});

// CA : '0x1F1F1B55CAAD018D1efCd34fedecB165b74b6413'
  • truffle test로 테스트 코드를 실행하여 정상적으로 smart contract들이 동작하는지 확인합니다.
  • const Counter = artifacts.require('Counter'); artifacts.require()를 사용하여 사용할 계약 인스턴스를 가져올 수 있습니다.

Front

Transaction을 일으키려면 Private Key가 필요한데 프론트에 개인키를 두고 작업하기에는 보안상 문제가 있으니 MetaMask같은 지갑을 사용할 것입니다.

Front -> MetaMask 연결하기 ( useWeb3 CustomHook )

npm install web3

/* useWeb3.jsx */

import { useEffect, useState } from 'react';
import Web3 from 'web3/dist/web3.min'; // 프론트에서는 최소한의 라이브러리 내용만 가져옵니다.

const useWeb3 = () => {
	const [account, setAccount] = useState('');
	const [web3, setWeb3] = useState(null);

	useEffect(() => {
		(async () => {
			if (!window.ethereum) return;

			// Front와 MetaMask 지갑 연결하기
			const [address] = await window.ethereum.request({
				method: 'eth_requestAccounts',
			});

			// 지갑 주소가져온후 Account 상태 변경
			setAccount(address);

			// 메타마스크와 Front web3 연결
			const web3 = new Web3(window.ethereum);

			// 연결한 web3로 상태변경
			setWeb3(web3);
		})();
	}, []);

	return [web3, account];
};

export default useWeb3;

Front에서 MetaMask는 연결은 지난 시간에 해보았기에 자세한 설명은 생략하겠습니다.


/* App.jsx */

import React from 'react';
import useWeb3 from './hooks/useWeb3';
import Counter from './components/Counter';

const App = () => {
	const [web3, account] = useWeb3();

	if (!account) return <>메타마스크 연결이 필요합니다.</>;

	return (
		<div>
			<span>Account : {account} </span>
			<Counter web3={web3} account={account} />
		</div>
	);
};

export default App;
  • App 함수형 컴포넌트가 실행될때 아래와 같이 MetaMask와 지갑이 연동이 안되어 있을경우 메타마스크 연결이 필요하다는 메세지가 보입니다.


  • 지갑을 연결하면 아래와 같이 해당 지갑의 주소와 Counter 컴포넌트를 propsweb3account를 전달해준 상태로 render합니다.


Counter

/* Counter.jsx*/

import { useState, useEffect } from 'react';
import CounterContract from '../contracts/Counter.json';

// props로 전달받은 web3와 account를 받아서 사용합니다.
const Counter = ({ web3, account }) => {
	const [count, setCounte] = useState(0);
	const [deployed, setDeployed] = useState();

	useEffect(() => {
		(async () => {
			if (deployed) return;

			const deploy = await new web3.eth.Contract(CounterContract.abi, '0x1F1F1B55CAAD018D1efCd34fedecB165b74b6413'); // 2가지 인자값 abi ,CA

			const current = await deploy.methods.current().call();

			setDeployed(deploy);
			setCounte(current);
		})();
	}, []);

	const increment = async () => {
		await deployed.methods.increment().send({ from: account });

		const current = await deployed.methods.current().call();
		setCounte(current);
	};

	const decrement = async () => {
		await deployed.methods.decrement().send({ from: account });

		const current = await deployed.methods.current().call();
		setCounte(current);
	};

	return (
		<div>
			<h2>Counter : {count} </h2>
			<button onClick={increment}>+</button>
			<button onClick={decrement}>-</button>
		</div>
	);
};

export default Counter;
  • import CounterContract from '../contracts/Counter.json'; 위에서 Trufflecompile하고 배포한 contract 내용을 가져옵니다.
  • Counter 컴포넌트의 props로 전달받은 web3account를 구조분해 할당으로 받아 사용할 것입니다.
  • ComponentDidMount가 되면 useEffect안에 즉시 실행 함수가 실행되며 처음에는 deployednull값이니 Ganache 네트워크와 연결된 web3.eth.Contract() 메서드를 사용하여 인자값으로 abi파일과 배포한 ContractCA값을 넣어 methods에 배포한 contract들이 추가된 deploy 인스턴스를 생성합니다.
  • 이후 deploymethods에 있는 current().call()을 하여 count값을 가져와 current 변수에 할당하고 setCounte로 상태를 변경합니다. 또한 deploy 인스턴스도 상태에 넣어 다른 함수에서도 사용할 수 있도록 해주었습니다.
  • 마지막으로 + - 버튼에 onClick 이벤트를 넣고 각각 incrementdecrement함수를 만들어 + 혹은 - 버튼을 누르면 deployed 메서드안에 increment 혹은 decrement contract가 실행되어 Transaction을 발생시키고 MetaMask에 연결한 지갑에서 가스 수수료를 지불하고 난후 Ganache 네트워크에서 블록을 자동으로 생성해주어 해당 Transactioncontract가 실행되어 상태변수를 바꿔줄것입니다. 값이 바뀌면 다시 current().call() 메서드를 실행하여 바뀐값을 가져와 setCounte로 상태를 바꿔주었습니다.

  • 버튼을 눌러 상태변수를 바꿀것이기에 가스 수수료를 지불해야 합니다.

가스 수수료를 지불하면 TransactionTransaction Pool에 담기고 Ganache 네트워크에서 자동으로 블록을 생성해주어 바로 Contract가 실행되며 Count값이 +1이 됩니다.

post-custom-banner

0개의 댓글