[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 구현
solidity// 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)
암호화폐를 담보로 다른 암호화폐를 빌리거나, 자신의 자산을 예치하고 이자를 받을 수 있는 서비스입니다.
solidity// 간단한 대출 프로토콜 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)
특정 토큰을 프로토콜에 예치(락업)하고, 그 대가로 보상을 받는 시스템입니다. 네트워크 보안에 기여하거나, 거버넌스에 참여하는 등의 역할을 합니다.
solidity// 간단한 스테이킹 보상 컨트랙트 contract StakingRewards is ReentrancyGuard { // ... (생략) // 스테이킹 함수 function stake(uint256 amount) external nonReentrant { // ... (생략) } // 보상 수령 함수 function getReward() external nonReentrant { // ... (생략) } }
💻 Next.js로 DeFi 대시보드 만들기
Next.js와 ethers.js 또는 thirdweb 같은 라이브러리를 사용하면 스마트 컨트랙트와 상호작용하는 동적인 프론트엔드를 쉽게 구축할 수 있습니다.
typescript// 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 생태계에 더 쉽게 참여하고, 안전하고 혁신적인 서비스를 만드는 데 도움이 되기를 바랍니다.