블록체인 세계에 막 발을 내딛은 당신에게, 이 용어들은 때로는 낯설고 복잡하게 느껴질 수 있다. 스마트컨트랙트를 작성할 때, “이 값은 어디 저장되는 걸까?”, “왜 어떤 자료형은 변경된 뒤에도 사라지지 않을까?”라는 질문을 한 번쯤 해봤을 것이다. 이 글에서는 그런 막연한 궁금증을 단순하고 명확한 개념으로 풀어준다.
먼저, 당신이 ‘왜 알아야 할까?’라는 의문을 가졌다면 답변이 있다. 왜냐하면 Ethereum Virtual Machine(EVM)의 핵심 구조를 이해하면 스마트컨트랙트의 동작 원리를 꿰뚫을 수 있고, 가스비가 왜 그렇게 나가는지, 오류가 왜 발생하는지, 저장된 값이 왜 남아 있는지 등이 훨씬 명확해진다.
예컨대, 당신이 단순히 “변수 X에 10을 저장했다”라는 말을 쓰더라도, 실제로는 어떤 영역(Stack, Memory, Storage)으로 들어갔는지, 얼마나 오래 남는지, 가스 비용은 얼마인지 등 여러 보이지 않는 기제들이 작동하고 있다. 이러한 내부 동작을 이해하면, 스마트컨트랙트를 설계하거나 디버깅할 때 막혔던 지점이 술술 풀릴 수 있다.
이제 막막했던 용어 ‘스택’, ‘메모리’, ‘스토리지’가 이 글을 통해 명확한 개념으로 전환될 것이다. 먼저 각각이 어떤 역할을 하는지 살펴본 뒤, 서로 어떻게 연관되며 왜 구분되어 있는지까지 단계별로 정리한다.
그럼 지금부터 EVM 내부의 세 가지 핵심 영역을 차근차근 탐구해보자.
Ethereum Virtual Machine(이하 EVM)은 Ethereum 블록체인에서 스마트컨트랙트를 실행하는 가상 컴퓨터 환경이다. Quicknode+2EVM.codes+2 이 환경은 스마트컨트랙트 바이트코드를 해석하고, 상태 전환을 수행하며, 전체 시스템 상태(World State)를 업데이트한다. 그 내부에는 크게 스택(Stack), 메모리(Memory), **스토리지(Storage)**라는 세 가지 저장 영역이 존재하며, 각 영역은 역할과 지속성, 비용 구조에서 명확히 구분된다. 이를 이해하면 스마트컨트랙트가 “값을 저장했다”라는 단순한 문장을 넘어 실제 어떤 영역에서 어떤 기제에 의해 처리되는지 파악할 수 있다.
스택은 ‘마지막에 들어간 것이 먼저 나오는(LIFO: Last-In First-Out)’ 구조로 운영된다. EVM 내부에서는 각 항목이 256비트(32바이트) 크기의 워드(word)이다. CoinsBench+1 스택의 최대 깊이는 1,024개 항목으로 제한되어 있으며, 이 한계를 넘으면 스택 오버플로우 오류가 발생한다. Quicknode
예컨대 PUSH1, ADD, POP, SWAP1 같은 오퍼코드는 스택에 값을 넣고 꺼내는 방식으로 실행된다. EVM.codes+1 스택은 함수 인자나 로컬 변수 저장용이라기보다는 연산 중간 값 처리 및 제어 흐름 관리에 주로 쓰인다. 따라서 개발자가 직접 스택을 지정할 일은 거의 없으며, 컴파일러가 대부분 관리한다. Ethereum Stack Exchange
메모리는 함수 호출 또는 트랜잭션 실행 중 임시적 데이터를 저장하는 선형 메모리 공간이다. 호출이 종료되면 메모리 내용은 사라진다. Medium+1 메모리는 바이트 단위로 확장 가능하며, 기본 단위는 32바이트(256비트) 워드이다. 명령어로는 MLOAD, MSTORE, MSTORE8 등이 있다. powersandwich.com.tw+1
메모리는 스택의 한계로 처리하기 힘든 배열, 문자열, 동적 데이터 등을 임시 저장할 때 쓰인다. 스택에 직접 올릴 수 없는 데이터 구조도 여기서 작업 된다. CoinsBench
메모리는 스택보다 저렴하지만, 사용 용량이 커질수록 비용도 커진다. 특히 메모리 확장은 비용이 제곱(quadratic) 형태로 증가할 수 있다. Ethereum Stack Exchange 따라서 메모리를 많이 사용하는 함수에서는 가능한 작은 데이터 구조, 적절한 초기 크기 지정, 불필요한 확장을 피하는 설계가 권장된다.
예를 들어, 임시 배열을 지나치게 크게 선언하거나, 반복문 내에서 메모리 동적 할당을 남발하면 가스 비용이 급격히 올라갈 수 있다.
스토리지는 계약 상태변수(state variables) 등 영구적 데이터 저장소로, 트랜잭션이 끝난 후에도 블록체인 상태 트리에 남는다. Medium+1 스토리지는 “키(key) → 값(value)” 형태의 256비트 워드 맵 형태로 구성되어 있고, 읽기에는 SLOAD, 쓰기에는 SSTORE 명령어가 사용된다. EVM.codes
스토리지는 가장 비용이 높다. 예컨대 “값이 0에서 다른 값으로 바뀌는 경우” 가스 비용이 크며, “값을 0으로 바꾸는 경우”에는 일부 가스 환급이 제공되기도 한다. Ethereum Stack Exchange
이들 영역이 존재하는 이유는 속도·비용·영속성의 균형을 맞추기 위함이다. 예컨대 모든 값을 스토리지에 저장한다면 비용이 급증하고 성능이 저하된다. 반대로 모든 값을 스택에 저장할 수는 없기에 메모리가 개입하며, 메모리만으로 영구 저장할 수 없기에 스토리지가 존재한다.
EVM에서 각 오퍼코드는 사전 정의된 가스 비용을 갖는다. 저장 구조 선택이 가스 비용에 직접적인 영향을 준다.
솔리디티(Solidity)로 작성된 계약 코드가 어떻게 EVM 내 저장 구조로 분산되는지를 이해하면 설계 품질이 높아진다. 예를 들어:
contract SimpleStorage {
uint256 public storedData; // → 스토리지
function set(uint256 x) public {
uint256 temp = x * 2; // → 메모리 또는 스택
storedData = temp; // → 스토리지 write
}
function get() public view returns (uint256) {
return storedData; // → 스토리지 read → 스택 return
}
}
위 코드에서 storedData는 상태변수이므로 스토리지에 저장된다. 함수 내 temp 변수는 임시 데이터이므로 메모리 또는 스택에 위치한다. 실제 EVM는 그 값을 스택에 올리고, 계산 후 다시 스토리지로 쓰는 흐름을 거친다. CoinsBench+1
이처럼 설계 시 저장 구조를 고려하면 디버깅이나 가스 최적화에서 우위를 가질 수 있다.
컨트랙트 호출 시 새로운 실행 컨텍스트가 생성된다. 이 맥락 안에서 스택·메모리 공간이 초기화되고, 호출이 끝나면 이들은 사라진다. EVM.codes 반면 스토리지는 이 컨텍스트 외부에 위치해 있으며 호출 간에도 유지된다. 이러한 구조 덕분에 EVM은 **결과의 결정성(determinism)**을 보장할 수 있다: 동일한 상태와 입력이면 동일한 출력이 나온다. Quicknode
스택(Stack), 메모리(Memory), 스토리지(Storage)는 Ethereum Virtual Machine(EVM)의 핵심 저장 구조로, 각각의 용도와 지속성이 다르다. 스택은 각 명령어가 실행되는 즉시 사용하는 임시 자료 공간으로서 연산 처리에서 가장 빠르고 비용도 낮다. 메모리는 트랜잭션 실행 중 사용할 임시 저장소로, 함수 호출이나 배열 / 문자열 등 복잡한 데이터가 처리되는 동안 생성되며, 호출이 종료되면 사라진다. 반면 스토리지는 스마트컨트랙트의 상태변수 등이 저장되는 영구적 공간으로 블록체인 상태에 반영되며, 읽기·쓰기 비용이 크게 들기 때문에 최적화가 중요하다. 이 글에서 이들 구조의 특징과 가스비와의 관계, 코드 설계 시 고려사항을 정리했다. 이를 통해 스마트컨트랙트를 설계하거나 디버깅할 때 내부 동작을 이해하고 비용을 합리적으로 제어할 수 있는 기반을 마련했다.