상세 컨텐츠

본문 제목

솔리디티 강좌 25강 - 에러 핸들러1 assert, revert, require 0.4.22 ~ 0.7.x 버전

솔리디티 깨부수기 - 기본

by D_One 2021. 9. 24. 10:21

본문


유튜브를 통해, 쉽고 간편하게 이해 해보아요!

https://youtu.be/EJNXXry3cTI

구독/좋아요 해주셔서 감사합니다 :) !!


 

 

안녕하세요, 

 

오늘은 에러핸들러 assert, revert, requre 에 대해 알아 보겠습니다.

 

세가지 핸들러는 단순하게 말하면, "정의된 조건에 부합하지 않으면(false 라면), 에러를 발생시킨다" 입니다. 

 

이 세가지 핸들러는 솔리디티 0.4.22 버전에 소개되어서 현재 버전 까지 사용하고 있답니다.

 

사실, 저는 세가지 핸들러에 대해서 쓰면서,  0.4.22~0.7.x 버전과 0.8 버전에서의 기능이 변한걸 알게 되었습니다. 

 

먼저, 

0.4.22~0.7.x 버전에 대해서 설명 드리겠습니다.

 

assert : gas를 다 소비한후, 특정한 조건에 부합하지 않으면 에러를 발생시킨다.

revert: 조건없이 에러를 발생시키고, gas를 환불 시켜준다. 

require: 특정한 조건에 부합하지 않으면 에러를 발생시키고, gas를 환불 시켜준다.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

// require, revert,assert 

contract lec25{
    
    //Gas is spent
    function assertNow() public pure{
        assert(false);
    }
    
    }

저희는 간단한 예제로 분석을 해보겠습니다.

 

먼저, 

assertNow() 라는 함수는 assert(false)를 품고 있습니다.

 

이 assert는 밑에와 같은식으로 정의 할 수 있답니다.

assert(조건문)

조건문 값이 false일때, 에러를 발생 시킨답니다.

 

assertNow() 함수는  assert(false)를 갖고 있기에, 조건문이 false이니 당연히 에러가 발생하겠죠. 

assertNow()를  실행이 다 되고 나서, assert 문에의해서 에러가 생기고, 가스를 다 소비가 된 걸 알 수가 있어요.

가스 비용이 정말 아깝네요 ㅠ.ㅠ. 

그렇기에, 주로 assert문은 코드가 정상적으로 작동하는지 테스트 용도로 사용합니다. 

 

위에 사진은 assertNow() 함수를 실행한 결과 입니다.

여기서 주목해야 할점은 VM error : invalid opcode 입니다

0.8 버전 부터 이 부분이 변경된걸 알 수 있으니, 0.8 버전 설명할때까지 기억해주세요.

그리고,당연히 에러가 나오고, 3000000 gas 가 소비가 된걸 알 수 있습니다.

솔직히, 3000000 gas 가 많은건지, 이 하나의 가스 값으로는 알 수 없잖아요. 

 

이어서 바로 revert와 require 함수로 확인 해보도록 해요!

 

가스소비전에 에러를 발생시키는, revert와 require을 보겠습니다. 

 

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

// require, revert,assert 

contract lec25{
    
    //Gas is not spent
    function revertNow() public pure{
        revert("revert");
    }

    
  }

 

 

revert의 형태는 

revert("에러 메세지") 입니다.

에러 메세지는 자유롭게, 글을 적으시면 됩니다.

 

이 revert는 특정한 조건문없이 revert만 들어가면 바로 에러를 발생시켜요.

기존에 봤던 assert와 다르게 gas를 환불 해준답니다. 

gas를 환불해준다라는 뜻이 잘이해 안가실수도 있어요.

 

예를 들어, 함수가있으면 함수의 길이, 어떤식으로 만들어졌는지등에의해서 가스의 가격이 정해지잖아요. 

그리고, 저희는 그 함수를 실행할때마다, 정해진 동일한 가스의 가격을 지불하잖아요.

 

그러면 저희가 함수를 실행할때 이런 프로세스 일거예요.

1. 특정 함수를 선택해서 버튼 누른다.

2. 솔리디티는 "이 함수를 돌리고 싶으면, 이만큼의 가스를 지불해라" 라고 요구한다.

3. 가스 비용을 지불한다. 

4. 함수 실행중, 어느 부분에서 revert 발생하여 에러가 난다.

5. 에러가 났으니, 함수의 모든 부분이 실행이 안되었으니, 실행 안된만큼 gas를 환불한다.

 

위의 과정처럼, revert 와 require 를 쓰면 gas 를 환불을 받아요.

그러나, assert 같은 경우는 아니랍니다. 이미 함수가 다 돌고나서(가스 다소비하고나서), assert로 체크가 되기 때문입니다.

 

자 그러면 revertNow()를 한번 실행 해볼게요.

 

 

기존에 asserNow() 함수를 실행시 무려 3000000 gas 소비했는데요, 

위에 revertNow() 함수를 실행시키니 21388gas 밖에 소비가 안되었네요. 

다 환불 받아서 그런것이죠.

그리고 vm Error: revert 와 제가 임의로 쓴 에러메세지 revert 가 출력된걸 알 수 있네요.

 

자 그런데, gas도 환불해주고, revert 좋긴한데, 솔직히 revert코드만 삽입 하면 바로 에러가 뜨니, 

딱히 실용성이 없어요. 왜냐하면, assert는 특정한 조건을 받을때만, 에러를 발생시켜주듯이 revert도 특정한 조건에만

revert가 실행이 되어 에러가 발생이 된다면 실용적이겠죠.

 

그래서 revert는 주로 if 문으로 조건을 주고 사용하거나, 아예 require을 대체하여 사용한답니다.

 

먼저 require 에 대해서 보겠습니다. 

require은 간단하게, revert + if 라고 생각하시면 됩니다.

revert 처럼, gas를 환불 받고 에러메세지도 넣어준답니다, 거기에 조건문까지 정의할 수 있어서, 

특정한 조건에만 에러를 발생시킬수 있어요. 

 

require 의 형태는 

require(조건문,"에러메세지")

입니다.

 

assert 처럼 조건문이 false 일때, 에러메시지와 에러가 실행이됩니다.

 

예시를 들어 설명하겠습니다.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract lec25{
    function requireNow()public pure{
        require(false,"occurred");
    }
 
}

위에 requireNow() 함수는 

 require(false,"occurred"); 를 갖고 있네요.

 

당연히 false가 있으니, 무조건 에러메세지 occurred 와 에러가 발생하게 되겠네요.

 

자 한번 실행 해볼게요. 

 

기존에 asserNow() 함수를 실행시 무려 3000000 gas 소비했는데요,

위에 requireNow() 함수를 실행시키니 21360 gas 밖에 소비가 안되었네요. 

그리고 VM error: revert를 나타내고, 맨밑에 에러메세지 occurred 나온걸 확인할 수 있네요.

 

 

 

자 그러면 다시 revert 로 돌아가서, revert를 실용적으로 쓰기 위해서는 

if 문과 함께 쓰거나, require 로 대체해서 쓰면된다고 했잖아요.

 

그러면 한번 if 문과 revert를 같이 써 볼게요.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract lec25{
    function onlyAdults(uint256 _age) public pure returns(string memory){
        if( _age < 19){
            revert("You are not allowed to pay for the cigarette");
        }
        return "Your payment is scceeded";
        
    }
   
}

onlyAdults 라는 함수를 만들었습니다. 

이 함수는 만 19세 미만 인지 아닌지 판단하는 함수입니다. 

만19세 성인이라면 담배를 살 수 있고, 성인이 아니라면 담배를 못 사도록 revert를 이용하여 에러를 주는 것입니다.

 

자 먼저, onlyAdults 함수는 _age를 입력 받습니다.

그 _age 가 19 미만인지 if 의 조건문으로 갑니다. 

 

만약 19 미만일경우, revert 가 발생합니다.

반대로, 19 이상이라면, revert가 발생하지 않죠.

 

이 처럼 revert 에 조건문 if를 써서 좀  더 실용적으로 사용할 수 있는걸 알게 되었네요.

 

자 그러면, 위의 예제를 require을 쓴다면 밑에 코드 처럼 되겠죠.

 

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract lec25{
    function onlyAdults2(uint256 _age) public pure returns(string memory){
        require(_age>19,"You are not allowed to pay for the cigarette");
        return "Your payment is scceeded";
    }
    
}

 

0.8 버전 부터의 assert,revert, require는 다음 강의에서 설명 할게요.

 

영상 꼭 참고 해주세요!!

 

관련글 더보기