티스토리 뷰

반응형

솔리디티에서는 변수가 저장되는 데이터 영역으로 크게 3가지가 있다. 각각의 데이터 영역은 사용되는 목적과 특성에 따라 다르게 사용되는데, 이 데이터 영역들의 특징을 알아보자.

설명 내용이 길면 맨 아래 요약된 글을 참고하자.


Storage

솔리디티에서 storage는 블록체인에 영구적인 상태를 저장하는 영역이다. 모든 스마트 컨트랙트의 상태는 블록체인의 상태로 저장되며, 상태 변경 함수가 호출될 때마다 해당 상태의 변경 사항이 블록체인의 storage에 저장된다. 

 

contract Example {
    uint256 public myNumber;
}

위 코드에서 myNumber는 uint256 타입의 상태 변수이다. 이 변수는 스마트 컨트랙트의 storage 영역에 저장된다. myNumber 변수는 Example 컨트랙트가 수행하는 모든 트랜잭션 작업에서 사용될 수 있다.

 

storage 영역에 저장된 데이터는 블록체인 상에 저장되기 때문에, 컨트랙트를 다시 배포하면 storage도 새로운 주소에 저장된다. (다시말해, 컨트랙트가 배포될 때 생성된 storage는 블록체인 상에 영구적으로 저장되긴 하지만, 다시 배포할 경우엔 저장된 상태 변수들은 블록체인 상에서 사라지고 새로운 storage에 저장된다.) 상태를 영구 저장하는 기능에는 storage가 적합하지만, storage에 접근하는 것은 가스 비용이 많이 들기 때문에, 상태를 저장할 때는 storage 변수를 최소한으로 사용해야 한다.

contract Example {
    uint256 storedValue; // 상태 변수
    mapping(address => uint256) balances; // 매핑

    function setValue(uint256 newValue) public {
        storedValue = newValue; // storage에 저장
    }

    function getValue() public view returns (uint256) {
        return storedValue; // storage에서 값 반환
    }

    function increment(address account) public {
        balances[account]++; // storage에 매핑된 값 변경
    }

    function getBalance(address account) public view returns (uint256) {
        return balances[account]; // storage에서 매핑된 값 반환
    }
}

위 코드에서 storedValue는 storage 영역에 저장되며, balances라는 mapping은 각 계정의 잔액을 저장한다. setValue 함수는 storedValue의 값을 변경하고, getValue 함수는 storedValue의 값을 반환한다. increment 함수는 balances 매핑에서 지정된 주소의 잔액을 증가시키고, getBalance 함수는 해당 계정의 잔액을 반환한다. 이 함수 4개는 모두 storage 영역을 사용하여 구현된 기능이다.


 

Memory

솔리디티에서 memory는 storage 영역과 다르게 호출 된 함수가 실행되는 동안에만 사용할 수 있는 가상 메모리 영역이다.

 

이 영역은 함수가 실행되는 동안에만 동적으로 할당되며, 함수 내에서 임시로 사용되는 데이터를 저장하기 위한 임시 저장 공간이다. 임시 공간이라서 함수 실행이 완료되면 자동으로 이 공간은 삭제된다. 

 

memory는 함수 호출시 일시적인 계산을 수행하거나 값을 복사하여 반환하고, 중간 계산 결과를 저장하거나 배열을 정렬하고, 문자열을 처리하고, 외부 컨트랙트에 데이터를 전달하고, 빠른 계산을 수행하는데 유용하다.

 

memory는 가변 길이 바이트 배열(bytes), 정적인 배열(uint[], address[] 등등), 문자열(string) 및 구조체(struct)와 같은 변수를 선언할 때 사용할 수 있다. 함수가 외부에서 호출되어 인자를 받는 경우, 그 인자는 memory에 저장되어야 하며, 함수 내부에서 다른 함수를 호출하여 반환값을 전달받는 경우에도 반환값은 memory에 저장될 수 있다.

pragma solidity ^0.8.0;

contract StringExample {
    function sum(uint256[] memory numbers) public pure returns (uint256) {
        uint256 total = 0;
        for (uint256 i = 0; i < numbers.length; i++) {
            total += numbers[i];
        }
        return total;
    }
}

위 코드의 함수는 uint256 타입의 배열을 인자로 받아 모든 요소의 합을 반환한다. 이 함수에서 memory를 사용하여 배열을 임시 저장하고 계산 후 반환한다. 그리고 이 memory는 함수 실행이 끝나면 삭제된다.

 

또 memory에서는 view 또는 pure 함수 접근자와 함께 사용할 수 있다. 이 접근자들은 함수에서 상태 변수를 수정하지 않는다는 것을 나타내는데, 따라서 읽기 전용 함수가 되므로 memory에 저장된 변수들을 읽을 수 있다.


 

Calldata

솔리디티에서 calldata는 함수 호출 시 전달되는 인자(argument)들이 저장되는 영역이다. 이 영역은 함수의 인자들을 변경할 수 없는 오직 읽기 전용 영역이며, 함수 외부에서 함수 내부로 전달되는 인자들을 저장하는 용도로 사용된다. 읽기 전용이므로 가스 비용이 적게 든다.

 

calldata 영역에 저장되는 데이터는 변하지 않으며, 일반적으로 함수 내부에서 읽기만 할 때 사용된다. 그래서 불록체인 상에서도 저장되지 않는다. calldata 영역의 크기는 컴파일할 때 결정되며, 함수 호출이 끝날 때 자동으로 삭제된다(memory는 함수 실행 종료 시 삭제됨). 따라서 함수 실행이 시작된 이후에는 해당 인자들에 접근할 수 없다.

pragma solidity ^0.8.0;

contract MyContract {
    function myFunction(uint256 a, uint256 b, uint256[] calldata c) external view returns (uint256) {
        // calldata를 사용하여 인자를 전달받음
        // c는 동적인 길이를 가지는 배열이므로 calldata를 사용하여 전달받음
        // 함수는 상태 변경이 없으므로 view를 사용
        // a와 b를 더하고 c 배열의 모든 요소를 더한 값을 반환
        
        uint256 sum = a + b;
        for (uint256 i = 0; i < c.length; i++) {
            sum += c[i];
        }
        return sum;
    }
}

위 코드에서 myFunction 함수는 calldata를 사용하여 함수 인자를 전달받고, 상태 변경이 없으므로 view 키워드를 사용한다. 함수는 a와 b를 더하고, c 배열의 모든 요소를 더한 값을 반환한다.


 

Memory와 Calldata의 차이점

솔리디티에서 memory와 calldata는 둘 다 데이터를 저장하거나 전달하는데 사용되는 데이터 영역을 나타내는 키워드이다. 하지만 둘은 서로 다른 특징을 가지고 있다.

 

● memory

① 함수의 실행 중에 생성되고 함수의 실행이 종료되면 사라짐

② 함수 실행시 일시적으로 저장되는 변수와 배열, 구조체 등의 데이터를 저장하는 임시공간

③ 함수의 인자 중 함수 내부에서 수정 가능

 

● calldata

① 함수를 호출할 때 생성되고 함수의 호출이 끝나면 사라짐

 읽기 전용

 외부에서 함수로 전달되는 인자들의 값 저장

④ 함수의 인자 중 함수 내부에서 수정 불가능

 

솔리디티에서 함수가 호출될 때, 함수의 인자들은 calldata 영역에 저장되어 전달된다. 그 후, 인자들은 스택(stack)* 영역에 복사된다. 이때, 스택에 저장된 인자들은 memory 영역에 저장된 변수들과 달리 계속해서 새로운 메모리 공간을 할당하지 않아도 된다. (memory 영역은 변수들을 저장할 때마다 저장 공간을 생성해줘야함) 스택에서 변수에 접근할 때는 이미 변수가 저장되어 있는 calldata 영역에서 값을 가져오기 때문에 메모리를 절약할 수 있다. 이 말은, 스택에 저장된 값들은 실행 중에만 유지되며, 함수가 실행을 마치면 스택에서 제거되어 사라진다. 따라서, 함수 내에서 계산에 상요하는 값들이 일시적이고 메모리에 영구적으로 저장할 필요가 없는 경우에는 memory보다 calldata를 사용하여 메모리를 절약할 수 있다.

더보기

* 스택(stack) : 함수가 실행되는 동안 임시적으로 사용되는 데이터 영역으로, 함수 호출 시 생성되며 함수 호출이 종료되면 삭제된다. 함수의 지역 변수나 함수 호출에 필요한 데이터를 저장하는데 사용된다.


 

솔리디티 데이터 영역 요약

 storage

- 변수가 영구적으로 저장되는 공간

- 컨트랙트 내에서 상태 변수로 사용됨

- 컨트랙트가 배포되면서 초기화되며, 블록체인 상에 영구적으로 저장됨

- 함수 외부에서도 값을 유지해야 하는 변수 사용시 적합

- 값 변경 가능

 

 memory

- 함수가 실행될 때 임시로 할당되는 공간

- 컨트랙트 내에서 지역 변수로 사용됨

- 함수 실행이 끝나면 저장된 데이터는 사라짐

- 값이 저장되어 사용되지만, 블록체인 상에 저장되지 않음

- 함수 내부에서 임시로 사용해야 하는 변수 사용시 적합

- 값 변경 가능

 

 calldata

- 함수가 호출될 때 함수의 인자(argument) 등이 저장되는 공간

- 함수 호출이 끝나면 저장된 데이터는 사라짐

- 값 변경 불가능(읽기 전용)

 

 

 

 

반응형
댓글
공지사항