들어가기 전에,
클레이튼 기반의 환경에서 지갑 연동을 구현하기 위하여 스터디 및 작업한 내용을 정리하였습니다.
이더리움 혹은 그 외의 환경이 궁금하신 분들께는 도움이 되지 않을 수 있습니다.
블록체인과 스마트 컨트랙트를 막 배워가는 입장에서 가장 어려웠던 점은
기본적인 FE-BE 구조와 어떤 차이점이 있고 어떤 방식으로 코드를 구성해야할지 모른다는 것이었다.
이에 대해 얕지만 쉽게 설명할 수 있는 방법이 있어 정리해본다.
REST API
FE
<-- API
를 통해 소통 --> BE
블록체인
FE
<-- ABI
를 통해 소통 --> Smart Contract
--> BlockChain
FE는 ABI
를 통해 스마트 컨트랙트와 통신하며, 블록체인에 영향을 미치게 된다.
일반적으로 스마트 컨트랙트는 Solidity
라는 언어를 통해 구현하는데,
그 결과로 구현된 스마트 컨트랙트 파일(json) 내에 ABI
가 포함되어 있다.
마치 계약의 양 측 당사자가 동일한 계약서의 사본을 각각 가지고 있는 것과 같이,
FE에서도 동일한 ABI
json 파일을 프로젝트 내에 저장해두고 스마트 컨트랙트를 호출할 때 함께 전달하여 통신을 시도하게 된다.
여기까지 이해가 되었다면 다음 단계!
Klaytn 네트워크에서 스마트 컨트랙트를 실행하기 위해서 caver-js
를 활용하였다.
[ caver-js ]
개발자가 HTTP 또는 웹소켓 연결을 사용하여 Klaytn 노드와 상호작용할 수 있도록 하는 자바스크립트 API 라이브러리
[ Smart Contract 실행을 위한 단계! ]
new Caver
정의ABI
를 활용하여 JSON 인터페이스 오브젝트에 정의된 모든 메소드 및 이벤트로 새 컨트랙트 인스턴스 생성const caver = new Caver(new Caver.providers.HttpProvider(URL, option))
const caver = new Caver('https://')
const caver = new Caver(window.klaytn)
new Caver
를 정의하는 방법은 위와 같이 여러가지인데,
위의 두 가지는 HTTP provider를 통해 provider를 정의하는 방법이며
마지막 방법은 브라우저 상에서 카이카스 지갑의 provider를 이용할 때 사용하는 방법이라 한다.
나는 해당 코드를 hook으로 만들어두고 필요할 때마다 간편하게 사용하는 편.
// Custom Hook
const useCaver = () => {
const caver = new Caver();
return caver;
}
export { useCaver }
// 임포트 시
import { useCaver } from '/경로'
const caver = useCaver();
new caver.klay.Contract(jsonInterface [, address] [, options])
*jsonInterface : 원하는 메소드를 보유한 전체 ABI
*address : contract address
caver-js의 해당 객체를 통해 ABI에 정의된 모든 메소드와 이벤트를 가진 컨트랙트 인스턴스(복제본)를 생성할 수 있다. 즉 이 작업을 통해 드디어 우리는 스마트 컨트랙트 내에 구현되어 있는 메소드(함수)를 사용할 수 있다는 이야기!!
+) ABI를 잘 살펴보면 name
이라는 키값으로 우리가 불러올 메소드명이 정리된 걸 볼 수 있다!
컨트랙트 인스턴스가 준비 되었다면, 이제는 그 중 원하는 메소드를 실행할 차례!
myContract.methods.methodName([param1 [, param2 [, ...]]])
myContract.methods['methodName']([param1 [, param2 [, ...]]])
*myContract : 2단계에서 생성한 컨트랙트 인스턴스를 변수에 저장한 값
*methodName : 실행을 원하는 메소드의 이름
*param : 메소드별 상이
위와 같이 메소드에 대한 트랜잭션 객체를 생성했다면,
뒤에 몇가지를 덧붙이는 것으로 원하는 방식으로 실행이 가능하다
myContract.methods.methodName().call(options [, callback])
myContract.methods.methodName().send(options [, callback])
call()
은 트랜잭션을 보내지 않고 메소드를 Klaytn 가상머신에서 실행한다.
데이터를 읽어오기만 하면될 때 주로 사용한다.
send()
는 Klaytn으로 트랜잭션을 전송하고 메소드를 실행한다. (스마트 컨트랙트 상태 변경 가능)
또한 이 호출은 옵션에 기본적으로 from / gas
를 포함하는데,
from
은 트랜잭션을 보낼 송신자 주소(트랜잭션 실행에 따른 수수료가 빠져나갈 주소)이며
gas
는 해당 트랜잭션을 위한 최대 가스값(limit)을 지정한다. gas
는 최댓값으로 그 안의 범위 내에서 수수료가 나가니 두려워하지 말 것.
이 외에도 다수의 방법이 있으니 공식문서 참고!
이 모든 방법을 이리저리 연구해서 실제로는 아래와 같이 활용 중이다.
// app.js
function App () {
const { onAdd } = useAddData(필요한 params 전달)
const addData = () => {
const res = onAdd();
if(!res){
alert('Failed!')
} else {
alert('Success!')
}
}
return (
<div>
<button onClick={addData}>버튼</button>
</div>
)
}
// useAddData.js
export const useAddData = () => {
let contract = getSmartContractPool(컨트랙트 주소, ABI)
const handleAdd = useCallback(async () => {
try {
const tx = await addData(contract, 송신자 주소, 필요한 params)
return tx;
} catch (err) {
console.error('contract err', err)
return false;
}
}, [의존하는 것 전부 기입]);
return { onAdd: handleAdd }
}
// getSmartContractPool.js
import { useCaver } from '/경로'
const caver = useCaver();
export const getSmartContractPool = (컨트랙트 주소, ABI) => {
const contract = new cav.klay.Contract(ABI, 컨트랙트 주소); //2단계
return contract;
}
// addData.js
export const addData = async(contract, 송신자 주소, params) => {
return contract.methods.addData(params).send({
from: 송신자 주소,
gas: 3000000,
}) //3단계
}
사실 전체 프로젝트를 지갑 연동과 함께 사용 중이라, 위에 작성한 전체 이전에 지갑 연결이 우선되어야 한다. 또한 가장 기본인 kaikas 연결을 기준으로 작성한 코드라 다른 지갑과 연결이 되면 코드가 달라질 수 있다. 이건 다음 포스트에서 다루기로!
한가지 질문이 있습니당. custom hook을 만들어주신다고하셨는데.. 저거는 그냥 서비스 함수라던지 유틸함수가 아닐까용??