dev-miri
Solidity Path: Beginner to Intermediate Smart Contracts [1] 좀비 공장 만들기 본문
Solidity Path: Beginner to Intermediate Smart Contracts [1] 좀비 공장 만들기
miri-dev 2022. 9. 1. 14:39https://cryptozombies.io/ko/course
#1 Solidity Tutorial & Ethereum Blockchain Programming Course | CryptoZombies
CryptoZombies is The Most Popular, Interactive Solidity Tutorial That Will Help You Learn Blockchain Programming on Ethereum by Building Your Own Fun Game with Zombies — Master Blockchain Development with Web3, Infura, Metamask & Ethereum Smart Contracts
cryptozombies.io
<위 컨텐츠를 실행 후 개념을 정리한 글입니다>
1. contract(컨트랙트)
솔리디티 코드는 컨트랙트 안에 싸여 있다.
컨트랙트는 이더리움 애플리케이션의 기본적인 구성 요소로, 모든 변수와 함수는 어느 한 컨트랙트에 속한다.
컨트랙트는 모든 프로젝트의 시작 지점이라고 할 수 있다.
(class와 비슷한 것 같다)
contract HelloWorld {
//비어 있는 HelloWorld 컨트랙트
}
2. Version Pragma
모든 솔리디티 소스 코드는 "version pragma"로 시작해야 하는데, 이는 해당 코드가 이용해야 하는 솔리디티 버전을 선언하는 것이다.
이후에 새로운 컴파일러 버전이 나와도 기존 코드가 깨지지 않도록 예방하는 것이다.
//컨트랙트 초기 뼈대
//새로운 프로젝트를 시작할 때마다 이 뼈대를 제일 먼저 작성해야 한다.
pragma solidity ^0.4.19;
contract HelloWorld {
}
3. 상태 변수 & 정수
상태변수는 컨트랙트 저장소에 영구적으로 저장된다
-> 즉, 이더리움 블록체인에 기록된다(데이터베이스에 데이터를 쓰는 것과 동일하다)
contract Example {
//이 변수는 블록체인에 영구적으로 저장된다
unit myUnsignedInteger = 100;
}
부호 없는 정수 : uint
uint 자료형은 부호 없는 정수로, 값이 음수가 아니어야 한다는 의미이다.
int 자료형은 부호 있는 정수이다.
** 솔리디티에서 uint는 실제로 uint256, 즉 256비트 부호 없는 정수의 다른 표현이다.
uint8, uint16. uint32 등과 같이 uint를 더 적은 비트로 선언할 수 있다.
4. 수학 연산
솔리디티에서의 수학 연산은 대부분의 프로그래밍 언어의 수학 연산과 동일하다
- 덧셈 : x+y
- 뺄셈 : x-y
- 곱셈 : x * y
- 나눗셈 : x / y
- 나머지 : x % y
- ☆지수 연산 : x ** y
- unit x = 5 ** 2; //즉, 5^2 = 25
5. 구조체
더 복잡한 자료형을 필요로 할때를 위해, 솔리디티는 구조체를 제공한다
struct Person {
uint age;
String name;
}
구조체를 통해 여러 특성을 가진, 보다 복잡한 자료형을 생성할 수 있다.
**String은 임의의 길이를 가진 UTF-8 데이터를 위해 활용된다
6. 배열
어떤 것의 모음집이 필요할 때 배열을 사용할 수 있다.
솔리디티에는 정적 배열과 동적 배열 두 가지 종류가 있다.
//2개의 원소를 담을 수 있는 고정 길이의 배열
uint[2] fixedArray;
//또다른 고정 배열으로 5개의 스트링을 담을 수 있다
string[5] stringArray;
//동적 배열은 고정된 크기가 없으며 계속 크기가 커질 수 있다
uint[] dynamicArray;
구조체의 배열을 생성할 수도 있다. 이전 챕터의 Person 구조체를 이용하면 된다
Person[] people; //이는 동적 배열로, 원소를 계속 추가할 수 있다.
상태 변수가 블록체인에 영구로 저장될 수 있으므로 구조체의 동적 배열을 생성하면 데이터베이스 처럼 컨트랙트에 구조화된 데이터를 저장하는 데 유용하다.
Public 배열
public으로 배열을 선언할 수 있다. 솔리디티는 이런 배열을 위해 getter 메소드를 자동적으로 생성한다.
Person[] public people;
public으로 배열을 선언하면 다른 컨트랙트들이 이 배열을 읽을 수 있게 된다(쓸 수는 없음)
이는 컨트랙트에 공개 데이터를 저장할 때 유용한 패턴이다.
7. 함수 선언
솔리디티에서 함수 선언은 다음과 같다.
function eatHanburgers(string _name, uint _amount) {
}
**함수 인자명을 언더스코어(_)로 시작해서 전역 변수와 구별하는 것이 관례이다(의무는 아님)
8. 구조체와 배열 활용하기
struct Person {
uint age;
string name;
}
Person[] public people;
새로운 Person을 생성하고 people 배열에 추가하는 방법을 알아보자.
//새로운 사람을 생성한다
Person satoshi = Person(172, "Satoshil");
//이 사람을 배열에 추가한다
people.push(satoshil);
위 두 코드를 조합하여 한 줄로 표현할 수 있다.
people.push(Person(16, "Vitalik"));
9. Private/Public 함수
솔리디티에서 함수는 기본적으로 public으로 선언된다.
누구나(혹은 다른 컨트랙트가) 나의 컨트랙트 함수를 호출하고 코드를 실행할 수 있다는 의미이다.
하지만 이는 항상 바람직하지는 않고, 나의 컨트랙트가 공격에 취약해질 수 있다.
따라서 기본적으로 함수를 private로 선언하고, 공개할 함수만 public으로 선언하는 것이 좋다.
//private 함수를 선언하는 방법
uint[] numbers;
function _addToArray(uint _number) private {
numbers.push(_number);
}
private는 컨트랙트 내의 다른 함수들만이 이 함수를 호출하여 numbers 배열로 무언가를 추가할 수 있다는 것을 의미한다.
예시와 같이, private 키워드는 함수명 다음에 적는다.
함수 인자명과 마찬가지로 private 함수명도 언더바(_)로 시작하는 것이 관례이다.
☆10. 함수 더 알아보기
지금까지 배웠던 언어들과 꽤 차이가 있으므로 쓰면서 익숙해져보자.
-반환값
함수에서 어떤 값을 반환 받으려면 다음과 같이 선언해야 한다.
string greeting = "What's up dog";
function sayHello() public returns (string) {
return greeting;
}
솔리디티에서 함수 선언은 반환값 종류를 포함한다(위의 코드에서는 반환값이 string 이다)
-함수 제어자
위에서 살펴 본 함수 sayHello()는 솔리디티에서 상태를 변화시키지 않는다.
즉, 어떤 값을 변경하거나 무언가를 쓰지 않는다.
이런 경우에는 함수를 view 함수로 선언한다. 이는 함수가 데이터를 보기만 하고 변경하지 않는다는 뜻이다.
function sayHello() public view returns (string)
솔리디티는 pure 함수도 가지고 있는데, 이는 함수가 앱에서 어떤 데이터도 접근하지 않는 것을 의미한다.
function _multiply(uint a, uint b) private pure returns (uint) {
return a*b;
}
pure 함수는 읽는 것도 하지 않는다. 다만 반환값이 함수에 전달된 인자값에 따라서 달라진다.
*함수를 pure나 view로 언제 표시할 지 기억하기 어려울 수 있다. 운 좋게도(?) 솔리디티 컴파일러는 어떤 제어자를 써야 하는지 경고 메시지를 통해 잘 알려준다.
11. Keccak256과 형 변환
function _generateRandomDna(string _str) private view returns (uint) {
}
_generateRandomDna 함수의 반환값이 (반)랜덤인 uint가 되기를 원하면 어떻게 해야할까?
이더리움은 SHA3의 한 버전인 keccak256를 내장 해시 함수로 가지고 있다.
해시함수는 기본적으로 입력 스트링을 랜덤 256비트 16진수로 매핑한다.
스트링에 약간의 변화라도 있으면 해시 값은 크게 달라진다.
**SHA3이란 SHA1과 SHA2를 대체하기 위해 미국 국립표준기술연구소(NIST)가 2015년 8월 5일에 발표한 암호화 해시함수이다.
**해시함수 : 해시(hash)를 이용한 함수, 임의의 길이의 데이터를 고정된 길이의 데이터로 출력하는 함수이다.
해시 함수는 이더리움에서 여러 용도로 활용되지만, 여기서는 의사 난수 발생기(pesudo-random number generator)로 이용하도록 한다.
**블록체인에서 안전한 의사 난수 발생기는 매우 어려운 문제이다. 여기서 우리가 활용한 방법은 안전하지는 않다.
-형 변환
uint8 a = 5;
uint b = 6;
// a * b가 uint8이 아닌 uint를 반환하기 때문에 에러 메시지가 난다.
uint8 c = a * b;
//b를 uint8으로 형 변환해서 코드가 제대로 작동하도록 해야 한다.
uint8 c = a * uint8(b);
위의 예시에서 a * b는 uint를 반환한다. 이 반환값을 uint8에 저장하려고 하니 잠재적으로 문제를 야기할 수 있다.
반환값을 uint8으로 형 변환하면 코드가 제대로 작동하고 컴파일러도 에러 메시지를 주지 않을 것이다.
**솔리디티의 형 변환
(참고)
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=blokorea&logNo=221309235179
Solidity 문법 #4
안녕하세요 Khan 입니다. 솔리디티문법을 공부하고 4번째 포스팅이네요. 이번시간에는 형 변환에 대해 알...
blog.naver.com
솔리디티에서 암묵적 변환은 데이터의 손실이 없는 자료형 변환에서만 일어난다
예를 들어서 int8을 int16으로 변환할 때 암묵적 변환이 일어난다.
하지만 음수를 표현할 수 있는 int형과 음수를 표현할 수 없는 uint형 사이에서는 암묵적 변환이 불가능하다.
자연수는 더 큰 크기의 바이트형으로 암묵적 변환이 가능하지만, 더 작은 크기로는 변환이 불가능하다.
명시적 변환은 변환할 자료형을 고정적으로 결정할 때 사용된다.
명시적 변환은 어떤 데이터의 '손실'을 일으킬지 모르므로 확실하게 확인 후 사용하여야 한다.
솔리디티 언어는 개발 편의를 위해 형추론을 지원한다. 변수를 선언할 때 자료형을 반드시 명시할 필요 없이 var이라는 키워드로 선언할 수 있다.
이렇게 선언된 변수는 처음 대입되는 데이터의 형태로 선언된다.
var 키워드는 함수의 매개변수나 변환자에 사용할 수 없다.
12. 이벤트
이벤트는 컨트랙트가 블록체인 상에서 앱의 사용자 단에서 무언가 액션이 발생했을 때 의사소통하는 방법이다.
컨트랙트는 특정 이벤트가 일어나는지 "귀를 기울이고", 그 이벤트가 발생하면 행동을 취한다.
//이벤트를 선언한다
event IntegersAdded(uint x, uint y, uint result);
function add(uint _x, uint _y) public {
uint result = _x + _y;
// 이벤트를 실행하여 앱에게 add 함수가 실행되었음을 알린다
IntegersAdded(_x, _y, result);
return result;
}
//자바스크립트로 구현하면 다음과 같다
YourContract.IntegersAdded(function(error, result) {
//결과와 관련된 행동을 취한다
})
13. Web3.js
이더리움은 Web3.js라고 하는 자바스크립트 라이브러리를 가지고 있다.
Web3.js가 구축된 컨트랙트와 어떤 방식으로 상호작용 하는지에 대한 샘플 코드를 살펴보자.
// 여기에 우리가 만든 컨트랙트에 접근하는 방법을 제시한다:
var abi = /* abi generated by the compiler */
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress = /* our contract address on Ethereum after deploying */
var ZombieFactory = ZombieFactoryContract.at(contractAddress)
// `ZombieFactory`는 우리 컨트랙트의 public 함수와 이벤트에 접근할 수 있다.
// 일종의 이벤트 리스너가 텍스트 입력값을 취한다:
$("#ourButton").click(function(e) {
var name = $("#nameInput").val()
// 우리 컨트랙트의 `createRandomZombie`함수를 호출한다:
ZombieFactory.createRandomZombie(name)
})
// `NewZombie` 이벤트가 발생하면 사용자 인터페이스를 업데이트한다
var event = ZombieFactory.NewZombie(function(error, result) {
if (error) return
generateZombie(result.zombieId, result.name, result.dna)
})
// 좀비 DNA 값을 받아서 이미지를 업데이트한다
function generateZombie(id, name, dna) {
let dnaStr = String(dna)
// DNA 값이 16자리 수보다 작은 경우 앞 자리를 0으로 채운다
while (dnaStr.length < 16)
dnaStr = "0" + dnaStr
let zombieDetails = {
// 첫 2자리는 머리의 타입을 결정한다. 머리 타입에는 7가지가 있다. 그래서 모듈로(%) 7 연산을 하여
// 0에서 6 중 하나의 값을 얻고 여기에 1을 더해서 1에서 7까지의 숫자를 만든다.
// 이를 기초로 "head1.png"에서 "head7.png" 중 하나의 이미지를 불러온다:
headChoice: dnaStr.substring(0, 2) % 7 + 1,
// 두번째 2자리는 눈 모양을 결정한다. 눈 모양에는 11가지가 있다:
eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
// 셔츠 타입에는 6가지가 있다:
shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
// 마지막 6자리는 색깔을 결정하며, 360도(degree)까지 지원하는 CSS의 "filter: hue-rotate"를 이용하여 아래와 같이 업데이트된다:
skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
zombieName: name,
zombieDescription: "A Level 1 CryptoZombie",
}
return zombieDetails
}
(챕터1을 완료하고 완성된 내 좀비ㅎㅎ)

'BlockChain > CryptoZombies' 카테고리의 다른 글
| Solidity Path: Beginner to Intermediate Smart Contracts [3] Advanced Solidity Concepts (0) | 2022.09.05 |
|---|---|
| Solidity Path: Beginner to Intermediate Smart Contracts [2] (0) | 2022.09.04 |