Bitroot블로그
공식 사이트로 돌아가기 ↗
© 2026 Bitroot · 본 사이트의 내용은 일반 정보이며 금융, 투자, 법률 또는 세무 조언이 아닙니다.
편집 기준공식 사이트로 돌아가기
← 전체 글
EVM 기초·2026/09/22·약 3분

헷갈리기 쉬운 세 가지 스토리지: Memory, Storage, Transient Storage

같은 32바이트 데이터를 memory, storage, transient storage 중 어디에 두느냐에 따라 가스 비용이 두 자릿수 배 차이 나고 수명도 완전히 달라집니다. 아래에서는 소유, 수명, 과금이라는 세 축으로 세 가지 스토리지를 나누어 설명하고, 재진입 잠금과 임시 배열, 상태 변수를 각각 어디에 두어야 하는지 정리합니다.

재진입 잠금을 스토리지에 두면, 콜드 슬롯에 처음으로 0을 1로 쓸 때 콜드 접근 추가 요금 2100에 Gsset 20000을 더해 지불하고, 같은 트랜잭션 안에서 다시 0으로 되돌려 쓰는 데 100을 더 지불하며, 총소비는 약 22200 gas입니다. 이 쓰기는 동시에 19900 gas 환불을 만들어내지만, 환불 상한은 트랜잭션 전체 소비의 1/5입니다. 대신 임시 스토리지 변수 하나로 바꾸면 두 번의 쓰기가 각각 100 gas이고 환불 카운터에 의존하지 않습니다. 기능은 완전히 같지만 총비용은 두 자릿수 배 차이가 납니다.

EVM에는 쓰기 가능한 데이터 영역이 세 가지 있습니다. 메모리(memory), 스토리지(storage, 영구 저장), 임시 스토리지(transient storage, EIP-1153)입니다. 셋 모두 32바이트 워드 단위로 읽고 쓰며 컨트랙트 안에서 값을 대입할 수 있지만, 소유 단위와 존속 시간, 과금 규칙은 서로 다릅니다. 잘못된 위치에 두어도 대개 오류가 나지 않고, 상태가 예상 밖에서 사라지거나 가스 청구서가 열 배로 불어날 뿐입니다. 답해야 할 질문은 이것입니다. 세 가지 스토리지를 각각 누가 소유하고, 얼마나 오래 살며, 어떤 규칙으로 과금되는가, 그리고 각각 무엇을 담기에 적합한가입니다.

수명 주기: 프레임과 계정, 트랜잭션이라는 세 척도

메모리는 실행 프레임(execution frame)이 소유합니다. CALL, DELEGATECALL, STATICCALL, CREATE로 진입한 컨텍스트 하나가 곧 하나의 프레임이고, 프레임에 들어갈 때 메모리는 0에서 시작해 프레임이 반환되거나 롤백되면 통째로 버려집니다. 부모 프레임의 메모리는 자식 프레임에 보이지 않고, 자식 프레임의 메모리도 부모 프레임으로 되돌아가지 않습니다. 두 프레임 사이에서 데이터를 주고받으려면 calldata와 returndata를 거치는 방법밖에 없습니다.

스토리지는 계정이 소유합니다. 256비트 슬롯에서 256비트 값으로 가는 매핑이 계정 이름 아래에 매달려 있고, 여기에 쓴 내용은 계정의 스토리지 트라이에 들어가 월드 스테이트의 일부가 되며 트랜잭션과 블록을 넘어 오래 남습니다. 0 값은 트라이에 기록되지 않으므로 슬롯을 0으로 지우면 해당 노드가 제거되며, 이 점이 환불 규칙 설계를 그대로 결정합니다.

임시 스토리지도 계정이 소유하지만, 범위는 트랜잭션 하나입니다. 같은 트랜잭션 안에서 그 계정의 모든 프레임은 같은 임시 스토리지를 공유하므로, 내부 호출이 쓴 값을 외부 호출이 읽을 수 있습니다. 프레임이 롤백되면 그 프레임 범위의 쓰기도 함께 롤백되어 동작이 스토리지와 같습니다. 반대로 프레임이 정상 반환될 때는 롤백되지 않으며, 이 점은 메모리와 반대입니다. 트랜잭션이 끝나면 모든 임시 스토리지가 무조건 0으로 지워집니다. 소유 규칙에는 예외가 하나 있습니다. DELEGATECALL과 CALLCODE의 임시 스토리지는 호출자(명령을 실행한 컨트랙트)에게, CALL과 STATICCALL은 피호출자에게 귀속됩니다.

세 척도는 이렇게 기억하면 됩니다. 메모리는 프레임 단위, 스토리지는 계정 더하기 영구 단위, 임시 스토리지는 계정 더하기 트랜잭션 단위입니다.

구분MemoryStorageTransient Storage
소유실행 프레임계정계정
존속 시간프레임 진입 시 생성, 프레임 종료 시 폐기영구, 월드 스테이트에 기록트랜잭션 종료 시 초기화
주소 지정바이트 주소, 32바이트 워드 단위로 확장256비트 슬롯256비트 슬롯
내부 호출 간공유하지 않음공유같은 계정의 모든 프레임이 공유
프레임 롤백프레임과 함께 폐기해당 프레임 쓰기 롤백해당 프레임 쓰기 롤백
1회 쓰기 비용3 gas 더하기 확장 비용100~20000 더하기 콜드 접근 비용고정 100 gas

Memory: 워드 단위로 확장되고, 확장 비용은 이차식입니다

메모리는 바이트 단위로 주소를 지정하지만, 할당은 32바이트 워드를 단위로 합니다. 아직 건드리지 않은 워드에 접근하면 확장이 일어나고, 확장 비용은 총 점유량으로 계산합니다. 공식은 C_mem(a) = 3a + ⌊a² / 512⌋이며 a는 워드 수입니다. 실제 차감액은 확장 후의 C_mem에서 확장 전의 C_mem을 뺀 값입니다. MSIZE는 늘기만 하고 줄지 않으며, 프레임 안에서 이미 할당한 메모리를 해제할 수 없습니다.

이 식의 앞부분은 선형 항이고 뒷부분은 이차 항입니다. a < 23(즉 704바이트)일 때는 이차 항이 내림 처리되어 0이 되므로 비용이 완만해 보이지만, 이 지점을 넘으면 증가가 빨라집니다. 아래 두 절대값은 모두 옐로 페이퍼 식 (328)로 계산한 것으로, 한 프레임이 빈 메모리에서 한 번에 확장하는 것을 기준으로 하며 MLOAD, MSTORE의 기본 비용은 포함하지 않습니다. 32 KB 메모리는 a = 1024에 대응하고 확장 비용은 3 × 1024 + 1024² / 512 = 5120 gas입니다. 1 MB 메모리는 a = 32768에 대응하고 확장 비용은 98304 + 2097152 ≈ 219만 gas입니다. 한 프레임이 메모리 확장만으로 수백만 gas를 소비할 수 있으며, 이는 보통 프레임 안에서 큰 버퍼를 쌓을 때의 단단한 제약이 됩니다.

MLOAD와 MSTORE의 기본 비용은 3 gas(Gverylow)이고 확장 비용은 별도로 부과됩니다. 한 번도 쓰지 않은 메모리를 읽으면 0이 반환되지만, 새로 건드린 워드에 대해서는 확장 비용을 여전히 지불해야 합니다. 할당 동작이 이미 일어났기 때문입니다. 이는 높은 주소에 드문드문 쓰는 경우가 특히 비싼 이유이기도 합니다. 아주 먼 오프셋에 값을 하나만 써도 그 사이의 모든 워드가 할당된 것으로 과금됩니다.

임시 배열, ABI 인코딩·디코딩 버퍼, 해시 함수의 입력은 모두 메모리에 둡니다. Solidity의 memory 변수와 memory 배열, memory 구조체가 모두 이 영역에 자리합니다. 다음 코드는 길이를 한 번에 할당해 루프 안에서 새 워드를 반복해서 건드리는 일을 피합니다.

// n개 워드로 한 번에 확장, 이후 쓰기에는 확장 비용이 발생하지 않음
uint256[] memory buf = new uint256[](n);
for (uint256 i = 0; i < n; ++i) {
    buf[i] = i;
}

경계는 분명합니다. CALL을 통해 자식 프레임에 전달되는 메모리는 내용의 복사본이고, 포인터 자체는 현재 프레임 안에서만 유효합니다. 프레임을 넘어 공유할 중간값을 메모리에 두는 것은 자식 프레임이 부모 프레임의 메모리를 볼 수 있다고 가정하는 것과 같고, 그 가정은 성립하지 않습니다.

실무에서는 메모리를 우회하는 선택이 더 흔합니다. 큰 데이터는 calldata로 들여오고 returndata로 내보내면 비용이 바이트 단위로 매겨지고 현재 프레임의 메모리를 차지하지 않습니다. 프레임 안에서 반복해서 읽고 쓰거나 해시 입력을 구성해야 할 때만 메모리로 펼칠 필요가 있습니다. 이러한 절충이 많은 컨트랙트가 계산을 외부 스크립트나 자식 호출에 맡기고 반환값으로 결과를 모아 한 프레임 안의 버퍼를 작게 유지하는 이유입니다.

Storage: 계정의 스토리지 트라이에 쓰며, 쓰기가 읽기보다 한 자릿수 비쌉니다

월드 스테이트에서 계정마다 스토리지 트라이 하나가 매달려 있고, 슬롯은 256비트 키, 값은 256비트 워드이며 0 값은 트리에 들어가지 않습니다. 읽기는 SLOAD로 처리합니다. EIP-2929(Berlin 업그레이드, 블록 12,244,000, 2021년 4월 15일)가 도입한 콜드·핫 접근 메커니즘에 따르면, 어떤 (주소, 슬롯) 쌍을 처음 접근하면 콜드 접근으로 2100 gas(Gcoldsload)를 받고, 이번 트랜잭션에서 이미 접근한 적이 있으면 핫 접근으로 100 gas(Gwarmaccess)를 받습니다. 콜드·핫 집합은 트랜잭션 범위이고, 범위가 롤백되면 집합도 롤백됩니다.

쓰기는 SSTORE로 처리하고, 비용은 EIP-2200의 순 계량 규칙이 결정하며 세 가지 값을 함께 봅니다. 트랜잭션 시작 시점의 슬롯 원본 값, 현재 값, 곧 쓸 새 값입니다. Berlin 이후 상수는 다음과 같습니다. 콜드 슬롯은 2100을 추가로 받습니다. 원본 값이 현재 값과 같을 때(이번 트랜잭션에서 아직 이 슬롯을 바꾸지 않았을 때) 0에서 0이 아닌 값으로 쓰면 Gsset = 20000을, 0이 아닌 값에서 다른 값으로 쓰거나 0으로 쓰면 Gsreset = 2900을 받습니다. 슬롯을 이번 트랜잭션에서 이미 바꿨으면(원본 값이 현재 값과 다르면) 핫 접근 100 gas만 한 번 받습니다. 새 값이 현재 값과 같은 빈 쓰기도 100을 받습니다.

환불 규칙은 EIP-3529(London 업그레이드, 블록 12,965,000, 2021년 8월 5일)로 더 좁아졌습니다. 0이 아닌 값을 0으로 바꿀 때의 환불이 15000에서 4800(SSTORE_RESET_GAS 더하기 ACCESS_LIST_STORAGE_KEY_COST)으로 내려갔고, SELFDESTRUCT 환불은 폐지되었으며, 트랜잭션 한 건의 총 환불 상한이 gas_used // 5로 눌렸습니다. 원본 값이 0이고 이번 트랜잭션에서 먼저 0이 아닌 값으로 썼다가 다시 0으로 되돌리는 패턴은 여전히 19900 gas 환불(20000 빼기 100)을 만들어내지만, 이 역시 1/5 상한의 적용을 받습니다.

연산(Berlin / London 기준)Gas
SLOAD, 콜드 접근 / 핫 접근2100 / 100
SSTORE, 원본 값이 현재 값과 같음, 0에서 0이 아닌 값으로 쓰기20000, 콜드 접근 2100 추가
SSTORE, 원본 값이 현재 값과 같음, 0이 아닌 값에서 다른 값 또는 0으로 바꾸기2900, 콜드 접근 2100 추가, 0으로 바꿀 때 환불 4800
SSTORE, 슬롯을 이번 트랜잭션에서 이미 바꿈100
재진입 잠금 0 → 1 → 0(같은 트랜잭션)총 22200, 환불 19900

트랜잭션을 넘어 유지해야 하는 것은 스토리지에 둡니다. 잔액, 소유권, 설정, 누적 카운터입니다. 대가는 두 층입니다. 하나는 gas이고, 다른 하나는 상태 팽창입니다. 모든 풀 노드가 이 슬롯들을 장기간 보관해야 합니다. 환불은 "0으로 되돌려 쓰면 공짜"라는 착각을 낳기 쉽지만, 실제 기준은 이렇습니다. 환불은 트랜잭션이 끝난 뒤에만 정산되고, 총소비의 최대 20%까지만 상쇄합니다. 30000 gas만 소비한 트랜잭션은 이론상 6000 gas의 환불만 받을 수 있습니다.

Transient Storage: 내부 호출을 넘어 유효하고, 트랜잭션이 끝나면 지워집니다

EIP-1153은 Cancun 업그레이드(블록 19,426,587, 2024년 3월 13일)에서 TLOAD(0x5c)와 TSTORE(0x5d)를 도입했습니다. 주소 지정 방식은 SLOAD, SSTORE와 같습니다. 32바이트 주소가 32바이트 값을 가리킵니다. 두 명령의 작업당 고정 비용은 모두 100 gas이고, 콜드·핫 구분도 환불도 없으며, 나중의 정리를 위해 비용을 미리 떼어 둘 필요도 없습니다. 명세상 이들은 결코 디스크에 기록되지 않기 때문입니다.

동작에서 스토리지와의 차이는 세 곳에 집중됩니다. 시간 척도에서는 트랜잭션이 끝나면 0으로 지워지고, 값이 어떤 영구 구조에도 직렬화되지 않습니다. 롤백 의미론에서는 프레임 롤백이 그 프레임의 쓰기를 되돌리는데, 이는 스토리지와 같고 메모리와는 다릅니다(메모리는 프레임이 반환되거나 롤백될 때 통째로 폐기됩니다). 컨텍스트 제한에서는 TSTORE가 STATICCALL 안에서 예외를 던지고, TLOAD는 허용됩니다. 또한 EIP-1153은 EIP-2200이 SSTORE에 둔 제한을 명시적으로 면제합니다. TSTORE는 gasleft가 2300보다 큰 호출 보조금을 요구하지 않습니다.

이 설계는 "프레임 간 통신"을 정면으로 겨냥합니다. EIP-1153 이전에는 컨트랙트 사이에 임시 상태를 전달하려면 CALL의 인자와 반환값을 거치거나(중간의 신뢰할 수 없는 컨트랙트가 변조할 수 있습니다), 스토리지 쓰기를 거쳐야 했습니다(비싸고 환불에 의존합니다). EIP-3529에서 환불이 gas_used의 1/5로 눌린 뒤로는 소액 트랜잭션이 사실상 비용을 회수하지 못합니다. 0 → 1 → 0으로 이어지는 잠금 쓰기 한 번은 환불 카운터에 19900 gas를 누적하고, gas_used // 5 상한에서 역산하면 트랜잭션 전체가 약 99500 gas를 써야 환불을 전액 돌려받을 수 있습니다. 이 99500은 EIP-3529 상한 공식으로 계산한 트랜잭션 전체 gas_used이며, EIP 원문이 제시한 값이 아닙니다. EIP-1153 본문은 다른 각도에서 저자 추정을 제시하는데, 원문대로면 트랜잭션이 "다른 연산"에 약 80k gas를 써야 재진입 잠금 하나의 환불을 전액 받을 수 있습니다. 두 숫자는 기준이 다르지만 서로 맞아떨어집니다. 99500에서 이 잠금 자체의 총소비 약 22200 gas를 빼면 약 77300이고, 이는 저자가 말한 "다른 연산" 80k 추정과 같은 자릿수입니다. 임시 스토리지는 환불 카운터에 참여하지 않으므로 이 문턱을 비켜갑니다.

재진입 잠금, 단일 트랜잭션 승인, 콜백 종료 시점의 잔액 균형 검사, 프록시 컨트랙트가 하위로 메타데이터를 전달하는 용도는 모두 여기에 두기에 적합합니다. Solidity는 0.8.28부터 transient 값 타입 상태 변수를 지원하며, EVM 버전은 cancun으로 설정해야 합니다. 참조 타입(배열, 매핑, 구조체)과 지역 변수, 매개변수는 지원되지 않으며 인라인 어셈블리를 직접 작성해야 합니다. 다음은 최소 형태의 재진입 잠금입니다.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28; // EVM 버전은 cancun이어야 함

contract TransientLock {
    uint256 transient entered;

    modifier nonReentrant() {
        require(entered == 0, "reentrant");
        entered = 1; // TSTORE, 100 gas
        _;
        entered = 0; // 같은 트랜잭션 내 후속 호출은 0을 읽음
    }

    function withdraw() external nonReentrant {}
}

컴파일러 버전이나 대상 체인이 조건을 만족하지 않으면 어셈블리를 직접 쓸 수 있고, 의미는 완전히 같습니다.

assembly {
    tstore(0, 1)       // 슬롯 0에 1을 씀, 100 gas
    let v := tload(0)  // 다시 읽음, 100 gas
}

오용의 네 가지 전형적인 결과

트랜잭션을 넘어 읽어야 하는 상태를 메모리나 임시 스토리지에 두면 증상은 "상태가 사라졌다"입니다. 트랜잭션이 끝나면 값이 0으로 지워지고 다음 호출은 기본값 0을 읽는데, 코드는 오류를 내지 않지만 비즈니스 로직은 이미 틀렸습니다. 이런 버그는 로컬 테스트에서 잘 드러나지 않습니다. 테스트가 대개 같은 트랜잭션 안이나 같은 시뮬레이션 환경에서 끝나기 때문입니다.

한 트랜잭션 안에서만 유효한 중간값을 스토리지에 두면 증상은 비용 폭주이고, 그 비용은 트랜잭션 규모에 따라 달라집니다. 재진입 잠금과 임시 승인은 기능적으로는 스토리지로 구현할 수 있지만, 경제적으로는 트랜잭션이 충분히 커야 환불을 회수할 수 있습니다. 트랜잭션이 작을수록 실제 순비용은 총비용에 가까워집니다.

호출을 넘어 공유할 중간값을 메모리에 두면 증상은 자식 프레임이 읽지 못하는 것입니다. CALL은 실행 프레임을 전환하고, 자식 프레임은 독립적이고 0에서 시작하는 메모리를 받습니다. 부모 프레임이 쓴 내용은 자식 프레임에 나타나지 않습니다. 유일하게 합법적인 전달 경로는 calldata와 returndata입니다.

임시 스토리지로 메모리 안의 매핑을 대체하면 증상은 재진입 시 예상 밖의 동작입니다. EIP-1153은 보안 고려 사항에서 이 점을 따로 경고했습니다. 임시 스토리지는 호출이 반환될 때 폐기되지 않으므로, 이를 메모리 매핑처럼 쓰면 같은 트랜잭션 안의 재진입 호출이 이전 회차에서 남은 값을 보게 됩니다. 의미론 문제 외에도 1회 100 gas라는 비용이 메모리 쓰기보다 훨씬 높습니다.

간과하기 쉬운 오용이 하나 더 있습니다. 0으로 지우는 것을 잊는 경우입니다. 재진입 잠금에 1을 쓴 뒤 일부 분기에서 0으로 되돌리지 않고 일찍 반환하면, 같은 트랜잭션 안의 후속 호출이 이 잠금에 영구히 막힙니다. EIP-1153 명세는 아주 직설적입니다. 이 슬롯들이 트랜잭션 내 후속 호출에서 실제로 사용될 때만 0이 아닌 값을 남겨 두어야 합니다.

경계와 불확실한 부분

본문의 모든 gas 숫자에는 기준이 붙어 있습니다. Berlin 업그레이드(2021년 4월 15일, 블록 12,244,000) 이후의 콜드·핫 접근 가격, London 업그레이드(2021년 8월 5일, 블록 12,965,000) 이후의 환불 규칙, Cancun 업그레이드(2024년 3월 13일, 블록 19,426,587)가 도입한 임시 스토리지입니다. EVM은 고정된 명세가 아니므로, 크로스체인 배포 전에는 대상 체인이 어떤 EIP를 구현했는지 하나씩 확인해야 합니다. London 이하에서 멈춘 EVM 호환 체인에서는 TLOAD와 TSTORE가 불법 오프코드로 취급되어 실행이 중단됩니다.

Solidity의 임시 스토리지 지원에는 이미 수정된 컴파일러 결함이 하나 있습니다. 0.8.28부터 0.8.33까지 IR 파이프라인(--via-ir)을 켠 상태에서, 같은 컴파일 단위 안에 delete로 임시 변수를 정리하는 코드와 같은 값 타입의 영구 스토리지를 정리하는 코드가 함께 있으면, 생성된 Yul 정리 헬퍼 함수가 이름이 같아 재사용되면서 잘못된 오프코드를 내보냅니다(TSTORE를 써야 할 때 SSTORE를 쓰거나 그 반대). 0.8.34에서 수정되었습니다. 결함은 IR 파이프라인에만 영향을 주고 legacy 파이프라인은 영향을 받지 않습니다. 임시 변수를 쓰면서 via-ir를 사용하는 프로젝트는 컴파일 버전을 수정 구간 밖에 두어야 합니다.

공개된 일정이 없어 불확실하다고만 표시할 수 있는 사안이 두 가지 있습니다. 임시 스토리지의 100 gas는 EIP-1153이 정한 현재 값이며, 향후 하드포크에서 조정될지에 대해서는 공개된 일정도, 이미 절차에 들어간 제안도 없습니다. 상태 트리 구현(예를 들어 Verkle 트리의 진전)은 스토리지 읽기의 실제 비용 기반을 바꿀 것입니다. EIP-2929의 동기에도 데이터베이스 레이아웃을 재설계해 클라이언트가 스토리지를 직접 읽게 하면 최악 처리 시간을 더 낮출 수 있다고 적혀 있습니다. 그러나 과금 상수는 여전히 EIP-2929와 EIP-3529가 정하며, 둘 사이에는 장기간 괴리가 남을 수 있습니다. 두 가지 모두 인용할 수 있는 정량적 결론을 낼 수 없어 방향성 판단으로만 남겨 둡니다.

출처

  • EIP-1153, Transient storage opcodes: https://eips.ethereum.org/EIPS/eip-1153
  • EIP-2929, Gas cost increases for state access opcodes: https://eips.ethereum.org/EIPS/eip-2929
  • EIP-3529, Reduction in refunds: https://eips.ethereum.org/EIPS/eip-3529
  • EIP-2200, Structured Definitions for Net Gas Metering: https://eips.ethereum.org/EIPS/eip-2200
  • Ethereum Yellow Paper(부록 G 수수료표, 식 (328) 메모리 과금 함수): https://ethereum.github.io/yellowpaper/paper.pdf
  • ethereum.org 오프코드 참고(TLOAD, TSTORE 각각 100 gas): https://ethereum.org/en/developers/docs/evm/opcodes/
  • Solidity 0.8.28 릴리스 노트(transient 값 타입 상태 변수 지원): https://www.soliditylang.org/blog/2024/10/09/solidity-0.8.28-release-announcement/
  • Solidity 문서, Transient Storage(EVM 버전 cancun 요구, 참조 타입과 지역 변수 미지원): https://docs.soliditylang.org/en/latest/contracts.html#transient-storage
  • Solidity 임시 스토리지 정리 헬퍼 충돌 결함(0.8.28~0.8.33, 0.8.34에서 수정): https://www.soliditylang.org/blog/2026/02/18/transient-storage-clearing-helper-collision-bug/
  • ethereum.org 네트워크 업그레이드 역사(각 포크의 블록 높이와 날짜): https://ethereum.org/en/history/

더 읽기

낙관적 동시성 제어(OCC) 입문: 데이터베이스에서 온체인 실행까지성능 지표 용어집: TPS, BPS, 확인 지연, 최종성, 충돌률단일 스레드 EVM이 TPS를 제한하는 이유: 혼잡의 역사와 실행 모델

이 글은 EVM 기초 시리즈에 속하며, 같은 시리즈에는 호출 데이터의 인코딩과 디코딩을 다루는 0.15 "ABI 정독: selector, 정적 매개변수와 동적 타입"이 있습니다.

← 이전스택 기계 해부: 256비트 워드, 1024 스택 깊이와 실행 루프다음 →Gas 메커니즘 정독: 계량 단위, 수수료 시장과 실행 중단
목차
수명 주기: 프레임과 계정, 트랜잭션이라는 세 척도Memory: 워드 단위로 확장되고, 확장 비용은 이차식입니다Storage: 계정의 스토리지 트라이에 쓰며, 쓰기가 읽기보다 한 자릿수 비쌉니다Transient Storage: 내부 호출을 넘어 유효하고, 트랜잭션이 끝나면 지워집니다오용의 네 가지 전형적인 결과경계와 불확실한 부분출처더 읽기
읽기 설정