[Solidity] modifier란?

동동주·2025년 10월 31일
post-thumbnail

간단한 정리

함수의 동작을 변경할 수 있는 기능으로, 함수 전/후에 특정 행동을 넣어줄 수 있다.

정의할 때는 function @@ 대신 modifier @@으로 작성하고,
클래스 상속이나 인터페이스 구현처럼 함수 옆에 표시하여 사용한다.
본문에서 함수의 실행 위치를 지정할 수 있다.

참고 : solidity - Modifier(함수변경자)




공식 문서 설명

링크 : https://docs.soliditylang.org/en/latest/contracts.html

explanation1


  • 상속이 가능한 속성이고, 오버라이딩할 수 있지만 조건이 있음
    오버라이딩의 조건은 원문에 링크로 걸려있는 Modifier Overriding 참고

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

contract owned {
    constructor() { owner = payable(msg.sender); }
    address payable owner;

    // This contract only defines a modifier but does not use
    // it: it will be used in derived contracts.
    // The function body is inserted where the special symbol
    // `_;` in the definition of a modifier appears.
    // This means that if the owner calls this function, the
    // function is executed and otherwise, an exception is
    // thrown.
    modifier onlyOwner {
        require(
            msg.sender == owner,
            "Only owner can call this function."
        );
        _;
    }
}

contract priced {
    // Modifiers can receive arguments:
    modifier costs(uint price) {
        if (msg.value >= price) {
            _;
        }
    }
}

contract Register is priced, owned {
    mapping(address => bool) registeredAddresses;
    uint price;

    constructor(uint initialPrice) { price = initialPrice; }

    // It is important to also provide the
    // `payable` keyword here, otherwise the function will
    // automatically reject all Ether sent to it.
    function register() public payable costs(price) {
        registeredAddresses[msg.sender] = true;
    }

    // This contract inherits the `onlyOwner` modifier from
    // the `owned` contract. As a result, calls to `changePrice` will
    // only take effect if they are made by the stored owner.
    function changePrice(uint price_) public onlyOwner {
        price = price_;
    }
}

contract Mutex {
    bool locked;
    modifier noReentrancy() {
        require(
            !locked,
            "Reentrant call."
        );
        locked = true;
        _;
        locked = false;
    }

    /// This function is protected by a mutex, which means that
    /// reentrant calls from within `msg.sender.call` cannot call `f` again.
    /// The `return 7` statement assigns 7 to the return value but still
    /// executes the statement `locked = false` in the modifier.
    function f() public noReentrancy returns (uint) {
        (bool success,) = msg.sender.call("");
        require(success);
        return 7;
    }
}

explanation2

  • 여러 modifier를 한 함수에 동시에 적용할 수 있다
    띄어쓰기로 구분하고, 작성한 순서대로 실행함

  • modifier가 함수의 값을 쓰려면 명시적으로 전달해야 한다
    적용되는 함수의 인자에 자동 접근, 변경 불가능
    modifier costs(uint price) --> costs(price) 처럼 사용

  • 수정자가 적용되는 함수를 언제 실행할지 지정해야 한다
    언더바( _ )로 지정할 수 있으며, 이 언더바는 변수 이름의 앞이나 뒤에 밑줄을 사용하는 것과는 다르다
    (* solidity 버전에 따라 표현이 조금 다르다. => 0.4.0 이하는 ; 없이 _ 사용 )
    예시:
contract priced {
    // Modifiers can receive arguments:
    modifier costs(uint price) {
        if (msg.value >= price) {
            _; // 이 modifier를 사용하는 함수 본문의 실행위치 지정
        }
    }
}

.
.
.

// 수정자가 적용되는 함수 = register()
function register() public payable costs(price) {
        registeredAddresses[msg.sender] = true;
    }

위의 경우
if (msg.value >= price) 가 true
_; = registeredAddresses[msg.sender] = true;
의 순서대로 실행된다


  • modifier나 함수 내부에서 return을 쓰면, 해당 modifier나 함수만 종료된다.
    만약 아래와 같은 함수라면 (임의로 작성한 코드, 공식문서 아님)
modifier first() {
    _;
    // 코드a
}

modifier second() {
    _;
    // 코드b
}

function test() public first second {
    return;
}

(test 사용 상황 시 코드 흐름)
first (modifier) 실행 → \_; → 함수본문 실행
(*여러 modifier 적용, 띄어쓰기로 구분) 그 다음 modifier 실행
second (modifier) 실행 → \_; → 함수본문 실행
return; (해당 함수만 종료)
→ second 마저 실행 (함수본문 내용 끝남) → 코드b → second 끝
→ first 마저 실행 (함수본문 내용 끝남) → 코드a → first 끝
→ 종료


  • modifier 안에서 return;을 써도 함수 리턴값에는 영향을 주지 않는다.
    하지만 modifier가 아예 함수 본문을 실행하지 않으면 함수의 리턴값은 기본값으로 반환된다.

예시 - (임의로 작성한 코드, 공식문서 아님)

contract Example {
    modifier skipIfZero(uint x) {
        if (x == 0) {
            return; 
        }
        _; // 함수 본문 실행
    }

    function test(uint x) public pure skipIfZero(x) returns (uint) {
        return 42;
    }
}

skipIfZero (modifier) 실행 하면

case1) <x=0>
return;
(_;를 건너뛰어서 함수본문을 실행하지 않음.)
= test의 return 값=0 (uint의 기본값)

case2) <x=3>
(_;부분에 도달, 함수본문을 실행)
return 42;
= test의 return 값=42


  • modifier 안에 _(함수실행)를 여러 번 쓸 수도 있다.
    다만, 함수는 최종 기호(마지막으로 실행한 함수 본문)의 반환값을 반환한다.

  • modifier에 인자를 전달할 때 어떤 표현식이든 넣고 쓸 수 있다.
    따라서 함수에서 볼 수 있는 심볼은 modifier 내부에서도 볼 수 있다.
    하지만 반대는 불가능함. (= modifier 내부에서 추가된 심볼은 함수가 못봄)



여담 - java의 modifier..?

java를 배우면서도 modifier를 봤는데, 서로 용어의 쓰임이 다른 것 같아 정리해봤다

접근 제어자 (Access Modifiers) : public, protected, default, private
그 외 : static, final, abstract...

- 자바 제어자 (Modifier)
- [Java] modifier(제어자)란?

java에서는 public, private.. 이 modifier로 불리는 반면,
solidity에서는 public, priavate, external, internal 을 Scope 혹은 Visibility 로 불리는 것 같다.
( 참고 : [Solidity] Scope와 Visibility (Public, Private, External, Internal) )

근데 또 java에서도 scope는 다른 의미로 쓰이는 것 같다...?
- [Java] 지역 변수와 스코프(Scope)
- Java에서 이해하는 Scope

아마도 이렇게 구분할 수 있는 것 같다..
(java scope 설명이 맞는지 모르겠다)

modifierscope
Javapublic, private..변수나 메소드 등의 접근 범위
Solidity함수의 동작을 변경할 수 있는 기능public, priavate, external...
profile
배운 내용 정리&기록, 스크랩

0개의 댓글