React js에서, Nodejs에서 각각 사용법을 분할해 적었으므로, 둘 중 한가지만 진행하면 됩니다.
1. puppeth를 통한 제네시스 블록 생성하기.
genesis.json 파일 생성을 원하는 디렉토리안에서 puppeth를 실행시켜 제네시스 파일을 만든다.
cd mydirectory
puppeth //기본적으로 geth를 설치했다면 puppeth가 존재한다.
2. mynetwork.json 파일을 초기화(Init)하자
geth --datadir . init mynetwork.json
geth 폴더가 생성되면서, 체인 데이터가 저장될 디렉토리를 생성해준다.
그럼 총 2개의 폴더가 생기는데 각 폴더의 용도는 다음과 같다.
geth = 체인 데이터 저장
keystore = 계정데이터 저장
3. Geth 실행
geth --datadir . --rpc --rpcapi "web3,eth,net" --rpccorsdomain "*" --allow-insecure-unlock
이런 화면이 뜬다면, Geth서버가 열린것이다. 이제 새 터미널을 열어 geth console에 접속해보자.
3. 새 터미널을 열어 geth console에 접속해보자.
위 geth 실행화면의 결과창에서 자신만의 ipc endpoint를 찾는다.
그리고 새 터미널을 열고 아래 명령어를 수행해 콘솔을 연다.
//geth attach (복사한 ipc endpoint)
geth attach /Users/handonglee/Desktop/mydapp/block/geth.ipc
4. 콘솔에서 명령어 수행하기 (어카운트 생성 && 마이닝)
**단 모든 명령어의 앞 web3는 생략이 가능하다.
//콘솔명령어
web3.personal.newAccount(); = 새 계정생성 (실행하면 비밀번호 입력하라 나옴)
web3.eth.accounts = 계정확인
web3.eth.accounts[0] = 계정 확인
web3.eth.coinbase = 코인베이스 계정확인
<만약, 코인베이스 계정이 존재하지 않는다면? miner.setEtherbase(eth.accounts[1]) 명령어를 통해 설정이 가능하다.>
web3.eth.getBalance(eth.accounts[0]) = 0번째 계정(1번째 계정)의 잔고확인
miner.start(1) = mining을 시작한다. ()안의 수는 가동할 쓰레드의 갯수다.
<miner.start 명령어 사용시 null을 반환하게 된다. 이런경우, eth.mining으로 채굴여부를 확인이 가능하다.>
5-1. Nodejs에 Web3 연결하기 (Step 5에서 택1)
1. 기본적인 express 서버 설정 및 web3.js파일을 생성했다.
(getBalance는 테스트코드로, 첫번째 인자로 주소값을 가지고, 두번째 인자로 콜백함수를 가진다.)
2. web3.js에 다음과 같이 작성해서 위에서 구동한 Geth서버와 붙여준다.
5-2. React.js 와 연결하기 (Step 5에서 택1)
Reactjs에서는 window객체가 있어 다음과 같이 메타마스크로 연결할 수 있다.
ComponentDidMount부분에서 아래 코드를 붙여넣으면 페이지에 접속했을 때 메타마스크와 연결될 것이다.
if (typeof window.ethereum !== 'undefined') {
window.web3 = new Web3(window.web3.currentProvider);
try {
await window.ethereum.enable();
console.log(`✅ Connected Properly`)
} catch (err) {
console.log(`❌ ETH NONO!`,err)
}
} else {
console.log("no !!!!!")
}
6. Web 3와 연결했다면, 이제 컨트렉트를 만들어야한다. Truffle을 구동하자.
아래 명령어를 수행했다면, truffle로인해 contract & migration & test & truffleconfig.js 파일 및 폴더가 생성된다.
contract 폴더는 Solidity 스마트컨트렉트 코드를 작성하는 곳이며,
migration 폴더는 스마트컨트렉트 배포코드이며,
test는 말그대로 테스트 코드 작성 폴더이며,
truffle-config파일은 truffle 설정파일이다.
truffle init
7. 스마트컨트렉트 배포하기 (Student)
1. Contract 폴더에 Student.sol파일을 만들고 간단한 학생 등록, 및 Read해오는 스마트 컨트렉트를 작성한다.
pragma solidity >=0.4.22 <0.9.0;
contract Student {
struct Student {
string name;
uint age;
}
mapping(uint256 => Student) studentInfo;
function setStudentInfo(uint256 _studentId, string memory name, uint age) public {
Student storage student = studentInfo[_studentId];
student.name = name;
student.age = age;
}
function getStudentInfo(uint256 _studentId) public view returns (string memory, uint) {
return (studentInfo[_studentId].name, studentInfo[_studentId].age);
}
}
2. Migration폴더에 2_deploy_student.js 파일을 생성해 아래 코드를 넣는다.
const Student = artifacts.require("Student");
module.exports = function (deployer) {
deployer.deploy(Student);
};
3.truffle-config 파일로 이동해, network부분의 development 객체를 통해 배포를 진행할 예정이므로 설정해주자.
development 객체 부분의 주석을 풀고 아래 명령어를 넣어보자.
truffle migrate --network development
아래와 같은 에러가 날 것이다 !
"Migrations" -- Returned error: authentication needed: password or unlock."
(코인베이스 계정의 암호 때문에 컨트렉트 배포가 안된다는 의미다.)
다음 명령어를 통해 에러를 해결해주자.
personal.unlockAccount(web3.eth.coinbase, "1234")
다시 첫번째 명령어를 통해 재배포 해본다. 아, 이번엔 될것같이 잘 돌아가다가 에러가 난다. (안나면 그냥 해도된다)
가스비가 초과했다는 에러다. truffle config파일의 development 부분에 gas를 추가해주자.
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
gas: 4600000,
},
이제 정상적으로 migration이 될 것이다.
8. 배포한 스마트 컨트렉트 사용하기
스마트컨트렉트를 정상적으로 배포했다면 아래와같은 deploy부분이 보일 것이다. 이 부분 중, contract address부분을 가져온다.
또한, build라는 폴더가 생성되었을텐데 폴더 안에 들어가 우리가 만든 Student.json파일에 들어가 abi를 가져오고 저장해둔다.
이제 가져온 abi 와 contract address를 통해 스마트컨트렉트를 가져오자. 일단 변수로 할당한다.
# Node.js에서는,
const StudentContract = new web3.eth.Contract(ContractABI, ContractAddress);
# React.js에서는,
const StudentContract = new window.web3.eth.Contract(student_contract_ABI, student_contract_ADRS)
위와 같이 연결해서 사용할 수 있다. 리액트에서는 State값으로 할당해두고 쓰길 권장한다. 자꾸 안그러면 사라진다 ^오^.. 아래처럼..
setStudentContract(new window.web3.eth.Contract(student_contract_ABI, student_contract_ADRS));
그럼 이제 다음 명령어를 통해, 컨트렉트를 실행할 수 있다.
1. setStudentInfo Contract 호출 (Send방식 = 데이터에 변동이 생기는 방식으로 from옵션이 최소한 반드시 필요하다)
StudentContract.methods.setStudentInfo(1, "Blockmonkey", 29).send({"from": "0x1e4ceafbfded02b9d607c89485f94a58819025d1" })
2. getStudentInfo 호출 (Call방식 = 단순히 읽어오는 것으로, 별다른 옵션이 필요없다)
StudentContract.methods.getStudentInfo(2).call()
위 코드를 통해 실행하고 , promise처리가 반드시 필요하다.
필자는 .then을 통해 했다.
9. 전체코드 Nodejs
const express = require("express");
const app = express();
const PORT = 5000;
const web3 = require("./web3");
const ContractAddress = "0xBED998843D2BfAaeD28508e8D6983486F638c267";
const ContractABI = [
{
"constant": false,
"inputs": [
{
"internalType": "uint256",
"name": "_studentId",
"type": "uint256"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
}
],
"name": "setStudentInfo",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "_studentId",
"type": "uint256"
}
],
"name": "getStudentInfo",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
];
const StudentContract = new web3.eth.Contract(ContractABI, ContractAddress);
app.get("/", (req, res)=> {
// web3.eth.getAccounts().then(user => {
// console.log(user[0]);
// StudentContract.methods.setStudentInfo(2, "Minseo", 24).send({"from": user[0] })
// .then(result => {
// console.log(result);
// });
// });
StudentContract.methods.getStudentInfo(2).call()
.then(result => {
console.log(result);
})
res.send("Success!");
});
app.listen(PORT, ()=> console.log(`✅ Server is Running At : ${PORT}`));
10. 전체 코드 React
import React, { useEffect, useState } from "react";
import Web3 from "web3";
function App(props) {
let student_contract_ADRS = "0x8Def2096F8cfBd0C2d7FAA58eE3c3d595A47Dca3";
let student_contract_ABI = [
{
"constant": false,
"inputs": [
{
"internalType": "uint256",
"name": "_studentId",
"type": "uint256"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
}
],
"name": "setStudentInfo",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "_studentId",
"type": "uint256"
}
],
"name": "getStudentInfo",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
const [StudentContract, setStudentContract] = useState({});
const [User, setUser] = useState("");
const [View_Id, setView_Id] = useState(0);
const [Add_Id, setAdd_Id] = useState(0);
const [Add_Name, setAdd_Name] = useState("");
const [Add_Age, setAdd_Age] = useState(0);
const [Balance, setBalance] = useState(0);
useEffect(() => {
initWeb3();
}, [])
const initWeb3 = async () => {
if (typeof window.ethereum !== 'undefined') {
window.web3 = new Web3(window.web3.currentProvider);
try {
await window.ethereum.enable();
console.log(`✅ Connected Properly`)
} catch (err) {
console.log(`❌ ETH NONO!`,err)
}
} else {
console.log("no !!!!!")
}
window.web3.eth.getAccounts().then(res => {
console.log(`현재 사용자 : ${res[0]}`);
setUser(res[0]);
});
console.log("CP:", window.web3.currentProvider);
// //가장 중요한.. 연결!..
// if (typeof window.ethereum !== 'undefined') {
// window.web3 = new Web3(window.ethereum);
// try {
// await window.ethereum.enable();
// console.log(`✅ Connected Properly`)
// } catch (err) {
// console.log(`❌ ETH NONO!`,err)
// }
// } else {
// console.log("no !!!!!")
// }
// //현재 사용자의 Account를 메타마스크로부터 가져온 뒤, User State값에 저장한다.
// window.web3.eth.getAccounts().then(res => {
// setUser(res[0]);
// console.log(`사용자 주소: `, res[0]);
// })
//컨트렉트를 가져오고, StudentContract State값에 저장한다.
setStudentContract(new window.web3.eth.Contract(student_contract_ABI, student_contract_ADRS));
};
//스마트 컨트렉트 호출
//call(단순호출) & send(값 변경);
const setStudentInfo = () => {
StudentContract.methods.setStudentInfo(Add_Id, Add_Name, Add_Age).send({ "from": User })
.then(result => {
console.log(result);
//칸 초기화
setAdd_Id(0);
setAdd_Name("");
setAdd_Age(0);
})
};
const getStudentInfo = () => {
StudentContract.methods.getStudentInfo(View_Id).call()
.then(result => {
console.log(result);
//칸 초기화
setView_Id(0);
})
};
const handleViewStudent_Id = (e) => {
setView_Id(e.currentTarget.value)
}
const handleAddStudent_Id = (e) => {
setAdd_Id(e.currentTarget.value);
}
const handleAddStudent_Name = (e) => {
setAdd_Name(e.currentTarget.value);
}
const handleAddStudent_Age = (e) => {
setAdd_Age(e.currentTarget.value);
}
const getBalance = () => {
window.web3.eth.getBalance(User, function(err, wei){
setBalance(window.web3.utils.fromWei(wei, "ether"));
})
}
return (
<div className="App">
<div>
<button onClick={getBalance}>이더잔고 가져오기</button>
<div>{Balance} ETH</div>
</div>
<h1>학생조회서비스</h1>
<div>
<label>ID</label>
<input type="number" placeholder="Student_ID" value={View_Id} onChange={handleViewStudent_Id} />
<button onClick={getStudentInfo}>학생보기</button>
</div>
<h1>학생추가서비스</h1>
<div>
<div>
<label>ID</label>
<input type="number" placeholder="ID" value={Add_Id} onChange={handleAddStudent_Id} />
</div>
<div>
<label>Name</label>
<input type="text" placeholder="Name" value={Add_Name} onChange={handleAddStudent_Name} />
</div>
<div>
<label>Age</label>
<input type="number" placeholder="Age" value={Add_Age} onChange={handleAddStudent_Age} />
</div>
<button onClick={setStudentInfo}>학생추가하기</button>
</div>
</div>
);
}
export default App;
'BlockChain Developer > Public Blockchain' 카테고리의 다른 글
[Ethereum] GSN (0) | 2022.03.15 |
---|---|
[Web3- 셋팅 문제해결] 최신 React에 Web3 설치하기 (0) | 2022.02.19 |
[Hyperledger Besu] EC2에 Hyperledger IBFT 2.0 (POA) Network 구성하기 (1) | 2021.12.15 |
[IPFS] Nodejs에서 사용하기 (2) | 2021.10.04 |
[Ethereum] Truffle 을 활용한 스마트컨트렉트 개발 (0) | 2021.09.21 |