assert 문
assert 는 절대 일어나서는 안 되는 조건, 곧 불변식(invariant)을 검사하는 도구입니다.
정상적인 코드라면 결코 거짓이 될 수 없는 조건을 확인하는 "내부 버그 탐지기"인 셈입니다.
require vs revert vs assert
| 구문 | 용도 | 실패 시 에러 타입 | 남은 가스 |
|---|---|---|---|
require | 사용자 입력·권한 검증 | Error(string) | 환불 |
revert | 복잡한 조건 분기에서 명시적 중단 | Error 또는 커스텀 | 환불 |
assert | 내부 불변식·버그 검사 | Panic(uint256) | 환불 |
핵심 차이는 에러 타입입니다. require/revert 는 Error 를, assert 는 Panic 을 던집니다.
0.8.0 부터는 셋 다 실패하면 남은 가스를 호출자에게 환불합니다. 0.8.0 이전에는 assert 만
남은 가스를 전부 소모했지만, 이제는 그 차이가 사라졌습니다.
불변식 검사 예시
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Ledger {
uint256 public total;
mapping(address => uint256) public balance;
function deposit() public payable {
uint256 totalBefore = total;
balance[msg.sender] += msg.value;
total += msg.value;
// 불변식 1: total 은 정확히 입금액만큼만 늘어나야 한다
assert(total == totalBefore + msg.value);
// 불변식 2: 장부에 기록된 총합이 실제 보유 잔액을 넘을 수 없다
assert(total <= address(this).balance);
}
}언제 무엇을 쓰는가
판단 기준은 간단합니다. 외부에서 들어온 값(사용자 입력, 권한)을 검사하면 require,
코드가 스스로 보장해야 할 내부 상태를 검사하면 assert 입니다. 0.8.0 이상에서는 정수
오버플로/언더플로나 0으로 나누기 같은 상황도 자동으로 Panic 을 발생시켜 assert 와 같은
계열로 보호해 줍니다.
실무에서 assert 는 자주 쓰이지 않습니다. 대부분의 검사는 require 로 충분하며, assert 는 핵심
회계 불변식처럼 "절대 깨지면 안 되는" 마지막 안전망에만 아껴 사용합니다.
assert 가 실패한다는 것은 곧 컨트랙트 로직에 결함이 있다는 신호입니다. "여기까지 오면 코드에
버그가 있다"는 상황에만 쓰세요.