블록체인 NFT 토큰게이팅 관련 프로젝트를 진행중 MetaMask를 통해서 로그인을 구현한 방법을 리마인드하고 코드리뷰와 개념정리를 할 겸 블로그 글로 남겨보려고 합니다. web3.js와 MetaMask를 연동하면 사용자가 웹 어플리케이션에 안전하게 로그인할 수 있습니다. 이번 포스팅에서는 MetaMask와 web3.js를 연동하는 방법과 이를 통해서 로그인 기능을 구현하는 전체적인 과정을 살펴보도록 하겠습니다.
사전준비사항
시작하기전에 진행한 코드의 환경과 준비사항은 다음과 같습니다.
1. Next.js 프로젝트 환경
2. MetaMask를 브라우저에 설치.
3. web3.js 자바스크립트 라이브러리를 프로젝트에 설치
MetaMask와 Web3 연동
1. useEffect 훅을 활용해 Web3 초기화 및 MetaMask 연결
MetaMask와 웹 어플리케이션을 연동하기 위해서 Web3 인스턴스를 초기화해야 합니다. 이 단계에서는 useEffect를 사용해 웹페이지 로드 시 MetaMask 지갑을 연결하고, Web3를 초기화합니다.
useEffect(() => {
const initWeb3 = async () => {
if ((window as any).ethereum) {
try {
// MetaMask와 연결
const _web3 = new Web3((window as any).ethereum);
setWeb3(_web3);
// MetaMask 계정 요청
await (window as any).ethereum.request({ method: 'eth_requestAccounts' });
} catch (error) {
console.error('사용자가 계정 접근을 거부했습니다.', error);
}
} else if ((window as any).web3) {
// MetaMask가 이미 연결된 경우
const _web3 = new Web3((window as any).web3.currentProvider);
setWeb3(_web3);
} else {
// MetaMask가 설치되지 않은 경우
console.log('이더리움 브라우저가 감지되지 않았습니다. MetaMask를 설치해주세요!');
}
};
initWeb3();
}, []);
처음 window.ethereum 객체를 사용하여 사용자 브라우저에 MetaMask가 설치되어 있는지 확인 후 eth_requestAccounts 메소드를 통하여 사용자의 지갑 주소 접근을 요청합니다. 만약 여기서 MetaMask가 설치되지 않은 경우, 사용자에게 설치를 요청하는 에러처리를 할 수 있습니다.
2. 블록체인 네트워크 전환
사용자의 MetaMask 지갑이 다른 블록체인 네트워크에 있을 경우, 특정 네트워크로의 전환이 필요합니다. 이를 고려하여 switchNetwork 함수를 이용해서 에러처리를 합니다. 만약 저와 같이 Polygon 네트워크로 전환이 필요하다면 아래와 같이 코드를 작성할 수 있습니다.
const switchNetwork = async (domain: Domain) => {
try {
await (window as any).ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: Web3.utils.toHex(domain.chainId) }],
});
} catch (switchError: any) {
// 네트워크 추가 요청 (네트워크가 MetaMask에 없는 경우)
if (switchError.code === 4902) {
try {
await (window as any).ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: Web3.utils.toHex(domain.chainId),
chainName: 'Polygon Mainnet',
nativeCurrency: {
name: 'MATIC',
symbol: 'MATIC',
decimals: 18,
},
rpcUrls: ['https://rpc-mainnet.maticvigil.com/'],
blockExplorerUrls: ['https://polygonscan.com/'],
},
],
});
} catch (addError) {
console.error('네트워크 추가 중 오류:', addError);
}
} else {
console.error('네트워크 변경 중 오류:', switchError);
}
}
};
wallet_switchEthereumChain 메소드를 사용하여 MetaMask 연동 네트워크를 전환합니다. 만약 사용자의 메타마스크에 해당 네트워크가 등록되어 있지 않은 경우 (code === 4902) 예외 조건으로 wallet_addEthereumChain 메소드를 사용해 네트워크를 추가합니다. 현재 코드는 Polygon 네트워크에 연결되도록 구성했습니다.
네트워크 전환이 필요한 이유
네트워크 전환이 필요한 이유는 블록체인 네트워크마다 고유한 체인 ID와 토큰 경제 구조가 존재합니다. 여기에는 이더리움, 폴리곤 등등 여러 블록체인 네트워크가 있으며, 각 네트워크는 노드, 트랜잭션, 토큰 등을 관리합니다.
본 코드에서는 Polygon 네트워크를 지원하도록 설계했으며, 만약 사용자가 다른 네트워크에 연결되어 있으면, 웹이 작동하지 않으므로, 네트워크 전환이 필요합니다! 물론 사용자가 직접 네트워크를 전환하도록 안내 메세지를 띄워주는 방법도 좋지만, UX 향상을 위해 자동화하도록 코드를 구성했습니다.
3. MetaMask 지갑 서명 요청
이제 지갑 연동까지 완료되었고, 사용자에게 서명 요청을 보내야 합니다. 서명 과정에서는 사용자의 지갑 주소와 네트워크 정보를 가져오고, 이를 통해 본 서비스의 서버로 정보를 전달하여 로그인 정보를 얻습니다.
'지갑 연결' 버튼에 onClickSing 함수를 연결하고, 이 함수가 실행되면 브라우저에 설치되어 있는 MetaMask가 실행되어 사용자에게 서명요청을 보냅니다.
const onClickSign = async () => {
if (!web3) return;
try {
// MetaMask 지갑 연결
await (window as any).ethereum.enable();
const accounts = await web3.eth.getAccounts();
const from = accounts[0];
setAccount(from);
localStorage.setItem('wallet_address', from);
// 로그인에 필요한 데이터를 요청
const challengeRequest = {
wallet_address: from,
chain_id: 137,
};
const challengeResponse = await CreateChallenge(challengeRequest);
const challengeData = challengeResponse.data.typed_data;
setResponseTypedData(challengeData);
const { domain, message, types } = JSON.parse(challengeData);
// 네트워크 전환
await switchNetwork(domain);
// 서명 요청을 위한 데이터 구성
const typedData = {
types: types,
domain: domain,
primaryType: 'AuthRequest',
message: message,
};
const params = [from, typedData];
const method = 'eth_signTypedData_v4';
// 서명 요청
const signature = await (window as any).ethereum.request({
method,
params,
from,
});
setSignature(signature);
} catch (error) {
console.error('서명 처리 중 오류:', error);
}
};
먼저 ethereum.enable을 통해 사용자의 MetaMask 지갑과 연결합니다. accounts 변수를 통해 지갑 주소를 가져와 localStorage에 저장합니다. CreateChallenge 함수로 서버 API를 호출하여 인증 요청 로그인에 필요한 typed_data를 가져옵니다. 네트워크를 전환 후 MetaMask를 통해서 서명을 요청합니다. 만약 서명 요청에 성공하면 해당 서명을 상태에 저장합니다.
이렇게 서명 정보와 지갑 주소를 얻었다면 UI를 통해 화면에 표시하고, 로그인 아이디, 지갑 주소, 서명 데이터 세 정보를 본 프로젝트의 서버 API로 보내주서 로그인을 요청합니다. 이후에 모든 로그인과 동일하게 성공하면 액세스토큰과 로그인 상태를 업데이트합니다.
결론
이번 포스팅에서는 본인이 진행중인 프로젝트에서 web3.js를 이용해서 MetaMask와 연동하여 로그인 기능을 구현하는 과정을 정리해보았습니다. 로그인을 굳이 블록체인으로 구현해야하나? 라는 생각이 들 수 있지만, 이 프로젝트는 블록체인 토큰게이팅을 구현, web3 경험이 목적이므로 그에 대해서는 아직 설득력이 부족하여 블록체인의 필요성에 대해서는 조금 더 공부하고, 고민해보고 판단해야겠다는 생각이 들었습니다.
'프론트엔드 기록 > 자바스크립트' 카테고리의 다른 글
자바스크립트 클로저(Closure)와 동작 원리 (1) | 2024.09.04 |
---|