반응형

Web3 프론트엔드를 구성할 때 가장 중요한 요소 중 하나는 지갑 연결과 스마트 컨트랙트 연동의 안정성과 편의성입니다. 이번 글에서는 @reown/appkitwagmi, ethers, viem을 활용하여 모던한 Web3 dApp 환경을 구성하는 방법을 소개합니다.

특히, Reown에서 제공하는 AppKitButton을 사용해 지갑 연결 UI를 심플하게 구성하고, 스마트 컨트랙트 연동은 wagmi를 활용하여 안전하게 처리합니다.


🧱 1. 프로젝트 구조 개요

bash
복사편집
src/
├── appkit/                 # wagmi + appkit 설정
│   └── config.js
├── contracts/              # 스마트 컨트랙트 ABI 및 주소
│   ├── CounterABI.json
│   └── address.js
├── hooks/                  # wagmi용 커스텀 훅 (읽기/쓰기)
│   ├── useCounterRead.js
│   └── useCounterWrite.js
├── pages/
│   └── CounterPage.jsx     # 실제 스마트 컨트랙트 연동 화면
├── App.jsx                 # 전체 앱 엔트리 포인트

각 폴더 및 파일의 목적은 다음과 같습니다:

폴더/파일 설명
appkit/config.js AppKit + wagmi의 핵심 설정을 담당
contracts/CounterABI.json 스마트 컨트랙트 ABI 정의 파일
contracts/address.js 스마트 컨트랙트 주소 정의
hooks/ 컨트랙트 호출을 담당하는 useContractRead, useContractWrite 활용 커스텀 훅 모음
pages/CounterPage.jsx dApp 메인 화면. 읽기/쓰기 기능 제공
App.jsx AppKitProvider 및 WagmiConfig로 앱 전체를 감싸는 설정

⚙️ 2. AppKit + wagmi 설정

📁 파일: appkit/config.js

import { WagmiAdapter } from '@reown/appkit-adapter-wagmi';
import { mainnet, arbitrum, sepolia } from '@reown/appkit/networks';
import type { AppKitNetwork } from '@reown/appkit/networks';

export const projectId = import.meta.env.VITE_PROJECT_ID || "b56e18d47c72ab683b10814fe9495694"; // localhost 테스트용

export const metadata = {
  name: 'AppKit',
  description: 'AppKit Example',
  url: 'https://reown.com',
  icons: ['https://avatars.githubusercontent.com/u/179229932']
};

export const networks = [mainnet, arbitrum, sepolia] as [AppKitNetwork, ...AppKitNetwork[]];

export const wagmiAdapter = new WagmiAdapter({
  projectId,
  networks,
});

export const config = wagmiAdapter.wagmiConfig;

🧠 설명:

  • projectId: reown/appkit 기반 연결을 위한 ID입니다. Reown Cloud에서 발급.
  • WagmiAdapter: wagmi와 AppKit을 연결하는 설정자 역할.
  • wagmiConfig: 전체 앱에서 사용할 wagmi 설정을 반환합니다.

🧩 3. App.jsx 설정

📁 파일: App.jsx

import { WagmiConfig } from 'wagmi';
import { AppKitProvider } from '@reown/appkit';
import { wagmiAdapter, config, networks, metadata } from './appkit/config';
import CounterPage from './pages/CounterPage';

function App() {
  return (
    <WagmiConfig config={config}>
      <AppKitProvider adapter={wagmiAdapter} metadata={metadata}>
        <CounterPage />
      </AppKitProvider>
    </WagmiConfig>
  );
}

export default App;

🧠 설명:

WagmiConfigAppKitProvider로 전체 애플리케이션에 Web3 상태(Context)를 적용합니다.


🔐 4. 지갑 연결 버튼 - AppKitButton 사용

Reown은 UI 모달과 연결 기능이 포함된 AppKitButton을 제공합니다.

사용 예시 (CounterPage.jsx 내부에서 사용):


<AppKitButton />

🧠 설명:

AppKitButton은 내부적으로 지갑 연결 상태를 감지하고, 연결되지 않았다면 모달을 띄워 사용자가 지갑을 선택하게 유도합니다. 커스텀 버튼 대신 이 컴포넌트를 활용하면 통일성 있는 UI/UX 제공이 가능합니다.


🔍 5. 컨트랙트 읽기 훅 – useContractRead

📁 파일: hooks/useCounterRead.js


import { useContractRead } from 'wagmi';
import { COUNTER_ADDRESS } from '../contracts/address';
import abi from '../contracts/CounterABI.json';

export const useCounterRead = () => {
  const { data, refetch } = useContractRead({
    address: COUNTER_ADDRESS,
    abi,
    functionName: 'getCount',
    watch: true,
  });

  return { count: data?.toString() ?? '0', refetch };
};

🧠 설명:

스마트 컨트랙트의 getCount()를 호출하여 현재 카운터 값을 가져옵니다. watch: true 옵션으로 자동 업데이트도 가능하게 설정되어 있습니다.


✍️ 6. 컨트랙트 쓰기 훅 – useContractWrite

📁 파일: hooks/useCounterWrite.js


import { useContractWrite, useWaitForTransaction } from 'wagmi';
import { COUNTER_ADDRESS } from '../contracts/address';
import abi from '../contracts/CounterABI.json';

export const useCounterWrite = () => {
  const { data, write } = useContractWrite({
    address: COUNTER_ADDRESS,
    abi,
    functionName: 'increment',
  });

  const { isLoading, isSuccess } = useWaitForTransaction({ hash: data?.hash });

  return { write, isLoading, isSuccess };
};

🧠 설명:

  • increment() 함수 호출을 위해 트랜잭션을 생성합니다.
  • useWaitForTransaction() 훅으로 실제 블록체인에 기록될 때까지 대기합니다.

📄 7. 최종 화면 구성 – CounterPage.jsx

📁 파일: pages/CounterPage.jsx


import { useCounterRead } from '../hooks/useCounterRead';
import { useCounterWrite } from '../hooks/useCounterWrite';
import { useEffect } from 'react';

const CounterPage = () => {
  const { count, refetch } = useCounterRead();
  const { write, isLoading, isSuccess } = useCounterWrite();

  useEffect(() => {
    if (isSuccess) refetch();
  }, [isSuccess]);

  return (
    <div className="p-6">
      <AppKitButton />
      <h1 className="text-xl font-bold mt-4">현재 카운트: {count}</h1>
      <buttononClick={() => write?.()}
        disabled={isLoading}
        className="mt-4 px-4 py-2 bg-green-500 text-white rounded"
      >
        {isLoading ? '처리 중...' : '카운트 증가'}
      </button>
    </div>
  );
};

export default CounterPage;

🧠 설명:

  • 버튼 클릭 시 increment() 실행
  • 트랜잭션 완료 후 상태값 재조회
  • AppKitButton으로 지갑 UI 자동 처리

 

반응형

'BlockChain > Web3' 카테고리의 다른 글

[1] Frontend in Web3  (0) 2025.04.06

+ Recent posts