체인 아이디(Chain ID)는 블록체인 네트워크를 식별하는 번호입니다
그리고 메인넷과 테스트넷도 서로 다른 체인 아이디를 가지고 있습니다
예를 들면 이더리움의 메인넷의 체인 아이디는 1이고, 테스트넷(Goerli)의 체인 아이디는 5입니다
(현재 가동중인 이더리움 테스트넷은 Goelri와 Sepolia뿐)
메인넷과 테스트넷은 동일한 블록체인 프로토콜을 사용하고, 그래서 거의 동일한 방식으로 동작합니다
주요 차이점은 테스트넷에서는 실제 이더를 사용하지 않고 가짜 이더로 트랜잭션을 처리한다는 것
즉, 테스트넷은 메인넷과 같은 동작 구조를 가진 개발자를 위한 네트워크라고 할 수 있습니다
지금까지는 트러플과 가나쉬 네트워크를 통해 디앱을 로컬 환경에서만 개발해왔지만,
이제는 테스트넷 환경에서 디앱 배포를 위한 방법을 익혀볼 차례입니다
그런데 막상 테스트넷을 이용하려 해도 내가 만든 컨트랙트 블록을 어떻게 네트워크에 띄우고
또 최신 블록을 어떻게 계속 동기화할 것인지, 노드 관리에 대한 여러 고민이 생겨납니다
블록 동기화는 실시간으로 끊임없이 이루어져야 하니까요
그래서 보통은 이 역할을 대신해줄 몇몇 프로바이더의 도움을 받게 됩니다
Infura는 원격 이더리움 노드를 통해 이더리움 네트워크에 접근할 수 있게 해주는 서비스입니다
Infura와 같은 프로바이더는 노드를 동기화하고, 네트워크에 접속해서 트랜잭션 전송, 컨트랙트 실행 등
블록체인 네트워크와 상호작용하는 데에 있어 여러 서비스 기능을 제공합니다
Infura 접속
(https://app.infura.io/dashboard)
테스트넷(Goerli)의 API키 발급받기 (web3 API
선택)
↑ API 키는 프로젝트를 구분하고 인증하는데 사용됩니다
그리고 Infura가 관리하는 원격 노드에 액세스할 수 있는 권한을 부여합니다
이제 트러플을 사용해서 컨트랙트를 테스트넷에 배포하는 과정을 구체적으로 알아보겠습니다
먼저 트러플 컨피그 파일에서 아래 코드들의 주석들을 해제합니다
[truffle-config.js]
require('dotenv').config();
// .env파일을 만들어서 MNEMONIC에는 개인키를, PROJECT_ID는 Infura에서 발급받은 API 키를 입력
const { MNEMONIC, PROJECT_ID } = process.env;
const HDWalletProvider = require('@truffle/hdwallet-provider');
goerli: {
provider: () => new HDWalletProvider(MNEMONIC, `https://goerli.infura.io/v3/${PROJECT_ID}`),
network_id: 5, // Goerli's id
confirmations: 2, // # of confirmations to wait between deployments. (default: 0)
timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
},
↓ 설치가 필요한 패키지
npm install dotenv
npm install @truffle/hdwallet-provider
(컨트랙트와 마이그레이션 파일 작성은 직전 포스트와 동일하므로 생략합니다)
모든 사전작업이 끝났다면 이제 배포를 진행합시다
npx truffle migrate --network goerli
배포 결과
01_deploy_counter.js
====================
Deploying 'Counter'
-------------------
^[[1;2D > transaction hash: 0xe94542417818d286c0c53b8888e6136c48fcdc701b96dea0bbc5eee07e25c597
> Blocks: 1 Seconds: 17
> contract address: 0x57cC92489F1444C127Cf3CeDef1eB7EB58d8180b
> block number: 9058558
> block timestamp: 1684977588
> account: 0xFA2e0fBBE47B0dbe0B3c4caD0cC81a57F8A663d3
> balance: 0.199637121777583473
> gas used: 145151 (0x236ff)
> gas price: 2.500004977 gwei
> value sent: 0 ETH
> total cost: 0.000362878222416527 ETH
Pausing for 2 confirmations...
-------------------------------
> confirmation number: 1 (block: 9058559)
> confirmation number: 2 (block: 9058560)
> Saving artifacts
-------------------------------------
> Total cost: 0.000362878222416527 ETH
Summary
=======
> Total deployments: 1
> Final cost: 0.000362878222416527 ETH
블록 생성까지 걸린 시간은 17초, 가스는 약 0.0004ETH가 발생했네요
[Counter.jsx]
import { useState, useEffect } from "react"
import CounterContract from "../contracts/Counter.json"
const Counter = ({ account, web3 }) => {
const [count, setCount] = useState(0);
const [deployed, setDeployed] = useState(null);
const [loading, setLoading] = useState(true);
const handleClick = async (type) => {
if (deployed === null) return console.log("no deployed")
await deployed.methods[type]().send({ from: account })
deployed.methods.getValue().call().then(value => {
setCount(value)
setLoading(false);
})
}
useEffect(() => {
if (!web3 || !account) return
// Contract(ABI, CA) ~ Goerli의 cahinId는 5
const Deployed = new web3.eth.Contract(CounterContract.abi, CounterContract.networks[5].address)
setDeployed(Deployed)
Deployed.methods.getValue().call().then(value => {
setLoading(false);
setCount(value)
})
}, [])
if (loading) {
return <div>Loading...</div>
}
return (
<div>
<h2>Counter: {count}</h2>
<button onClick={()=>handleClick("increment")}>+</button>
<button onClick={()=>handleClick("decrement")}>-</button>
</div>
);
};
export default Counter;
프론트와의 연동도 잘 됩니다
덧붙여서 이벤트를 발동시킬 때마다 약 0.0001이더가 소모되었습니다