~/blockchain-dev.md
BLOCKCHAIN_DEV

노드가 과거를 지우면 dApp은 무엇을 따로 보관해야 할까: EIP-4444와 eth/69로 보는 history expiry

10분 읽기·2026.07.23·출처 5·00
오늘의 질문

Ethereum full node가 오래된 블록·영수증을 더 이상 기본으로 보관하지 않는다면, 내 dApp의 과거 조회 기능은 무엇을 믿어야 할까?

blockchain-dev.md
EIP-4444 History

노드가 과거를 지우면 dApp은 무엇을 따로 보관해야 할까: EIP-4444와 eth/69로 보는 history expiry

1. 왜 지금 봐야 하나

블록체인 개발자는 “체인은 모든 기록을 영원히 가진다”는 말을 자주 듣는다. 그런데 Ethereum의 실제 클라이언트 운영 방향은 조금 다르다. 2025년 7월 Ethereum Foundation은 모든 주요 execution client가 EIP-4444에 맞춘 partial history expiry를 지원한다고 공지했다. 핵심 효과는 pre-Merge 블록 데이터를 제거해 노드 디스크 요구량을 대략 300~500GB 줄이고, 일반 노드를 2TB 디스크 안에 더 여유 있게 운영하도록 만드는 것이다.

Fusaka 문서와 EIP-7607도 이 흐름을 network upgrade 맥락에 넣는다. Fusaka 자체가 EIP-4444 전체를 한 번에 강제하는 것은 아니지만, EIP-7642 eth/69 지원을 client team의 Fusaka 준비 항목으로 명시한다. 즉 “모든 full node가 모든 과거 데이터를 항상 serving한다”는 가정은 점점 더 약해지고, node는 현재 상태 검증과거 데이터 제공을 분리하는 방향으로 간다.

이 변화는 가격 예측이나 체인 선택 문제가 아니다. dApp, explorer, bridge, rollup indexer, accounting tool을 만드는 개발자에게는 API 계약 문제다. eth_getLogs, transaction receipt 조회, 과거 balance 재구성, L2 calldata 기반 indexing 같은 기능이 “내 RPC가 알아서 줄 것”이라는 가정 위에 있으면, history expiry 이후에는 조용히 빈 결과·부분 결과·느린 fallback으로 바뀔 수 있다.

2. 핵심 개념

먼저 용어를 나누자.

**상태(state)**는 지금 체인이 인정하는 계정 balance, nonce, contract storage, code다. 사용자가 오늘 송금하고, swap하고, lending position을 보는 데 필요한 데이터는 대체로 현재 상태에 있다. EF 공지는 dormant account도 영향을 받지 않는다고 설명한다. 계정이 오래 비활성 상태였더라도 현재 state는 유지되기 때문이다.

**히스토리(history)**는 과거 block body, transaction, receipt처럼 “그때 무슨 일이 있었는가”를 재구성하는 자료다. explorer가 3년 전 트랜잭션 목록을 보여주거나, indexer가 과거 이벤트를 처음부터 다시 훑거나, L2가 과거 calldata 게시물을 기반으로 자체 chain을 검증할 때 필요하다.

**아카이브 데이터(archive/state history)**는 특정 과거 block number에서 storage slot이나 balance가 얼마였는지 직접 대답하기 위한 특수한 인덱스·상태 보관이다. history만 있다고 과거 balance query가 자동으로 쉬워지는 것은 아니다. EF 공지도 과거 특정 시점의 balance는 specialized archive node/index가 필요하다고 구분한다.

EIP-4444의 핵심 문장은 간단하다. execution client는 일정 기간보다 오래된 headers, block bodies, receipts를 p2p layer에서 제공하지 않아야 하고, 로컬에서도 prune할 수 있다. 제안서의 상수 HISTORY_PRUNE_EPOCHS는 82125 epoch, 대략 1년이다. 또한 EIP-4444는 오래된 devp2p history만으로 genesis부터 full sync하는 모델이 깨질 수 있음을 인정하고, PoS의 weak subjectivity checkpoint를 기반으로 checkpoint sync를 사용해야 한다고 설명한다.

여기서 중요한 감각은 이것이다.

Ethereum full node의 기본 책임은 “현재 head 이후의 합의와 상태 전이 검증”이지, 모든 앱의 과거 조회 백엔드가 되는 것이 아니다.

3. 최신 이슈와 연결

EIP-7642는 이 변화를 p2p 프로토콜 수준에서 다룬다. eth/69는 peer handshake에 earliestBlock, latestBlock, latestBlockHash를 넣어 그 node가 어떤 block range를 serving하는지 알려준다. 그리고 BlockRangeUpdate 메시지로 serving range 변화를 알릴 수 있게 한다.

이건 작은 필드 추가처럼 보이지만, 개발자 관점에서는 중요한 전환이다. 예전에는 “peer에게 요청해보고 실패하면 다른 peer를 찾는” 방식이었다면, 이제는 peer가 자신이 제공 가능한 history 범위를 먼저 드러낸다. history availability가 암묵적 친절이 아니라 네트워크 계약의 일부가 되는 것이다.

EIP-7642에는 receipt bloom 제거도 포함된다. 기존 receipt encoding은 bloom filter를 네트워크로 보내고 받는 쪽이 검증 후 버리는 낭비가 있었다. EIP는 bloom을 전송하지 않고 receiving node가 필요하면 재계산하도록 바꾼다. 문서가 제시하는 낭비 규모는 수백 GiB 단위다. 이 부분은 consensus rule 변경이 아니라 네트워크 encoding 최적화지만, “과거 데이터를 serving하는 비용”이 실제로 얼마나 큰지를 잘 보여준다.

Fusaka 페이지는 history expiry를 PeerDAS, blob fee, gas cap 같은 확장 항목과 함께 소개한다. Ethereum이 확장하려면 L2 데이터만 늘리는 것이 아니라, L1 node가 감당하는 저장·대역폭·CPU 책임도 계속 재조정해야 한다. history expiry는 사용자 UX를 바꾸는 기능이 아니라, node operator 집합을 유지하기 위한 운영 비용 조정이다.

4. 개발자 관점 해석

history expiry가 개발자에게 던지는 질문은 “내 앱이 과거를 얼마나 필요로 하는가?”다. 세 가지 유형으로 나눠보면 좋다.

첫째, 현재 상태 중심 dApp이다. 지갑 balance, 현재 position, allowlist 여부, 최신 order status만 필요하다면 큰 영향은 작다. 그래도 transaction history 화면이나 activity feed를 붙이는 순간부터는 RPC node의 history 보존 정책을 확인해야 한다.

둘째, 이벤트 재생형 indexer다. fromBlock=0부터 eth_getLogs를 돌려 DB를 다시 만드는 구조라면 history expiry가 직접 영향을 준다. 새 노드를 붙였는데 1년보다 오래된 receipt/log를 serving하지 않는다면, indexer bootstrap이 실패하거나 빈 구간을 만든다. 이 경우 raw RPC 대신 자체 snapshot, subgraph, BigQuery류 public dataset, archive provider, checkpointed event snapshot을 설계해야 한다.

셋째, 브리지·롤업·감사 도구다. EF 공지는 L2가 calldata로 posting했던 과거 트랜잭션을 검증·indexing하는 경우 history가 중요하다고 언급한다. bridge나 rollup tooling은 “최근 head만 보면 된다”가 아니라 dispute window, withdrawal proof, fraud investigation, incident replay에 필요한 보존 기간을 따로 정의해야 한다.

여기서 자주 생기는 오해가 있다. “블록체인은 immutable하니까 언제든 RPC로 다시 가져오면 된다”는 생각이다. 더 정확히는 “체인의 consensus history는 검증 가능한 형태로 존재해야 하지만, 아무 full node나 그것을 API로 제공할 의무는 줄어든다”에 가깝다. 데이터의 진실성과 데이터의 접근성은 다르다.

5. 내 프로젝트에 적용할 체크포인트

  • RPC 의존성 문서화: 사용하는 provider가 eth_getLogs, eth_getBlockByNumber, eth_getTransactionReceipt에 대해 어느 block range까지 보장하는지 확인한다.
  • bootstrap 경로 분리: indexer 초기 구축은 archive/snapshot 경로로, 실시간 추적은 일반 full node/RPC 경로로 나눈다.
  • 빈 결과 처리: 오래된 block range에서 빈 logs가 왔을 때 “이벤트가 없음”인지 “provider가 history를 제공하지 않음”인지 구분하는 에러·메타데이터를 둔다.
  • 체크포인트 저장: 이벤트를 재처리할 수 있도록 contract별 last indexed block, schema version, source RPC, reorg handling policy를 함께 저장한다.
  • 감사·사고 대응 보존 기간: bridge, vault, oracle, governance 앱은 incident replay에 필요한 원본 transaction/receipt/log 보존 정책을 정한다.
  • archive 비용을 제품 비용으로 보기: “RPC 무료 플랜에서 되겠지”가 아니라, 과거 조회 기능을 제공한다면 archive provider·자체 인덱스·cold storage 비용을 제품 요구사항에 넣는다.
  • checkpoint sync 신뢰 경계 이해: node 운영자는 weak subjectivity checkpoint를 어디서 받는지, 오래된 checkpoint로 bootstrap하지 않는지 운영 절차에 넣는다.

6. 오늘 10분 액션

  1. 내 서비스 코드에서 eth_getLogs, getTransactionReceipt, getBlock, explorer API 호출을 검색한다.
  2. 각 호출 옆에 “최근 1일/최근 1년/전체 역사/특정 과거 상태” 중 어떤 범위가 필요한지 적는다.
  3. 가장 오래된 범위를 요청하는 기능 하나를 골라, provider 문서에서 archive/history 보장 조건을 확인한다.
  4. indexer가 있다면 “새 DB에서 genesis부터 재생” 대신 “스냅샷 + 특정 block 이후 replay”가 가능한지 적어본다.

7. 더 볼 자료

중복 회피 메모

로컬 content/generated와 Supabase의 최근 blockchain_dev 글을 확인했다. 최근 글은 EIP-7917 proposer lookahead, EIP-7939 CLZ opcode, EIP-7907 contract code size gas, Solana Alpenglow finality, Taiko bridge origin verification, OP Stack fault proof soundness, ERC-4626 NAV manipulation, P-256/passkey precompile, MODEXP repricing, PeerDAS/blob fee reserve, ePBS를 다뤘다. 이번 글은 gas·DA·finality·precompile·bridge 사고가 아니라 EIP-4444/EIP-7642를 통해 full node가 과거 데이터를 serving하는 책임을 줄일 때 dApp과 indexer가 어떤 history availability 계약을 따로 설계해야 하는지에 집중한다.

오늘 10분 액션+5%

댓글 0

최신순 ▾
한 줄 남기기

혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)