본문 바로가기
Study/Solidity

Solidity - 크립토 좀비(1)

Solidity의 입문과 같은 크립토 좀비를 통해 Solidity를 공부해 보자. 

 

우선 가장 먼저 가르쳐 주는 것은 Solidity의 구조에 대해서 가르쳐 준다. 솔리디티 코드는 컨트랙트 안에 싸여 있다. 컨트랙트는 이더리움 애플리케이션의 기본적인 구성 요소로, 모든 변수와 함수는 어느 한 컨트랙트에 속하기 마련이다. 흔히 Main함수라고 생각하면 된다. 

 

모든 솔리디티 코드는 version pragma로 시작해야 하는데, 해당 코드가 이용해야 하는 솔리디티 버전을 선언하는 것이지, 이를 통해 이후에 새로운 컴파일러 버전이 나와도 기존 코드가 깨지지 않도록 예방해준다. 

 

pragma solidity ^0.4.19;

contract ZombieFactory {

}

위와 같이 적어주면 0.4.19의 솔리디티 버전을 선언하고 ZombieFactory의 기본 컨트랙트를 생성해준 것이다. 

 

컨트랙트를 생성해 주었으니 그 안에 변수도 생성해줄 수 있다. 부호 있는 정수를 생성하려면 int, 부호 없는 정수를 생성하려면 uint를 사용해야 한다. 

 

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16

}

 

위와 같이 적어주면 dnaDigits를 16으로 초기화하고 생성해 준 것이다. 상태 변수는 컨트랙트 저장소에 영구적으로 저장이 된다. 기본적인 사칙연산도 솔리디티에서 동일하게 적용된다.

 

좀 더 복잡한 자료형을 사용하기 위해서 C언어에서와 같이 구조체를 사용할 수 있다. 구조체 역시 다른 언어에서 사용하는 구조체와 구조는 동일하다. 

pragma solidity ^0.4.19;

contract ZombieFactory {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

}

위 처럼 적어주면 Zombie라는 구조체 안에 string 타입의 name과 uint 타입의 dna가 들어가 있게 된다.

 

배열도 제공해준다. 역시 C언어와 마찬가지로 정적 배열과 동적 배열이 있는데 배열 안에 값을 초기화해주면 정적 배열이고, 값을 비워 놓으면 동적 배열이 되어 계속 크기가 커질 수 있게 된다. 배열 선언은 아래와 같이 할 수 있다.

// 2개의 원소를 담을 수 있는 고정 길이의 배열:
uint[2] fixedArray;
// 또다른 고정 배열으로 5개의 스트링을 담을 수 있다:
string[5] stringArray;
// 동적 배열은 고정된 크기가 없으며 계속 크기가 커질 수 있다:
uint[] dynamicArray;
// 생성해 주었던 구조체를 사용하여 배열을 생성해줄 수도 있다.
Person[] people;

특이하게 Public 배열이라는 것이 존재한다. 솔리디티는 이런 배열을 위해 getter 메서드를 자동적으로 생성해준다. 

Person[] public people;

위와 같이 적어주면 다른 컨트랙트들이 이 배열을 읽을 수 있게 된다. 자바에서의 Public과 같은 역할을 한다. 컨트랙트에 공개 데이터를 저장할 때 유용한 패턴이다.

 

함수 선언도 다른 언어들과 동일하게 해줄 수 있다. 

pragma solidity ^0.4.19;

contract ZombieFactory {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    function createZombie(string _name, uint _dna) {
	zombies.push(Zombie(_name, _dna));
    }

}

createZombie라는 함수를 선언하고, 인자 값으로 string과 uint를 받아준다. 함수 안에 들어온 2개의 인자 값을 활용해 구조체를 생성해주고 public 배열로 선언한 zombies에 push를 해주면 함수를 통해 배열을 추가해줄 수 있다. 

 

함수는 기본적으로 public으로 선언이 된다. 다른 컨트랙트에서 바로 접근을 할 수 있다는 것이다. 그렇기 때문에 접근하면 안 되는 함수들은 private을 선언해 주어야 한다. 

function _createZombie(string _name, uint _dna) private{
	zombies.push(Zombie(_name, _dna));
}

기존 함수에서 뒤에 private을 붙여주고 함수명에 _만 붙여주면 된다. 

 

함수의 반환값도 넣어줄 수 있다.

string greeting = "What's up dog";

function sayHello() public returns (string) {
  return greeting;
}

위처럼 함수 이름 뒤에 public returns를 적어주어 return해준 다는 것을 명시해주고, 뒤에 타입을 명시해주면 string으로 반환된다는 것을 적어주면 반환 값 설정도 완료된다. 하지만 위의 sayHello 함수는 상태를 변화시키지 않는다. 어떤 값을 변경하거나 무언가를 쓰지 않는다는 것이다. 그냥 반환만 해주는 함수 이기 때문에 이경우는 view로 선언해주어야 한다. 

 

pure함수도 제공을 해주는데 이는 함수가 어떤 데이터도 접근하지 않는 것을 의미한다. 앱에서 읽는 것도 하지 않고, 반환값이 함수에 전달된 인자 값에 따라 달라지는 경우에 pure로 선언해 주어야 한다.

 

함수를 생성할 때 view로 선언할지 pure로 선언할지 헷갈리긴 하지만, 솔리디티 자체 내에서 경고 메시지가 뜬다고 한다. 

function _generateRandomDna(string _str) private view returns (uint)

위와 같이 적어주면 generateRandomDna라는 private함수를 view로 선언하여 uint를 반환하게 해 줄 수 있다.

 

이더리움은 SHA3의 한 버전인 keccak256를 내장 해시 함수로 가지고 있다. 이 덕분에 쉽게 난수를 생성할 수 있다. 형 변환도 다른 언어와 마찬가지로 진행해줄 수 있다. 

 

다른 언어들의 eventListener와 같이 액션이 발생했을 때 의사소통할 수 있게 해주는 이벤트를 사용하여 애플리케이션을 더 다양하게 만들어줄 수 있다. 

event NewZombie(uint zombieId, string name, uint dna);

Zombie가 생성된다는 것을 event로 알려주기 위해서 새로운 event를 만들어 주고

function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        NewZombie(id, _name, _dna);
    }

Zombie를 만들어주는 함수에서 zombie를 생성한 뒤에 event 함수를 불러오면 성공적으로 이벤트를 전달할 수 있다. 

'Study > Solidity' 카테고리의 다른 글

Solidity - 크립토 좀비(2)  (0) 2022.09.17