Skip to content
Published on

개발자를 위한 DeFi와 블록체인 완벽 가이드: 스마트 컨트랙트부터 수익 전략까지

Authors
  • Name
    Twitter

들어가며

2026년 DeFi 시장은 TradFi(전통 금융)와의 경계가 빠르게 허물어지고 있습니다. 토큰화된 증권, 온체인 프라이버시, 기관 투자자의 유입이 가속화되면서 DeFi는 더 이상 니치 시장이 아닙니다. 개발자라면 이 기술을 이해하는 것이 점점 중요해지고 있습니다.

이 글에서는 개발자의 눈으로 DeFi의 핵심 메커니즘을 파헤칩니다.

블록체인 기초 — 개발자 관점

스마트 컨트랙트란?

스마트 컨트랙트는 블록체인에 배포된 프로그램입니다. 한번 배포하면 변경할 수 없고(immutable), 누구나 코드를 검증할 수 있으며, 조건이 충족되면 자동으로 실행됩니다.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

// 간단한 토큰 교환 컨트랙트
contract SimpleSwap {
    mapping(address => uint256) public balanceA;
    mapping(address => uint256) public balanceB;

    uint256 public reserveA;
    uint256 public reserveB;

    // 유동성 공급
    function addLiquidity(uint256 amountA, uint256 amountB) external {
        // 토큰을 컨트랙트로 전송 (실제로는 ERC20 transferFrom)
        reserveA += amountA;
        reserveB += amountB;
    }

    // 토큰 A -> B 교환 (AMM: x * y = k)
    function swapAforB(uint256 amountA) external returns (uint256 amountB) {
        uint256 k = reserveA * reserveB;  // 상수곱 불변량
        uint256 newReserveA = reserveA + amountA;
        uint256 newReserveB = k / newReserveA;
        amountB = reserveB - newReserveB;

        reserveA = newReserveA;
        reserveB = newReserveB;

        return amountB;
    }
}

이더리움 트랜잭션 흐름

사용자 지갑 (MetaMask)
    ↓ 트랜잭션 서명
노드 (Infura/Alchemy)
    ↓ 브로드캐스트
멤풀 (Mempool)
    ↓ 블록 포함 (가스비 경쟁)
블록 확정
    ↓ 상태 변경
스마트 컨트랙트 실행

DeFi 핵심 프로토콜 분석

1. DEX (탈중앙화 거래소) — Uniswap

Uniswap은 AMM(Automated Market Maker) 방식으로, 주문서(Order Book) 없이 유동성 풀에서 자동으로 토큰을 교환합니다.

핵심 공식: x * y = k

# AMM 시뮬레이션
class ConstantProductAMM:
    def __init__(self, reserve_a: float, reserve_b: float, fee: float = 0.003):
        self.reserve_a = reserve_a
        self.reserve_b = reserve_b
        self.fee = fee
        self.k = reserve_a * reserve_b

    def get_price(self) -> float:
        """토큰 A의 가격 (토큰 B 기준)"""
        return self.reserve_b / self.reserve_a

    def swap_a_for_b(self, amount_a: float) -> float:
        """토큰 A를 B로 교환"""
        # 수수료 차감
        amount_a_after_fee = amount_a * (1 - self.fee)

        # x * y = k 공식 적용
        new_reserve_a = self.reserve_a + amount_a_after_fee
        new_reserve_b = self.k / new_reserve_a
        amount_b_out = self.reserve_b - new_reserve_b

        self.reserve_a = new_reserve_a
        self.reserve_b = new_reserve_b
        self.k = self.reserve_a * self.reserve_b

        return amount_b_out

    def calculate_price_impact(self, amount_a: float) -> float:
        """가격 영향도 계산"""
        price_before = self.get_price()
        # 임시 스왑
        amount_b = self.swap_a_for_b(amount_a)
        price_after = self.get_price()
        # 롤백
        self.reserve_a -= amount_a * (1 - self.fee)
        self.reserve_b += amount_b
        self.k = self.reserve_a * self.reserve_b

        return abs(price_after - price_before) / price_before * 100

# 사용 예시
pool = ConstantProductAMM(1_000_000, 1_000_000)  # 1:1 풀
print(f"초기 가격: {pool.get_price():.4f}")
# 초기 가격: 1.0000

amount_out = pool.swap_a_for_b(10_000)
print(f"10,000 A -> {amount_out:.2f} B")
# 10,000 A -> 9,940.18 B (슬리피지 발생)

print(f"교환 후 가격: {pool.get_price():.4f}")
# 교환 후 가격: 1.0202 (가격 변동)

2. Lending Protocol — Aave

# 대출 프로토콜 메커니즘 시뮬레이션
class LendingPool:
    def __init__(self):
        self.deposits = {}      # 사용자별 예치금
        self.borrows = {}       # 사용자별 대출금
        self.total_deposits = 0
        self.total_borrows = 0
        self.base_rate = 0.02   # 기본 이자율 2%
        self.slope1 = 0.04      # 최적 이용률 이하 기울기
        self.slope2 = 0.75      # 최적 이용률 이상 기울기
        self.optimal_utilization = 0.80  # 최적 이용률 80%

    def utilization_rate(self) -> float:
        """이용률 = 총대출 / 총예치"""
        if self.total_deposits == 0:
            return 0
        return self.total_borrows / self.total_deposits

    def borrow_rate(self) -> float:
        """이용률에 따른 변동 대출 이자율"""
        u = self.utilization_rate()
        if u <= self.optimal_utilization:
            return self.base_rate + (u / self.optimal_utilization) * self.slope1
        else:
            excess = (u - self.optimal_utilization) / (1 - self.optimal_utilization)
            return self.base_rate + self.slope1 + excess * self.slope2

    def supply_rate(self) -> float:
        """예치 이자율 = 대출이자율 * 이용률"""
        return self.borrow_rate() * self.utilization_rate()

    def deposit(self, user: str, amount: float):
        self.deposits[user] = self.deposits.get(user, 0) + amount
        self.total_deposits += amount

    def borrow(self, user: str, amount: float, collateral_value: float):
        # LTV(Loan-to-Value) 체크
        max_borrow = collateral_value * 0.75  # 75% LTV
        current_borrow = self.borrows.get(user, 0)
        if current_borrow + amount > max_borrow:
            raise ValueError(f"LTV 초과: 최대 {max_borrow}, 현재 {current_borrow}")

        self.borrows[user] = current_borrow + amount
        self.total_borrows += amount

# 시뮬레이션
pool = LendingPool()
pool.deposit("Alice", 1_000_000)  # 100만 달러 예치
pool.borrow("Bob", 600_000, 1_000_000)  # 60만 달러 대출

print(f"이용률: {pool.utilization_rate():.1%}")
# 이용률: 60.0%
print(f"대출 이자율: {pool.borrow_rate():.2%}")
# 대출 이자율: 5.00%
print(f"예치 이자율: {pool.supply_rate():.2%}")
# 예치 이자율: 3.00%

3. Yield Farming

# Yield Farming 수익 계산
class YieldFarm:
    def __init__(self, pool_name: str, tvl: float, daily_rewards: float):
        self.pool_name = pool_name
        self.tvl = tvl  # Total Value Locked
        self.daily_rewards = daily_rewards

    def apr(self) -> float:
        """연간 수익률 (단리)"""
        return (self.daily_rewards * 365) / self.tvl * 100

    def apy(self, compounds_per_year: int = 365) -> float:
        """연간 수익률 (복리)"""
        daily_rate = self.daily_rewards / self.tvl
        return ((1 + daily_rate) ** compounds_per_year - 1) * 100

    def impermanent_loss(self, price_ratio: float) -> float:
        """비영구적 손실 계산
        price_ratio: 현재 가격 / 초기 가격
        """
        il = 2 * (price_ratio ** 0.5) / (1 + price_ratio) - 1
        return il * 100  # 퍼센트

# ETH-USDC 풀 예시
farm = YieldFarm("ETH-USDC", tvl=10_000_000, daily_rewards=5_000)
print(f"APR: {farm.apr():.1f}%")
# APR: 18.2%
print(f"APY: {farm.apy():.1f}%")
# APY: 20.0%

# ETH 가격이 2배가 되면
il = farm.impermanent_loss(2.0)
print(f"비영구적 손실 (가격 2배): {il:.2f}%")
# 비영구적 손실 (가격 2배): -5.72%

DeFi 리스크 분석

1. 스마트 컨트랙트 리스크

// 취약한 코드 예시 - 재진입 공격 (reentrancy)
// 이 패턴은 절대 사용하지 마세요!
contract Vulnerable {
    mapping(address => uint256) public balances;

    // 위험: 외부 호출 후 상태 변경
    function withdraw() external {
        uint256 amount = balances[msg.sender];
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
        balances[msg.sender] = 0;  // 상태 변경이 외부 호출 뒤에!
    }
}

// 안전한 패턴: Checks-Effects-Interactions
contract Safe {
    mapping(address => uint256) public balances;

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        balances[msg.sender] = 0;  // 상태 변경 먼저!
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
    }
}

2. 주요 리스크 요약

리스크설명대응
스마트 컨트랙트 버그코드 취약점으로 자산 탈취감사(audit) 받은 프로토콜만 사용
비영구적 손실유동성 공급 시 가격 변동으로 손실상관 자산 쌍 선택
오라클 조작가격 피드 조작으로 부당 청산다중 오라클 사용 프로토콜 선택
규제 리스크국가별 규제 변화규제 동향 모니터링
러그풀개발자가 자금 탈취TVL, 팀 이력, 코드 검증

개발자를 위한 DeFi 참여 전략

보수적 전략: Stablecoin 예치

리스크: ★☆☆☆☆
예상 수익: 3-8% APY

방법:
1. USDC/USDTAave에 예치
2. 대출 이자 수취
3. 추가: Aave 토큰 리워드

주의: 스테이블코인도 디페깅 리스크 존재

중간 전략: 상관 자산 유동성 공급

리스크: ★★★☆☆
예상 수익: 10-25% APY

방법:
1. ETH-stETH 등 상관 자산 쌍의 유동성 풀에 공급
2. 거래 수수료 + 리워드 토큰 수취
3. 비영구적 손실이 작음 (가격이 비슷하게 움직이므로)

주의: 스마트 컨트랙트 리스크는 여전히 존재

적극적 전략: 레버리지 Yield Farming

리스크: ★★★★★
예상 수익: 30-100%+ APY

방법:
1. 자산 예치
2. 예치 자산을 담보로 대출
3. 대출금으로 다시 유동성 공급 (레버리지)
4. 수수료 + 리워드로 이자 상회

주의: 청산 리스크, 복잡한 포지션 관리 필요

Web3 개발 도구

# Hardhat - 스마트 컨트랙트 개발 환경
npm install --save-dev hardhat
npx hardhat init

# Foundry - Rust 기반 빠른 개발 도구
curl -L https://foundry.paradigm.xyz | bash
foundryup

# ethers.js - 프론트엔드 연동
npm install ethers
// ethers.js로 DeFi 프로토콜 조회
import { ethers } from 'ethers'

const provider = new ethers.JsonRpcProvider('https://eth.llamarpc.com')

// Uniswap V3 풀 가격 조회
const poolAddress = '0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8' // USDC/ETH
const poolABI = [
  'function slot0() view returns (uint160, int24, uint16, uint16, uint16, uint8, bool)',
]
const pool = new ethers.Contract(poolAddress, poolABI, provider)

const [sqrtPriceX96] = await pool.slot0()
const price = (Number(sqrtPriceX96) / 2 ** 96) ** 2 * 1e12 // 가격 계산
console.log(`ETH/USDC: $${price.toFixed(2)}`)

세금과 규제

한국에서 가상자산 과세는 2027년으로 유예되었지만, 미리 준비하는 것이 좋습니다:

  • 매매 차익: 250만원 초과분에 22% 과세 (예정)
  • DeFi 수익: 기타소득으로 분류될 가능성
  • 기록 관리: 모든 트랜잭션의 원화 환산 가치 기록 필수
# 트랜잭션 기록 관리 예시
import csv
from datetime import datetime

def log_transaction(tx_type, asset, amount, usd_value, tx_hash):
    with open('defi_transactions.csv', 'a', newline='') as f:
        writer = csv.writer(f)
        writer.writerow([
            datetime.now().isoformat(),
            tx_type,  # deposit, withdraw, swap, harvest
            asset,
            amount,
            usd_value,
            tx_hash,
        ])

정리

DeFi는 개발자에게 금융 시스템의 내부 구조를 코드로 이해할 수 있는 독특한 기회를 제공합니다:

  • AMM의 수학: x * y = k 공식으로 주문서 없는 거래 실현
  • 대출 프로토콜: 이용률 기반 변동 이자율로 수요/공급 균형
  • Yield Farming: 복리 효과 + 리워드 토큰으로 수익 극대화
  • 리스크 관리: 스마트 컨트랙트 감사, 비영구적 손실 이해 필수

✅ 퀴즈: DeFi 이해도 점검 (8문제)

Q1. AMM의 x * y = k 공식에서 k가 의미하는 것은?

유동성 풀의 두 토큰 수량의 곱으로, 교환 전후에 일정하게 유지되는 상수곱 불변량입니다.

Q2. 슬리피지(Slippage)는 왜 발생하나요?

AMM에서 거래량이 클수록 풀의 비율이 크게 변하기 때문에 예상 교환 비율과 실제 교환 비율의 차이가 발생합니다.

Q3. 비영구적 손실(Impermanent Loss)이란?

유동성 공급 시 토큰 가격이 변하면, 단순 보유했을 때보다 자산 가치가 줄어드는 현상입니다.

Q4. Lending Protocol에서 이용률이 높아지면?

대출 이자율이 급격히 상승하여 대출 수요를 억제하고 예치 수요를 유인합니다.

Q5. 재진입 공격(Reentrancy)을 방지하는 패턴은?

Checks-Effects-Interactions 패턴: 외부 호출 전에 상태를 먼저 변경합니다.

Q6. APR과 APY의 차이는?

APR은 단리 수익률, APY는 복리 수익률입니다. 복리 빈도가 높을수록 APY가 APR보다 높아집니다.

Q7. 러그풀(Rug Pull)을 식별하는 방법은?

감사(audit) 여부, TVL 규모, 팀 신원 공개 여부, 코드 오픈소스 여부, 유동성 잠금(lock) 기간을 확인합니다.

Q8. 한국에서 가상자산 과세 시 기록해야 할 정보는?

모든 트랜잭션의 날짜, 유형, 자산, 수량, 원화 환산 가치, 트랜잭션 해시를 기록해야 합니다.