Develop
Solidity 치트시트
728x90
1강: Hello Solidity & 스마트 컨트랙트 기초
- 솔리디티(Solidity): 이더리움 블록체인에서 스마트 컨트랙트(Smart Contract)를 개발하기 위한 언어.
- 스마트 컨트랙트: 미리 정의된 조건이 충족되면 블록체인 내에서 자동으로 실행되는 프로그램.
- 개발 환경: 온라인 IDE인 Remix(리믹스)를 주로 사용함. https://remix.ethereum.org/
- 기본 구조
- 라이선스 명시: 파일 최상단에 SPDX-License-Identifier를 반드시 명시해야 함.
- 버전 설정: pragma solidity를 통해 컴파일러 버전을 지정.
- 컨트랙트 정의: contract 키워드 뒤에 이름을 붙여 시작.
- 문장 종료: 모든 문장 끝에는 세미콜론(;)을 붙여야 함.
2강: 데이터 타입(Data Type)
- 주요 타입
- bool: true 또는 false 값을 가짐.
- bytes: 1~32바이트까지 지정 가능하며, bytes만 쓰면 스트링을 바이트화하여 유동적으로 저장함.
- address: 계좌 주소 또는 스마트 컨트랙트 배포 주소를 의미함 (길이 20바이트).
- int/uint: 정수형. int는 음수 포함, uint는 부호 없는 정수(0부터 시작)로 실무에서 더 자주 쓰임.
- 주의사항: uint8과 같이 범위를 지정했을 때 해당 범위를 넘어가면 에러가 발생함.
3강: 이더(Ether) 단위와 가스(Gas)
- 이더 단위:
- 1 Ether = 10^9Gwei = wei.
- Gwei: 가스비를 낼 때 주로 사용하는 단위 ( Wei).
- 가스(Gas): 스마트 컨트랙트를 실행할 때 지불하는 비용.
- 코드가 길거나 복잡할수록 더 많은 가스가 소비됨.
- 보안: 가스 비용은 해커가 무차별적으로 네트워크를 공격(DDoS)하는 것을 방지하는 역할도 함.
4강: 함수(Function) 정의
- 기본 구조: function name(parameter) access_modifier [view|pure] returns(type) { ... }.
function retrieve() public view returns (uint256){
return number;
}
5강: 접근 제한자(Visibility Specifiers)
- public: 어디서든(내부, 외부, 상속 컨트랙트) 접근 가능.
- private: 오직 해당 컨트랙트 내부에서만 접근 가능 (상속받아도 접근 불가).
- external: 오직 외부에서만 호출 가능 (자기 컨트랙트 내 호출 불가).
- internal: 내부와 상속받은 컨트랙트에서 접근 가능.
6강: View와 Pure 함수
- view: 함수 밖의 변수(상태 변수)를 읽을 수 있지만 변경은 불가능할 때 사용.
- pure: 함수 밖의 변수를 읽지도 못하고 변경도 불가능할 때 사용. 오직 함수 내부의 값만 활용함.
- 상태 변수를 변경하는 경우에는 두 키워드 모두 쓰지 않음.
uint256 public a = 1;
function read_a() public view returns(uint256){
return a+2;
}
function read_a2() public pure returns(uint256){
uint256 b = 1;
return b+2;
}
function read_a2() public returns(unit256){
a = 13;
return a;
}
7강: 저장 영역(Storage Areas) 및 String
- 저장 영역 4가지:
- storage: 블록체인에 영구적으로 기록됨. 가스비가 가장 비쌈 : 대부분의 변수, 함수들이 저장
- memory: 함수 실행 중에만 존재하고 사라지는 휘발성 데이터 : 함수의 파라미터, 리턴값, 레퍼런스 타입
- calldata: 주로 external 함수의 파라미터에 사용됨.
- stack: EVM에서 관리하는 제한적인 영역 1024MB 제한적.
- String: 솔리디티의 기본 타입이 아닌 레퍼런스 타입이므로, 함수에서 쓸 때 memory 키워드를 함께 붙여줘야 함.
8강: 인스턴스(Instance)
- 정의: 하나의 컨트랙트에서 다른 컨트랙트에 접근하기 위해 사용함.
- 사용법: A인스턴스이름 = new A(); 와 같이 선언하여 다른 컨트랙트의 변수나 함수를 호출함.
- 특징: 인스턴스는 원본 컨트랙트의 구조는 같지만 별개의 객체임. 따라서 인스턴스에서 값을 바꿔도 원본 컨트랙트의 값은 변하지 않음.
컨트랙트 자체를 직접 실행하면 static class 처럼 동작하는 느낌이고, 인스턴스는 자바와 같은 느낌.
9강: 생성자(Constructor)
- 역할: 스마트 컨트랙트가 처음 배포되거나 인스턴스화될 때 변수값을 초기화함.
- 특징: 컨트랙트 배포 시 한 번만 실행되며, 필요한 파라미터를 배포 시점에 전달할 수 있음.
- 가스비 고려: 컨트랙트 내에서 다른 컨트랙트를 인스턴스화하면 가스 소비가 커지므로 주의가 필요함.
contract A{
string public name;
uint256 public age;
constructor(string memory _name, uint256 _age){
name = _name;
age = _age;
}
function change(string memory _name, uint256 _age) public {
name = _name;
age = _age;
}
}
contract B{
A instance = new A("jyami", 29);
function change (string memory _name, uint256 _age) public {
instance.change(_name, _age);
}
function get() public view returns(string memory, uint256){
return (instance.name(), instance.age());
}
}
10강~12강: 상속 (Inheritance)
- 상속의 정의: 기존 컨트랙트의 기능을 다른 컨트랙트가 물려받는 것.
- 오버라이딩(Overriding): 부모 컨트랙트의 함수를 자식 컨트랙트에서 재정의하여 사용하는 것.
- 다중 상속: 두 개 이상의 컨트랙트를 동시에 상속받을 수 있음. override 뒤에 다중으로 상속받는다.
- 상속의 부모가 되는 함수에는 kotlin의 open 처럼 virtual 을 사용해줘야한다.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.34;
contract Father{
uint256 public fatherMoney = 100;
function getFatherName() public pure returns(string memory) {
return "KimJung";
}
function getMoney() public view virtual returns(uint256){
return fatherMoney;
}
}
contract Mother{
uint256 public motherMoney = 200;
function getMotherName() public pure returns(string memory){
return "Leesol";
}
function getMoney() public view virtual returns (uint256){
return motherMoney;
}
}
contract Son is Father, Mother {
function getMoney() public view override(Father, Mother) returns(uint256){
return motherMoney + fatherMoney;
}
}
13강~14강: 이벤트(Event)와 Indexed
- 이벤트(Event): 솔리디티에는 print 함수가 없는 대신, 이벤트를 통해 값을 출력하고 이를 블록 안에 저장함. 블록에 저장된 데이터는 필요할 때 언제든 꺼내 쓸 수 있음.
- indexed: 이벤트 파라미터에 사용하는 키워드로, 특정한 이벤트 값만 필터링하여 가져올 때 사용함. 예를 들어 수많은 이벤트 중 특정인의 이름이 포함된 것만 찾고 싶을 때 유용함.
contract A{
event numberTracker(uint256 indexed num, string str);
uint256 num;
function pushEvent(string memory _str) public{
emit numberTracker(num, _str);
num++;
}
}
function getEvent(){
let events = await a.getPastEvents('numberTracker', {filter:{num[2,1]}, fromBlock: 1, toBlock: 'latest'});
}
indexed 가 붙어있지 않으면 filter를 해도 필터링이 되지않음.
15강~16강: 상속의 심화 (super & 순서)
- super: 자식 컨트랙트에서 부모 컨트랙트의 함수를 호출할 때 사용함. 오버라이딩을 하면서 부모의 원래 기능도 함께 실행하고 싶을 때 유용함.
- 상속 순서: 여러 컨트랙트를 상속받을 때, 맨 오른쪽(최신)에 명시된 컨트랙트가 가장 우선순위가 높으며 super는 이 최신 컨트랙트를 가리킴.
17강: 맵핑 (Mapping)
- 구조: 키(Key)와 값(Value)으로 이루어진 데이터 구조 (예: mapping(uint => uint)).
- 특징: 특정 키를 넣으면 대응하는 값을 즉시 얻을 수 있으나, 배열과 달리 길이(length)를 구할 수 없음.
contract A{
mapping(uint256=>uint256) private ageMap;
function setAge(uint256 index, uint age) public {
ageMap[index] = age;
}
function getAge(uint256 index) public view returns(uint256 ){
return ageMap[index];
}
}
18강: 배열 (Array)
- 기능: 값의 추가(push), 삭제(pop, delete), 길이 확인(length)이 가능함.
- delete의 특징: 특정 인덱스의 값을 지우면 데이터는 기본값(0 등)으로 변하지만, 배열의 길이는 변하지 않음. 완전히 길이를 줄이려면 pop을 사용해야 함.
- 가스비와 보안: 배열을 무한히 순환(loop)시키면 가스비가 폭등하거나 디도스(DDoS) 공격에 취약해질 수 있으므로 사이즈를 제한하는 것이 좋음. 그래서 보통 매핑을 많이 사용함.
contract A{
uint256[] public ageList;
function ageGet(uint256 _index) public view returns(uint256) {
return ageList[_index];
}
function agePop() public {
ageList.pop();
}
function ageDelete(uint256 _index) public{
delete ageList[_index];
}
function ageChange(uint256 _index, uint256 _age) public {
ageList[_index] = _age;
}
}
19강: 맵핑과 배열 사용 시 주의점
- 값의 캡처: 맵핑이나 배열에 값을 넣을 때, 당시의 값을 복사(Capture)하여 저장함. 따라서 원본 변수의 값이 나중에 변경되어도 맵핑이나 배열 내의 값은 자동으로 업데이트되지 않으므로 직접 갱신해줘야 함.
20강: 구조체 (Struct)
- 정의: 사용자가 직접 만드는 커스텀 데이터 타입. 여러 종류의 변수(uint, string 등)를 하나의 그룹으로 묶어 관리할 수 있음.
- 활용: 구조체 자체를 배열이나 맵핑의 값(Value)으로 사용하여 복잡한 데이터를 체계적으로 저장함.
contract A{
struct Character{
uint256 age;
string name;
string job;
}
mapping(uint256=>Character) characterMap;
Character[] characterList;
function createCharacter(uint256 _age, string memory _name, string memory _job) pure public returns(Character memory){
return Character(_age, _name, _job);
}
}
21강: 조건문 (If)
- 구조: if, else if, else를 사용하여 특정 조건이 충족될 때만 코드를 실행함.
- 특징: else 문을 생략하더라도 조건에 맞지 않으면 다음 코드로 넘어가거나 함수가 종료됨.
22강~24강: 반복문 (Loop) 및 응용
- 종류: for, while, do-while의 세 가지 형태가 있음.
- while: 조건을 먼저 체크하고 실행함.
- do-while: 조건이 맞지 않더라도 최소 한 번은 무조건 실행한 뒤 조건을 체크함.
- 키워드:
- continue: 현재 단계를 건너뛰고 다음 반복으로 넘어감.
- break: 반복문을 즉시 완전히 종료함.
- 선형 탐색(Linear Search)과 문자열 비교:
- 배열을 하나씩 돌며 특정 값을 찾는 방식을 선형 탐색이라 함.
- 중요: 솔리디티에서 string끼리는 직접 비교(==)가 불가능하므로, keccak256 함수를 이용해 해시(Hash) 값으로 변환한 뒤 비교해야 함.
contract A{
event CountryIndexName(uint indexed _index, string _name);
string[] countries = ["South Korea", "Japen", "USA", "China"];
function linearSearch(string memory name) public view returns(uint256, string memory){
for(uint256 i = 0; i<countries.length; i++){
if(keccak256(bytes(countries[i])) == keccak256(bytes(name))){
return (i, countries[i]);
}
}
return(0, "Nothing");
}
}
댓글
Comments