Solidity 개발 시작하기

공형규·2024년 4월 9일

이 글은 아래 코트스테이츠의 내용을 정리한 것입니다.
참고 링크

설치 방법

  • terminal에서 아래와 같이 실행
brew update
brew tap ethereum/ethereum  // tap: 추가적인 Homebrew repo 저장소 사용자 시스템에 등록 tap <사용자명/저장소명>
brew install solidity
  • 이후 solc --version 명령어로 설치 확인

솔리디티 개발 시작하기

기본적인 스마트 컨트랙트 구조

  • 라이센스 나타내는 식별자
  • 솔리디티 버전
  • 배포하고자 하는 컨트랙트 코드
// 1. SPDX 라이센스 식별자
// SPDX-License-Identifier: GPL-3.0

// 2. Version Pragma
pragma solidity >= 0.7.0 < 0.9.0;

// 3. 배포할 컨트랙트
contract Ex1 {
	// code
}
  1. SDPX 라이센스 식별자
    • 솔리디티 소스 코드 사용은 저작권과 관련된 법적 문제를 다룸
    • 컴파일러는 기계 판독이 가능한 SPDX 라이센스 식별자 사용 권장
    • 모든 소스 파일은 라이센스를 나타내는 주석으로 시작
    • 주석은 파일의 어느 곳에 있어도 컴파일러가 인식, 하지만 최상단 추천
    • 위 코드는 GPL 3.0 버전 라이센스 하에 있다는 것 의미
  2. Version Pragma
    • 솔리디티 버전 선언
    • 새로운 컴파일러 버전이 나와도 기존 코드 깨지는 것 방지
    • 컴파일러 버전 간 호환되지 않은 변경 사항 생기는 것 차단
  3. 컨트랙트
    • 배포할 컨트랙트 작성

컨트랙트 코드 실행 & 배포

  1. Remix에 새 워크스페이스 생성

  1. 아래와 같이 폴더 practice, Ex1.sol 파일 생성

  1. 다음과 같이 코드 입력

    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
        // 코드 입력
    }
  2. 컴파일러 누르고 Ex.1 파일 컴파일

  1. 컨트랙트 배포 실행

  1. 아래 터미널에서 컨트랙트 정상 배포 확인 가능


솔리디티 문법

변수

  • 솔리디티에서 변수 선언을 위해서는 자료형 명시해야 함
// 자료형/변수/대입 연산자/값
uint a = 5;
  • 솔리디티 3가지 변수 타입

    • 지역 변수 (local)
      • 함수 안에 선언
      • 블록체인에 기록되지 않음
    • 상태 변수 (state)
      • 함수 밖에 선언
      • 블록체인에 저장되어 영속성 가짐
      • 함수 내부 어디서든지 사용 가능
      • 가시성 지정자에 따라 변수 접근 범위가 달라짐
    • 전역 변수 (global)
      • 블록체인에 관한 정보 제공
    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
        // 상태 변수
        string public a = "Hello World";
        uint public b = 1;
    
        function getEx() view public {
            // 지역 변수
            uint c = 2;
    
            // 전역 변수
            uint d = block.timestamp;
            address e = msg.sender;
        }
    }

자료형

솔리디티 자료형의 분류

  1. 값 타입 (value type)
    • 값 할당이나 함수의 인자로 활용되면 해당 값 자체가 복사
  2. 참조 타입 (reference type)
    • 현재 해당하는 값의 주소만 복사
    • 즉, 데이터를 어디에 저장할 지 명시해야 함
    • 기본적으로 다음과 같이 구성됨
      • 배열 array
      • 매핑 mapping
      • 구조체 struct

값 타입

1. bool - Boolean
  • 참과 거짓으로 이루어진 자료형

  • 솔리디티에서는 bool 로 표기

  • 특정 조건에 발생하는 행동 통제에 쓰임

  • 주로 비교 연산자, 논리 연산자와 함께 사용

2. uint & int - Integer 정수
  • 솔리디티는 기존 프로그래밍 언어와 달리 소수점 있는 수자 지원하지 않음
  • 정수형의 목적은 가격, 토큰의 아이디와 같이 숫자로 된 정보 표현
  • 솔리디티 정수형 타입은 부호 없는 정수형 uint 와 정수형 int 이며 둘의 차이점은 음수 포함 여부
  • 주로 산술 연산자와 비교 연산자에서 함께 사용
3. bytes - 바이트 타입
  • 고정 크기 바이트(값 타입) 배열과 동적 크기 바이트 배열로 나뉠 수 있음
  • 고정 크기 바이트 배열은 값 타입이며 사용할 바이트 미리 지정
  • 동적 크기 바이트는 참조 타입, 사용할 바이트 값 지정하지 않아도 됨
4. string - 문자열 타입
  • 동적 크기 UTF-8로 인코딩된 배열
5. address - 주소 타입
  • 계정의 주소 나타냄
  • 주소형 타입의 크기는 20bytes로 지정되어 있음
  • 유저의 고유 아이디 또는 배포된 스마트 컨트랙트의 아이디로 볼 수 있음
  • 이 주소를 통해 암호화폐 주고 받게 되는 것

참조 타입

1. 매핑(Mapping)
  • 매핑은 JS의 Object와 같이 키와 값의 형태로 저장

  • mapping(key type ⇒ value type) 가시성 지정자 매핑명) 으로 정의됨

  • key type

    • 모든 기본 값(정수 등), bytes, string, 컨트랙트가 될 수 있음
  • value type

    • 다른 매핑과 배열을 포함한 모든 유형이 될 수 있음
  • 대표적인 예시 - 각 유저 토큰 잔액을 나타내는 것을 매핑으로 구현 가능

    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
    	
    	// mapping(key type ⇒ value type)/가시성 지정자/매핑명)
    	mapping(address => uint) public myAddr;
    	
    }
  • 매핑에 키와 값 추가 가능

    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
    	
    	// key: address type, value: uint type
    	mapping(address => uint) public myAddr;
    	
    	function addM(address _key, uint _val) public {
    	myAddr[_key] = _val;
    	}
    	
    	function getM(address _key) public view returns(uint) {
    	return myAddr[_key];
    	}
    }
    • myAddr 매핑 키는 address 자료형이고 값의 자료형은 uint
    • addM 함수는 myAddr 매핑에 데이터 저장하는 함수
    • 매개변수로 address와 uint 자료형을 받고 있는 것 확인 가능
  • 위 코드를 실행해보자

    • 실행 전 주소를 복사해두자

  • 이후 배포하면 하단에 컨트랙트를 실행할 수 있는 공간이 생김

  • 복사한 주소를 넣고 실행해보자

  • 이후 getM 을 실행하여 올바른 값이 나오는 지 확인

  • 매핑에서 키와 값을 삭제할 수도 있음

    function deleteM(address _key) public {
    	delete(myAddr[_key]);
    }
    
    function deleteM2(address _key) public {
    	// mapping의 value가 uint 자료형일 경우만 가능
    	myAddr[_key] = 0;
    }
    • 기존 100이 저장되어 있는 키가 delete 실행 후 0으로 바뀐 것 확인 가능

  • 매핑의 특성상, 저장되지 않은 키에 대응하는 값은 기본적으로 0
2. 배열(Array)
  • 배열: 어떤 것의 모음집

  • 한 개의 배열에 여러 개의 값을 순차적으로 저장

  • 값을 순차적으로 저장하기에 배열의 길이 지원

  • 타입[] 가시성 지정자 배열명으로 정의

    // 자료형/배열/가시성 지정자/배열명
    uint [] public arr;
  • 솔리디티에서 배열은 정적/동적 배열로 사용 가능

  1. 동적 배열 (dynamic)

    • 런타임에 크기가 결정됨
    • 즉, 고정된 크기 없이 계속 커질 수 있음
    uint[] public arr;
  2. 정적 배열 (static)

    • 선언 시 크기가 결정됨
    • 미리 사용할 배열의 크기 지정 가능
    string[10] public arr2 = ["apple", "banana", "tomato"];
  • 배열의 인덱스에 대응하는 값 구할 수 있음

  • getArrleng getArr2leng 은 배열 길이 반환하는 함수

    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
    	
        uint[] public arr;
        // 3개 값 저장된 상태, 나머지 인덱스는 공백으로 남겨져 있을 것
        string[10] public arr2 = ["apple", "banana", "tomato"];
    
        function getArrLeng() public view returns(uint) {
            return arr.length;
        }
    
        function getArr2Lneg() public view returns(uint) {
            return arr2.length;
        }
    
        function getArr(uint _index) public view returns(uint) {
            return arr[_index];
        }
    
        function getArr2(uint _index) public view returns(string memory) {
            return arr2[_index];
        }
    }
    • arr 함수에 0 집어 넣으면 다음과 같은 오류 발생

  • arr 크기가 0이며 값을 저장할 수 있는 공간이 존재하지 않기 때문에 발생

  • arr2 또한 인덱스 값으로 10 이상 넣으면 동일 오류 발생할 것, 크기가 10으로 지정되어 있기 때문

  • 배열 값 추가

    function plusArr(uint _value) public {
    	arr.push(_value);
    }
    
    // arr2는 크기가 10으로 고정되어 있기 때문에 push method 사용 시 에러
    function plusArr2(string memory _value) public {
    	arr2.push(_value);
    }
    • 코드 실행 시 다음과 같은 결과 값 얻을 수 있음

- `plusArr` 실행 후 `getArrLeng` 실행
  • 배열 값 변경

    function changeArr(uint _index, uint _value) public {
    	arr[_index] = _value;
    }
    
    function changeArr2(uint _index, string memory _value) public {
    	arr2[_index] = _value;
    }
    • 값 변경 확인

  • 배열 값 삭제

    • 두 가지 방법이 있음
      1. pop
      2. delete
    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
    	
        uint[] public arr = [1, 2, 3];
    
        function getLeng() public view returns(uint) {
            return arr.length;
        }
    
        function removeArr() public {
            arr.pop();
        }
    
        function deleteArr(uint _index) public {
            delete arr[_index];
        }
    }
    • 실행 전

  • removeArr (pop) 실행 → 배열 길이 2로 변경

  • deleteArr(0) 실행

* 배열의 길이가 줄지는 않았지만, arr[0] 이 0으로 변경
3. 구조체 (struct)
  • 구조체: 우리만의 타입을 만드는 것
  • 사용자 정의 자료형
  • 자신이 원하는 자료형 만들어 변수, 배열, 매핑 등과 같이 자료형을 명시해야 하는 곳에 적용 가능
  • 구조체는 한 개 이상 변수의 집단으로 구성됨
  • 컨트랙트 밖에서 선언 가능
  • 다른 컨트랙트로 imported 역시 가능
struct Human {
	uint age;
	string name;
	string job;
}
  • 아래 코드는 구조체를 자료형으로 명시 후 이를 반환하는 코드 예제
// SPDX-License-Identifier: GPL-3.0

pragma solidity >= 0.7.0 < 0.9.0;

contract Ex1 {
	
    struct Human {
        uint age;
        string name;
        string job;
    }

    Human public human1 = Human(22, "sol", "dr");

    Human public human2;

    function getH1() public view returns (Human memory) {
        return human1;
    }

    function getH2() public view returns (Human memory) {
        return human2;
    }
}

  • human2human 자료형 값을 대입하는 함수를 작성해보자

    function newH2(uint _age, string memory _name, string memory _job) public {
    	human2 = Human(_age, _name, _job);
    }

  • human1의 직업을 바꿔보자

    function changeH1Job(string memory _job) public {
    	human1.job = _job;
    }

함수

  • 함수: 어떤 기능을 수행하도록 작성된 코드
  • 함수의 필수 요소
    • function 키워드
    • 함수명
    • 가시성 지정자

가시성 지정자

  • 가시성 지정자란, 변수나 함수의 공개 범위제한하는 것
  • 어떠한 주체가 특정 변수나 함수에 접근하려 시도 할 때 변수나 함수에 지정된 가시성 지정자에 의해 접근 여부가 판가름 남
  • 종류
    • public: 외부, 내부 어디서든 접근 가능
    • external: 선언된 컨트랙트 외부에서만 접근 가능
    • private: 오직 선언된 컨트랙트 내부에서만 접근 가능
    • internal: 선언된 컨트랙트 내부와 이를 상속 받은 컨트랙트 내부에서 접근 가능

함수의 정의

  • 기본적으로 function 키워드, 함수 이름, 가시성 지정자 순으로 작성하여 함수 정의 가능
function functionName() public {
	// logic
}
  • 만일 매개변수나 반환하는 리턴 값이 존재하는 경우 아래와 같이 정의
function functionName(매개변수) public returns (반환값 자료형) {
	// logic
}
  • 매개변수로 정수 두 개를 받아 서로 더한 값을 반환하는 함수를 작성해보자
// SPDX-License-Identifier: GPL-3.0

pragma solidity >= 0.7.0 < 0.9.0;

contract Ex1 {
    function plusNum(uint a, uint b) public pure returns (uint) {
        return a + b;
    }
}

함수의 매개변수 및 반환값

  • 솔리디티에는 다음과 같이 네 가지 영역이 존재

    • 스토리지 Storage
    • 메모리 Memory
    • 콜데이터 Calldata
    • 스택 Stack
  • 매개변수 혹은 반환값으로 참조 타입 사용 시, 이 네 개 영역 중 알맞은 하나의 저장 영역을 명시해 줘야 함

  • 스토리지

    • 데이터가 영구적으로 저장되는 공간
  • 메모리

    • 단기적으로만 데이터를 저장하는 공간
  • 콜데이터

    • 트랜잭션 혹은 call 함수의 매개변수가 유지되는 읽기 전용 공간
  • 스택

    • 이더리움 가상 머신에서 휘발성을 가진 데이터를 유지 및 관리하는 공간
  • 사용 예시

// SPDX-License-Identifier: GPL-3.0

pragma solidity >= 0.7.0 < 0.9.0;

contract Ex1 {
    function getValue(uint a) public pure returns (uint) {
        return a;
    }

    function getReference(string memory a) public pure returns (string memory) {
        return a;
    }
}

모디파이어 Modifier

  • 위 예시에서, 함수 정의를 위해 배운 세 가지 키워드 이외에 pure 라는 키워드를 확인할 수 있음
  • 해당 키워드는 모디파이어의 한 종류
  • 가시성 지정자 다음에 명시하여 함수 로직에 제약을 주는 역할
  • pure
    • 함수 밖에 선언된 변수를 함수 내부로 가져오지 못하게 하는 키워드
    • 순수하게 함수 내부에 정의된 변수나 전달받은 매개변수만 사용 가능
  • view
    • 함수 외부의 변수를 읽을 수 있으나
    • 값을 함수 내부에서는 변경할 수 없음
  • 위 두 가지 경우 모두에 해당하지 않는 경우에는 생략 가능

연산자

  • 대입 연산자

    • 변수에 값을 할당
    • =
    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
        string a = "Hello World!"
    }
  • 산술 연산자

    • 수학적 연산을 수행하는 연산하는 데 사용
    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
        uint a = 1 + 2;
        uint b = 2 - 1;
        uint c = 3 * 2;
        uint d = 5 / 5;
        uint e = 5 % 2;
        uint f = 2 ** 2;
    
        function getResult() public view returns(uint, uint, uint, uint, uint, uint) {
            return (a, b, c, d, e, f);
        }
    }

  • 할당 연산자

    • 대입과 산술이 합쳐진 형태
    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
        uint a = 10;
        uint b = 10;
        uint c = 10;
        uint d = 10;
        uint e = 10;
    
        function getResult() public returns(uint, uint, uint, uint, uint) {
            a += 2;
            b -= 2;
            c *= 2;
            d /= 2;
            e %= 2;
            return (a, b, c, d, e);
        }
    }

  • 비교 연산자

    • bool 자료형인 true, false 로 결과값 반환
    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
        bool a = 1 > 3;
        bool b = 1 < 3;
        bool c = 10 >= 2;
        bool d = 3 <= 3;
        bool e = 1 == 2;
        bool f = 1 != 2;
    
        function getResult() public view returns (bool, bool, bool, bool, bool, bool) {
            return (a, b, c, d, e, f);
        }
    }

  • 논리 연산자

    • AND, OR, NOT 등의 논리 연산을 수행하여 bool 타입 반환
    // SPDX-License-Identifier: GPL-3.0
    
    pragma solidity >= 0.7.0 < 0.9.0;
    
    contract Ex1 {
        bool a = true && true;
        bool b = true && false;
        bool c = false && false;
        bool d = true || true;
        bool e = true || false;
        bool f = false || false;
        bool g = !false;
    
        function getResult() public view returns(bool, bool, bool, bool, bool, bool, bool) {
            return(a, b, c, d, e, f, g);
        }
    }

업로드중..

profile
즐겁게 개발하는 개발자

0개의 댓글