컨트랙트의 구조
컨트랙트의 구조 (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
}