🕸️ Web3에서 프론트엔드의 역할은 무엇일까?
Web3가 점차 주목받으면서 프론트엔드 개발자에게도 새로운 흐름이 요구되고 있습니다. 구조는 Web2와 크게 다르지 않지만, 기술적 환경이 변화하면서 프론트엔드가 맡는 역할도 달라지고 있죠. 이번 글에서는 Web2와 Web3의 차이점부터, 프론트엔드의 핵심 역할과 실제 코드 예시까지 정리해보겠습니다.
1. Web2 vs Web3
Web2와 Web3는 구조적으로 유사해 보일 수 있지만, 사용하는 기술 환경이 완전히 다르기 때문에 처음에는 진입 장벽을 느끼기 쉽습니다. 이를 아래 표로 정리하면 다음과 같습니다.
📊 Web2와 Web3 비교
구분 | Web2 프론트엔드 | Web3 프론트엔드 |
---|---|---|
백엔드 | REST API 서버 | 스마트 컨트랙트 |
인증 방식 | JWT, 세션 등 | 지갑 로그인 (Metamask 등) |
데이터 저장 | DB (MySQL 등) | 블록체인, IPFS 등 |
주요 연동 | API, SDK | Web3 라이브러리 (ethers.js, web3.js 등) |
위 표를 보면 Web3에서도 프론트엔드-백엔드 구조는 여전하지만, 각 구성 요소의 성격이 완전히 다름을 알 수 있습니다. 특히 다양한 블록체인 네트워크(Ethereum, Solana, Polygon 등)의 등장으로 인해 각 생태계에 맞춘 개발이 중요해졌습니다.
2. Web3 프론트엔드 구성 요소 자세히 보기
✅ 스마트 컨트랙트
Web3에서 스마트 컨트랙트는 Web2의 서버(Service layer) 역할을 합니다. 비즈니스 로직을 담고 있으며, 블록체인 위에서 실행됩니다. Solidity 언어를 사용하며, 클래스 구조와 유사합니다.
📌 간단한 스마트 컨트랙트 예시 (Solidity)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Counter {
uint256 public count;
function increment() public {
count += 1;
}
function getCount() public view returns (uint256) {
return count;
}
}
count
: 상태 변수로 저장됨 (블록체인 상 저장됨)increment()
: 트랜잭션을 발생시켜 값을 1 증가시킴getCount()
: 현재 값을 조회하는 view 함수 (가스비 X)
✅ 지갑 로그인
Web2에서는 이메일·소셜 로그인 등으로 사용자를 인증했다면, Web3에서는 지갑을 통해 서명 기반 로그인 방식이 일반적입니다.
- 대표 지갑: Metamask, WalletConnect, Kaikas 등
- 최근에는 Google/Apple 계정 기반으로 지갑을 만들 수 있는 소셜 로그인 지갑도 많이 생겼습니다.
✅ 블록체인
Web2에서의 DB 역할을 합니다. 스마트 컨트랙트에서 실행된 데이터는 블록체인에 영구 저장되며, 누구나 열람 가능하고 위조가 불가능합니다.
✅ Web3 라이브러리
Web2에서 REST API를 호출했다면, Web3에서는 ethers.js
나 web3.js
를 통해 스마트 컨트랙트와 직접 상호작용합니다.
📌 ethers.js 예시: 프론트에서 스마트 컨트랙트와 통신
import { ethers } from "ethers";
// 1. 컨트랙트 주소와 ABI
const contractAddress = "0x1234567890abcdef1234567890abcdef12345678";
const contractABI = [
"function getCount() view returns (uint256)",
"function increment()"
];
// 2. provider와 signer 준비 (지갑 연결 필요)
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
// 3. 컨트랙트 인스턴스 생성
const counterContract = new ethers.Contract(contractAddress, contractABI, signer);
// 4. 함수 호출
async function getCount() {
const count = await counterContract.getCount();
console.log("현재 count:", count.toString());
}
async function incrementCount() {
const tx = await counterContract.increment(); // 트랜잭션 발생
await tx.wait(); // 블록에 기록될 때까지 대기
console.log("카운트 증가 완료");
}
🧠 getCount()
는 읽기 함수로 가스비가 들지 않지만, increment()
는 쓰기 함수로 서명과 트랜잭션 처리가 필요합니다.
3. Ethereum, Solana
구분 | Ethereum | Solana |
---|---|---|
스마트 컨트랙트 언어 | Solidity | Rust (Anchor 프레임워크 사용) |
주요 라이브러리 | ethers.js , web3.js |
@solana/web3.js , @project-serum/anchor |
트랜잭션 처리 | 느리지만 탈중앙성 강함 (PoS/PoW → 현재 PoS) | 빠른 처리 속도 (PoH + PoS) |
지갑 | MetaMask, WalletConnect 등 EVM 기반 | Phantom, Solflare 등 Solana 전용 지갑 |
ABI 사용 여부 | 필수 (프론트에서 ABI로 함수 접근) | X (Anchor IDL 또는 직접 구조화 필요) |
컨트랙트 주소 접근 | 단일 주소 사용 | 프로그램 ID, 계정 주소 구분 필요 |
읽기/쓰기 구분 | 명확 (view/pure vs transaction) | 계정 상태 조회와 서명 구조가 복잡함 |
✅ 예시 비교: 이더리움 vs 솔라나에서 getCount
호출
🔹 이더리움 (ethers.js)
const contract = new ethers.Contract(contractAddress, abi, signer);
const count = await contract.getCount(); // 간단한 함수 호출
🔹 솔라나 (web3.js + Anchor IDL)
const provider = new Anchor.AnchorProvider(connection, wallet, {});
const program = new Anchor.Program(idl, programId, provider);
const account = await program.account.counter.fetch(counterAccountAddress);
console.log("현재 count:", account.count.toNumber());
📌 Solana는 ‘프로그램’과 ‘계정’을 분리하여 접근하며, 태는 ‘계정’에 저장된다.
4. Web3 프론트엔드 프로젝트 폴더 구조
처음 웹 3 프로젝트를 빌딩하다보면 프로젝트 구조를 어떻게 세워야할지 난감한 때가 오게된다. 다음과 같이 폴더를 정리하면 좀 더 쉽게 정리가 가능하다.
📦 your-dapp/
├── 📁 public/
│ └── favicon.ico
├── 📁 src/
│ ├── 📁 abi/ # 스마트 컨트랙트 ABI JSON 파일 보관
│ ├── 📁 assets/ # 이미지, 폰트 등 정적 리소스
│ ├── 📁 components/ # 공통 UI 컴포넌트 (Button, Navbar 등)
│ ├── 📁 contracts/ # 스마트 컨트랙트 주소 등 관련 정보
│ ├── 📁 hooks/ # 커스텀 훅 (useWallet, useContract 등)
│ ├── 📁 pages/ # 각 페이지 단위 컴포넌트
│ ├── 📁 utils/ # 블록체인 유틸 함수 (주소 포맷팅, 트랜잭션 처리 등)
│ ├── 📁 contexts/ # 상태 공유용 Context (예: WalletContext)
│ ├── 📁 config/ # 네트워크 설정, 환경변수 관리
│ ├── 📁 styles/ # 전역 스타일, Tailwind, SCSS 등
│ ├── App.jsx # 앱 라우팅 설정
│ ├── index.jsx # 진입점 (ReactDOM 렌더링)
├── .env # 환경 변수 (RPC URL, Contract 주소 등)
├── package.json
└── README.md
🔍 각 폴더 설명
폴더/파일 | 설명 |
---|---|
abi/ |
컴파일된 스마트 컨트랙트 ABI를 JSON으로 저장해서 불러오기 용이 |
contracts/ |
배포된 컨트랙트 주소, 네트워크 ID 등 정리 |
hooks/ |
useWallet() , useContract() , useBalance() 등의 커스텀 훅으로 Web3 로직 모듈화 |
contexts/ |
지갑 상태, 로그인 여부, 현재 네트워크 등을 Context API로 전역 공유 |
utils/ |
트랜잭션 헬퍼 함수, 토큰 포맷팅 등 유틸 함수 |
config/ |
지원 네트워크, RPC URL, 체인 ID 등 환경설정 모음 |
.env |
프로젝트 환경에 따라 컨트랙트 주소, RPC URL을 바꿀 수 있도록 외부에서 주입 |
다음에는 프론트엔드와 지갑 및 컨트랙트 연결 구조를 집중적으로 알아보겠습니다.