블록체인Mar 28, 2026
Deep EVM #3: 가스 이해하기 — 컨트랙트 비용이 결정되는 이유
OS
Open Soft Team
Engineering Team
가스는 추상이 아니다
개발자들은 종종 가스를 모호한 “비용” 지표로 취급합니다. 실제로 가스는 정확한 공식을 가진 정밀한 자원 회계 시스템입니다. 모든 옵코드, 모든 콜데이터 바이트, 모든 스토리지 접근에는 옐로 페이퍼와 후속 EIP에 정의된 결정론적 가스 비용이 있습니다.
이 비용을 이해하면 가스 최적화가 추측에서 엔지니어링으로 변합니다.
내재 가스: 기준선
컨트랙트 코드가 단일 옵코드를 실행하기 전에 트랜잭션은 이미 가스를 소비했습니다:
intrinsic_gas = 21000 // 기본 트랜잭션 비용
+ 16 * 비영_콜데이터_바이트 // 비영 바이트당
+ 4 * 영_콜데이터_바이트 // 영 바이트당
+ access_list_cost // EIP-2930 접근 목록
+ 32000 (컨트랙트 생성인 경우) // CREATE 트랜잭션만
EIP-2929: 접근 목록 혁명
EIP-2929 이전에는 SLOAD 비용이 항상 800 가스였습니다. EIP-2929는 트랜잭션별 접근 세트를 도입하여 모든 것을 변경했습니다.
| 옵코드 | Cold | Warm | 절감 |
|---|---|---|---|
| SLOAD | 2100 | 100 | 2000 |
| BALANCE | 2600 | 100 | 2500 |
| CALL | 2600 | 100 | 2500 |
SSTORE: 가장 복잡한 옵코드
SSTORE 가스 비용은 세 가지 요소에 따라 달라집니다: 원래 값(트랜잭션 시작 시), 현재 값, 새 값.
핵심 인사이트: 제로 슬롯을 nonzero로 설정하면 20000 가스가 들고(새 상태 트리 리프 노드 기록), 기존 nonzero 슬롯을 수정하면 2900 가스(warm)입니다. 이 7배 차이가 다음을 설명합니다:
- 새 사용자를 위한 매핑 초기화가 비용이 높은 이유
- 재진입 잠금에
0대신1을 “미설정” 값으로 사용하면 17100 가스를 절약하는 이유
실제로 중요한 최적화 패턴
1. 스토리지 읽기를 메모리에 캐시
// 좋음: 1 SLOAD
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0);
balances[msg.sender] = 0;
}
2. 스토리지 변수 패킹
여러 변수를 하나의 슬롯에 = 하나의 SLOAD.
3. 외부 매개변수에 메모리 대신 콜데이터 사용
// 좋음: 콜데이터에서 직접 읽기
function processOrders(Order[] calldata orders) external { ... }
4. 안전할 때 unchecked 산술 사용
for (uint256 i = 0; i < length;) {
// ... 루프 본문 ...
unchecked { ++i; }
}
5. require 문자열 대신 커스텀 에러 사용
error InvalidAmount();
if (amount == 0) revert InvalidAmount();
결론
가스 최적화는 산술 옵코드를 미세 최적화하는 것이 아닙니다 — 스토리지 접근과 외부 호출을 최소화하는 것입니다. 가장 많은 가스를 절약하는 패턴은 아키텍처적입니다: 스토리지 패킹, 캐싱, 콜데이터 사용, 접근 목록 최적화.