---
id: 16
title: "EVM 호환성의 실제 의미: 바이트코드, 프리컴파일, JSON-RPC, 툴링"
slug: evm-compatibility-explained
date: 2026/09/03
summary: "EVM 호환"은 흔히 한 줄짜리 마케팅 문구로 납작해집니다. 실제로 마이그레이션 비용을 결정하는 것은 바이트코드 의미론, 프리컴파일 집합, JSON-RPC 동작, 그리고 Foundry/Hardhat이 그대로 작동하는지 여부입니다. 이 글은 그 네 계층을 분리하고 병렬화가 호환성을 왜 더 어렵게 만드는지 설명합니다.
keywords: EVM 호환성,바이트코드,프리컴파일,JSON-RPC,Foundry,Hardhat
heroImage: /images/community-bg.png
---

거의 모든 신규 체인의 홈페이지는 "완전한 EVM 호환"을 내세웁니다. 그 문구는 기술적 보장처럼 들리지만 실제로는 후속 질문이 필요한 주장처럼 행동합니다. 컨트랙트가 바이트코드를 바꾸지 않고 배포될 수 있는가? 지갑과 익스플로러가 RPC 엔드포인트만 바꾸면 되는가? 아니면 "호환"이 그저 "Solidity를 작성할 수 있다"는 뜻인가? 이것은 마케팅이 습관적으로 한 문장으로 압축하는 세 가지 매우 다른 엔지니어링 약속입니다.

병렬성을 중심으로 실행을 재설계하면서 EVM 호환성을 유지하는 체인에게 이것은 수사가 아니라 일상적 제약입니다. 스케줄을 재배열하고 충돌 탐지와 다중 엔진 실행을 추가한 뒤에도, 컨트랙트 작성자에게 보이는 동작이 여전히 이더리움과 일치하는지가 중요합니다. [Bitroot 포지셔닝](/ko/blog/bitroot-positioning)에서 Bitroot가 완전한 EVM 호환성을 고집하는 이유를 설명했고, 이 글은 "완전"을 검증 가능한 네 계층으로 나눕니다.

## 바이트코드: 호환성의 최소 단위는 문법이 아니라 옵코드다

EVM은 Solidity 소스를 실행하지 않습니다. 컴파일된 옵코드, 즉 스택과 메모리, 스토리지를 중심으로 만들어진 명령 집합을 실행합니다. 그리고 각 주소가 nonce와 잔액, 코드 해시, 자체 스토리지 트리를 가지는 계정 모델도 포함합니다. 엄격한 호환이란 옵코드 의미론, 가스 계량, 스택 깊이 제한, 계정 읽기/쓰기 규칙이 목표 이더리움 하드포크와 일치한다는 뜻입니다. 그 기준에서 재컴파일하지 않은 바이트코드는 원칙적으로 다른 체인에서도 같은 상태 전이를 만들어야 합니다.

이 기준은 마이그레이션 비용의 하한을 정합니다. 바이트코드 수준 호환이라면 감사받은 컨트랙트가 문법 계층의 조용한 차이 때문에 전면 재감사를 받을 필요가 없고, CREATE2 주소 도출이나 재진입 방어를 특정 옵코드의 가스 비용에 의존하는 컨트랙트도 내부 가격이 바뀌어 오동작하지 않아야 합니다. 어떤 체인이 "Solidity로 컨트랙트를 작성할 수 있다"만 지원한다면 개발자는 문법의 친숙함을 얻지만, 드문 옵코드나 가스 가정을 건드리는 순간 어긋납니다.

하드포크 정합성도 확인해야 합니다. 합법적인 옵코드 집합은 Shanghai와 Cancun, 이후 업그레이드에서 동일하지 않습니다. 호환성 주장은 흐릿한 "EVM"을 흔드는 대신 추적하는 이더리움 업그레이드를 명시해야 합니다. 마이그레이션 팀은 메인넷 런타임 바이트코드를 재배포하고 코드 해시와 핵심 호출 경로의 반환 값을 비교할 수 있습니다.

병렬 실행은 압력을 더합니다. 낙관적 병렬화는 동시에 추측하고 나중에 롤백할 수 있지만, 컨트랙트 작성자 입장에서 확정된 상태 전이는 여전히 어떤 명확한 직렬 순서(대개 블록 내 트랜잭션 순서)와 동등해야 합니다. 스케줄링이 보이는 최종 상태를 바꾼다면 그것은 호환이 아니라 다른 가상 머신입니다. 정확성 경계와 직렬화 가능성은 [낙관적 동시성 제어(OCC) 입문](/ko/blog/optimistic-concurrency-control-intro)을 참고하세요. 병렬화를 더 넓은 확장 지도에 놓으려면 [블록체인 확장 지도](/ko/blog/blockchain-scaling-map)와 [EVM 단일 스레드 병목](/ko/blog/evm-single-thread-bottleneck)을 비교해 보십시오. 호환성이 보존해야 하는 것은 컨트랙트 작성자가 의존하는 결정론적 의미론이지 단일 스레드 인터프리터 자체가 아닙니다.

## 프리컴파일: 같은 주소, 다른 구현은 호환이 아니다

이더리움은 타원 곡선 페어링, 모듈러 거듭제곱, 해싱 같은 값비싼 연산을 `0x01`~`0x0a` 같은 고정 주소의 프리컴파일 컨트랙트로 구현합니다. 애플리케이션과 라이브러리는 그 주소와 가스 비용을 하드코딩합니다. 체인이 프리컴파일 집합이나 주소 매핑, 반환 의미론을 바꾸면 Solidity는 여전히 컴파일될 수 있지만 호환성은 이미 깨진 상태입니다. 감사받은 라이브러리가 온체인에서 실패하거나 잘못된 결과를 반환할 수 있습니다.

흔한 "가짜 호환" 패턴에는 인기 있는 프리컴파일만 구현하기, 예약된 이더리움 주소에 사용자 정의 프리컴파일 추가하기, 문서화 없이 가스를 바꾸기가 있습니다. 마이그레이션 팀은 백서의 "EVM 호환" 문구를 믿지 말고 목표 포크의 프리컴파일 표와 대조해야 합니다. 같은 입력과 calldata에 대해 메인넷과 대상 체인의 반환 값과 가스가 같은지 확인해야 합니다. 페어링과 modexp 경로는 값비싸고 브리지, 증명 검증, 암호 라이브러리가 의존하는 경우가 많으므로 특히 점검이 필요합니다.

병렬 EVM은 프리컴파일 의미론을 바꿀 필요가 없습니다. 진짜 위험은 "속도를 위해" 특수화하고 싶은 유혹입니다. 처리량을 위한 프리컴파일 변경은 호환성 서사에 슬그머니 끼워 넣는 것이 아니라 명시적인 프로토콜 차이여야 합니다. 특수 연산을 위해 사용자 정의 프리컴파일을 추가하는 팀은 명확히 예약되지 않은 주소 공간을 사용하고, 이더리움 예약 주소는 건드리지 않도록 차이를 문서화해야 합니다.

## JSON-RPC: 툴링은 구호가 아니라 인터페이스에서 살고 죽는다

배포 가능한 컨트랙트는 사용 가능한 생태계와 같지 않습니다. 지갑과 익스플로러, 인덱서, 모니터는 JSON-RPC에 의존합니다. `eth_call`, `eth_getLogs`, `eth_estimateGas`, `eth_getTransactionReceipt` 등입니다. 필드 의미나 오류 코드, 로그 인덱싱, pending 대 latest 의미론이 이더리움 클라이언트 관행과 어긋나면 프런트엔드와 인프라에 어댑터가 필요해지고, 마이그레이션 비용이 "RPC 교체"에서 "운영 경로 재구축"으로 커집니다.

`eth_estimateGas`와 트레이스류 API는 특히 주의가 필요합니다. 병렬 실행 아래에서 최종 직렬 의미론을 시뮬레이션하지 않는 가스 추정은 체계적으로 낮거나 높을 수 있습니다. 메인넷과 동형인 트레이스가 없으면 Foundry 포크 테스트와 사고 포렌식이 어려워집니다. 따라서 JSON-RPC 호환성은 "MetaMask가 연결된다"가 아니라 일상적인 개발·운영 경로가 그대로 재사용 가능한지입니다. 필터 구독과 `eth_feeHistory`, EIP-1559 관련 필드가 기존 SDK를 설정 변경만으로 가동할 수 있는지를 자주 결정합니다.

인덱서와 익스플로러는 안정적인 로그 순서와 영수증 필드도 필요로 합니다. 병렬 실행이 중간 상태의 가시 시점을 바꾸더라도 최종 영수증이 메인넷과 동형이라면 애플리케이션은 대개 대응할 수 있습니다. 반대로 영수증 필드가 빠지거나 오류 코드가 독자적이면 생태계 도구는 포크 유지보수를 해야 합니다. 호환성 인수 테스트는 "읽기 호출 + 트랜잭션 전송 + 로그 조회 + 가스 추정"을 하나의 닫힌 루프로 다루어야 하며, 배포 성공만 보아서는 안 됩니다.

## 툴링: Foundry와 Hardhat이 최후의 심판이다

대부분의 팀에게 호환성은 로컬에서 판정됩니다. `forge test`, Hardhat 스크립트, OpenZeppelin 컨트랙트, 일반적인 검증 플러그인이 대상 RPC에서 거의 또는 전혀 변경 없이 작동하는지입니다. Foundry(Forge/Cast/Anvil)는 많은 신규 프로젝트의 기본 테스트 스택이고, Hardhat은 여전히 큰 설치 기반을 커버합니다. 둘 다 컴파일러 출력과 온체인 프리컴파일, RPC 동작이 이더리움에 충분히 가깝다고 가정합니다.

짧고 반복 가능한 인수 체크리스트는 다음과 같습니다.

1. 같은 Solidity 버전과 옵티마이저 설정으로 컴파일하고, 배포된 바이트코드 해시가 메인넷 배포와 일치하는지(또는 설명 가능한 immutable만 다른지) 확인합니다.
2. 핵심 프리컴파일을 대조합니다.
3. Anvil/Hardhat Network로 대상 체인 상태를 포크하고 기존 통합 테스트를 실행합니다.
4. 지갑에서 chainId와 RPC만 바꾸고 서명 → 전송 → 영수증 흐름을 완료합니다.
5. 핵심 컨트랙트에 불변성 테스트를 실행합니다. 잔액 보존, 접근 제어, 재진입 방어가 대상 체인에서도 유지되어야 합니다.

어느 하나라도 실패하면 "호환"에는 아직 빈틈이 있습니다. Bitroot는 개발자에게 Solidity 재작성이나 툴체인 교체를 요구하는 대신 복잡성을 런타임 병렬 스케줄링에 두므로 위 경로를 그대로 유지할 수 있습니다. [Bitroot 병렬 EVM 해설](/ko/blog/bitrootevm-)과 [다중 엔진 병렬 실행 설계](/ko/blog/bitroot-evm)도 참고하세요. [병렬 실행의 세 가지 접근](/ko/blog/parallel-execution-approaches)과 비교하면, 바이트코드 호환성을 고집한다는 것은 새 계정 모델로 얻을 수 있는 일부 병렬화 상한을 포기하는 대신 개발자 생태계의 마이그레이션 반경을 넓히는 것입니다.

## "EVM 호환" 주장을 읽는 방법

마케팅 문장을 네 가지 예/아니오 질문으로 바꿉니다. 바이트코드 의미론이 목표 하드포크와 일치하는가? 프리컴파일 표가 동일한가? JSON-RPC가 동형 의미론으로 주요 메인넷 메서드를 커버하는가? 기존 Foundry/Hardhat 프로젝트가 거의 또는 전혀 변경 없이 실행되는가? 네 개 모두 예라면 엄격한 호환에 가깝습니다. 하나라도 아니오라면 구호 뒤에 숨기지 말고 명시적인 차이 문서로 공개해야 합니다.

병렬화는 충돌 상황의 처리량과 지연을 바꿀 수 있지만, 컨트랙트 작성자가 의존하는 결정론적 의미론을 다시 써서는 안 됩니다. 핫 워크로드에서 사용자가 보는 재시도와 지연 변화는 "의미론이 호환되는가"와 별개인 성능·워크로드 문제입니다. 이후 글인 [충돌 핫스팟과 워크로드](/ko/blog/parallel-evm-workload-hotspots)와 [성능 지표 용어집](/ko/blog/performance-metrics-glossary)을 참고하세요. 더 빠른 벤치마크를 사면서 툴링과 바이트코드 동작을 예측 불가능하게 만드는 체인은 단기 포스터를 위해 장기 생태계 자본을 쓰는 것이며, EVM 경로의 네트워크 효과야말로 그 포스터가 사려는 대상입니다.

다음 글은 이미 발행된 글만 가리키는 역할별 읽기 경로를 제시하여, 서로 다른 배경이 아무도 끝까지 읽지 못하는 한 편의 글로 뭉개지지 않게 합니다.

## 더 읽기

- 이전 글: [Bitroot 포지셔닝: 낙관적 병렬 EVM 기반의 고성능 Layer 1](/ko/blog/bitroot-positioning)
- 다음 글: [병렬 EVM을 누가 읽어야 하는가: 컨트랙트 개발자, 클라이언트 엔지니어, 연구자를 위한 세 가지 경로](/ko/blog/who-should-read-parallel-evm)
- 관련 글: [EVM 단일 스레드 병목](/ko/blog/evm-single-thread-bottleneck), [낙관적 동시성 제어(OCC) 입문](/ko/blog/optimistic-concurrency-control-intro)
