컨트랙트의 구조

컨트랙트의 구조 (Structure of a Contract)

Solidity의 컨트랙트는 객체지향 언어의 클래스와 비슷해요. 컨트랙트 하나는 상태 변수, 함수, 함수 수정자, 이벤트, 에러, 구조체 타입, 열거형 타입 같은 여러 선언을 담을 수 있고, 다른 컨트랙트로부터 상속도 받을 수 있어요. 컨트랙트를 처음 접할 때 큰 그림을 잡으려면, 이 절이 한눈에 보는 개요 역할을 해요. 라이브러리와 인터페이스처럼 특별한 종류의 컨트랙트에 대한 자세한 내용은 컨트랙트 절에서 다룰게요.

출처: 문서

본문

상태 변수 (State Variables)

상태 변수는 값이 컨트랙트 스토리지에 영구적으로 저장되거나, 또는 각 트랜잭션이 끝날 때 정리되는 일시적 스토리지(transient storage)에 잠깐 저장되는 변수예요. 자세한 내용은 데이터 위치(data locations)를 참고해요.

open in Remix

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

contract SimpleStorage {
    uint storedData; // State variable
    // ...
}

유효한 상태 변수 타입은 타입(Types) 절에서, 가시성 선택지는 가시성과 게터(Visibility and Getters) 절에서 확인할 수 있어요.

함수 (Functions)

함수는 코드의 실행 단위예요. 함수는 보통 컨트랙트 안에 정의하지만, 컨트랙트 밖에서도 정의할 수 있어요.

open in Remix

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

contract SimpleAuction {
    function bid() public payable { // Function
        // ...
    }
}

// Helper function defined outside of a contract
function helper(uint x) pure returns (uint) {
    return x * 2;
}

함수 호출은 내부적(internal) 또는 외부적(external)으로 일어날 수 있고, 다른 컨트랙트에 대해 서로 다른 수준의 가시성을 가져요. 함수는 매개변수를 받고 반환 변수를 돌려주면서 파라미터와 값을 주고받아요.

함수 수정자 (Function Modifiers)

함수 수정자는 함수의 의미를 선언적인 방식으로 바꿀 때 사용해요(컨트랙트 절의 함수 수정자 참고). 같은 이름의 수정자를 다른 매개변수로 다시 정의하는 오버로딩은 할 수 없어요. 수정자는 함수처럼 오버라이드할 수 있어요.

open in Remix

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

contract Purchase {
    address public seller;

    modifier onlySeller() { // Modifier
        require(
            msg.sender == seller,
            "Only seller can call this."
        );
        _;
    }

    function abort() public view onlySeller { // Modifier usage
        // ...
    }
}

이벤트 (Events)

이벤트는 EVM 로깅 기능을 위한 편리한 인터페이스예요.

open in Remix

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.22;

event HighestBidIncreased(address bidder, uint amount); // Event

contract SimpleAuction {
    function bid() public payable {
        // ...
        emit HighestBidIncreased(msg.sender, msg.value); // Triggering event
    }
}

이벤트를 어떻게 선언하고 디앱(dapp) 안에서 어떻게 사용하는지는 컨트랙트 절의 이벤트(Events) 부분을 참고해요.

에러 (Errors)

에러는 실패 상황에 대해 설명적인 이름과 데이터를 정의할 수 있게 해줘요. 에러는 revert 문에서 사용할 수 있어요. 문자열 설명과 비교하면 에러는 훨씬 저렴하고 추가 데이터를 인코딩할 수 있게 해줘요. NatSpec을 사용하면 그 에러를 사용자에게 설명할 수 있어요.

open in Remix

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

/// Not enough funds for transfer. Requested `requested`,
/// but only `available` available.
error NotEnoughFunds(uint requested, uint available);

contract Token {
    mapping(address => uint) balances;
    function transfer(address to, uint amount) public {
        uint balance = balances[msg.sender];
        if (balance < amount)
            revert NotEnoughFunds(amount, balance);
        balances[msg.sender] -= amount;
        balances[to] += amount;
        // ...
    }
}

자세한 내용은 컨트랙트 절의 커스텀 에러(Custom Errors) 부분을 참고해요.

구조체 타입 (Struct Types)

구조체는 여러 변수를 묶을 수 있는 사용자 정의 타입이에요(타입 절의 구조체 참고).

open in Remix

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

contract Ballot {
    struct Voter { // Struct
        uint weight;
        bool voted;
        address delegate;
        uint vote;
    }
}

열거형 타입 (Enum Types)

열거형은 유한한 '상수 값' 집합을 가진 사용자 정의 타입을 만들 때 사용해요(타입 절의 열거형 참고).

open in Remix

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

contract Purchase {
    enum State { Created, Locked, Inactive } // Enum
}

더 알아보기 (Learn more)