Ver3.0 ๐ ํ์ด์ฌ๊ณผ web3.py๋ก DApp ๊ฑฐ๋๋ด์ญ ์๊ฐํ ํ๋ก๊ทธ๋จ ๊ตฌํ

๐ ํ์ด์ฌ๊ณผ web3.py๋ก DApp ๊ฑฐ๋๋ด์ญ ์๊ฐํ ํ๋ก๊ทธ๋จ ๊ตฌํ
๋ธ๋ก์ฒด์ธ ๋ฐ์ดํฐ๋ฅผ ๋์ผ๋ก ๋ณด๋ ๋ง๋ฒ, ์ง๊ธ ์์ํฉ๋๋ค! โจ
๐ฌ ์ผ, ๋ธ๋ก์ฒด์ธ ๋ฐ์ดํฐ ๋ณด๋ฉด์ ๋จธ๋ฆฌ ์ํ์ง ์์? 16์ง์๋ก ๊ฐ๋ํ ํธ๋์ญ์
ํด์, ๋์์ด ์ด์ด์ง๋ ์ซ์๋ค... ์ด๊ฑฐ ์ฌ๋์ด ๋ณผ ์ ์๋ ํํ๊ฐ ์๋์์! ๊ทธ๋์ ์ค๋์ ํ์ด์ฌ๊ณผ web3.py๋ฅผ ํ์ฉํด์ DApp(ํ์ค์ํ ์ ํ๋ฆฌ์ผ์ด์
)์ ๊ฑฐ๋๋ด์ญ์ ์์๊ณ ์ง๊ด์ ์ธ ์ฐจํธ์ ๊ทธ๋ํ๋ก ๋ง๋ค์ด๋ณด๋ ๋ฐฉ๋ฒ์ ์๋ ค์ค๊ฒ. ๐จ
์ด ๊ธ์ ๋ค ์ฝ๊ณ ๋๋ฉด, ๋๋ ๋ธ๋ก์ฒด์ธ ๋ฐ์ดํฐ ๋ถ์๊ฐ์ฒ๋ผ ๋ฉ์ง ๋์๋ณด๋๋ฅผ ๋ง๋ค ์ ์์ ๊ฑฐ์ผ!
๐ฏ ์ ๊ฑฐ๋๋ด์ญ ์๊ฐํ๊ฐ ํ์ํ ๊น?
๋ธ๋ก์ฒด์ธ์ ํฌ๋ช
ํ๋ค๊ณ ํ์ง๋ง, ์ค์ ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ฉด ์ ํ ํฌ๋ช
ํ์ง ์์ ๋ณด์ฌ. ๐
์ด๋์ค์บ(Etherscan) ๊ฐ์ ๋ธ๋ก ์ต์คํ๋ก๋ฌ๋ฅผ ๋ด๋ ์ซ์์ ํด์๊ฐ ์ฒ์ง์์? ๊ทธ๋์ ์๊ฐํ๊ฐ ํ์ํ ๊ฑฐ์ผ.
์๊ฐํ๋ฅผ ํ๋ฉด ์ด๋ฐ ๊ฒ ๊ฐ๋ฅํด์ ธ:
๐ ๊ฑฐ๋ ํจํด ํ์
: ํน์ ์๊ฐ๋์ ๊ฑฐ๋๊ฐ ๋ชฐ๋ฆฌ๋์ง, ์ด๋ค ์ฃผ์๊ฐ ํ๋ฐํ์ง ํ๋์ ํ์ธ
๐ฐ ์๊ธ ํ๋ฆ ์ถ์ : ํฐ ๊ธ์ก์ด ์ด๋๋ก ์ด๋ํ๋์ง ์๊ฐ์ ์ผ๋ก ์ถ์
๐ ์ด์ ๊ฑฐ๋ ํ์ง: ํ์์ ๋ค๋ฅธ ํจํด์ ๋น ๋ฅด๊ฒ ๋ฐ๊ฒฌ
๐ ๋น์ฆ๋์ค ์ธ์ฌ์ดํธ: DApp์ ์ฌ์ฉ๋, ์ธ๊ธฐ ๊ธฐ๋ฅ ๋ฑ์ ๋ฐ์ดํฐ๋ก ๋ถ์
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ๋ธ๋ก์ฒด์ธ ๊ธฐ๋ฐ ์๋น์ค๋ฅผ ๊ตฌ์ถํ๋ค๋ฉด, ์ด๋ฐ ์๊ฐํ ๋๊ตฌ๊ฐ ์์ฒญ ์ ์ฉํ ๊ฑฐ์ผ. ์ฌ์ฉ์๋ค์ ๊ฑฐ๋ ํจํด์ ๋ถ์ํด์ ๋ ๋์ ์๋น์ค๋ฅผ ์ ๊ณตํ ์ ์๊ฑฐ๋ ! ๐ฏ
๐ ๏ธ ๊ฐ๋ฐ ํ๊ฒฝ ์ค๋นํ๊ธฐ
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์์ํด๋ณผ๊น? ๋จผ์ ํ์ํ ๋๊ตฌ๋ค์ ์ค์นํด์ผ ํด. ๊ฑฑ์ ๋ง, ํ๋ํ๋ ์ฐจ๊ทผ์ฐจ๊ทผ ์๋ ค์ค๊ฒ! ๐
1๏ธโฃ Python ์ค์น ํ์ธ
Python 3.8 ์ด์์ด ํ์ํด. ํฐ๋ฏธ๋์์ ํ์ธํด๋ณด์:
python --version
# ๋๋
python3 --version
๋ง์ฝ ์ค์น๋์ด ์์ง ์๋ค๋ฉด python.org์์ ๋ค์ด๋ก๋ํ๋ฉด ๋ผ!
2๏ธโฃ ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
์ด์ ํ์ํ ํจํค์ง๋ค์ ์ค์นํ ์ฐจ๋ก์ผ. ํฐ๋ฏธ๋์ ์ด๊ณ ๋ค์ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํด:
pip install web3
pip install pandas
pip install matplotlib
pip install seaborn
pip install plotly
pip install python-dotenv
๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ญ ํ๋ ๊ฑด์ง ๊ถ๊ธํ์ง? ๊ฐ๋จํ ์ค๋ช ํด์ค๊ฒ:
๐ web3.py: ์ด๋๋ฆฌ์ ๋ธ๋ก์ฒด์ธ๊ณผ ํต์ ํ๋ ํต์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๐ pandas: ๋ฐ์ดํฐ ๋ถ์๊ณผ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ต๊ฐ ๋๊ตฌ
๐ matplotlib: ๊ธฐ๋ณธ์ ์ธ ์ฐจํธ๋ฅผ ๊ทธ๋ฆฌ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๐จ seaborn: matplotlib์ ๋ ์์๊ฒ ๋ง๋ค์ด์ฃผ๋ ์น๊ตฌ
โจ plotly: ์ธํฐ๋ํฐ๋ธํ ์ฐจํธ๋ฅผ ๋ง๋ค ์ ์๋ ๋ฉ์ง ๋๊ตฌ
๐ python-dotenv: API ํค ๊ฐ์ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ์์ ํ๊ฒ ๊ด๋ฆฌ
3๏ธโฃ ๋ธ๋ก์ฒด์ธ ๋ ธ๋ ์ ๊ทผ ์ค์
๋ธ๋ก์ฒด์ธ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ค๋ฉด ๋ ธ๋์ ์ ๊ทผํด์ผ ํด. ์ง์ ๋ ธ๋๋ฅผ ์ด์ํ ์๋ ์์ง๋ง, ์ด๋ณด์์๊ฒ๋ Infura๋ Alchemy ๊ฐ์ ์๋น์ค๋ฅผ ์ถ์ฒํด!
๐ก Infura ์์ํ๊ธฐ
1. infura.io์ ๊ฐ์
2. ์ ํ๋ก์ ํธ ์์ฑ
3. PROJECT ID ๋ณต์ฌ (์ด๊ฒ ๋์ API ํค์ผ!)
4. ํ๋ก์ ํธ ํด๋์ .env ํ์ผ ์์ฑ
5. ๋ค์ ๋ด์ฉ ์
๋ ฅ: INFURA_PROJECT_ID=์ฌ๊ธฐ์_๋์_ํ๋ก์ ํธ_ID
๐ฌ ์ฒซ ๋ฒ์งธ ํ๋ก์ ํธ: ๊ธฐ๋ณธ ๊ฑฐ๋๋ด์ญ ๊ฐ์ ธ์ค๊ธฐ
์ด์ ์ง์ง ์ฝ๋ฉ์ ์์ํด๋ณด์! ๋จผ์ ๊ฐ๋จํ๊ฒ ํน์ ์ฃผ์์ ๊ฑฐ๋๋ด์ญ์ ๊ฐ์ ธ์ค๋ ์ฝ๋๋ฅผ ์์ฑํ ๊ฑฐ์ผ. ๐
๐ ๊ธฐ๋ณธ ์ฐ๊ฒฐ ์ฝ๋
from web3 import Web3
from dotenv import load_dotenv
import os
# ํ๊ฒฝ ๋ณ์ ๋ก๋
load_dotenv()
# Infura ์ฐ๊ฒฐ
infura_url = f"https://mainnet.infura.io/v3/{os.getenv('INFURA_PROJECT_ID')}"
web3 = Web3(Web3.HTTPProvider(infura_url))
# ์ฐ๊ฒฐ ํ์ธ
if web3.is_connected():
print("โ
๋ธ๋ก์ฒด์ธ ์ฐ๊ฒฐ ์ฑ๊ณต!")
print(f"ํ์ฌ ๋ธ๋ก ๋ฒํธ: {web3.eth.block_number}")
else:
print("โ ์ฐ๊ฒฐ ์คํจ... ์ค์ ์ ํ์ธํด๋ณด์ธ์!")
์ด ์ฝ๋๋ฅผ ์คํํ๋ฉด ํ์ฌ ์ด๋๋ฆฌ์ ๋ฉ์ธ๋ท์ ์ต์ ๋ธ๋ก ๋ฒํธ๊ฐ ์ถ๋ ฅ๋ ๊ฑฐ์ผ. ๋ง์ฝ ์ฐ๊ฒฐ์ด ์ ๋๋ค๋ฉด API ํค๋ฅผ ๋ค์ ํ์ธํด๋ด! ๐
๐ ํน์ ์ฃผ์์ ์์ก ์กฐํ
def get_balance(address):
"""
ํน์ ์ฃผ์์ ์ด๋๋ฆฌ์ ์์ก์ ์กฐํํ๋ ํจ์
"""
# ์ฒดํฌ์ฌ ์ฃผ์๋ก ๋ณํ (๋์๋ฌธ์ ๊ตฌ๋ถ)
checksum_address = web3.to_checksum_address(address)
# Wei ๋จ์๋ก ์์ก ์กฐํ
balance_wei = web3.eth.get_balance(checksum_address)
# Ether ๋จ์๋ก ๋ณํ
balance_eth = web3.from_wei(balance_wei, 'ether')
return balance_eth
# ์์: Vitalik์ ์ฃผ์ (์ด๋๋ฆฌ์ ์ฐฝ์์)
vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
balance = get_balance(vitalik_address)
print(f"๐ฐ ์์ก: {balance} ETH")
โ ๏ธ ์ฃผ์์ฌํญ
๋ธ๋ก์ฒด์ธ ์ฃผ์๋ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ๋ ์ฒดํฌ์ฌ ํ์์ด์ผ. ๋ฐ๋์ web3.to_checksum_address()๋ฅผ ์ฌ์ฉํด์ ๋ณํํด์ผ ํด! ์ ๊ทธ๋ฌ๋ฉด ์๋ฌ๊ฐ ๋ ์ ์์ด.
๐ ๊ฑฐ๋๋ด์ญ ์์ง ๋ฐ ๋ถ์
์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ๊ฑฐ๋๋ด์ญ์ ์์งํด๋ณผ ๊ฑฐ์ผ. ํน์ ๋ธ๋ก ๋ฒ์ ๋ด์ ๋ชจ๋ ๊ฑฐ๋๋ฅผ ๊ฐ์ ธ์์ ๋ถ์ํ๋ ์ฝ๋๋ฅผ ๋ง๋ค์ด๋ณด์! ๐ช
๐ ๋ธ๋ก ๋ฐ์ดํฐ ์์ง ํจ์
import pandas as pd
from datetime import datetime
def get_block_transactions(start_block, end_block):
"""
ํน์ ๋ธ๋ก ๋ฒ์์ ๋ชจ๋ ๊ฑฐ๋๋ฅผ ์์งํ๋ ํจ์
"""
transactions = []
print(f"๐ฆ ๋ธ๋ก {start_block}๋ถํฐ {end_block}๊น์ง ์์ง ์ค...")
for block_num in range(start_block, end_block + 1):
# ์งํ ์ํฉ ํ์
if block_num % 10 == 0:
print(f"์งํ ์ค: {block_num}/{end_block}")
# ๋ธ๋ก ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
block = web3.eth.get_block(block_num, full_transactions=True)
# ๋ธ๋ก ๋ด ๋ชจ๋ ๊ฑฐ๋ ์ฒ๋ฆฌ
for tx in block.transactions:
transactions.append({
'block_number': block_num,
'timestamp': datetime.fromtimestamp(block.timestamp),
'hash': tx.hash.hex(),
'from': tx['from'],
'to': tx.to if tx.to else 'Contract Creation',
'value_eth': web3.from_wei(tx.value, 'ether'),
'gas_price': web3.from_wei(tx.gasPrice, 'gwei'),
'gas_used': tx.gas
})
# DataFrame์ผ๋ก ๋ณํ
df = pd.DataFrame(transactions)
print(f"โ
์ด {len(df)}๊ฐ์ ๊ฑฐ๋ ์์ง ์๋ฃ!")
return df
# ์ต๊ทผ 100๊ฐ ๋ธ๋ก์ ๊ฑฐ๋ ์์ง
latest_block = web3.eth.block_number
start = latest_block - 100
end = latest_block
df_transactions = get_block_transactions(start, end)
print(df_transactions.head())
์ด ์ฝ๋๋ ์๊ฐ์ด ์ข ๊ฑธ๋ฆด ์ ์์ด. ๋ธ๋ก์ฒด์ธ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฑด ๋คํธ์ํฌ ์๋์ ์ํฅ์ ๋ฐ๊ฑฐ๋ . ์ธ๋ด์ฌ์ ๊ฐ์ง๊ณ ๊ธฐ๋ค๋ ค๋ด! โฐ
๐ก ์ฑ๋ฅ ์ต์ ํ ํ
โข ๋๋ฌด ๋ง์ ๋ธ๋ก์ ํ ๋ฒ์ ๊ฐ์ ธ์ค์ง ๋ง! 100~1000๊ฐ ์ ๋๊ฐ ์ ๋นํด
โข ๋ฐ์ดํฐ๋ฅผ ์์งํ ํ์๋ CSV ํ์ผ๋ก ์ ์ฅํด๋๋ฉด ๋ค์์ ๋ค์ ์์งํ ํ์๊ฐ ์์ด
โข Infura ๋ฌด๋ฃ ํ๋์ ํ๋ฃจ 10๋ง ๊ฑด ์์ฒญ ์ ํ์ด ์์ผ๋ ์ฃผ์!
โข ๋ฉํฐ์ค๋ ๋ฉ์ ์ฌ์ฉํ๋ฉด ๋ ๋น ๋ฅด๊ฒ ์์งํ ์ ์์ง๋ง, API ์ ํ์ ์กฐ์ฌํด์ผ ํด
๐ ๋ฐ์ดํฐ ์๊ฐํ - ๊ธฐ๋ณธ ์ฐจํธ ๋ง๋ค๊ธฐ
๋๋์ด ์๊ฐํ ๋จ๊ณ์ผ! ์์งํ ๋ฐ์ดํฐ๋ฅผ ์์ ์ฐจํธ๋ก ๋ง๋ค์ด๋ณด์. ๋จผ์ matplotlib๊ณผ seaborn์ ์ฌ์ฉํ ๊ธฐ๋ณธ ์ฐจํธ๋ถํฐ ์์ํ ๊ฒ. ๐จ
๐ ์๊ฐ๋๋ณ ๊ฑฐ๋๋ ์ฐจํธ
import matplotlib.pyplot as plt
import seaborn as sns
# ํ๊ธ ํฐํธ ์ค์ (๋งฅ: AppleGothic, ์๋์ฐ: Malgun Gothic)
plt.rcParams['font.family'] = 'AppleGothic'
plt.rcParams['axes.unicode_minus'] = False
def plot_transactions_over_time(df):
"""
์๊ฐ๋๋ณ ๊ฑฐ๋๋์ ์๊ฐํํ๋ ํจ์
"""
# ์๊ฐ๋๋ณ ๊ฑฐ๋ ์ ์ง๊ณ
df['hour'] = df['timestamp'].dt.hour
hourly_counts = df.groupby('hour').size()
# ์ฐจํธ ์์ฑ
plt.figure(figsize=(12, 6))
sns.barplot(x=hourly_counts.index, y=hourly_counts.values, palette='viridis')
plt.title('์๊ฐ๋๋ณ ๊ฑฐ๋๋ ๋ถํฌ', fontsize=16, fontweight='bold')
plt.xlabel('์๊ฐ (Hour)', fontsize=12)
plt.ylabel('๊ฑฐ๋ ์', fontsize=12)
plt.xticks(range(24))
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.savefig('transactions_by_hour.png', dpi=300)
plt.show()
print("๐ ์ฐจํธ๊ฐ 'transactions_by_hour.png'๋ก ์ ์ฅ๋์์ต๋๋ค!")
# ์คํ
plot_transactions_over_time(df_transactions)
๐ ๊ฑฐ๋ ๊ธ์ก ๋ถํฌ ์ฐจํธ
def plot_value_distribution(df):
"""
๊ฑฐ๋ ๊ธ์ก ๋ถํฌ๋ฅผ ํ์คํ ๊ทธ๋จ์ผ๋ก ์๊ฐํ
"""
# 0 ETH ๊ฑฐ๋ ์ ์ธ (์ปจํธ๋ํธ ํธ์ถ ๋ฑ)
df_filtered = df[df['value_eth'] > 0]
plt.figure(figsize=(12, 6))
# ๋ก๊ทธ ์ค์ผ์ผ๋ก ํ์ (๊ธ์ก ์ฐจ์ด๊ฐ ํฌ๊ธฐ ๋๋ฌธ)
plt.hist(df_filtered['value_eth'], bins=50, color='#667eea',
alpha=0.7, edgecolor='black')
plt.title('๊ฑฐ๋ ๊ธ์ก ๋ถํฌ', fontsize=16, fontweight='bold')
plt.xlabel('๊ฑฐ๋ ๊ธ์ก (ETH)', fontsize=12)
plt.ylabel('๋น๋', fontsize=12)
plt.yscale('log') # Y์ถ์ ๋ก๊ทธ ์ค์ผ์ผ๋ก
plt.grid(axis='y', alpha=0.3)
# ํต๊ณ ์ ๋ณด ์ถ๊ฐ
mean_value = df_filtered['value_eth'].mean()
median_value = df_filtered['value_eth'].median()
plt.axvline(mean_value, color='red', linestyle='--',
label=f'ํ๊ท : {mean_value:.4f} ETH')
plt.axvline(median_value, color='green', linestyle='--',
label=f'์ค์๊ฐ: {median_value:.4f} ETH')
plt.legend()
plt.tight_layout()
plt.savefig('value_distribution.png', dpi=300)
plt.show()
plot_value_distribution(df_transactions)
โจ ์ธํฐ๋ํฐ๋ธ ๋์๋ณด๋ ๋ง๋ค๊ธฐ - Plotly ํ์ฉ
์ ์ ์ธ ์ฐจํธ๋ ์ข์ง๋ง, ๋ง์ฐ์ค๋ก ํ๋ํ๊ณ ๋ฐ์ดํฐ๋ฅผ ํ์ํ ์ ์๋ ์ธํฐ๋ํฐ๋ธ ์ฐจํธ๋ ํจ์ฌ ๋ ๋ฉ์ ธ! Plotly๋ฅผ ์ฌ์ฉํ๋ฉด ์น ๋ธ๋ผ์ฐ์ ์์ ๋์ํ๋ ๋ฉ์ง ๋์๋ณด๋๋ฅผ ๋ง๋ค ์ ์์ด. ๐
๐ ์ธํฐ๋ํฐ๋ธ ์๊ณ์ด ์ฐจํธ
import plotly.graph_objects as go
import plotly.express as px
def create_interactive_timeline(df):
"""
์๊ฐ์ ๋ฐ๋ฅธ ๊ฑฐ๋๋๊ณผ ํ๊ท ๊ธ์ก์ ๋ณด์ฌ์ฃผ๋ ์ธํฐ๋ํฐ๋ธ ์ฐจํธ
"""
# ์๊ฐ๋๋ณ ์ง๊ณ
df['date'] = df['timestamp'].dt.date
daily_stats = df.groupby('date').agg({
'hash': 'count', # ๊ฑฐ๋ ์
'value_eth': 'mean', # ํ๊ท ๊ฑฐ๋ ๊ธ์ก
'gas_price': 'mean' # ํ๊ท ๊ฐ์ค ๊ฐ๊ฒฉ
}).reset_index()
daily_stats.columns = ['date', 'tx_count', 'avg_value', 'avg_gas']
# Figure ์์ฑ
fig = go.Figure()
# ๊ฑฐ๋ ์ ์ถ๊ฐ
fig.add_trace(go.Scatter(
x=daily_stats['date'],
y=daily_stats['tx_count'],
name='๊ฑฐ๋ ์',
mode='lines+markers',
line=dict(color='#667eea', width=3),
marker=dict(size=8)
))
# ํ๊ท ๊ฑฐ๋ ๊ธ์ก ์ถ๊ฐ (๋ณด์กฐ Y์ถ)
fig.add_trace(go.Scatter(
x=daily_stats['date'],
y=daily_stats['avg_value'],
name='ํ๊ท ๊ฑฐ๋ ๊ธ์ก (ETH)',
mode='lines+markers',
line=dict(color='#f5576c', width=3, dash='dash'),
marker=dict(size=8),
yaxis='y2'
))
# ๋ ์ด์์ ์ค์
fig.update_layout(
title='์ผ๋ณ ๊ฑฐ๋ ํต๊ณ ๋์๋ณด๋',
xaxis=dict(title='๋ ์ง'),
yaxis=dict(title='๊ฑฐ๋ ์', side='left'),
yaxis2=dict(title='ํ๊ท ๊ฑฐ๋ ๊ธ์ก (ETH)',
side='right', overlaying='y'),
hovermode='x unified',
template='plotly_white',
height=600
)
# HTML ํ์ผ๋ก ์ ์ฅ
fig.write_html('interactive_dashboard.html')
print("โ
์ธํฐ๋ํฐ๋ธ ๋์๋ณด๋๊ฐ ์์ฑ๋์์ต๋๋ค!")
print("๐ 'interactive_dashboard.html' ํ์ผ์ ๋ธ๋ผ์ฐ์ ๋ก ์ด์ด๋ณด์ธ์!")
return fig
# ์คํ
fig = create_interactive_timeline(df_transactions)
fig.show()
๐ฏ Plotly์ ์ฅ์
์ค ์ธ/์์: ๋ง์ฐ์ค ๋๋๊ทธ๋ก ํน์ ๊ตฌ๊ฐ ํ๋ ๊ฐ๋ฅ
ํธ๋ฒ ์ ๋ณด: ๋ง์ฐ์ค๋ฅผ ์ฌ๋ฆฌ๋ฉด ์์ธ ๋ฐ์ดํฐ ํ์
๋ฒ๋ก ํด๋ฆญ: ํน์ ๋ฐ์ดํฐ ์๋ฆฌ์ฆ๋ฅผ ์จ๊ธฐ๊ฑฐ๋ ํ์
์ด๋ฏธ์ง ์ ์ฅ: ์ฐจํธ๋ฅผ PNG๋ก ๋ค์ด๋ก๋ ๊ฐ๋ฅ
๋ฐ์ํ: ๋ชจ๋ฐ์ผ์์๋ ์ ์๋
๐จ ๊ณ ๊ธ ์๊ฐํ - ๋คํธ์ํฌ ๊ทธ๋ํ
๋ธ๋ก์ฒด์ธ์ ์ง์ง ๋งค๋ ฅ์ ์ฃผ์ ๊ฐ์ ์ฐ๊ฒฐ ๊ด๊ณ๋ฅผ ์๊ฐํํ๋ ๊ฑฐ์ผ! ๋๊ฐ ๋๊ตฌ์๊ฒ ๋์ ๋ณด๋๋์ง, ์ด๋ค ์ฃผ์๊ฐ ํ๋ธ ์ญํ ์ ํ๋์ง ํ๋์ ๋ณผ ์ ์์ด. ๐ธ๏ธ
๐ ๊ฑฐ๋ ๋คํธ์ํฌ ์๊ฐํ
import networkx as nx
import plotly.graph_objects as go
def create_transaction_network(df, min_value=0.1):
"""
๊ฑฐ๋ ๋คํธ์ํฌ๋ฅผ ์๊ฐํํ๋ ํจ์
min_value: ํ์ํ ์ต์ ๊ฑฐ๋ ๊ธ์ก (ETH)
"""
# ํฐ ๊ฑฐ๋๋ง ํํฐ๋ง
df_filtered = df[df['value_eth'] >= min_value].copy()
# ๋คํธ์ํฌ ๊ทธ๋ํ ์์ฑ
G = nx.DiGraph()
# ๋
ธ๋์ ์ฃ์ง ์ถ๊ฐ
for _, row in df_filtered.iterrows():
from_addr = row['from'][:10] + '...' # ์ฃผ์ ์ถ์ฝ
to_addr = row['to'][:10] + '...' if row['to'] != 'Contract Creation' else 'Contract'
value = float(row['value_eth'])
# ์ฃ์ง ์ถ๊ฐ (๊ฐ์ค์น๋ ๊ฑฐ๋ ๊ธ์ก)
if G.has_edge(from_addr, to_addr):
G[from_addr][to_addr]['weight'] += value
else:
G.add_edge(from_addr, to_addr, weight=value)
# ๋ ์ด์์ ๊ณ์ฐ
pos = nx.spring_layout(G, k=0.5, iterations=50)
# ์ฃ์ง ๊ทธ๋ฆฌ๊ธฐ
edge_trace = []
for edge in G.edges():
x0, y0 = pos[edge[0]]
x1, y1 = pos[edge[1]]
weight = G[edge[0]][edge[1]]['weight']
edge_trace.append(go.Scatter(
x=[x0, x1, None],
y=[y0, y1, None],
mode='lines',
line=dict(width=min(weight * 2, 10), color='#cbd5e1'),
hoverinfo='none',
showlegend=False
))
# ๋
ธ๋ ๊ทธ๋ฆฌ๊ธฐ
node_x = []
node_y = []
node_text = []
node_size = []
for node in G.nodes():
x, y = pos[node]
node_x.append(x)
node_y.append(y)
# ๋
ธ๋ ์ ๋ณด
in_degree = G.in_degree(node)
out_degree = G.out_degree(node)
node_text.append(f'{node}<br>๋ฐ์ ๊ฑฐ๋: {in_degree}<br>๋ณด๋ธ ๊ฑฐ๋: {out_degree}')
node_size.append(20 + (in_degree + out_degree) * 5)
node_trace = go.Scatter(
x=node_x, y=node_y,
mode='markers+text',
hoverinfo='text',
text=node_text,
marker=dict(
size=node_size,
color='#667eea',
line=dict(width=2, color='white')
)
)
# Figure ์์ฑ
fig = go.Figure(data=edge_trace + [node_trace])
fig.update_layout(
title='๊ฑฐ๋ ๋คํธ์ํฌ ์๊ฐํ',
showlegend=False,
hovermode='closest',
margin=dict(b=0, l=0, r=0, t=40),
xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
height=700,
template='plotly_white'
)
fig.write_html('transaction_network.html')
print("๐ธ๏ธ ๋คํธ์ํฌ ๊ทธ๋ํ๊ฐ ์์ฑ๋์์ต๋๋ค!")
return fig
# ์คํ
network_fig = create_transaction_network(df_transactions, min_value=0.5)
network_fig.show()
์ด ๋คํธ์ํฌ ๊ทธ๋ํ๋ฅผ ๋ณด๋ฉด ์ด๋ค ์ฃผ์๊ฐ ์ค์ฌ ์ญํ ์ ํ๋์ง, ์๊ธ์ด ์ด๋ป๊ฒ ํ๋ฅด๋์ง ์ง๊ด์ ์ผ๋ก ํ์ ํ ์ ์์ด. ๊ฑฐ๋์ ์ฃผ์๋ ํฐ ๊ณ ๋(whale) ์ง๊ฐ์ ์ฐพ๋ ๋ฐ ์ ์ฉํ์ง! ๐
๐ ์ค์ ํ๋ก์ ํธ: DApp ๋ถ์ ๋์๋ณด๋
์ด์ ๋ฐฐ์ด ๊ฑธ ๋ค ํฉ์ณ์ ์ค์ ๋ก ์ฌ์ฉํ ์ ์๋ DApp ๋ถ์ ๋์๋ณด๋๋ฅผ ๋ง๋ค์ด๋ณด์! Uniswap ๊ฐ์ DEX(ํ์ค์ํ ๊ฑฐ๋์)์ ๊ฑฐ๋๋ฅผ ๋ถ์ํ๋ ํ๋ก๊ทธ๋จ์ ๋ง๋ค ๊ฑฐ์ผ. ๐ฆ
๐ ์ค๋งํธ ์ปจํธ๋ํธ ์ด๋ฒคํธ ๋ชจ๋ํฐ๋ง
from web3 import Web3
import json
# Uniswap V2 Router ์ฃผ์
UNISWAP_ROUTER = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"
# ERC20 Transfer ์ด๋ฒคํธ ์๊ทธ๋์ฒ
TRANSFER_EVENT_SIGNATURE = web3.keccak(text="Transfer(address,address,uint256)").hex()
def get_uniswap_swaps(start_block, end_block):
"""
Uniswap ์ค์ ๊ฑฐ๋๋ฅผ ์์งํ๋ ํจ์
"""
swaps = []
# ์ด๋ฒคํธ ํํฐ ์์ฑ
event_filter = {
'fromBlock': start_block,
'toBlock': end_block,
'address': UNISWAP_ROUTER
}
# ๋ก๊ทธ ๊ฐ์ ธ์ค๊ธฐ
logs = web3.eth.get_logs(event_filter)
print(f"๐ฅ {len(logs)}๊ฐ์ ์ด๋ฒคํธ๋ฅผ ์ฐพ์์ต๋๋ค!")
for log in logs:
try:
# ํธ๋์ญ์
์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
tx = web3.eth.get_transaction(log['transactionHash'])
receipt = web3.eth.get_transaction_receipt(log['transactionHash'])
swaps.append({
'block': log['blockNumber'],
'tx_hash': log['transactionHash'].hex(),
'from': tx['from'],
'gas_used': receipt['gasUsed'],
'status': receipt['status']
})
except Exception as e:
print(f"โ ๏ธ ์๋ฌ ๋ฐ์: {e}")
continue
return pd.DataFrame(swaps)
# ์ต๊ทผ 1000๋ธ๋ก ๋ถ์
latest = web3.eth.block_number
df_swaps = get_uniswap_swaps(latest - 1000, latest)
print(df_swaps.head())
๐ก ์ค๋งํธ ์ปจํธ๋ํธ ABI ํ์ฉํ๊ธฐ
์ค๋งํธ ์ปจํธ๋ํธ์ ์ ๋๋ก ์ํธ์์ฉํ๋ ค๋ฉด ABI(Application Binary Interface)๊ฐ ํ์ํด. ABI๋ ์ปจํธ๋ํธ์ ํจ์์ ์ด๋ฒคํธ ์ ์๋ฅผ ๋ด๊ณ ์๋ JSON ํ์ผ์ด์ผ.
Etherscan์์ ๊ฒ์ฆ๋ ์ปจํธ๋ํธ์ ABI๋ฅผ ๋ณต์ฌํ ์ ์์ด. ๊ทธ๋ผ ํจ์ ํธ์ถ์ด๋ ์ด๋ฒคํธ ๋์ฝ๋ฉ์ด ํจ์ฌ ์ฌ์์ ธ!
๐ ์ข ํฉ ๋์๋ณด๋ ํด๋์ค
class DAppAnalyticsDashboard:
"""
DApp ๋ถ์์ ์ํ ์ข
ํฉ ๋์๋ณด๋ ํด๋์ค
"""
def __init__(self, web3_provider, contract_address):
self.web3 = Web3(Web3.HTTPProvider(web3_provider))
self.contract_address = contract_address
self.data = None
def collect_data(self, start_block, end_block):
"""๋ฐ์ดํฐ ์์ง"""
print("๐ ๋ฐ์ดํฐ ์์ง ์์...")
self.data = get_block_transactions(start_block, end_block)
print(f"โ
{len(self.data)}๊ฐ ๊ฑฐ๋ ์์ง ์๋ฃ!")
def analyze_users(self):
"""์ฌ์ฉ์ ๋ถ์"""
if self.data is None:
print("โ ๋จผ์ ๋ฐ์ดํฐ๋ฅผ ์์งํ์ธ์!")
return
# ํ์ฑ ์ฌ์ฉ์ ์
unique_users = pd.concat([
self.data['from'],
self.data['to']
]).nunique()
# ๊ฑฐ๋ ๋น๋ ๋ถ์
user_activity = self.data['from'].value_counts()
print(f"๐ฅ ์ด ํ์ฑ ์ฌ์ฉ์: {unique_users}๋ช
")
print(f"๐ ํ๊ท ๊ฑฐ๋ ํ์: {user_activity.mean():.2f}ํ")
print(f"๐ฅ ์ต๋ค ๊ฑฐ๋ ์ฌ์ฉ์: {user_activity.iloc[0]}ํ")
return {
'total_users': unique_users,
'avg_transactions': user_activity.mean(),
'top_user_txs': user_activity.iloc[0]
}
def analyze_gas(self):
"""๊ฐ์ค ๋น์ฉ ๋ถ์"""
avg_gas = self.data['gas_price'].mean()
total_gas = (self.data['gas_price'] * self.data['gas_used']).sum()
print(f"โฝ ํ๊ท ๊ฐ์ค ๊ฐ๊ฒฉ: {avg_gas:.2f} Gwei")
print(f"๐ธ ์ด ๊ฐ์ค ๋น์ฉ: {web3.from_wei(total_gas, 'ether'):.4f} ETH")
return {
'avg_gas_price': avg_gas,
'total_gas_cost': total_gas
}
def create_dashboard(self):
"""์ข
ํฉ ๋์๋ณด๋ ์์ฑ"""
from plotly.subplots import make_subplots
# ์๋ธํ๋กฏ ์์ฑ (2x2 ๊ทธ๋ฆฌ๋)
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('์๊ฐ๋๋ณ ๊ฑฐ๋๋', '๊ฑฐ๋ ๊ธ์ก ๋ถํฌ',
'๊ฐ์ค ๊ฐ๊ฒฉ ์ถ์ด', '์์ ๊ฑฐ๋์'),
specs=[[{'type': 'bar'}, {'type': 'histogram'}],
[{'type': 'scatter'}, {'type': 'bar'}]]
)
# 1. ์๊ฐ๋๋ณ ๊ฑฐ๋๋
hourly = self.data.groupby(self.data['timestamp'].dt.hour).size()
fig.add_trace(
go.Bar(x=hourly.index, y=hourly.values, name='๊ฑฐ๋๋'),
row=1, col=1
)
# 2. ๊ฑฐ๋ ๊ธ์ก ๋ถํฌ
fig.add_trace(
go.Histogram(x=self.data['value_eth'], name='๊ธ์ก ๋ถํฌ'),
row=1, col=2
)
# 3. ๊ฐ์ค ๊ฐ๊ฒฉ ์ถ์ด
fig.add_trace(
go.Scatter(x=self.data['timestamp'],
y=self.data['gas_price'],
mode='lines', name='๊ฐ์ค ๊ฐ๊ฒฉ'),
row=2, col=1
)
# 4. ์์ ๊ฑฐ๋์
top_users = self.data['from'].value_counts().head(10)
fig.add_trace(
go.Bar(x=top_users.values, y=top_users.index,
orientation='h', name='๊ฑฐ๋ ํ์'),
row=2, col=2
)
# ๋ ์ด์์ ์ค์
fig.update_layout(
height=900,
showlegend=False,
title_text="DApp ์ข
ํฉ ๋ถ์ ๋์๋ณด๋"
)
fig.write_html('dapp_dashboard.html')
print("๐ ๋์๋ณด๋๊ฐ ์์ฑ๋์์ต๋๋ค!")
return fig
# ์ฌ์ฉ ์์
dashboard = DAppAnalyticsDashboard(infura_url, UNISWAP_ROUTER)
dashboard.collect_data(latest - 500, latest)
dashboard.analyze_users()
dashboard.analyze_gas()
dashboard.create_dashboard()
๐ ์ฑ๋ฅ ์ต์ ํ ๋ฐ ์ค์ ํ
์ค์ ๋ก ํ๋ก๋์ ํ๊ฒฝ์์ ์ฌ์ฉํ๋ ค๋ฉด ์ฑ๋ฅ ์ต์ ํ๊ฐ ํ์์ผ! ๋ธ๋ก์ฒด์ธ ๋ฐ์ดํฐ๋ ๋ฐฉ๋ํ๊ณ , API ํธ์ถ์๋ ์ ํ์ด ์๊ฑฐ๋ . ๋ช ๊ฐ์ง ๊ฟํ์ ์๋ ค์ค๊ฒ! ๐ฏ
1๏ธโฃ ๋ฐ์ดํฐ ์บ์ฑ
ํ ๋ฒ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ ๋ก์ปฌ์ ์ ์ฅํด๋์. ๋งค๋ฒ ๋ธ๋ก์ฒด์ธ์์ ๊ฐ์ ธ์ฌ ํ์ ์์ด!
import pickle
from pathlib import Path
def save_cache(data, filename):
"""๋ฐ์ดํฐ๋ฅผ ์บ์ ํ์ผ๋ก ์ ์ฅ"""
cache_dir = Path('cache')
cache_dir.mkdir(exist_ok=True)
with open(cache_dir / filename, 'wb') as f:
pickle.dump(data, f)
print(f"๐พ ์บ์ ์ ์ฅ ์๋ฃ: {filename}")
def load_cache(filename):
"""์บ์ ํ์ผ์์ ๋ฐ์ดํฐ ๋ก๋"""
cache_file = Path('cache') / filename
if cache_file.exists():
with open(cache_file, 'rb') as f:
data = pickle.load(f)
print(f"๐ ์บ์ ๋ก๋ ์๋ฃ: {filename}")
return data
return None
# ์ฌ์ฉ ์์
cached_data = load_cache('transactions.pkl')
if cached_data is None:
# ์บ์๊ฐ ์์ผ๋ฉด ์๋ก ์์ง
cached_data = get_block_transactions(start, end)
save_cache(cached_data, 'transactions.pkl')
2๏ธโฃ ๋ฐฐ์น ์ฒ๋ฆฌ
ํ ๋ฒ์ ๋๋ฌด ๋ง์ ๋ธ๋ก์ ์ฒ๋ฆฌํ์ง ๋ง๊ณ , ๋ฐฐ์น๋ก ๋๋ ์ ์ฒ๋ฆฌํ์!
def batch_process_blocks(start_block, end_block, batch_size=100):
"""
๋ธ๋ก์ ๋ฐฐ์น ๋จ์๋ก ์ฒ๋ฆฌ
"""
all_data = []
for i in range(start_block, end_block, batch_size):
batch_end = min(i + batch_size, end_block)
print(f"๐ ๋ฐฐ์น ์ฒ๋ฆฌ ์ค: {i} ~ {batch_end}")
try:
batch_data = get_block_transactions(i, batch_end)
all_data.append(batch_data)
# ์ค๊ฐ ์ ์ฅ
if len(all_data) % 10 == 0:
temp_df = pd.concat(all_data)
save_cache(temp_df, f'temp_batch_{i}.pkl')
except Exception as e:
print(f"โ ์๋ฌ ๋ฐ์: {e}")
continue
# ์ต์ข
๊ฒฐํฉ
final_df = pd.concat(all_data, ignore_index=True)
return final_df
3๏ธโฃ ๋น๋๊ธฐ ์ฒ๋ฆฌ
์ฌ๋ฌ ์์ฒญ์ ๋์์ ์ฒ๋ฆฌํ๋ฉด ํจ์ฌ ๋นจ๋ผ์ ธ! asyncio๋ฅผ ํ์ฉํด๋ณด์.
import asyncio
from web3 import AsyncWeb3
async def get_block_async(block_number):
"""๋น๋๊ธฐ๋ก ๋ธ๋ก ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ"""
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(infura_url))
block = await w3.eth.get_block(block_number)
return block
async def get_multiple_blocks(block_numbers):
"""์ฌ๋ฌ ๋ธ๋ก์ ๋์์ ๊ฐ์ ธ์ค๊ธฐ"""
tasks = [get_block_async(num) for num in block_numbers]
blocks = await asyncio.gather(*tasks)
return blocks
# ์ฌ์ฉ ์์
block_range = range(latest - 100, latest)
blocks = asyncio.run(get_multiple_blocks(block_range))
print(f"โ
{len(blocks)}๊ฐ ๋ธ๋ก์ ๋์์ ๊ฐ์ ธ์์ต๋๋ค!")
โ ๏ธ API ์ ํ ์ฃผ์
โข Infura ๋ฌด๋ฃ ํ๋: ํ๋ฃจ 10๋ง ์์ฒญ
โข Alchemy ๋ฌด๋ฃ ํ๋: ์ 3์ต ์ปดํจํ
์ ๋
โข ๋๋ฌด ๋น ๋ฅด๊ฒ ์์ฒญํ๋ฉด ์ผ์์ ์ผ๋ก ์ฐจ๋จ๋ ์ ์์ด
โข ์์ฒญ ์ฌ์ด์ ์ ์ ํ ๋๋ ์ด๋ฅผ ๋ฃ๋ ๊ฒ ์ข์ (time.sleep(0.1) ๊ฐ์ ๊ฑฐ)
โข ํ๋ก๋์
์์๋ ์ ๋ฃ ํ๋์ ๊ณ ๋ คํด๋ด!
๐จ ๊ณ ๊ธ ์๊ฐํ ๊ธฐ๋ฒ
์ด์ ์ข ๋ ๊ณ ๊ธ ์๊ฐํ ๊ธฐ๋ฒ์ ์์๋ณด์! ๋ฐ์ดํฐ๋ฅผ ๋ ํจ๊ณผ์ ์ผ๋ก ์ ๋ฌํ๋ ๋ฐฉ๋ฒ๋ค์ด์ผ. ๐
๐ ์ ๋๋ฉ์ด์ ์ฐจํธ
import plotly.express as px
def create_animated_chart(df):
"""
์๊ฐ์ ๋ฐ๋ผ ๋ณํํ๋ ์ ๋๋ฉ์ด์
์ฐจํธ
"""
# ์ผ๋ณ ๋์ ๊ฑฐ๋๋ ๊ณ์ฐ
df['date'] = df['timestamp'].dt.date
df_sorted = df.sort_values('timestamp')
df_sorted['cumulative_txs'] = range(1, len(df_sorted) + 1)
# ์ ๋๋ฉ์ด์
์ฐจํธ ์์ฑ
fig = px.scatter(
df_sorted,
x='timestamp',
y='value_eth',
animation_frame='date',
size='gas_price',
color='value_eth',
hover_name='hash',
size_max=30,
range_y=[0, df_sorted['value_eth'].max() * 1.1],
title='๊ฑฐ๋ ๊ธ์ก ๋ณํ ์ ๋๋ฉ์ด์
'
)
fig.update_layout(
height=600,
showlegend=False
)
fig.write_html('animated_transactions.html')
return fig
animated_fig = create_animated_chart(df_transactions)
๐ 3D ์๊ฐํ
def create_3d_visualization(df):
"""
3D ์ฐ์ ๋๋ก ๊ฑฐ๋ ๋ฐ์ดํฐ ์๊ฐํ
"""
# ์๊ฐ, ๊ธ์ก, ๊ฐ์ค ๊ฐ๊ฒฉ์ 3์ฐจ์์ผ๋ก ํํ
fig = go.Figure(data=[go.Scatter3d(
x=df['timestamp'],
y=df['value_eth'],
z=df['gas_price'],
mode='markers',
marker=dict(
size=5,
color=df['value_eth'],
colorscale='Viridis',
showscale=True,
colorbar=dict(title="๊ฑฐ๋ ๊ธ์ก (ETH)")
),
text=df['hash'],
hovertemplate='<b>์๊ฐ</b>: %{x}<br>' +
'<b>๊ธ์ก</b>: %{y:.4f} ETH<br>' +
'<b>๊ฐ์ค</b>: %{z:.2f} Gwei<br>'
)])
fig.update_layout(
title='3D ๊ฑฐ๋ ๋ถ์',
scene=dict(
xaxis_title='์๊ฐ',
yaxis_title='๊ฑฐ๋ ๊ธ์ก (ETH)',
zaxis_title='๊ฐ์ค ๊ฐ๊ฒฉ (Gwei)'
),
height=700
)
fig.write_html('3d_transactions.html')
return fig
fig_3d = create_3d_visualization(df_transactions)
๐ ๋ณด์ ๋ฐ ๋ฒ ์คํธ ํ๋ํฐ์ค
๋ธ๋ก์ฒด์ธ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฐ ๋๋ ๋ณด์์ด ์ ๋ง ์ค์ํด! ํนํ API ํค๋ ๊ฐ์ธ ํค๋ฅผ ๋ค๋ฃฐ ๋๋ ๋์ฑ ์กฐ์ฌํด์ผ ํด. ๐ก๏ธ
โ ๏ธ ์ ๋ ํ์ง ๋ง์์ผ ํ ๊ฒ๋ค
โ API ํค๋ฅผ ์ฝ๋์ ์ง์ ํ๋์ฝ๋ฉ
โ GitHub์ .env ํ์ผ ์
๋ก๋
โ ๊ฐ์ธ ํค(Private Key)๋ฅผ ํ๋ฌธ์ผ๋ก ์ ์ฅ
โ ๊ฒ์ฆ๋์ง ์์ ์ค๋งํธ ์ปจํธ๋ํธ์ ์ํธ์์ฉ
โ ์ฌ์ฉ์ ์
๋ ฅ์ ๊ฒ์ฆ ์์ด ์ฌ์ฉ
โ
๋ณด์ ์ฒดํฌ๋ฆฌ์คํธ
1. ํ๊ฒฝ ๋ณ์ ์ฌ์ฉ: python-dotenv๋ก ๋ฏผ๊ฐํ ์ ๋ณด ๊ด๋ฆฌ
2. .gitignore ์ค์ : .env, cache/, *.pkl ํ์ผ ์ ์ธ
3. ์
๋ ฅ ๊ฒ์ฆ: ์ฃผ์ ํ์, ๋ธ๋ก ๋ฒํธ ๋ฒ์ ๋ฑ ํ์ธ
4. ์๋ฌ ์ฒ๋ฆฌ: try-except๋ก ์์ธ ์ํฉ ๋๋น
5. ๋ก๊น
: ์ค์ํ ์์
์ ๋ก๊ทธ๋ก ๊ธฐ๋ก
6. Rate Limiting: API ํธ์ถ ๋น๋ ์ ํ
7. ๋ฐ์ดํฐ ๊ฒ์ฆ: ๋ธ๋ก์ฒด์ธ์์ ๋ฐ์ ๋ฐ์ดํฐ๋ ๊ฒ์ฆ
๐ ์์ ํ ์ค์ ๊ด๋ฆฌ
import os
from dotenv import load_dotenv
import logging
# ๋ก๊น
์ค์
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('dapp_analytics.log'),
logging.StreamHandler()
]
)
class SecureConfig:
"""์์ ํ ์ค์ ๊ด๋ฆฌ ํด๋์ค"""
def __init__(self):
load_dotenv()
self._validate_config()
def _validate_config(self):
"""ํ์ ํ๊ฒฝ ๋ณ์ ํ์ธ"""
required_vars = ['INFURA_PROJECT_ID']
for var in required_vars:
if not os.getenv(var):
raise ValueError(f"โ ํ์ ํ๊ฒฝ ๋ณ์๊ฐ ์์ต๋๋ค: {var}")
logging.info("โ
์ค์ ๊ฒ์ฆ ์๋ฃ")
@property
def infura_url(self):
"""Infura URL ๋ฐํ"""
project_id = os.getenv('INFURA_PROJECT_ID')
return f"https://mainnet.infura.io/v3/{project_id}"
@property
def cache_dir(self):
"""์บ์ ๋๋ ํ ๋ฆฌ ๊ฒฝ๋ก"""
return os.getenv('CACHE_DIR', 'cache')
# ์ฌ์ฉ ์์
config = SecureConfig()
web3 = Web3(Web3.HTTPProvider(config.infura_url))
๐ ์ค์ ํ์ฉ ์ฌ๋ก
์ด์ ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ๊ตฌ์ฒด์ ์ธ ์์๋ฅผ ๋ณด์ฌ์ค๊ฒ! ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ์ด๋ฐ ๊ธฐ์ ์ ํ์ฉํ ์ ์์ด. ๐ผ
๐ฆ DeFi ํ๋กํ ์ฝ ๋ชจ๋ํฐ๋ง
Compound, Aave ๊ฐ์ ๋์ถ ํ๋กํ ์ฝ์ ํ๋์ ์ค์๊ฐ์ผ๋ก ๋ชจ๋ํฐ๋งํ ์ ์์ด. ํฐ ์์น๋ ์ธ์ถ์ด ๋ฐ์ํ๋ฉด ์๋ฆผ์ ๋ณด๋ด๋ ์์คํ ์ ๋ง๋ค ์ ์์ง!
def monitor_defi_protocol(contract_address, threshold_eth=100):
"""
DeFi ํ๋กํ ์ฝ์ ๋๊ท๋ชจ ๊ฑฐ๋ ๋ชจ๋ํฐ๋ง
"""
latest_block = web3.eth.block_number
while True:
try:
# ์ต์ ๋ธ๋ก ํ์ธ
new_block = web3.eth.block_number
if new_block > latest_block:
# ์ ๋ธ๋ก ์ฒ๋ฆฌ
block = web3.eth.get_block(new_block, full_transactions=True)
for tx in block.transactions:
if tx.to == contract_address:
value_eth = web3.from_wei(tx.value, 'ether')
if value_eth >= threshold_eth:
# ๋๊ท๋ชจ ๊ฑฐ๋ ๊ฐ์ง!
logging.warning(
f"๐จ ๋๊ท๋ชจ ๊ฑฐ๋ ๊ฐ์ง!\n"
f"๊ธ์ก: {value_eth} ETH\n"
f"From: {tx['from']}\n"
f"Tx: {tx.hash.hex()}"
)
# ์ฌ๊ธฐ์ ํ
๋ ๊ทธ๋จ, ์ด๋ฉ์ผ ๋ฑ์ผ๋ก ์๋ฆผ ์ ์ก
latest_block = new_block
time.sleep(12) # ์ด๋๋ฆฌ์ ๋ธ๋ก ์๊ฐ
except Exception as e:
logging.error(f"๋ชจ๋ํฐ๋ง ์๋ฌ: {e}")
time.sleep(60)
๐ NFT ๋ง์ผํ๋ ์ด์ค ๋ถ์
OpenSea ๊ฐ์ NFT ๋ง์ผํ๋ ์ด์ค์ ๊ฑฐ๋๋ฅผ ๋ถ์ํด์ ํธ๋ ๋๋ฅผ ํ์ ํ ์ ์์ด!
def analyze_nft_sales(collection_address, days=7):
"""
NFT ์ปฌ๋ ์
์ ํ๋งค ๋ฐ์ดํฐ ๋ถ์
"""
# ์ต๊ทผ N์ผ๊ฐ์ ๋ธ๋ก ๋ฒ์ ๊ณ์ฐ
blocks_per_day = 6500 # ์ฝ 13์ด๋น 1๋ธ๋ก
end_block = web3.eth.block_number
start_block = end_block - (blocks_per_day * days)
# Transfer ์ด๋ฒคํธ ์์ง
transfer_signature = web3.keccak(text="Transfer(address,address,uint256)").hex()
logs = web3.eth.get_logs({
'fromBlock': start_block,
'toBlock': end_block,
'address': collection_address,
'topics': [transfer_signature]
})
# ๋ถ์
sales_data = []
for log in logs:
# ๊ฑฐ๋ ์ ๋ณด ํ์ฑ
tx = web3.eth.get_transaction(log['transactionHash'])
sales_data.append({
'timestamp': web3.eth.get_block(log['blockNumber']).timestamp,
'token_id': int(log['topics'][3].hex(), 16),
'from': '0x' + log['topics'][1].hex()[-40:],
'to': '0x' + log['topics'][2].hex()[-40:],
'tx_hash': log['transactionHash'].hex()
})
df = pd.DataFrame(sales_data)
# ํต๊ณ ๊ณ์ฐ
print(f"๐ {days}์ผ๊ฐ NFT ๊ฑฐ๋ ๋ถ์")
print(f"์ด ๊ฑฐ๋ ์: {len(df)}")
print(f"์ผํ๊ท ๊ฑฐ๋: {len(df) / days:.1f}")
print(f"๊ณ ์ ํ๋งค์: {df['from'].nunique()}")
print(f"๊ณ ์ ๊ตฌ๋งค์: {df['to'].nunique()}")
return df
๐ฏ ๊ฐ์ค ๊ฐ๊ฒฉ ์์ธก
๊ณผ๊ฑฐ ๊ฐ์ค ๊ฐ๊ฒฉ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํด์ ์ต์ ์ ๊ฑฐ๋ ์์ ์ ์์ธกํ ์ ์์ด!
from sklearn.ensemble import RandomForestRegressor
import numpy as np
def predict_gas_price(df_historical):
"""
๋จธ์ ๋ฌ๋์ผ๋ก ๊ฐ์ค ๊ฐ๊ฒฉ ์์ธก
"""
# ํน์ฑ ์์ง๋์ด๋ง
df = df_historical.copy()
df['hour'] = df['timestamp'].dt.hour
df['day_of_week'] = df['timestamp'].dt.dayofweek
df['is_weekend'] = df['day_of_week'].isin([5, 6]).astype(int)
# ์ด๋ ํ๊ท ์ถ๊ฐ
df['gas_ma_10'] = df['gas_price'].rolling(10).mean()
df['gas_ma_50'] = df['gas_price'].rolling(50).mean()
# ๊ฒฐ์ธก์น ์ ๊ฑฐ
df = df.dropna()
# ํน์ฑ๊ณผ ํ๊ฒ ๋ถ๋ฆฌ
features = ['hour', 'day_of_week', 'is_weekend', 'gas_ma_10', 'gas_ma_50']
X = df[features]
y = df['gas_price']
# ํ์ต/ํ
์คํธ ๋ถํ
split_idx = int(len(X) * 0.8)
X_train, X_test = X[:split_idx], X[split_idx:]
y_train, y_test = y[:split_idx], y[split_idx:]
# ๋ชจ๋ธ ํ์ต
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# ์์ธก ๋ฐ ํ๊ฐ
predictions = model.predict(X_test)
mae = np.mean(np.abs(predictions - y_test))
print(f"โ
๋ชจ๋ธ ํ์ต ์๋ฃ!")
print(f"ํ๊ท ์ ๋ ์ค์ฐจ: {mae:.2f} Gwei")
# ํ์ฌ ์์ ์์ธก
current_features = X.iloc[-1:].values
predicted_gas = model.predict(current_features)[0]
print(f"๐ฎ ์์ธก ๊ฐ์ค ๊ฐ๊ฒฉ: {predicted_gas:.2f} Gwei")
return model, predicted_gas
๐ ์น ๋์๋ณด๋ ๋ฐฐํฌํ๊ธฐ
๋ง๋ ๋์๋ณด๋๋ฅผ ๋ค๋ฅธ ์ฌ๋๋ค๊ณผ ๊ณต์ ํ๊ณ ์ถ์ง? Flask๋ Streamlit์ ์ฌ์ฉํ๋ฉด ์ฝ๊ฒ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ๋ง๋ค ์ ์์ด! ๐
๐ Streamlit์ผ๋ก ๊ฐ๋จํ ์น์ฑ ๋ง๋ค๊ธฐ
import streamlit as st
import plotly.graph_objects as go
# ํ์ด์ง ์ค์
st.set_page_config(
page_title="DApp ๋ถ์ ๋์๋ณด๋",
page_icon="๐",
layout="wide"
)
# ์ ๋ชฉ
st.title("๐ DApp ๊ฑฐ๋๋ด์ญ ๋ถ์ ๋์๋ณด๋")
st.markdown("---")
# ์ฌ์ด๋๋ฐ
with st.sidebar:
st.header("โ๏ธ ์ค์ ")
contract_address = st.text_input(
"์ปจํธ๋ํธ ์ฃผ์",
value="0x..."
)
block_range = st.slider(
"๋ถ์ํ ๋ธ๋ก ์",
min_value=100,
max_value=10000,
value=1000,
step=100
)
if st.button("๐ ๋ฐ์ดํฐ ์๋ก๊ณ ์นจ"):
st.cache_data.clear()
st.success("์บ์๊ฐ ์ด๊ธฐํ๋์์ต๋๋ค!")
# ๋ฐ์ดํฐ ๋ก๋ (์บ์ฑ)
@st.cache_data
def load_data(address, blocks):
latest = web3.eth.block_number
df = get_block_transactions(latest - blocks, latest)
return df
# ๋ฉ์ธ ์ปจํ
์ธ
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("์ด ๊ฑฐ๋ ์", "15,234", "+234")
with col2:
st.metric("ํ์ฑ ์ฌ์ฉ์", "3,456", "+12")
with col3:
st.metric("์ด ๊ฑฐ๋๋", "1,234 ETH", "+45 ETH")
with col4:
st.metric("ํ๊ท ๊ฐ์ค", "45 Gwei", "-5 Gwei")
st.markdown("---")
# ์ฐจํธ ํ์
tab1, tab2, tab3 = st.tabs(["๐ ๊ฑฐ๋๋", "๐ฐ ๊ธ์ก ๋ถํฌ", "โฝ ๊ฐ์ค ๋ถ์"])
with tab1:
st.subheader("์๊ฐ๋๋ณ ๊ฑฐ๋๋")
# ์ฌ๊ธฐ์ Plotly ์ฐจํธ ์ถ๊ฐ
st.plotly_chart(create_interactive_timeline(df), use_container_width=True)
with tab2:
st.subheader("๊ฑฐ๋ ๊ธ์ก ๋ถํฌ")
# ํ์คํ ๊ทธ๋จ ์ถ๊ฐ
fig = px.histogram(df, x='value_eth', nbins=50)
st.plotly_chart(fig, use_container_width=True)
with tab3:
st.subheader("๊ฐ์ค ๊ฐ๊ฒฉ ์ถ์ด")
# ์ ๊ทธ๋ํ ์ถ๊ฐ
fig = px.line(df, x='timestamp', y='gas_price')
st.plotly_chart(fig, use_container_width=True)
# ์คํ: streamlit run app.py
Streamlit์ ์ ๋ง ๊ฐ๋จํ๊ฒ ์น์ฑ์ ๋ง๋ค ์ ์์ด์ ํ๋กํ ํ์ ์ ์์ ์ต๊ณ ์ผ! ์ฝ๋ ๋ช ์ค๋ง์ผ๋ก ๋ฉ์ง ๋์๋ณด๋๊ฐ ์์ฑ๋ผ. ๐
๐ก ๋ฐฐํฌ ์ต์
โข Streamlit Cloud: ๋ฌด๋ฃ๋ก Streamlit ์ฑ ํธ์คํ
(GitHub ์ฐ๋)
โข Heroku: ๊ฐ๋จํ ๋ฐฐํฌ, ๋ฌด๋ฃ ํฐ์ด ์์
โข AWS EC2: ๋ ๋ง์ ์ ์ด๊ถ, ๋น์ฉ ๋ฐ์
โข Vercel: Next.js์ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ข์
โข Docker: ์ด๋์๋ ๋์ผํ ํ๊ฒฝ์ผ๋ก ์คํ
๐ฏ ํ๋ก์ ํธ ํ์ฅ ์์ด๋์ด
๊ธฐ๋ณธ์ ์ธ ์๊ฐํ๋ฅผ ๋ง์คํฐํ๋ค๋ฉด, ์ด์ ๋ ์ฌ๋ฏธ์๋ ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐํด๋ณผ ์ฐจ๋ก์ผ! ๋ช ๊ฐ์ง ์์ด๋์ด๋ฅผ ์ ์ํด์ค๊ฒ. ๐ก
1. ์ค์๊ฐ ์๋ฆผ ์์คํ ๐
ํน์ ์กฐ๊ฑด์ด ๋ง์กฑ๋๋ฉด ํ
๋ ๊ทธ๋จ์ด๋ ๋์ค์ฝ๋๋ก ์๋ฆผ์ ๋ณด๋ด๋ ๋ด์ ๋ง๋ค์ด๋ด. ์๋ฅผ ๋ค์ด:
โข ํน์ ์ฃผ์์์ ๋๋ ๊ฑฐ๋ ๋ฐ์
โข ๊ฐ์ค ๊ฐ๊ฒฉ์ด ํน์ ์๊ณ๊ฐ ์ดํ๋ก ๋จ์ด์ง
โข ์๋ก์ด ์ค๋งํธ ์ปจํธ๋ํธ ๋ฐฐํฌ ๊ฐ์ง
2. ์ง๊ฐ ํฌํธํด๋ฆฌ์ค ํธ๋์ปค ๐ผ
์ฌ๋ฌ ์ง๊ฐ ์ฃผ์๋ฅผ ์ถ์ ํด์ ์ ์ฒด ํฌํธํด๋ฆฌ์ค ๊ฐ์น๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ณด์ฌ์ฃผ๋ ์ฑ. DeFi ํ๋กํ ์ฝ์ ์์น๋ ์์ฐ๊น์ง ํฌํจํด์ ๊ณ์ฐํ๋ฉด ๋ ์ข์!
3. ๊ฐ์ค ์ต์ ํ ๋๊ตฌ โก
๊ณผ๊ฑฐ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํด์ ๊ฐ์ค ๊ฐ๊ฒฉ์ด ๊ฐ์ฅ ์ ๋ ดํ ์๊ฐ๋๋ฅผ ์ถ์ฒํด์ฃผ๋ ๋๊ตฌ. ์ฌ์ฉ์๊ฐ ๊ฑฐ๋๋ฅผ ์์ฝํ ์ ์๊ฒ ํ๋ฉด ์๋ฒฝ!
4. ์ค๋งํธ ๋จธ๋ ์ถ์ ๊ธฐ ๐ง
์ฑ๊ณต์ ์ธ ํธ๋ ์ด๋๋ค์ ์ง๊ฐ์ ์ถ์ ํด์ ๊ทธ๋ค์ ๊ฑฐ๋ ํจํด์ ๋ถ์. ์ด๋ค ํ ํฐ์ ์ฌ๊ณ ํ๋์ง, ์ธ์ ์์ง์ด๋์ง ํ์ ํ ์ ์์ด.
5. ๋ฉํฐ์ฒด์ธ ๋์๋ณด๋ ๐
์ด๋๋ฆฌ์๋ฟ๋ง ์๋๋ผ BSC, Polygon, Arbitrum ๋ฑ ์ฌ๋ฌ ์ฒด์ธ์ ๋ฐ์ดํฐ๋ฅผ ํ ๊ณณ์์ ๋ณผ ์ ์๋ ํตํฉ ๋์๋ณด๋!
๐ ํธ๋ฌ๋ธ์ํ ๊ฐ์ด๋
๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ๊ผญ ๋ฌธ์ ๊ฐ ์๊ธฐ์ง. ์์ฃผ ๋ฐ์ํ๋ ์๋ฌ๋ค๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ ๋ฆฌํด๋ดค์ด! ๐ง
โ ์์ฃผ ๋ฐ์ํ๋ ์๋ฌ๋ค
1. "ValueError: {'code': -32000, 'message': 'execution reverted'}"
โ ์ค๋งํธ ์ปจํธ๋ํธ ํจ์ ํธ์ถ์ด ์คํจํ ๊ฑฐ์ผ. ํจ์ ํ๋ผ๋ฏธํฐ๋ ๊ถํ์ ํ์ธํด๋ด.
2. "Web3.exceptions.BlockNotFound"
โ ์กด์ฌํ์ง ์๋ ๋ธ๋ก ๋ฒํธ๋ฅผ ์์ฒญํ ๊ฑฐ์ผ. ์ต์ ๋ธ๋ก ๋ฒํธ๋ฅผ ๋จผ์ ํ์ธํ๊ณ ๋ฒ์ ๋ด์์ ์์ฒญํด.
3. "requests.exceptions.HTTPError: 429"
โ API ์์ฒญ ์ ํ์ ๊ฑธ๋ฆฐ ๊ฑฐ์ผ. ์์ฒญ ์ฌ์ด์ ๋๋ ์ด๋ฅผ ๋ฃ๊ฑฐ๋ ์ ๋ฃ ํ๋์ ๊ณ ๋ คํด๋ด.
4. "InvalidAddress"
โ ์ฃผ์ ํ์์ด ์๋ชป๋์ด. web3.to_checksum_address()๋ฅผ ์ฌ์ฉํด์ ๋ณํํด.
5. "OverflowError: Python int too large"
โ Wei ๋จ์์ ํฐ ์ซ์๋ฅผ ๋ค๋ฃฐ ๋ ๋ฐ์ํด. web3.from_wei()๋ก ๋ณํํด์ ์ฌ์ฉํด.
๐ ์๋ฌ ์ฒ๋ฆฌ ๋ฒ ์คํธ ํ๋ํฐ์ค
def safe_get_transaction(tx_hash, max_retries=3):
"""
์์ ํ ํธ๋์ญ์
์กฐํ (์ฌ์๋ ๋ก์ง ํฌํจ)
"""
for attempt in range(max_retries):
try:
tx = web3.eth.get_transaction(tx_hash)
return tx
except Exception as e:
logging.warning(f"์๋ {attempt + 1}/{max_retries} ์คํจ: {e}")
if attempt < max_retries - 1:
time.sleep(2 ** attempt) # ์ง์ ๋ฐฑ์คํ
else:
logging.error(f"์ต์ข
์คํจ: {tx_hash}")
return None
# ์ฌ์ฉ ์์
tx = safe_get_transaction("0x...")
if tx:
print("โ
๊ฑฐ๋ ์กฐํ ์ฑ๊ณต!")
else:
print("โ ๊ฑฐ๋ ์กฐํ ์คํจ")
๐ ์ถ๊ฐ ํ์ต ๋ฆฌ์์ค
๋ ๊น์ด ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด ์ด๋ฐ ๋ฆฌ์์ค๋ค์ ์ถ์ฒํด! ๐
๊ณต์ ๋ฌธ์
โข Web3.py ๊ณต์ ๋ฌธ์: web3py.readthedocs.io
โข Ethereum ๊ฐ๋ฐ์ ๋ฌธ์: ethereum.org/developers
โข Plotly ๋ฌธ์: plotly.com/python
์จ๋ผ์ธ ๊ฐ์
โข Udemy: "Ethereum and Solidity: The Complete Developer's Guide"
โข Coursera: "Blockchain Specialization"
โข YouTube: "Dapp University" ์ฑ๋
์ปค๋ฎค๋ํฐ
โข Ethereum Stack Exchange
โข Reddit: r/ethdev
โข Discord: Ethereum ๊ฐ๋ฐ์ ์๋ฒ
โข ์ฌ๋ฅ๋ท์ ๋ธ๋ก์ฒด์ธ ์นดํ
๊ณ ๋ฆฌ์์๋ ๋ค์ํ ์ ๋ฌธ๊ฐ๋ค๊ณผ ์ํตํ ์ ์์ด!
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ฌ๊ธฐ๊น์ง ์๋ค๋ ์ ๋ง ๋๋จํด! ๐ ์ฐ๋ฆฌ๊ฐ ํจ๊ป ๋ฐฐ์ด ๋ด์ฉ์ ์ ๋ฆฌํด๋ณผ๊น?
โ
์ฐ๋ฆฌ๊ฐ ๋ฐฐ์ด ๊ฒ๋ค
1. Web3.py๋ก ๋ธ๋ก์ฒด์ธ ๋ฐ์ดํฐ ์์งํ๊ธฐ
2. Pandas๋ก ๋ฐ์ดํฐ ์ ์ ๋ฐ ๋ถ์ํ๊ธฐ
3. Matplotlib, Seaborn์ผ๋ก ์ ์ ์ฐจํธ ๋ง๋ค๊ธฐ
4. Plotly๋ก ์ธํฐ๋ํฐ๋ธ ๋์๋ณด๋ ๊ตฌํํ๊ธฐ
5. ๋คํธ์ํฌ ๊ทธ๋ํ๋ก ๊ฑฐ๋ ๊ด๊ณ ์๊ฐํํ๊ธฐ
6. ์ค์๊ฐ ๋ชจ๋ํฐ๋ง ์์คํ
๊ตฌ์ถํ๊ธฐ
7. ์น ์ ํ๋ฆฌ์ผ์ด์
์ผ๋ก ๋ฐฐํฌํ๊ธฐ
8. ๋ณด์ ๋ฐ ์ต์ ํ ๋ฒ ์คํธ ํ๋ํฐ์ค
์ด์ ๋๋ ๋ธ๋ก์ฒด์ธ ๋ฐ์ดํฐ๋ฅผ ์์ ์์ฌ๋ก ๋ค๋ฃฐ ์ ์๋ ๋ฅ๋ ฅ์ ๊ฐ์ท์ด! ๐ ์ด ๊ธฐ์ ์ ๋จ์ํ ๊ฑฐ๋๋ด์ญ์ ๋ณด๋ ๊ฒ์ ๋์ด์, DeFi ํ๋กํ ์ฝ ๋ถ์, NFT ํธ๋ ๋ ํ์
, ์จ์ฒด์ธ ๋ฐ์ดํฐ ๊ธฐ๋ฐ ํฌ์ ์ ๋ต ์๋ฆฝ ๋ฑ ๋ค์ํ ๋ถ์ผ์ ํ์ฉํ ์ ์์ด.
ํนํ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๋ธ๋ก์ฒด์ธ ๊ด๋ จ ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๊ฑฐ๋, ๋ฐ์ดํฐ ๋ถ์ ์ฌ๋ฅ์ ๊ณต์ ํ ๋ ์ด๋ฐ ๊ธฐ์ ์ด ํฐ ๋์์ด ๋ ๊ฑฐ์ผ. ๋ธ๋ก์ฒด์ธ ๋ฐ์ดํฐ ๋ถ์์ ์์ผ๋ก ๋์ฑ ์ค์ํด์ง ๋ถ์ผ๋๊น, ์ง๊ธ ๋ฐฐ์ด ๊ฑธ ๋ฐํ์ผ๋ก ๊ณ์ ๋ฐ์ ์์ผ๋๊ฐ๋ฉด ์ข๊ฒ ์ด! ๐ช
ํน์ ๋งํ๋ ๋ถ๋ถ์ด ์๊ฑฐ๋ ๋ ๊ถ๊ธํ ๊ฒ ์๋ค๋ฉด, ์ปค๋ฎค๋ํฐ์ ์ง๋ฌธํ๊ฑฐ๋ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด๋ด. ๊ทธ๋ฆฌ๊ณ ๋ฌด์๋ณด๋ค ์ค์ํ ๊ฑด ์ง์ ๋ง๋ค์ด๋ณด๋ ๊ฒ์ด์ผ! ์์ ํ๋ก์ ํธ๋ถํฐ ์์ํด์ ์ ์ ํ์ฅํด๋๊ฐ๋ค ๋ณด๋ฉด, ์ด๋์ ๋ฉ์ง DApp ๋ถ์ ํ๋ซํผ์ ๋ง๋ค๊ณ ์๋ ์์ ์ ๋ฐ๊ฒฌํ๊ฒ ๋ ๊ฑฐ์ผ! ๐
ํ์ดํ
! ๊ทธ๋ฆฌ๊ณ ์ฆ๊ฑฐ์ด ์ฝ๋ฉ ๋๊ธธ ๋ฐ๋๊ฒ! ๐จโจ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.