[Blockchain] Next.js와 함께하는 DeFi 서비스 개발 가이드
🏦 Next.js와 함께하는 DeFi (탈중앙화 금융) 서비스 개발 가이드
전통 금융을 혁신하는 블록체인 기반 금융 서비스들을 Next.js와 Solidity로 구축하는 방법을 알아봅니다.
🌟 DeFi란 무엇인가?
**DeFi (Decentralized Finance)**는 블록체인 기술, 특히 스마트 컨트랙트를 활용하여 은행, 증권사 같은 중앙 중개자 없이 금융 서비스를 제공하는 시스템입니다. 이를 통해 누구나 인터넷만 연결되어 있다면 자유롭게 금융 활동에 참여할 수 있습니다.
전통 금융 vs. DeFi
항목 | 전통 금융 (CeFi) | 탈중앙화 금융 (DeFi) |
---|---|---|
중개자 | 은행, 정부, 카드사 | 스마트 컨트랙트 (코드) |
접근성 | KYC/AML, 지역/신용 제한 | 누구나 참여 가능 (무허가성) |
투명성 | 제한적, 내부 정보 | 모든 거래가 공개 (온체인) |
운영 시간 | 영업시간 제한 | 24/7, 365일 중단 없음 |
상호운용성 | 폐쇄적인 시스템 | 레고 블록처럼 조합 가능 |
🔧 핵심 DeFi 서비스와 Solidity 코드 예제
1. 탈중앙화 거래소 (DEX - Decentralized Exchange)
중앙화된 오더북 없이 사용자들이 직접 토큰을 교환하는 플랫폼입니다. AMM (Automated Market Maker) 모델이 주로 사용됩니다.
Uniswap 스타일의 간단한 AMM 구현
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
// 간단한 DEX 컨트랙트
contract SimpleDEX is ReentrancyGuard {
IERC20 public immutable tokenA;
IERC20 public immutable tokenB;
uint256 public reserveA;
uint256 public reserveB;
// 유동성 공급자의 지분을 추적
mapping(address => uint256) public liquidity;
uint256 public totalLiquidity;
// 이벤트 정의
event LiquidityAdded(address indexed provider, uint256 amountA, uint256 amountB);
event TokensSwapped(address indexed trader, address indexed tokenIn, uint256 amountIn, uint256 amountOut);
constructor(address _tokenA, address _tokenB) {
tokenA = IERC20(_tokenA);
tokenB = IERC20(_tokenB);
}
// 유동성 공급 함수
function addLiquidity(uint256 _amountA, uint256 _amountB) external nonReentrant {
// ... (생략)
}
// 토큰 스왑 함수 (A -> B)
function swapAtoB(uint256 _amountAIn) external nonReentrant {
require(_amountAIn > 0, "Input amount must be positive");
uint256 amountBOut = getAmountOut(_amountAIn, reserveA, reserveB);
require(amountBOut > 0, "Insufficient output");
// 토큰 전송
tokenA.transferFrom(msg.sender, address(this), _amountAIn);
tokenB.transfer(msg.sender, amountBOut);
// 유동성 풀 업데이트
reserveA += _amountAIn;
reserveB -= amountBOut;
emit TokensSwapped(msg.sender, address(tokenA), _amountAIn, amountBOut);
}
// 교환될 토큰 양 계산 (x * y = k 모델)
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) public pure returns (uint256) {
uint256 amountInWithFee = amountIn * 997; // 0.3% 수수료 적용
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = (reserveIn * 1000) + amountInWithFee;
return numerator / denominator;
}
}
2. 대출/차용 프로토콜 (Lending/Borrowing)
암호화폐를 담보로 다른 암호화폐를 빌리거나, 자신의 자산을 예치하고 이자를 받을 수 있는 서비스입니다.
// 간단한 대출 프로토콜
contract LendingProtocol is ReentrancyGuard {
// ... (생략)
// 자산 예치 (공급)
function supply(address token, uint256 amount) external nonReentrant {
// ... (생략)
}
// 담보 기반 대출
function borrow(address token, uint256 amount) external nonReentrant {
// ... (생략)
// isAccountHealthy(msg.sender) 와 같은 함수로 담보 비율을 확인해야 함
}
}
3. 스테이킹 (Staking)
특정 토큰을 프로토콜에 예치(락업)하고, 그 대가로 보상을 받는 시스템입니다. 네트워크 보안에 기여하거나, 거버넌스에 참여하는 등의 역할을 합니다.
// 간단한 스테이킹 보상 컨트랙트
contract StakingRewards is ReentrancyGuard {
// ... (생략)
// 스테이킹 함수
function stake(uint256 amount) external nonReentrant {
// ... (생략)
}
// 보상 수령 함수
function getReward() external nonReentrant {
// ... (생략)
}
}
💻 Next.js로 DeFi 대시보드 만들기
Next.js와 ethers.js
또는 thirdweb
같은 라이브러리를 사용하면 스마트 컨트랙트와 상호작용하는 동적인 프론트엔드를 쉽게 구축할 수 있습니다.
// components/DeFiDashboard.tsx
import { useState } from "react";
import { useContract, useAddress, useContractRead, Web3Button } from "@thirdweb-dev/react";
import { ethers } from "ethers";
export default function DeFiDashboard() {
const address = useAddress();
const [stakingAmount, setStakingAmount] = useState("");
// thirdweb SDK를 사용하여 컨트랙트 인스턴스 생성
const { contract: stakingContract } = useContract("YOUR_STAKING_CONTRACT_ADDRESS");
// 컨트랙트의 public 변수 읽기
const { data: stakedBalance } = useContractRead(stakingContract, "balances", [address]);
const { data: earnedRewards } = useContractRead(stakingContract, "earned", [address]);
return (
<div className="max-w-4xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-6">My DeFi Dashboard</h1>
{/* 스테이킹 섹션 */}
<div className="bg-gray-800 rounded-lg p-6 text-white">
<h2 className="text-xl font-semibold mb-4">Staking</h2>
<p>Staked Balance: {stakedBalance ? ethers.utils.formatEther(stakedBalance) : "0"} STK</p>
<p>Rewards: {earnedRewards ? ethers.utils.formatEther(earnedRewards) : "0"} RWD</p>
<input
type="number"
value={stakingAmount}
onChange={(e) => setStakingAmount(e.target.value)}
className="w-full p-2 rounded text-black mt-4"
placeholder="Amount to stake"
/>
{/* Web3Button을 사용하여 트랜잭션 처리 */}
<Web3Button
contractAddress="YOUR_STAKING_CONTRACT_ADDRESS"
action={(contract) => contract.call("stake", [ethers.utils.parseEther(stakingAmount)])}
onSuccess={() => alert("Staking successful!")}
onError={(error) => alert(`Error: ${error.message}`)}
className="w-full bg-blue-500 py-2 rounded mt-2"
>
Stake Now
</Web3Button>
</div>
</div>
);
}
🔒 보안과 위험 관리
DeFi는 혁신적이지만, 스마트 컨트랙트 버그, 오라클 문제, 유동성 위험 등 다양한 위험이 따릅니다. 개발 시 다음 사항을 반드시 고려해야 합니다.
- 재진입 공격 (Reentrancy) 방지: OpenZeppelin의
ReentrancyGuard
사용 또는 Checks-Effects-Interactions 패턴 적용. - 정수 오버플로우/언더플로우: Solidity 0.8.x 이상 버전 사용.
- 접근 제어:
Ownable
등 명확한 권한 관리 패턴 사용. - 코드 감사: 외부 전문 업체를 통한 스마트 컨트랙트 감사.
- 테스트: 테스트 커버리지를 최대한 높여 엣지 케이스 검증.
DeFi는 빠르게 발전하는 분야이며, 높은 수익의 기회와 기술적 도전 과제를 동시에 제공합니다. 이 가이드를 통해 Next.js 개발자들이 DeFi 생태계에 더 쉽게 참여하고, 안전하고 혁신적인 서비스를 만드는 데 도움이 되기를 바랍니다.