revert 문
revert 는 require 와 마찬가지로 트랜잭션을 되돌리지만, 조건문과 분리해 직접 호출한다는 점이 다릅니다.
복잡한 if/else 분기 안에서 특정 경로에 도달하면 명시적으로 중단하고 싶을 때 유용합니다.
revert 역시 변경된 상태를 모두 원래대로 되돌리고, 남은 가스를 환불합니다.
문자열 메시지와 커스텀 에러
// 1) 문자열 메시지 형태
revert("not allowed");
// 2) 커스텀 에러 형태 (가스 효율적, 0.8.4+)
error NotAllowed(address caller);
revert NotAllowed(msg.sender);문자열 메시지는 읽기 쉽지만 바이트코드에 문자열을 통째로 저장해 가스를 많이 씁니다. 반면 커스텀 에러는 4바이트 셀렉터만 저장하므로 더 저렴하고, 인자를 담아 실패 맥락까지 전달할 수 있습니다.
분기 안에서의 명시적 중단
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Vault {
error Unauthorized(address caller);
address public owner;
constructor() {
owner = msg.sender;
}
function adminAction(uint256 code) public view returns (string memory) {
if (msg.sender != owner) {
// 조건 분기 안에서 명시적으로 중단
revert Unauthorized(msg.sender);
}
if (code == 0) {
revert("code cannot be zero");
}
return "ok";
}
}require 와 언제 나눠 쓰나
단순히 "이 조건이 맞아야 한다"를 한 줄로 표현할 때는 require 가 간결합니다.
하지만 여러 분기를 거친 끝에 "이 경로는 허용되지 않는다"를 표현하거나, 인자를 담은 커스텀 에러를
던질 때는 revert 가 더 명확합니다.
사실 require(cond, "msg") 는 if (!cond) revert("msg"); 와 같습니다. 표현하는 방식이 다를 뿐이니
읽기 좋은 쪽을 고르면 됩니다.
실무에서는 가스를 아끼려고 문자열 revert 대신 커스텀 에러(error NotOwner();)를 즐겨
씁니다. 이름이 의미를 드러내고, 실패 데이터를 구조적으로 전달할 수 있기 때문입니다.
revert 는 상태를 되돌리고 가스를 환불한다는 점에서 require 와 같지만, 코드 어디에나 원하는
자리에 놓을 수 있다는 점이 다릅니다.