[Solidity] 크립토 좀비로 공부하기 - 챕텨2
오늘은 조금더 심화 과정으로 들어갑니다.
한번 정리해보겠습니다.
1. 주소(address)
- 모든 이더리움 계정은 ETH 잔액을 가집니다.
- 계정을 통해 다른 계정과 이더를 주고 받을 수 있습니다.
- "주소는 특정 유저(혹은 스마트 컨트랙트)가 소유한다"라는 점을 이해하고 넘어가면 될 것 같습니다.
2. 매핑(mapping)
- struct와 비슷하게 솔리디티에서 구조화된 데이터를 저장하는 또다른 방법입니다.
- HashMap 또는 Hashtable 이라고 생각하면 편합니다.(key - value 구조)
- 매핑은 기본적으로 키-값 (key-value) 저장소로, 데이터를 저장하고 검색하는 데 이용됩니다.
// 금융 앱용으로, 유저의 계좌 잔액을 보유하는 uint를 저장한다:
mapping (address => uint) public accountBalance;
// 혹은 userID로 유저 이름을 저장/검색하는 데 매핑을 쓸 수도 있다
mapping (uint => string) userIdToName;
3. Msg.sender
- 솔리디티에는 모든 함수에서 이용 가능한 특정 전역 변수들이 있습니다.
- 그 중의 하나가 현재 함수를 호출한 사람 (혹은 스마트 컨트랙트)의 주소를 가리키는 msg.sender 입니다.
- 솔리디티에서 함수 실행은 항상 외부 호출자에서 시작됩니다. 컨트랙트는 누군가가 컨트랙트의 함수를 호출할 때까지 블록체인 상에서 아무 것도 하지 않습니다.
- 그렇기 때문에 항상 msg.sender는 있다고 생각하시면 됩니다.
mapping (address => uint) favoriteNumber;
function setMyNumber(uint _myNumber) public {
// `msg.sender`에 대해 `_myNumber`가 저장되도록 `favoriteNumber` 매핑을 업데이트한다 `
favoriteNumber[msg.sender] = _myNumber;
// ^ 데이터를 저장하는 구문은 배열로 데이터를 저장할 떄와 동일하다
}
function whatIsMyNumber() public view returns (uint) {
// sender의 주소에 저장된 값을 불러온다
// sender가 `setMyNumber`을 아직 호출하지 않았다면 반환값은 `0`이 될 것이다
return favoriteNumber[msg.sender];
}
4. Require(조건문 같은 느낌!)
- require은 특정 조건이 참이 아닐 때 함수가 에러 메시지를 발생하고5 실행을 멈추게 됩니다.
function sayHiToVitalik(string _name) public returns (string) {
// _name이 "Vitalik"인지 비교한다. 참이 아닐 경우 에러 메시지를 발생하고 함수를 벗어난다
// (참고: 솔리디티는 고유의 스트링 비교 기능을 가지고 있지 않기 때문에
// 스트링의 keccak256 해시값을 비교하여 스트링 값이 같은지 판단한다)
require(keccak256(_name) == keccak256("Vitalik"));
// 참이면 함수 실행을 진행한다:
return "Hi!";
}
5. 상속
- 컨트랙트는 상속이 가능합니다.
- java/c#의 상속과 동일한 개념입니다.
- 문법은 is를 사용하게 됩니다.
- BabyDoge는 Doge에 정의된 public 함수는 모두 접근이 가능합니다.
contract Doge {
function catchphrase() public returns (string) {
return "So Wow CryptoDoge";
}
}
contract BabyDoge is Doge {
function anotherCatchphrase() public returns (string) {
return "Such Moon BabyDoge";
}
}
6. Import
- 다른 파일을 불러오는 기능입니다.
import "./someothercontract.sol";
contract newContract is SomeOtherContract {
}
7. Storage vs Memory
- 솔리디티에는 변수를 저장할 수 있는 공간으로 storage와 memory 두 가지가 있습니다.
- Storage는 블록체인 상에 영구적으로 저장되는 변수를 의미합니다.
- Memory는 임시적으로 저장되는 변수로, 컨트랙트 함수에 대한 외부 호출들이 일어나는 사이에 지워집니다.
- 솔리디티가 알아서 처리해 주기 때문이지 대부분의 경우에 이 키워드들을 이용할 필요가 없습니다.
- 상태 변수(함수 외부에 선언된 변수)는 초기 설정상 storage로 선언되어 블록체인에 영구적으로 저장되는 반면, 함수 내에 선언된 변수는 memory로 자동 선언되어서 함수 호출이 종료되면 사라지게 됩니다.
- 하지만 이 키워드들을 사용해야 하는 경우가 있습니다. 바로 함수 내의 구조체와 배열을 처리할 때!!
contract SandwichFactory {
struct Sandwich {
string name;
string status;
}
Sandwich[] sandwiches;
function eatSandwich(uint _index) public {
// Sandwich mySandwich = sandwiches[_index];
// ^ 꽤 간단해 보이나, 솔리디티는 여기서
// `storage`나 `memory`를 명시적으로 선언해야 한다는 경고 메시지를 발생한다.
// 그러므로 `storage` 키워드를 활용하여 다음과 같이 선언해야 한다:
Sandwich storage mySandwich = sandwiches[_index];
// ...이 경우, `mySandwich`는 저장된 `sandwiches[_index]`를 가리키는 포인터이다.
// 그리고
mySandwich.status = "Eaten!";
// ...이 코드는 블록체인 상에서 `sandwiches[_index]`을 영구적으로 변경한다.
// 단순히 복사를 하고자 한다면 `memory`를 이용하면 된다:
Sandwich memory anotherSandwich = sandwiches[_index + 1];
// ...이 경우, `anotherSandwich`는 단순히 메모리에 데이터를 복사하는 것이 된다.
// 그리고
anotherSandwich.status = "Eaten!";
// ...이 코드는 임시 변수인 `anotherSandwich`를 변경하는 것으로
// `sandwiches[_index + 1]`에는 아무런 영향을 끼치지 않는다. 그러나 다음과 같이 코드를 작성할 수 있다:
sandwiches[_index + 1] = anotherSandwich;
// ...이는 임시 변경한 내용을 블록체인 저장소에 저장하고자 하는 경우이다.
}
}
8. 함수 접근 제어자 더 알아보기
- private으로 선언된 함수는, 상속받은 컨트랙트에서 접근할 수 없다.
- internal 접근제어자 : internal은 함수가 정의된 컨트랙트를 상속하는 컨트랙트에서도 접근이 가능하다 점을 제외하면 private과 동일합니다.
- external 접근제어자 : external은 함수가 컨트랙트 바깥에서만 호출될 수 있고 컨트랙트 내의 다른 함수에 의해 호출될 수 없다는 점을 제외하면 public과 동일합니다.
contract Sandwich {
uint private sandwichesEaten = 0;
function eat() internal {
sandwichesEaten++;
}
}
contract BLT is Sandwich {
uint private baconSandwichesEaten = 0;
function eatWithBacon() public returns (string) {
baconSandwichesEaten++;
// eat 함수가 internal로 선언되었기 때문에 여기서 호출이 가능하다
eat();
}
}
9. Interface(다른 컨트랙트와 상호작용하기)
- 블록체인 상에 있으면서 우리가 소유하지 않은 컨트랙트와 우리 컨트랙트가 상호작용을 하려면 우선 인터페이스를 정의해야합니다.
- interface 정의를 할 때에도 contract 명령어를 이용하여 정의한다.
// 임의의 contract
contract LuckyNumber {
mapping(address => uint) numbers;
function setNum(uint _num) public {
numbers[msg.sender] = _num;
}
function getNum(address _myAddress) public view returns (uint) {
return numbers[_myAddress];
}
}
// LuckyNumber을 참조하기 위하여 interface 선언
contract NumberInterface {
function getNum(address _myAddress) public view returns (uint);
}
// 크립토kitty의 공개함수(external로 선언되어 외부에서만 호출할 수 있다.)
// solidity는 그리고 아래 함수처럼 한번에 여러 값을 반환 할 수 있다.
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
) {
Kitty storage kit = kitties[_id];
// if this variable is 0 then it's not gestating
isGestating = (kit.siringWithId != 0);
isReady = (kit.cooldownEndBlock <= block.number);
cooldownIndex = uint256(kit.cooldownIndex);
nextActionAt = uint256(kit.cooldownEndBlock);
siringWithId = uint256(kit.siringWithId);
birthTime = uint256(kit.birthTime);
matronId = uint256(kit.matronId);
sireId = uint256(kit.sireId);
generation = uint256(kit.generation);
genes = kit.genes;
}
10. Interface 연동(다른 컨트랙트와 연동 방법)
- 다른 컨트랙트의 주소를 입력하여 참조를 할 수 있다.
// NumberInterface는 위 9.번 내용 소스 참조
contract MyContract {
address NumberInterfaceAddress = 0xab38...
// ^ 이더리움상의 FavoriteNumber 컨트랙트 주소이다
NumberInterface numberContract = NumberInterface(NumberInterfaceAddress)
// 이제 `numberContract`는 다른 컨트랙트를 가리키고 있다.
function someFunction() public {
// 이제 `numberContract`가 가리키고 있는 컨트랙트에서 `getNum` 함수를 호출할 수 있다:
uint num = numberContract.getNum(msg.sender);
// ...그리고 여기서 `num`으로 무언가를 할 수 있다
}
}
11. 다수의 반환 값 처리
- 다수의 값을 반환하는 방법은 "return (1, 2, 3);" 와 같은 방법을 사용한다.
function multipleReturns() internal returns(uint a, uint b, uint c) {
return (1, 2, 3);
}
function processMultipleReturns() external {
uint a;
uint b;
uint c;
// 다음과 같이 다수 값을 할당한다:
(a, b, c) = multipleReturns();
}
// 혹은 단 하나의 값에만 관심이 있을 경우:
function getLastReturnValue() external {
uint c;
// 다른 필드는 빈칸으로 놓기만 하면 된다:
(,,c) = multipleReturns();
}
12. if문
- if문은 여느 언어의 사용법과 동일합니다.
- 스트링 간의 동일 여부를 판단하기 위해 keccak256 해시 함수를 이용해야 합니다.
function eatBLT(string sandwich) public {
// 스트링 간의 동일 여부를 판단하기 위해 keccak256 해시 함수를 이용해야 한다는 것을 기억하자
if (keccak256(sandwich) == keccak256("BLT")) {
eat();
}
}
[US$140.00](▼54%)샤오미 드리미 V10 무선 청소기 / 유럽버전! / 6개월무료A/S / 무료배송/
WWW.QOO10.COM음... 역시 쓸말이 없군요! ㅋ
즐거운 한주 보내세요^^
외국인 만난 느낌이 ㅎㅎ
댓글들 반응이 ㅎㅎ
눈에 쏙 들어옵니다.
회색바탕에 검은글씨 ㅎㅎㅎㅎ
@happyberrysboy transfered 23 KRWP to @krwp.burn. voting percent : 85.80%, voting power : 82.80%, steem power : 1884924.10, STU KRW : 1200.
@happyberrysboy staking status : 7450 KRWP
@happyberrysboy limit for KRWP voting service : 22.35 KRWP (rate : 0.003)
What you sent : 23 KRWP
Refund balance : 0.649 KRWP [46078746 - 23fd6e4ab6c1d282e6c2d15804f436e458f9f649]
^^