Web3 통합
이 페이지에서는 지갑 연결, 트랜잭션, 온체인 이벤트 처리를 포함하여 블록체인 기능을 Redux 및 RTK Query와 통합하는 패턴을 다룹니다.
핵심 원칙: Web3 객체는 Redux 밖에 두기
권장 아키텍처
Web3 컨텍스트 (Redux 밖)
// src/contexts/Web3Context.tsx
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { ethers } from 'ethers';
interface Web3ContextValue {
provider: ethers.providers.Web3Provider | null;
signer: ethers.Signer | null;
account: string | null;
chainId: number | null;
connect: () => Promise<void>;
disconnect: () => void;
}
const Web3Context = createContext<Web3ContextValue | undefined>(undefined);
export function Web3Provider({ children }: { children: ReactNode }) {
const [provider, setProvider] = useState<ethers.providers.Web3Provider | null>(null);
const [signer, setSigner] = useState<ethers.Signer | null>(null);
const [account, setAccount] = useState<string | null>(null);
const [chainId, setChainId] = useState<number | null>(null);
const connect = async () => {
if (!window.ethereum) {
throw new Error('MetaMask가 설치되어 있지 않습니다');
}
const web3Provider = new ethers.providers.Web3Provider(window.ethereum);
const accounts = await web3Provider.send('eth_requestAccounts', []);
const network = await web3Provider.getNetwork();
const signer = web3Provider.getSigner();
setProvider(web3Provider);
setSigner(signer);
setAccount(accounts[0]);
setChainId(network.chainId);
};
const disconnect = () => {
setProvider(null);
setSigner(null);
setAccount(null);
setChainId(null);
};
// 계정 변경을 감지
useEffect(() => {
if (!window.ethereum) return;
const handleAccountsChanged = (accounts: string[]) => {
if (accounts.length === 0) {
disconnect();
} else {
setAccount(accounts[0]);
}
};
const handleChainChanged = (chainIdHex: string) => {
const newChainId = parseInt(chainIdHex, 16);
setChainId(newChainId);
};
window.ethereum.on('accountsChanged', handleAccountsChanged);
window.ethereum.on('chainChanged', handleChainChanged);
return () => {
window.ethereum?.removeListener('accountsChanged', handleAccountsChanged);
window.ethereum?.removeListener('chainChanged', handleChainChanged);
};
}, []);
return (
<Web3Context.Provider
value={{ provider, signer, account, chainId, connect, disconnect }}
>
{children}
</Web3Context.Provider>
);
}
export function useWeb3() {
const context = useContext(Web3Context);
if (!context) {
throw new Error('useWeb3는 Web3Provider 내부에서 사용해야 합니다');
}
return context;
}Web3 상태를 위한 Redux Slice
Redux에는 직렬화 가능한 Web3 상태만 저장하세요:
컨텍스트와 Redux 동기화
Web3 컨텍스트를 Redux에 연결하세요:
트랜잭션 생명주기
트랜잭션 전송
컴포넌트에서 사용하기
온체인 이벤트 수신
Web3 데이터와 RTK Query
블록체인 데이터를 사용하는 엔드포인트를 만드세요:
캐시 무효화 전략
네트워크 변경 시
계정 변경 시
트랜잭션 확인 후
모범 사례
1. 관심사 분리
2. 트랜잭션 상태 처리
3. 롤백이 있는 낙관적 업데이트
4. 이벤트 리스너 정리
다음 단계
마지막 업데이트