require vs revert vs assert 가스 비교
세 가지 에러 도구는 "조건이 틀리면 되돌린다"는 점에서 비슷해 보이지만, 되돌림 타입과 가스 사용이 다릅니다. 올바른 도구를 골라야 가스를 아끼고 코드의 의도도 분명해집니다.
세 도구의 차이
| 도구 | 용도 | 되돌림 타입 | 남은 가스 |
|---|---|---|---|
require | 입력·조건 검증 | Error(string) 또는 무메시지 | 환불됨 |
revert | 흐름 제어형 되돌리기 | Error(string) 또는 커스텀 에러 | 환불됨 |
assert | 내부 불변식 검사 | Panic(uint256) | 환불됨(0.8 이후) |
require 와 revert 는 사용자가 마주칠 수 있는 정상적인 실패(잔액 부족, 권한 없음)를 표현하고, assert 는 절대 일어나면 안 되는 버그성 상황을 Panic 으로 표시합니다. 셋 다 0.8 이후로는 실패 시 남은 gas 를 환불합니다.
커스텀 에러가 가스를 아끼는 이유
가장 큰 가스 차이는 메시지 표현 방식에서 나옵니다. 긴 문자열 메시지(Error(string))는 바이트코드에 저장되어 배포·실행 비용을 높입니다. 같은 의미를 4바이트 선택자로 표현하는 커스텀 에러가 더 저렴합니다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
error NotOwner();
contract Cheap {
address public owner = msg.sender;
// 비싼 방식: 긴 문자열이 바이트코드에 저장됨 (Error(string))
function expensive() public view {
require(msg.sender == owner, "caller is not the contract owner");
}
// 저렴한 방식: 4바이트 선택자만 인코딩됨 (커스텀 에러)
function cheap() public view {
if (msg.sender != owner) revert NotOwner();
}
}도구 선택 기준
- 입력값/권한 검사처럼 흔하고 단순한 검증 →
require(또는if + revert 커스텀에러) - 복잡한 분기 끝에서 되돌릴 때, 가스를 아끼고 싶을 때 →
revert 커스텀에러 - "이건 절대 안 일어나야 한다"는 내부 가정 →
assert
assert 는 절대 발생하면 안 되는 상황을 표시합니다. 정상 코드라면 assert 는 결코 실패하지
않아야 하므로, 사용자 입력 검증에는 쓰지 마세요. assert 가 실제로 깨졌다면 컨트랙트의 버그
신호입니다.
반복 호출되는 함수일수록 커스텀 에러의 가스 절감 효과가 누적됩니다. 검증이 많은 컨트랙트라면 커스텀 에러로 통일하세요.