궁금한 주제를 찾아보세요

비트코인, 스테이블코인, 온체인 데이터처럼 주제로 검색하세요.

다시 읽을 이야기

저장한 글은 이 브라우저에만 보관됩니다.

해설기술·생태계

EVM storage·memory·calldata는 비용과 수명이 어떻게 다를까

영구 storage, 호출 frame의 memory, 읽기 전용 calldata가 복사·수정·가스와 수명에서 어떻게 다른지 배열 처리 사례로 설명합니다.

난이도 중급주제 카테고리와 개념 밀도 기준검토 정보AI 보조 초안 · 출처 목록 제공 · 주장별 대조 진행 중
영구 보관 금고·작업 책상·입력 봉투로 구분한 EVM storage·memory·calldata 저장 영역 그림
주제의 이해를 돕기 위해 imagegen으로 제작한 AI 생성 개념 일러스트
먼저 읽는 핵심

Storage는 영구 상태, memory는 호출별 작업 공간, calldata는 읽기 전용 입력입니다.
참조형 대입은 위치 조합에 따라 alias 또는 전체 복사가 됩니다.
가스 비교는 bytes 길이, memory expansion과 storage의 warm·cold 상태를 함께 봐야 합니다.

storage는 계약 계정에 영구 보존되는 256비트 key-value 영역이고 상태 변경 transaction 사이에도 남습니다. memory는 message call마다 새로 생기는 byte-addressed 작업 공간으로 호출이 끝나면 사라지며 커질수록 expansion gas가 듭니다. calldata는 외부 호출에 들어온 ABI bytes를 담는 읽기 전용 영역이라 원본을 수정할 수 없고 필요한 부분만 읽을 수 있습니다. Solidity의 참조형에서 data location은 단순 성능 표시가 아니라 alias와 copy 여부를 바꾸므로 storage 포인터를 수정하면 영구 상태가 바뀌고 storage에서 memory로 대입하면 별도 복사본이 만들어집니다.

세 영역은 수명부터 다릅니다

각 contract account의 storage는 256비트 slot에서 256비트 값으로 이어지는 영구 key-value 공간입니다. 함수가 끝나도 남고 다음 transaction이 같은 상태를 읽습니다. Solidity state variable은 packing, mapping hash와 dynamic array 규칙에 따라 slot에 배치됩니다. 다른 계약 storage를 SLOAD로 직접 읽을 수 없으며 CALL과 DELEGATECALL의 문맥에 따라 어느 계정 storage인지 정해집니다.

Memory는 각 message call에 freshly cleared 상태로 주어지고 byte 단위 주소를 갖지만 일반 word 연산은 32바이트를 사용합니다. 높은 offset을 처음 건드리면 확장 비용이 붙고 크기에 따라 선형과 제곱 항이 함께 증가합니다. Calldata는 transaction 또는 external call이 전달한 bytes이며 read-only라 CALLDATALOAD·CALLDATACOPY로 읽습니다.

데이터 위치는 같은 배열을 어디에 놓을지 정하는 표식이 아니라, 언제 사라지고 누가 수정 결과를 보게 되는지 정하는 실행 규칙입니다.

JOBCOIN 해설

VISUAL GUIDE프로토콜 실행의 기본 구조
입력이 실행 규칙을 통과할 때 상태가 바뀌는 프로토콜 개념도
그림과 함께 짚어볼 본문 내용

그림은 이 주제의 공통 개념을 단순화한 설명입니다. 아래 항목에서 이 글의 구체적인 조건과 예외를 함께 읽어보세요.

  1. 세 영역은 수명부터 다릅니다

    각 contract account의 storage는 256비트 slot에서 256비트 값으로 이어지는 영구 key-value 공간입니다.

  2. 가변성과 복사 규칙을 표로 확인합니다

    External 함수의 dynamic parameter를 calldata로 선언하면 compiler가 처음부터 memory로 전체 decode하지 않고 접근 시 필요한 부분을 읽을 수 있습니다.

  3. 배열 하나가 위치에 따라 다른 작업을 만듭니다

    외부 함수가 1,000개 uint 배열을 받아 합계만 계산한다고 가정하면 calldata 순회는 원본을 그대로 읽습니다.

AI로 제작한 개념도 · 실제 가격·거래 내역·통계가 아닙니다.

가변성과 복사 규칙을 표로 확인합니다

External 함수의 dynamic parameter를 calldata로 선언하면 compiler가 처음부터 memory로 전체 decode하지 않고 접근 시 필요한 부분을 읽을 수 있습니다. 함수 안에서 값을 바꿔야 한다면 memory 복사가 필요합니다. Public 함수와 internal 호출에서는 compiler version과 ABI 경로가 다를 수 있으므로 생성된 IR·gas report로 확인합니다.

storage 참조끼리의 대입은 같은 상태 객체를 가리킬 수 있지만 memory로 옮기면 copy가 생깁니다. Memory 참조 대입은 같은 memory 객체를 공유할 수 있어 한 변수를 통해 원소를 바꾸면 다른 참조에서도 보입니다. 값형은 stack 값 복사가 기본이므로 배열·struct와 같은 참조형 규칙을 그대로 적용하지 않습니다.

EVM 데이터 영역 비교
영역 수명 쓰기 대표 비용 요인
storage transaction 사이 영구 가능 SLOAD·SSTORE, warm·cold, 값 전이
memory 현재 call frame 가능 최대 사용 offset과 expansion
calldata 현재 call 입력 불가 입력 byte 비용과 copy·decode
returndata 직전 외부 호출 결과 직접 수정 불가 RETURNDATACOPY 크기
stack 현재 실행 frame 상단 word 연산 opcode와 1024 깊이 제한

배열 하나가 위치에 따라 다른 작업을 만듭니다

외부 함수가 1,000개 uint 배열을 받아 합계만 계산한다고 가정하면 calldata 순회는 원본을 그대로 읽습니다. 매 원소를 변경해야 해서 uint[] memory로 받으면 ABI decoder가 배열 길이와 내용을 memory에 놓고 확장 비용을 냅니다. 배열을 state variable에 저장하면 각 slot에 SSTORE가 생겨 가장 큰 지속 비용이 발생합니다.

Storage 배열을 memory 지역 변수로 복사한 뒤 수정하면 영구 배열은 바뀌지 않습니다. 반대로 storage 포인터를 잡아 원소를 수정하면 transaction 성공 시 상태 root에 반영됩니다. 리뷰에서는 함수 signature만 보지 말고 대입 양쪽 data location과 compiler가 만든 copy loop를 확인해야 합니다.

  • 데이터가 다음 transaction에도 필요한지 먼저 묻습니다.
  • 입력을 수정하지 않으면 calldata 사용 가능성을 확인합니다.
  • Memory 최대 offset과 예상 배열 길이에 상한을 둡니다.
  • Storage pointer와 memory copy를 코드 리뷰에서 표시합니다.
  • Compiler 버전을 고정하고 gas snapshot을 비교합니다.

비용은 같은 transaction에서 처음 접근한 cold slot

SSTORE 비용은 slot의 original, current, new value와 refund counter에 따라 달라집니다. 같은 transaction에서 처음 접근한 cold slot과 이미 접근한 warm slot도 비용이 다릅니다. ‘storage write는 항상 20,000 gas’라는 규칙은 0에서 nonzero로 처음 설정하는 일부 경우만 설명합니다.

Memory는 한번 확장한 범위 안의 추가 접근과 새 high-water mark를 늘리는 접근이 다릅니다. Calldata도 chain의 transaction data pricing과 실행 중 copy 비용을 구분해야 합니다. Optimizer가 loop·bounds check를 바꿀 수 있으므로 추정 수치 대신 목표 compiler·EVM version의 bytecode를 측정합니다.

안전성은 길이와 alias를 검증하는 데서 시작합니다

공격자가 매우 긴 calldata 배열을 보내면 전체 복사·loop가 block gas 안에서 실패하거나 서비스 estimate를 소진시킬 수 있습니다. 길이 상한, pagination 또는 Merkle proof처럼 bounded input 설계를 사용합니다. Assembly에서 calldata offset과 length를 직접 다룰 때 overflow와 ABI 범위 검사를 생략하지 않습니다.

Upgrade 계약은 storage layout을 바꾸면 기존 slot 의미를 잃습니다. Memory 최적화가 맞더라도 영구 변수 순서와 mapping base slot은 별도 호환성 검사 대상입니다. 성능 검토와 upgrade 안전 검토를 한 숫자의 gas 절감으로 합치지 않습니다.

자주 묻는 질문

Calldata 배열은 함수 안에서 원소를 바꿀 수 있나요?

읽기 전용이므로 직접 바꿀 수 없습니다. 수정이 필요하면 memory로 복사해 별도 배열을 사용합니다.

Memory는 transaction 동안 계속 공유되나요?

아닙니다. 각 message call frame은 새 memory를 가지며 외부 호출 사이에는 calldata와 returndata로 값을 전달합니다.

Storage 변수를 읽기만 해도 gas가 드나요?

On-chain 실행에서는 SLOAD 비용이 들며 cold·warm 접근에 따라 달라집니다. Off-chain eth_call은 사용자가 fee를 내지 않아도 실행 규칙은 적용됩니다.

더 깊이 읽기

본문에서 다룬 개념과 확인 절차를 다음 글에서 이어서 살펴보세요.

참고한 원문 자료

자료 확인 기준일 2026.09.27
  1. Solidity Documentation — Storage, Memory and Stackdocs.soliditylang.org
  2. Solidity Documentation — Data locationdocs.soliditylang.org
자료 대조 기록과 확인 범위
출처 수집
원문 링크 2개 제공
핵심 주장 대조
완료 근거가 아직 기록되지 않았습니다.
분야 전문가 검수
별도 완료 기록이 없습니다.

원문 링크와 자료 확인일은 글 전체의 주장 대조나 전문가 검수 완료를 뜻하지 않습니다. 별도 확인이 필요한 절차는 원문의 적용 대상과 최신 안내를 함께 확인해 주세요.

AI 활용 안내

이 글은 초안 구성과 자료 정리에 AI를 활용했습니다. 글에 표시된 출처와 기준일을 함께 확인해 주세요. 별도 검토 정보가 없다면 전문가 검수를 뜻하지 않습니다.

이해를 위한 정보 콘텐츠

이 글은 특정 자산의 매수·매도 또는 수익을 권유하지 않습니다. 자료의 발표 시점과 이후 변경 사항을 함께 확인해 주세요.

편집 원칙 보기 →이 기사 정정 제보 →