- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 도입
- 1. 블록체인 기초 원리
- 2. Ethereum과 EVM 깊이 이해하기
- 3. Solidity 스마트 컨트랙트 개발
- 4. 개발 도구: Hardhat vs Foundry
- 5. 스마트 컨트랙트 보안
- 6. Layer 2 솔루션 비교
- 7. DeFi 프로토콜 이해
- 8. Gas Optimization 기법
- 9. Web3 프론트엔드 통합
- 10. Web3 커리어 가이드
- 11. 실전 퀴즈
- 12. 참고 자료
도입
2025년 블록체인 생태계는 투기적 광풍의 시대를 지나 실질적인 기술 인프라로 자리 잡았습니다. Ethereum의 Dencun 업그레이드, Layer 2 롤업의 폭발적 성장, 그리고 RWA(Real World Assets) 토큰화가 주류 금융과 만나면서, Web3 개발자에 대한 수요는 사상 최고치를 기록하고 있습니다.
이 가이드에서는 블록체인의 핵심 원리부터 Solidity 스마트 컨트랙트 개발, 보안, DeFi/NFT 생태계, 그리고 Web3 개발자 커리어까지 체계적으로 다룹니다.
핵심 표현 정리
표현 의미 Smart Contract 블록체인 위에서 자동 실행되는 프로그램. 코드가 곧 계약 Layer 2 메인넷(L1) 위에 구축된 확장 솔루션. 처리량 증가, 비용 감소 DeFi 탈중앙화 금융. 중개자 없이 스마트 컨트랙트로 금융 서비스 제공 Gas Optimization 스마트 컨트랙트 실행 비용(Gas)을 최소화하는 최적화 기법
1. 블록체인 기초 원리
1.1 블록체인이란?
블록체인은 분산 네트워크의 모든 참여자가 동일한 거래 기록(원장)을 공유하는 기술입니다. 각 블록은 이전 블록의 해시를 포함하여 체인을 형성합니다.
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Block 0 │───▶│ Block 1 │───▶│ Block 2 │
│ (Genesis)│ │ │ │ │
│ Hash: 0a │ │ Prev: 0a │ │ Prev: 7f │
│ Nonce: 42│ │ Hash: 7f │ │ Hash: b3 │
│ Txs: [] │ │ Txs: [..] │ │ Txs: [..]│
└──────────┘ └──────────┘ └──────────┘
1.2 합의 메커니즘 비교
| 항목 | PoW (Proof of Work) | PoS (Proof of Stake) | DPoS |
|---|---|---|---|
| 에너지 소비 | 매우 높음 | 낮음 | 낮음 |
| 분산성 | 높음 | 중간 | 낮음 |
| TPS | 7-15 | 30-100K+ | 1000+ |
| 대표 체인 | Bitcoin | Ethereum 2.0 | EOS, Tron |
| 참여 조건 | 해시파워 (GPU/ASIC) | 스테이킹 자산 | 투표 위임 |
| 파이널리티 | 확률적 (6블록) | 에포크 기반 확정 | 빠른 확정 |
1.3 해시 함수와 머클 트리
블록체인의 무결성은 암호학적 해시 함수에 의존합니다.
import hashlib
def calculate_hash(block_data):
"""블록 데이터의 SHA-256 해시 계산"""
data_string = str(block_data).encode()
return hashlib.sha256(data_string).hexdigest()
# 머클 트리 구현
def merkle_root(transactions):
if len(transactions) == 0:
return hashlib.sha256(b'').hexdigest()
if len(transactions) == 1:
return transactions[0]
new_level = []
for i in range(0, len(transactions), 2):
left = transactions[i]
right = transactions[i + 1] if i + 1 < len(transactions) else left
combined = hashlib.sha256((left + right).encode()).hexdigest()
new_level.append(combined)
return merkle_root(new_level)
1.4 트랜잭션 생명주기
사용자 → 트랜잭션 생성 → 서명(개인키) → 브로드캐스트
→ Mempool 진입 → 검증자/채굴자 선택 → 블록 포함
→ 합의 → 블록 확정 → 상태 업데이트
2. Ethereum과 EVM 깊이 이해하기
2.1 Ethereum 아키텍처
Ethereum은 월드 컴퓨터라는 비전으로 시작되었습니다. 비트코인이 거래 기록만 저장하는 반면, Ethereum은 Turing-complete 가상 머신(EVM)을 통해 임의의 로직을 실행할 수 있습니다.
┌─────────────────────────────────────────┐
│ Ethereum Network │
├─────────────────────────────────────────┤
│ ┌─────────┐ ┌──────────┐ ┌────────┐ │
│ │ EOA │ │ Contract │ │Contract│ │
│ │ Account │──│ Account │──│Account │ │
│ │ (사용자)│ │ (코드) │ │(코드) │ │
│ └─────────┘ └──────────┘ └────────┘ │
├─────────────────────────────────────────┤
│ EVM (실행 엔진) │
├─────────────────────────────────────────┤
│ State Trie (상태 저장) │
└─────────────────────────────────────────┘
2.2 EVM 동작 원리
EVM은 스택 기반 가상 머신으로, 256비트 워드 크기를 사용합니다.
EVM 메모리 모델:
┌─────────────┐
│ Stack │ ← 최대 1024 깊이, LIFO
│ (256-bit) │
├─────────────┤
│ Memory │ ← 바이트 배열, 휘발성
│ (확장 가능)│
├─────────────┤
│ Storage │ ← key-value, 영구 저장
│ (256→256) │
└─────────────┘
주요 Opcode 예시:
PUSH1 0x60 // 스택에 0x60 push
PUSH1 0x40 // 스택에 0x40 push
MSTORE // Memory[0x40] = 0x60
CALLVALUE // msg.value를 스택에
DUP1 // 스택 top 복제
ISZERO // 0이면 1, 아니면 0
2.3 Gas 시스템
모든 EVM 연산에는 Gas 비용이 부과됩니다.
| 연산 | Gas 비용 | 설명 |
|---|---|---|
| ADD / SUB | 3 | 산술 연산 |
| MUL / DIV | 5 | 곱셈 / 나눗셈 |
| SLOAD | 2100 (cold) / 100 (warm) | Storage 읽기 |
| SSTORE | 20000 (new) / 5000 (update) | Storage 쓰기 |
| CALL | 2600 (cold) / 100 (warm) | 외부 호출 |
| CREATE | 32000 | 컨트랙트 생성 |
Gas 비용 계산 공식:
총 비용 = Gas Used * Gas Price (Gwei)
= Gas Used * (Base Fee + Priority Fee)
예시: 21000 Gas * 30 Gwei = 630,000 Gwei = 0.00063 ETH
3. Solidity 스마트 컨트랙트 개발
3.1 기본 문법
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title SimpleStorage
* @dev 기본적인 상태 저장 컨트랙트
*/
contract SimpleStorage {
// 상태 변수
uint256 private storedValue;
address public owner;
mapping(address => uint256) public balances;
// 이벤트
event ValueChanged(address indexed setter, uint256 newValue);
// 수정자
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
constructor(uint256 _initialValue) {
owner = msg.sender;
storedValue = _initialValue;
}
function setValue(uint256 _value) external onlyOwner {
storedValue = _value;
emit ValueChanged(msg.sender, _value);
}
function getValue() external view returns (uint256) {
return storedValue;
}
}
3.2 ERC-20 토큰 구현
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20, Ownable {
uint256 public constant MAX_SUPPLY = 1_000_000 * 10**18;
constructor() ERC20("MyToken", "MTK") Ownable(msg.sender) {
_mint(msg.sender, MAX_SUPPLY);
}
function burn(uint256 amount) external {
_burn(msg.sender, amount);
}
}
3.3 ERC-721 NFT 구현
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyNFT is ERC721, ERC721URIStorage, Ownable {
uint256 private _nextTokenId;
uint256 public mintPrice = 0.05 ether;
uint256 public maxSupply = 10000;
constructor() ERC721("MyNFT", "MNFT") Ownable(msg.sender) {}
function mint(string memory uri) external payable {
require(msg.value >= mintPrice, "Insufficient payment");
require(_nextTokenId < maxSupply, "Max supply reached");
uint256 tokenId = _nextTokenId++;
_safeMint(msg.sender, tokenId);
_setTokenURI(tokenId, uri);
}
function tokenURI(uint256 tokenId)
public view override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public view override(ERC721, ERC721URIStorage)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
function withdraw() external onlyOwner {
payable(owner()).transfer(address(this).balance);
}
}
3.4 고급 패턴: Proxy (업그레이드 가능 컨트랙트)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// Implementation V1
contract BoxV1 {
uint256 private value;
function store(uint256 newValue) public {
value = newValue;
}
function retrieve() public view returns (uint256) {
return value;
}
}
// Implementation V2 (업그레이드 후)
contract BoxV2 {
uint256 private value;
function store(uint256 newValue) public {
value = newValue;
}
function retrieve() public view returns (uint256) {
return value;
}
function increment() public {
value += 1;
}
}
4. 개발 도구: Hardhat vs Foundry
4.1 Hardhat 설정 및 사용
# 프로젝트 초기화
mkdir my-project && cd my-project
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
# Hardhat 프로젝트 생성
npx hardhat init
// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
module.exports = {
solidity: {
version: "0.8.20",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL || "",
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
hardhat: {
forking: {
url: process.env.MAINNET_RPC_URL || "",
blockNumber: 18_000_000,
},
},
},
};
Hardhat 테스트 예시:
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("SimpleStorage", function () {
let storage;
let owner;
let addr1;
beforeEach(async function () {
[owner, addr1] = await ethers.getSigners();
const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
storage = await SimpleStorage.deploy(42);
});
it("Should store the initial value", async function () {
expect(await storage.getValue()).to.equal(42);
});
it("Should allow owner to set value", async function () {
await storage.setValue(100);
expect(await storage.getValue()).to.equal(100);
});
it("Should reject non-owner setValue", async function () {
await expect(
storage.connect(addr1).setValue(100)
).to.be.revertedWith("Not the owner");
});
});
4.2 Foundry 설정 및 사용
# Foundry 설치
curl -L https://foundry.paradigm.xyz | bash
foundryup
# 프로젝트 생성
forge init my-foundry-project
cd my-foundry-project
# foundry.toml
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
optimizer = true
optimizer_runs = 200
solc_version = "0.8.20"
[profile.default.fuzz]
runs = 1000
max_test_rejects = 65536
[rpc_endpoints]
mainnet = "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
sepolia = "https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY"
Foundry 테스트 (Solidity로 작성):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/SimpleStorage.sol";
contract SimpleStorageTest is Test {
SimpleStorage public store;
address public owner = address(this);
address public user = address(0x1);
function setUp() public {
store = new SimpleStorage(42);
}
function testInitialValue() public view {
assertEq(store.getValue(), 42);
}
function testSetValue() public {
store.setValue(100);
assertEq(store.getValue(), 100);
}
function testFailNonOwnerSetValue() public {
vm.prank(user);
store.setValue(100); // 이 호출은 revert 됨
}
// Fuzz 테스트
function testFuzzSetValue(uint256 value) public {
store.setValue(value);
assertEq(store.getValue(), value);
}
}
4.3 Hardhat vs Foundry 비교
| 기능 | Hardhat | Foundry |
|---|---|---|
| 언어 | JavaScript/TypeScript | Solidity |
| 테스트 속도 | 보통 | 매우 빠름 (10x+) |
| Fuzz 테스트 | 플러그인 필요 | 내장 |
| 디버깅 | console.log | forge debug (step) |
| 포크 테스트 | 지원 | 지원 (더 빠름) |
| 플러그인 생태계 | 풍부 | 성장 중 |
| Gas 리포트 | hardhat-gas-reporter | forge test --gas-report |
| 학습 곡선 | JS 개발자 친화적 | Solidity 집중 |
5. 스마트 컨트랙트 보안
5.1 Reentrancy 공격
가장 유명한 스마트 컨트랙트 취약점으로, 2016년 The DAO 해킹(6000만 달러 피해)의 원인입니다.
// 취약한 코드
contract VulnerableVault {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
// 위험: 외부 호출 후 상태 업데이트
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// 외부 호출 (공격 벡터)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
// 상태 업데이트가 외부 호출 뒤에!
balances[msg.sender] = 0;
}
}
// 공격자 컨트랙트
contract Attacker {
VulnerableVault public vault;
constructor(address _vault) {
vault = VulnerableVault(_vault);
}
function attack() external payable {
vault.deposit{value: msg.value}();
vault.withdraw();
}
// withdraw()의 call이 여기로 돌아옴 -> 다시 withdraw() 호출
receive() external payable {
if (address(vault).balance > 0) {
vault.withdraw();
}
}
}
해결 방법: Checks-Effects-Interactions 패턴
contract SecureVault {
mapping(address => uint256) public balances;
bool private locked;
modifier noReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
function withdraw() external noReentrant {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// Effects (상태 업데이트 먼저)
balances[msg.sender] = 0;
// Interactions (외부 호출 나중에)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
5.2 Integer Overflow/Underflow
Solidity 0.8.0 이전에는 자동 오버플로우 체크가 없었습니다.
// Solidity 0.7.x 이전: 위험!
// uint8 max = 255; max + 1 = 0 (overflow)
// uint8 min = 0; min - 1 = 255 (underflow)
// Solidity 0.8.0+: 자동으로 revert
// 성능이 필요한 경우 unchecked 블록 사용
function unsafeIncrement(uint256 x) pure returns (uint256) {
unchecked {
return x + 1; // overflow 체크 안 함 (Gas 절약)
}
}
5.3 보안 체크리스트
보안 감사 체크리스트:
[1] Reentrancy
- CEI 패턴 적용 확인
- ReentrancyGuard 사용 여부
[2] 접근 제어
- onlyOwner / Role 기반 접근 제어
- 초기화 함수 보호
[3] 산술 연산
- Solidity 0.8+ 사용 (자동 체크)
- unchecked 블록 검토
[4] 외부 호출
- call 반환값 체크
- 가스 제한 설정
[5] 프론트러닝
- commit-reveal 패턴
- Flashbots 사용 고려
[6] 오라클 조작
- TWAP (시간 가중 평균 가격) 사용
- 다중 오라클 소스
[7] 플래시론 공격
- 단일 트랜잭션 조작 방어
- 가격 검증 로직
[8] 스토리지 충돌 (Proxy)
- EIP-1967 스토리지 슬롯
- 업그레이드 테스트
5.4 주요 해킹 사례
| 사건 | 연도 | 피해액 | 취약점 |
|---|---|---|---|
| The DAO | 2016 | 6000만 달러 | Reentrancy |
| Ronin Bridge | 2022 | 6.25억 달러 | 개인키 탈취 |
| Wormhole | 2022 | 3.2억 달러 | 서명 검증 우회 |
| Euler Finance | 2023 | 1.97억 달러 | Donate 함수 취약점 |
| Mango Markets | 2022 | 1.14억 달러 | 오라클 조작 |
6. Layer 2 솔루션 비교
6.1 Layer 2란?
Layer 2(L2)는 Ethereum 메인넷(L1)의 보안을 상속하면서 처리량을 확장하는 솔루션입니다.
┌─────────────────────────────────────────┐
│ 사용자 (Dapp) │
├────────────┬────────────┬───────────────┤
│ Optimistic │ ZK Rollup │ Validium │
│ Rollup │ │ │
│ (Arbitrum, │ (zkSync, │ (StarkEx, │
│ Optimism, │ Scroll, │ zkPorter) │
│ Base) │ Linea) │ │
├────────────┴────────────┴───────────────┤
│ Ethereum L1 (보안 레이어) │
└─────────────────────────────────────────┘
6.2 Optimistic vs ZK Rollup
| 특성 | Optimistic Rollup | ZK Rollup |
|---|---|---|
| 검증 방식 | 사기 증명 (Fraud Proof) | 유효성 증명 (Validity Proof) |
| 인출 시간 | 7일 (챌린지 기간) | 수분~수시간 |
| EVM 호환 | 높음 | 성장 중 (zkEVM) |
| 가스 비용 | L1 대비 10-50x 저렴 | L1 대비 50-100x 저렴 |
| 대표 체인 | Arbitrum, Optimism, Base | zkSync Era, Scroll, Linea |
| 성숙도 | 높음 | 빠르게 성장 |
6.3 주요 L2 체인 상세 비교
TVL (Total Value Locked) 순위 (2025년 기준):
1. Arbitrum One - TVL: 약 180억 달러
- Nitro 기술 스택
- EVM 완전 호환
- Stylus (Rust/C++ 지원)
2. Base - TVL: 약 120억 달러
- Coinbase 지원
- OP Stack 기반
- 소비자 앱 강세
3. Optimism - TVL: 약 80억 달러
- OP Stack (Superchain 비전)
- Bedrock 업그레이드
- RetroPGF (공공재 펀딩)
4. zkSync Era - TVL: 약 40억 달러
- zkEVM (LLVM 기반)
- 네이티브 Account Abstraction
- Hyperchain 확장
5. Scroll - TVL: 약 20억 달러
- Type-2 zkEVM
- 이더리움 바이트코드 호환
6.4 L2에서의 개발
// Arbitrum에 배포하기 (Hardhat)
// hardhat.config.js에 네트워크 추가
module.exports = {
networks: {
arbitrum: {
url: "https://arb1.arbitrum.io/rpc",
accounts: [process.env.PRIVATE_KEY],
chainId: 42161,
},
base: {
url: "https://mainnet.base.org",
accounts: [process.env.PRIVATE_KEY],
chainId: 8453,
},
optimism: {
url: "https://mainnet.optimism.io",
accounts: [process.env.PRIVATE_KEY],
chainId: 10,
},
},
};
// 배포 스크립트
async function main() {
const Contract = await ethers.getContractFactory("MyContract");
const contract = await Contract.deploy();
await contract.waitForDeployment();
console.log("Deployed to:", await contract.getAddress());
}
7. DeFi 프로토콜 이해
7.1 DeFi 핵심 프리미티브
DeFi 스택:
┌─────────────────────────────────────┐
│ Aggregator / Frontend │
│ (1inch, DefiLlama, Zapper) │
├─────────────────────────────────────┤
│ Application Layer │
│ (Aave, Uniswap, MakerDAO) │
├─────────────────────────────────────┤
│ Protocol Layer │
│ (AMM, Lending, Derivatives) │
├─────────────────────────────────────┤
│ Asset Layer │
│ (ERC-20, ERC-721, Wrapped) │
├─────────────────────────────────────┤
│ Settlement Layer │
│ (Ethereum, L2 Rollups) │
└─────────────────────────────────────┘
7.2 AMM (Automated Market Maker)
Uniswap V2의 핵심 공식: x * y = k
// 간단한 AMM 구현
contract SimpleAMM {
uint256 public reserveA;
uint256 public reserveB;
uint256 public totalLiquidity;
mapping(address => uint256) public liquidity;
// 유동성 공급
function addLiquidity(uint256 amountA, uint256 amountB) external {
// 실제로는 토큰 전송 로직 포함
reserveA += amountA;
reserveB += amountB;
uint256 liq;
if (totalLiquidity == 0) {
liq = sqrt(amountA * amountB);
} else {
liq = min(
(amountA * totalLiquidity) / reserveA,
(amountB * totalLiquidity) / reserveB
);
}
liquidity[msg.sender] += liq;
totalLiquidity += liq;
}
// 스왑: A를 넣고 B를 받기
function swapAForB(uint256 amountAIn) external returns (uint256) {
// x * y = k 기반 계산
uint256 amountBOut = (amountAIn * reserveB) / (reserveA + amountAIn);
// 0.3% 수수료 적용
amountBOut = (amountBOut * 997) / 1000;
reserveA += amountAIn;
reserveB -= amountBOut;
return amountBOut;
}
function sqrt(uint256 x) internal pure returns (uint256) {
uint256 z = (x + 1) / 2;
uint256 y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
return y;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}
7.3 Lending Protocol 개념
Lending Protocol (예: Aave):
1. 예치 (Supply)
사용자 → 자산 예치 → aToken 수령 (이자 자동 누적)
2. 대출 (Borrow)
담보 예치 → 건전성 비율(HF) 확인 → 자산 대출
HF = 담보가치 * LTV / 대출가치
3. 청산 (Liquidation)
HF가 1 미만 → 청산자가 부채 상환 → 담보 할인 매입
담보비율 예시:
- ETH: LTV 80%, 청산 임계값 82.5%
- USDC: LTV 77%, 청산 임계값 80%
8. Gas Optimization 기법
8.1 Storage 최적화
// Bad: 각 변수가 별도 슬롯 사용
contract BadPacking {
uint256 a; // slot 0 (32 bytes)
uint8 b; // slot 1 (1 byte, 나머지 31 낭비)
uint256 c; // slot 2
uint8 d; // slot 3
}
// 총 4 슬롯 사용
// Good: 변수 패킹으로 슬롯 절약
contract GoodPacking {
uint256 a; // slot 0
uint256 c; // slot 1
uint8 b; // slot 2 (b와 d가 같은 슬롯)
uint8 d; // slot 2
}
// 총 3 슬롯 사용 (SSTORE 20000 gas 절약)
8.2 루프 최적화
// Bad
function sumBad(uint256[] memory arr) public pure returns (uint256) {
uint256 total = 0;
for (uint256 i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
// Good: length 캐싱 + unchecked increment
function sumGood(uint256[] memory arr) public pure returns (uint256) {
uint256 total = 0;
uint256 len = arr.length;
for (uint256 i = 0; i < len; ) {
total += arr[i];
unchecked { ++i; }
}
return total;
}
8.3 기타 최적화 기법
// 1. calldata vs memory (읽기 전용 매개변수)
function process(uint256[] calldata data) external pure { /* ... */ }
// calldata: 수정 불가, Gas 저렴
// memory: 수정 가능, Gas 비쌈
// 2. Custom Errors (Solidity 0.8.4+)
error Unauthorized(address caller);
error InsufficientBalance(uint256 available, uint256 required);
function withdraw(uint256 amount) external {
if (msg.sender != owner) revert Unauthorized(msg.sender);
if (balances[msg.sender] < amount)
revert InsufficientBalance(balances[msg.sender], amount);
}
// require(string)보다 Gas 효율적
// 3. immutable과 constant
uint256 public constant MAX_SUPPLY = 10000; // 컴파일 타임 상수
address public immutable deployer; // 배포 시 설정, 이후 불변
// 둘 다 SLOAD 대신 코드에 직접 임베드 = Gas 절약
// 4. 비트 연산 활용
function isEven(uint256 n) pure returns (bool) {
return n & 1 == 0; // n % 2 == 0 보다 효율적
}
function multiplyByPowerOf2(uint256 n, uint256 pow) pure returns (uint256) {
return n << pow; // n * (2**pow)
}
9. Web3 프론트엔드 통합
9.1 ethers.js / viem
// ethers.js v6 기본 사용
import { ethers, BrowserProvider, Contract } from "ethers";
// 지갑 연결
async function connectWallet() {
if (!window.ethereum) {
throw new Error("MetaMask not installed");
}
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const address = await signer.getAddress();
const balance = await provider.getBalance(address);
console.log("Address:", address);
console.log("Balance:", ethers.formatEther(balance), "ETH");
return signer;
}
// 컨트랙트 인터랙션
async function interactWithContract(signer) {
const contractAddress = "0x...";
const abi = [
"function getValue() view returns (uint256)",
"function setValue(uint256) external",
"event ValueChanged(address indexed, uint256)"
];
const contract = new Contract(contractAddress, abi, signer);
// 읽기
const value = await contract.getValue();
console.log("Current value:", value.toString());
// 쓰기
const tx = await contract.setValue(42);
await tx.wait();
console.log("Transaction confirmed:", tx.hash);
}
9.2 wagmi + viem (React)
// wagmi v2 설정
import { createConfig, http } from "wagmi";
import { mainnet, arbitrum, base } from "wagmi/chains";
import { injected, walletConnect } from "wagmi/connectors";
const config = createConfig({
chains: [mainnet, arbitrum, base],
connectors: [
injected(),
walletConnect({ projectId: "YOUR_PROJECT_ID" }),
],
transports: {
[mainnet.id]: http(),
[arbitrum.id]: http(),
[base.id]: http(),
},
});
// React 컴포넌트
function WalletConnect() {
const { address, isConnected } = useAccount();
const { connect } = useConnect();
const { disconnect } = useDisconnect();
const { data: balance } = useBalance({ address });
if (isConnected) {
return (
<div>
<p>Connected: {address}</p>
<p>Balance: {balance?.formatted} {balance?.symbol}</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
return (
<button onClick={() => connect({ connector: injected() })}>
Connect Wallet
</button>
);
}
10. Web3 커리어 가이드
10.1 포지션별 연봉 (2025년 기준)
| 포지션 | 경력 | 연봉 범위 (USD) |
|---|---|---|
| Junior Solidity Dev | 0-2년 | 80K-120K |
| Mid Solidity Dev | 2-4년 | 120K-180K |
| Senior Smart Contract Dev | 4년+ | 180K-250K |
| Smart Contract Auditor | 3년+ | 150K-300K+ |
| Protocol Engineer | 5년+ | 200K-350K+ |
| DeFi Researcher | 3년+ | 150K-250K |
| Web3 Frontend (React + ethers) | 2년+ | 100K-180K |
| Blockchain Infra Engineer | 3년+ | 140K-220K |
10.2 필수 기술 스택
Web3 개발자 로드맵:
기초 (3-6개월):
├── Solidity 문법 및 패턴
├── EVM 이해
├── Hardhat 또는 Foundry
├── ethers.js / viem
└── OpenZeppelin 라이브러리
중급 (6-12개월):
├── DeFi 프로토콜 이해 (AMM, Lending)
├── Security (일반 취약점 + 감사)
├── 가스 최적화
├── Layer 2 개발
├── The Graph (인덱싱)
└── IPFS / Arweave (분산 스토리지)
고급 (12개월+):
├── MEV (Maximal Extractable Value)
├── ZK Proof 기초
├── Cross-chain 브릿지
├── Account Abstraction (ERC-4337)
├── 프로토콜 설계
└── 정형 검증 (Formal Verification)
10.3 포트폴리오 전략
Web3 채용 시장에서 가장 중요한 것은 온체인 증거입니다.
- GitHub에 프로젝트 공개: 테스트, 문서화 포함
- 테스트넷 배포: Sepolia, Base Goerli 등에 실제 배포
- 감사 경험: Code4rena, Sherlock 등의 감사 경진대회 참여
- 기여 활동: OpenZeppelin, Uniswap 등 OSS 기여
- 블로그/스레드: 기술 분석 글 공유
11. 실전 퀴즈
각 문제를 풀고 답을 확인해 보세요.
Q1. Reentrancy 공격을 방지하기 위한 패턴 이름은?
Checks-Effects-Interactions (CEI) 패턴
상태 확인(Checks) 후 상태 변경(Effects)을 먼저 수행하고, 마지막에 외부 호출(Interactions)을 합니다. 추가로 OpenZeppelin의 ReentrancyGuard(nonReentrant modifier)를 사용할 수 있습니다.
Q2. Optimistic Rollup과 ZK Rollup의 가장 큰 차이점은?
검증 방식의 차이입니다.
- Optimistic Rollup: 트랜잭션이 유효하다고 낙관적으로 가정하고, 7일간의 챌린지 기간 동안 사기 증명(Fraud Proof)으로 이의를 제기할 수 있습니다.
- ZK Rollup: 모든 트랜잭션 배치에 대해 수학적 유효성 증명(Validity Proof)을 즉시 생성하여 L1에 제출합니다. 인출이 더 빠릅니다.
Q3. EVM에서 SSTORE (새 값 저장)의 가스 비용은 얼마인가?
20,000 Gas (새로운 스토리지 슬롯에 값을 기록하는 경우)
기존 값을 업데이트하는 경우는 5,000 Gas입니다. 이것이 Storage 최적화가 중요한 이유이며, 변수 패킹, constant/immutable 사용 등으로 SSTORE 호출을 줄여야 합니다.
Q4. Uniswap V2의 AMM 핵심 공식은?
x * y = k (Constant Product Formula)
x는 토큰 A의 수량, y는 토큰 B의 수량, k는 상수입니다. 한 토큰을 넣으면 k를 유지하도록 다른 토큰이 빠져나옵니다. 이 공식은 슬리피지와 가격 영향을 자연스럽게 만듭니다.
Q5. Solidity에서 Gas를 절약하기 위해 require(string) 대신 사용하는 것은?
Custom Errors (Solidity 0.8.4+)
error Unauthorized();로 선언하고 revert Unauthorized();로 사용합니다. require에 문자열 메시지를 넣는 것보다 Gas 효율적이며, 매개변수도 포함할 수 있어 디버깅에도 유용합니다.
12. 참고 자료
- Ethereum 공식 문서 - Ethereum 개발 기초
- Solidity 공식 문서 - Solidity 언어 레퍼런스
- OpenZeppelin Contracts - 검증된 스마트 컨트랙트 라이브러리
- Hardhat 공식 문서 - Hardhat 개발 환경
- Foundry Book - Foundry 개발 도구
- Ethereum EVM Illustrated - EVM 시각 자료
- SWC Registry - 스마트 컨트랙트 취약점 분류
- DeFiLlama - DeFi TVL 및 프로토콜 데이터
- L2Beat - Layer 2 비교 및 TVL 트래킹
- Chainlink 공식 문서 - 오라클 솔루션
- EIP-4337: Account Abstraction - Account Abstraction 표준
- Secureum - 스마트 컨트랙트 보안 학습
- Code4rena - 스마트 컨트랙트 감사 경진대회