2. Solidity(저장고 공장)

정예찬·2022년 7월 14일
4

solidity

목록 보기
4/13
post-custom-banner

본 글은 freeCodeCamp.Org의 Youtube 영상 'Solidity, Blockchain, and Smart Contract Course – Beginner to Expert Python Tutorial'와 관련 코드인 SmartContract의 Github 코드를 기초로 작성되었다.

Youtube 영상 링크: https://www.youtube.com/watch?v=M576WGiDBdQ&t=10336s
Github 코드 링크: https://github.com/smartcontractkit/full-blockchain-solidity-course-py
오늘의 코드: https://github.com/PatrickAlphaC/storage_factory/blob/main/StorageFactory.sol

이번 포스팅은 유튜브 영상 02:09:32~02:26:35에 해당하는 내용이다.

저번 포스팅에서 (1)정수 하나를 저장하고 불러오는 기능과 (2)여러 이름과 각 사람마다 대응하는 숫자 하나를 저장하고 불러오는 기능을 가진 계약을 만들었다.(추가적으로 이름을 입력하면 대응하는 숫자를 출력하는 기능도 있었다.) 즉, 특정 기능을 하는 저장고를 계약 형태로 만들었다. 이번 포스팅에서는 제목에서 알 수 있듯이 그러한 저장고를 여러 개 만드는 코드를 짜보겠다. 그 전에 저번 포스팅에 작성한 SimpleStorage.sol 파일을 미리 contracts 폴더에 추가, 컴파일해두자. 이번 포스팅에 활용된다.

먼저 remix의 contracts 폴더에 'StorageFactory.sol'이란 파일을 생성해주자.

// SPDX-License-Identifier: MIT

첫 줄에 어김없이 라이센스를 명시해주자.

pragma solidity ^0.6.0;

버전은 저번 포스팅 그대로 설정해두자.

import "./SimpleStorage.sol"; 

다음으로 위 코드를 입력하자. 'import "./파일명";'을 입력하면 해당 파일의 모든 코드를 붙여넣는 것과 같은 효과가 발생한다. 즉 이 코드 한 줄로 SimpleStorage.sol에 있는 모든 코드가 StorageFactory.sol에 복사되었다고 이해하면 된다. 다만 직접 코드를 긁어 붙여넣었을 때와 다르게 import된 contract를 import된 파일 안에서 실행할 수는 없다. 그러나 이 또한 코드 한 줄만 추가하면 가능해진다.

contract StorageFactory is SimpleStorage {
...
}

위 코드에서는 'StorageFactory'라는 계약을 선언하고 있다. 그러나 이에 그치지 않고 'is 타 계약 이름'이라는 새로운 문법이 등장했다. 이는 객체지향 언어의 '상속'이라는 개념을 이용해 만들어진 문법이다.(해당 개념에 대해 더 알고 싶으면 검색해보라.) 해당 계약은 이 선언을 통해 타 계약의 모든 기능을 물려받는다.
위 예시에서 'is SimpleStorage'라는 선언으로 인해 StorageFactory는 SimpleStorage의 모든 기능을 활용할 수 있게 된다. SimpleStorage라는 계약은 아까 import 선언에 의해 본 파일에 추가되었기에 활용가능하다. StorageFactory 계약에 아무런 내용도 입력하지 않고 컴파일, deploy해보자. 'Deployed Contracts'에 저번 시간에 활용했던 함수들이 그대로 출력됨을 확인할 수 있다.

본격적으로 StorageFactory 계약에 내용을 추가해보자.

SimpleStorage[] public simpleStorageArray;

저장고(SimpleStorage)를 여러 개 생성하기 위해 '[]'를 활용하여 저장고 배열을 만들고, 이를 simpleStorageArray라 명명하고 있다. 가시성 키워드로 public이 선언됨으로써 다른 함수가 이 배열에 자유롭게 접근할 수 있게 되었다.

function createSimpleStorageContract() public {
	SimpleStorage simpleStorage = new SimpleStorage();
    simpleStorageArray.push(simpleStorage);
}

위 함수는 앞서 선언한 저장고 배열을 활용하여 저장고를 생성하는 함수이다. 'new'라는 새로운 키워드가 등장하였다. new 키워드는 뒤이어 나오는 대상만큼의 메모리를 컴퓨터에 할당한다. 위 예시에서는 SimpleStorage만큼의 메모리를 할당하여 이를 simpleStorage에 부여한다. 그러나 new 키워드를 사용하여 메모리를 할당하는 이유는 뭘까? 'SimpleStorage simpleStorage;'로 변수를 선언해도 될 듯한데, 왜 굳이 'SimpleStorage simpleStorage = new SimpleStorage();'로 선언했을까?
이는 컴퓨터 메모리의 할당 방식과 관련이 있다. 전자의 경우는 SimpleStorage 크기에 해당하는 메모리가 simpleStorage 변수에 할당되었다가 함수의 실행이 끝나면 회수된다. 즉, 이 함수를 실행함과 동시에 저장고가 생성되었다가 없어진다. 따라서 함수를 실행하는 의미가 없다. 후자의 경우 new 선언을 통해 SimpleStorage만큼의 메모리가 할당된 후 'delete' 선언을 하지 않는 한 메모리가 회수되지 않는다. 따라서 함수 실행이 끝난 이후에도 생성된 저장고가 유지된다. 따라서 다른 함수에서 생성된 저장고를 활용하고 싶다면 'new'선언을 통해 저장고를 만들어야 한다.
저장고를 여러 개 생성하기 위해 push함수와 SimpleStorage를 여러 개 담는 simpleStorageArray를 활용하고 있다. 타 함수에서도 이 저장고에 접근할 수 있도록 가시성 키워드를 public으로 설정하고 있다.

function sfStore(uint256 _simpleStorageIndex, uint256 _simpleStorageNumber) public {
	SimpleStorage(address(simpleStorageArray[_simpleStorageIndex])).store(_simpleStorageNumber); 
}

짧지만 복잡한 코드이다. 이 함수는 앞서 선언한 저장고 배열에서 저장고를 하나 골라 양의 정수를 입력한다. 코드를 구체적으로 뜯어보기 전에 '계약 이름(대상의 주소).계약 내 함수;'라는 형태의 선언(위 예시에서는 SimpleStorage(address(대상)).store(값)이다.)이 어떤 기능을 수행하는지 알아보자.

'address(입력값)'은 입력값의 주소를 반환하는 함수이다.

위 선언을 하면 대상에 대해 계약 내 함수가 실행된다. 즉 대상에 대해 SimpleStorage의 store 함수가 실행된다. 위 함수에서 대상은 'simpleStorageArray[_simpleStorageIndex]'이다. _simpleStorageIndex는 저장고 배열의 몇 번째 요소인지를 확인하기 위한 정수로, 함수의 입력값 중 하나이다. 해당 배열 요소의 주소가 address함수에 의해 반환되고, 이 주소가 SimpleStorage에 입력되고 있음을 확인할 수 있다. _simpleStorageNumber 또한 위 함수의 입력값 중 하나로, SimpleStorage 계약 내 store 함수의 입력값이 되어 대상 내 데이터로서 입력된다.
코드가 복잡하여 잘 이해되지 않을 수 있는데, 추후 구체적으로 위 함수가 어떻게 작동하는지 살펴보면 좀 더 이해가 될 것이다.

function sfGet(uint256 _simpleStorageIndex) public view returns (uint256) {
	return SimpleStorage(address(simpleStorageArray[_simpleStorageIndex])).retrieve();
}

sfStore 함수가 저장고를 골라 그 저장고에 양의 정수 하나를 입력하는 역할을 했다면, 지금 살펴볼 sfGet 함수는 저장된 양의 정수를 출력하는 역할을 한다. sfGet 함수는 sfStore와 형태가 유사하다. 다만 store(_simpleStorageNumber) 대신 retrieve()를 활용하여 저장된 값을 확인하고, 이 값을 return을 통해 출력하고 있다. sfGet 함수 또한 sfStore처럼 배열의 인덱스(_simpleStorageIndex)를 입력값으로 받아 원하는 배열 요소에 접근하고 있다. 출력용 함수이니 view 키워드를 추가하였고, 양의 정수 형태로 값을 반환하고 있다.

코드 작성이 끝났다. 컴파일, deploy해보면 다음과 같이 뜬다.

Deployed Contracts에 함수가 너무 많아 헷갈리니 'is SimpleStorage'를 지워 오늘 다룬 함수만 볼 수 있게 하자.

함수 실행의 예를 들겠다.
(1) 'createSimpleStorageContract' 버튼을 3번 눌러 0번, 1번, 2번 배열을 생성하자.
(2) sfStore에 '2, 54'를 입력하여 세 번째 배열인 2번 배열에 54를 입력하자.
(3) sfGet에 '2'를 입력하면 54가 출력된다.

세 번째 포스팅이 끝났다. 포스팅이 거듭될수록 영상 길이도 길어지고, 내용도 많아지고, 사용되는 프로그램도 많아진다. 내용이야 필자가 공부하면 되지만 사지방 컴퓨터가 여러 프로그램을 감당할 수 있을지 걱정이 된다. remix만 돌려도 튕길 때가 있는 컴퓨터여서 그렇다. 그래도 어떻게든 방법을 찾아보겠다. 필자의 좌우명은 'I never give up'이다. 일론 머스크의 인터뷰에서 영감을 받은 좌우명이다. 남은 11편의 포스트도 꼭 일정대로 업로드하도록 하겠다. 많이 기대해주고 응원해달라! ㅎㅎㅎ

Github Code License:

MIT License

Copyright (c) 2021 SmartContract

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
profile
BlockChain Researcher
post-custom-banner

0개의 댓글