본문으로 건너뛰기
0%

EVM vs SVM: 개발자 가이드

Uttam Singh

작성자 Uttam Singh

2026년 9월 25일에 게시됨8분 읽기

EVM vs SVM: 개발자 가이드

모든 블록체인은 프로그램을 실행하고 그 상태 변경을 적용하는 엔진인 가상 머신 위에서 동작합니다. Ethereum의 엔진은 EVM(Ethereum Virtual Machine)이며, Ethereum의 Layer 2 대부분도 EVM으로 구동됩니다. Solana의 엔진은 트랜잭션을 병렬로 실행하도록 만들어진 SVM(Solana Virtual Machine)입니다. 핵심적인 아키텍처 차이는 EVM이 컨트랙트의 코드와 상태를 함께 저장하는 반면, SVM은 이를 별도의 계정으로 분리한다는 점입니다. 이 차이가 프로그램이 데이터를 저장하는 방식, 트랜잭션이 실행되는 방식, 그리고 혼잡에 따라 수수료가 반응하는 방식을 결정합니다.

이 가이드는 계정 모델, 실행, 수수료, 프로그램 설계, 툴링 측면에서 두 VM을 짧은 코드 예제와 함께 비교하고, 어디에서 구축하거나 이식할지 결정하기 위한 프레임워크로 마무리합니다. 영상을 선호한다면 저희 SVM vs EVM 빌더 가이드에서 같은 비교를 다룹니다.

EVM과 SVM이란 무엇인가?

EVM은 트랜잭션을 한 번에 하나씩 처리하며 하나의 공유 상태 트리를 업데이트하는 스택 기반 가상 머신입니다. EVM의 주요 장점은 호환성입니다. 동일한 Solidity 컨트랙트를 Ethereum, Ethereum의 Layer 2(Arbitrum, Base, Optimism, Unichain, World Chain, Ink), 그리고 독립적인 EVM 체인(BNB Chain, Avalanche, Polygon, Monad, Berachain)에 수정 없이 배포할 수 있으며, 주변 툴링도 바이트코드가 동작하는 모든 곳에서 작동합니다. EVM이 처음이라면 저희 Ethereum Virtual Machine 설명 글에서 기본 개념을 다룹니다.

SVM은 병렬 실행을 중심으로 설계되었습니다. 프로그램은 eBPF(Linux 커널이 사용하는 것과 같은 바이트코드 설계)에서 파생된 레지스터 기반 형식인 sBPF 바이트코드로 컴파일되며, 모든 트랜잭션은 읽고 쓸 계정을 사전에 선언합니다. 이 선언 덕분에 Solana의 병렬 런타임인 Sealevel은 충돌하지 않는 트랜잭션을 여러 CPU 코어에서 동시에 실행할 수 있습니다. 저희 Solana Virtual Machine 설명 글에서 실행 파이프라인을 자세히 다룹니다.

아래 표는 주요 차이점을 요약합니다.

항목
EVM
SVM

계정 모델

코드와 저장소가 컨트랙트 계정에 함께 묶임

모든 것이 계정이며, 프로그램은 상태를 갖지 않음

실행

블록 내에서 순차적

충돌하지 않는 트랜잭션 간 병렬

가상 머신

스택 기반, 256비트 워드

레지스터 기반, eBPF 파생

수수료 시장

하나의 전역 기본 수수료와 팁

서명당 기본 수수료와 경합 계정에 한정된 우선순위 수수료

주요 언어

Solidity

Rust와 Anchor

토큰

토큰마다 하나의 컨트랙트

공유 Token Program, 토큰마다 하나의 민트 계정

업그레이드

기본적으로 불변, 업그레이드에는 프록시 사용

기본적으로 업그레이드 가능, 권한을 철회하면 고정

계정 모델은 어떻게 다른가?

Ethereum에는 두 가지 계정 유형이 있습니다. 개인키로 제어되는 외부 소유 계정과 코드로 제어되는 컨트랙트 계정입니다. 컨트랙트 계정은 바이트코드와 함께 자체 저장소, 즉 32바이트 슬롯으로 이루어진 key-value 저장소를 보유합니다. ERC-20 토큰에서 balanceOf를 호출하면 컨트랙트는 자체 저장소의 매핑에서 잔액을 읽습니다. 코드와 상태는 하나의 주소를 공유하며 분리할 수 없습니다.

Solana는 모든 것에 단일 계정 모델을 사용합니다. Solana의 모든 상태는 계정이며 동일한 구조를 가집니다. lamports 단위의 잔액(wei처럼 Solana의 최소 단위), 데이터 필드, 소유자, 그리고 executable 플래그입니다. 프로그램은 데이터가 sBPF 바이트코드이고 executable 플래그가 true로 설정된 계정입니다. 프로그램이 관리하는 상태는 별도의 데이터 계정에 존재합니다. 런타임이 소유권을 직접 강제하므로 계정을 소유한 프로그램만 그 데이터를 수정할 수 있으며, 이 덕분에 Solidity 컨트랙트가 직접 구현하는 접근 제어 로직의 상당 부분이 필요 없어집니다. 저희 Solana 데이터 계정 vs. 프로그램 계정 가이드에서 이 구분을 자세히 다룹니다.

Program derived address(PDA)는 보통 EVM 개발자에게 가장 낯선 개념입니다. Solidity 컨트랙트가 사용자별 데이터를 매핑에 저장하는 반면, Solana 프로그램은 프로그램 ID와 일련의 시드(일반적으로 사용자의 공개키)로부터 사용자마다 전용 계정 주소를 파생합니다. 파생은 결정론적이며 Ed25519 곡선을 벗어난 주소를 생성하므로, 해당 주소에는 개인키가 존재하지 않습니다. 오직 프로그램만이 그 주소를 대신해 서명할 수 있습니다. 실제로는 Solidity 매핑의 각 항목이 Solana에서는 각각 별도의 계정이 됩니다.

두 모델은 저장소 비용을 매기는 방식도 다릅니다. Ethereum에서는 저장소에 쓸 때 gas로 한 번 비용을 지불합니다. Solana에서는 모든 계정이 크기에 비례하는 환불 가능한 보증금을 보유해야 하며, 이를 rent 면제라고 합니다. 보증금은 계정을 닫을 때 반환되므로, 개발자에게는 사용하지 않는 상태를 제거할 직접적인 유인이 생깁니다.

SVM에서 병렬 실행은 어떻게 작동하는가?

EVM은 블록 내 트랜잭션을 순차적으로 실행합니다. 트랜잭션은 어떤 저장소 슬롯이든 읽거나 쓸 수 있고 그 접근은 실행해 보기 전까지 알 수 없으므로, EVM은 두 트랜잭션을 동시에 안전하게 실행할 수 없습니다.

Solana는 각 트랜잭션이 실행 전에 읽고 쓸 모든 계정을 나열하도록 요구하여 이 불확실성을 없앱니다. 스케줄러는 공유 계정을 읽기만 하는 트랜잭션은 병렬로 실행하고, 같은 계정에 쓰는 트랜잭션은 직렬화합니다. 쓰기 잠금은 한 번에 하나의 스레드만 허용합니다. 같은 잠금이 필요한 트랜잭션은 큐에 들어가 순서대로 처리되며, 충돌 때문에 실패하지는 않습니다.

이 모델은 프로그램 설계에 영향을 줍니다. 많은 사용자가 동시에 업데이트하는 상태는 여러 계정에 나누어야 하며, SPL 토큰(Solana의 토큰 표준)이 이런 방식으로 구성되어 있습니다. 서로 관련 없는 지갑 간의 USDC 전송 두 건은 서로 다른 토큰 계정 네 개에 쓰므로 동시에 실행될 수 있습니다. Ethereum에서는 같은 두 전송이 모두 하나의 ERC-20 컨트랙트 저장소를 업데이트하므로 차례로 실행됩니다.

병렬 실행은 EVM 생태계에도 도입되고 있습니다. Monad는 바이트코드와 완전히 호환되는 병렬 실행 EVM L1을 운영하며, MegaETH는 Ethereum L2로서 유사한 기법을 적용합니다. 이러한 체인은 트랜잭션에 계정 선언을 요구하는 대신 런타임에 충돌을 감지하므로, EVM 호환성을 유지하면서 처리량을 높입니다.

수수료는 어떻게 비교되는가?

Ethereum은 하나의 전역 수수료 시장을 사용합니다. 모든 트랜잭션은 프로토콜이 블록마다 조정하고 소각하는 동일한 기본 수수료를 지불하며, 여기에 검증자에게 주는 선택적 우선순위 팁이 더해집니다. 표준 ETH 전송에는 21,000 gas가 들며, 블록은 3,000만 gas를 목표로 하고 6,000만 gas 한도를 가집니다. 기본 수수료는 체인 전체에 적용되므로 한 애플리케이션의 수요가 모두에게 영향을 줍니다. 인기 있는 NFT 민팅이나 에어드롭 청구는 관련 없는 애플리케이션의 사용자를 포함한 모든 사용자의 기본 수수료를 올립니다.

Solana는 다른 수수료 구조를 사용합니다. 모든 트랜잭션은 서명당 5,000 lamports의 기본 수수료를 지불하며, 이 중 절반은 소각되고 절반은 검증자에게 지급됩니다. 컴퓨트는 컴퓨트 유닛(CU)으로 측정되며, 기본 예산은 명령어당 200,000 CU, 최대치는 트랜잭션당 140만 CU입니다. 트랜잭션에는 다른 트랜잭션보다 먼저 스케줄링되도록 컴퓨트 유닛당 micro-lamports로 책정되는 우선순위 수수료를 포함할 수도 있습니다.

핵심 차이는 수수료 경쟁의 범위입니다. 모든 트랜잭션이 쓰는 계정을 명시하므로, 우선순위 수수료 경쟁은 같은 계정을 두고 경합하는 트랜잭션들 사이에 집중됩니다. 생태계에서는 이러한 동작을 로컬 수수료 시장(local fee markets)이라고 부릅니다. getRecentPrioritizationFees RPC 메서드는 트랜잭션이 잠글 쓰기 가능한 계정을 기준으로 수수료 샘플을 필터링하여 이 설계를 반영합니다. 인기 있는 민팅 중에는 해당 민팅의 계정에 쓰는 트랜잭션의 수수료가 오르지만, 관련 없는 트랜잭션은 대부분 영향을 받지 않습니다.

이는 용량 계획에 영향을 줍니다. Ethereum에서는 관련 없는 애플리케이션의 활동이 트랜잭션 비용을 올릴 수 있으며, 애플리케이션 설계로는 이를 막을 수 없습니다. Solana에서는 수수료 압력이 주로 자신의 계정에 대한 경합에서 발생하므로, 자주 쓰이는 상태를 더 많은 계정에 분산하면 이를 줄일 수 있습니다.

프로그램 설계에서는 무엇이 달라지는가?

Solidity 컨트랙트는 코드와 저장소로 이루어진 단일 단위이며, 설계상 불변입니다. 배포 후 로직을 변경하려면 delegatecall 기반의 프록시 패턴이 필요하며, 이 패턴은 호출을 교체 가능한 구현 컨트랙트로 라우팅합니다. 최소한의 Solidity 카운터는 다음과 같습니다:

solidity
Copied
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract Counter { uint64 public count; // state lives inside the contract itself function increment() external { count += 1; } }

Anchor(널리 사용되는 Solana 프로그램 프레임워크)로 작성한 동등한 Solana 프로그램은 자체적으로 상태를 저장하지 않습니다. 카운터 값은 각 호출이 명시적으로 전달하는 별도의 계정에 존재합니다:

rust
Copied
use anchor_lang::prelude::*; // Run anchor keys sync to set your program ID here and in Anchor.toml. declare_id!("REPLACE_WITH_YOUR_PROGRAM_ID"); #[program] mod counter { use super::*; pub fn increment(ctx: Context<Increment>) -> Result<()> { ctx.accounts.counter.count += 1; Ok(()) } } #[derive(Accounts)] pub struct Increment<'info> { #[account(mut)] pub counter: Account<'info, Counter>, // state account passed in explicitly } #[account] pub struct Counter { pub count: u64, }

완전한 버전에는 카운터 계정을 생성하고 자금을 넣는 initialize 명령어도 포함됩니다. Anchor는 핸들러가 실행되기 전에 모든 계정을 Increment 구조체에 대해 검증하여, 계정이 예상한 타입과 소유자를 가지고 있는지 확인합니다. 이 검증은 누가 명령어를 호출할 수 있는지를 제한하지 않습니다. 작성된 그대로라면 누구나 increment를 호출할 수 있으므로, 실제 프로그램에서는 서명자 요건이나 파생 주소 제약 조건을 추가해 카운터를 업데이트할 수 있는 주체를 제어해야 합니다.

기본 업그레이드 동작은 반대입니다. 업그레이드 권한을 지정해 배포한 Solana 프로그램은 네이티브로 업그레이드 가능하며, 그 권한을 철회하면 프로그램이 불변이 됩니다. 따라서 Solana 프로그램에는 프록시 컨트랙트가 필요 없으며, EVM 감사에서 흔히 집중하는 코드 범주 하나가 사라집니다.

토큰도 같은 패턴을 따릅니다. 각 ERC-20 토큰은 표준 인터페이스를 구현하는 독립된 컨트랙트이므로, 많은 토큰을 지원하는 애플리케이션은 여러 독립적인 코드베이스에 의존하게 됩니다. Solana에서 토큰은 공유 Token Program을 통해 발행되며, Token-2022는 전송 수수료 같은 선택적 기능을 지원하는 확장 버전을 제공합니다. 각 토큰은 공급량과 소수점 자릿수를 기록하는 민트 계정이며, 각 보유자의 잔액은 토큰 계정에 저장됩니다. 이 프로그램들과 통합한 애플리케이션은 토큰별 코드 없이 모든 SPL 토큰을 지원할 수 있습니다.

이벤트 처리는 EVM이 분명한 이점을 가진 영역입니다. EVM 컨트랙트는 eth_getLogs와 인덱서가 네이티브로 소비하는 인덱싱된 이벤트를 발생시킵니다. Solana에는 네이티브 이벤트 시스템이 없습니다. 프로그램은 로그 메시지를 기록하고, Anchor의 이벤트 매크로는 구조화된 데이터를 이 로그에 인코딩하는데, 로그는 잘릴 수 있습니다. 그 결과 Solana의 프로덕션 인덱싱은 일반적으로 웹훅과 Solana gRPC 스트리밍 같은 스트리밍 인프라에 의존합니다.

클라이언트 스택은 어떤 모습인가?

VM 선택은 언어와 툴링도 결정합니다. EVM 개발은 Solidity와 함께 테스트, 퍼징, 배포를 폭넓게 지원하는 성숙한 툴체인(Foundry, Hardhat)을 사용합니다. Solana 개발은 Rust와 Anchor를 사용하며, 학습 곡선이 더 가파르고 감사자 풀은 성장하고 있지만 아직 더 작습니다. 저희 Web3 프로그래밍 언어 개요에서 언어 선택지를 더 자세히 다룹니다.

데이터 모델의 차이는 클라이언트 코드에서도 드러납니다. EVM에서 상태를 읽는다는 것은 컨트랙트의 함수를 호출하는 것이며, 그 함수는 자체 저장소의 데이터를 반환합니다. 이 예제는 viem을 사용합니다:

typescript
Copied
import { createPublicClient, http, parseAbi, formatUnits } from "viem"; import { mainnet } from "viem/chains"; const client = createPublicClient({ chain: mainnet, transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"), }); const balance = await client.readContract({ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC contract abi: parseAbi(["function balanceOf(address) view returns (uint256)"]), functionName: "balanceOf", args: ["0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503"], }); console.log(formatUnits(balance, 6));

Solana에서 상태를 읽는다는 것은 데이터를 보유한 계정을 가져오는 것입니다. 토큰 잔액은 Token Program이 아니라 토큰 계정에 저장되므로, 클라이언트는 그 계정을 직접 읽습니다. 이 예제는 @solana/kit을 사용합니다:

typescript
Copied
import { createSolanaRpc, address } from "@solana/kit"; const rpc = createSolanaRpc("https://solana-mainnet.g.alchemy.com/v2/YOUR_API_KEY"); // SOL balance: fetch the account's lamports by its address const { value: lamports } = await rpc .getBalance(address("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri")) .send(); // SPL balance: read the token account directly const { value: tokenBalance } = await rpc .getTokenAccountBalance(address("2ocS3orPq3jyszjsJ4NozKWyhdotr3csDjAizmkj65aH")) .send(); console.log(lamports, tokenBalance.uiAmountString);

이 패턴은 클라이언트 계층 전반에 적용됩니다. EVM 클라이언트는 컨트랙트에 쿼리하는 반면, Solana 클라이언트는 각 데이터를 어떤 계정이 보유하는지 알아야 하므로, 계정 레이아웃이 애플리케이션 설계의 일부가 됩니다.

EVM 앱을 Solana로 이식하면 무엇이 깨지는가?

EVM에서 Solana로 애플리케이션을 옮기려면 온체인 코드를 다시 작성해야 합니다. 계정 모델과 실행 모델이 다르기 때문입니다. 주요 변경 사항은 다음과 같습니다:

  • msg.sender는 서명자 계정이 됩니다. Solana에서 권한 부여는 트랜잭션에서 서명자로 표시되고 프로그램이 확인하는 계정에서 오며, 프로그램 자체가 권한을 가질 때는 프로그램이 대신 서명하는 PDA에서 옵니다.
  • 매핑은 PDA가 됩니다. Solidity 매핑의 각 키는 처음 사용하기 전에 생성하고 rent 자금을 넣어야 하는 파생 계정이 됩니다. 클라이언트가 컨트랙트 저장소를 쿼리하는 대신 계정을 가져오므로 데이터를 읽는 방식도 달라집니다.
  • 컨트랙트 저장소는 크기가 정해지고 rent 자금이 들어간 계정이 됩니다. 상태는 알려진 크기로 미리 할당해야 하며, 계정을 닫으면 보증금이 반환됩니다.
  • 프록시 업그레이드 패턴이 더 이상 필요 없습니다. 프로그램의 업그레이드 권한이 프록시 구성을 대체하며, 그 권한을 철회하면 프로그램이 불변이 됩니다.
  • 이벤트는 로그와 스트리밍이 됩니다. eth_getLogs를 소비하던 모든 시스템은 로그 파싱, 웹훅 또는 gRPC 스트림을 중심으로 다시 구축해야 합니다.
  • 클라이언트와 테스트 스택이 바뀝니다. 클라이언트 코드는 viem에서 @solana/kit으로, 테스트는 Foundry에서 Anchor의 테스트 프레임워크로 옮겨가며, CI에는 로컬 검증자가 필요합니다.

반대 방향, 즉 SVM에서 EVM으로 옮기는 팀은 일반적으로 툴링의 깊이, 네이티브 이벤트 인덱싱, 더 큰 감사 시장을 얻는 대신 병렬 실행, 1초 미만의 확인 시간, 계정별 수수료 격리를 포기합니다.

어느 방향이든 보통 가장 큰 비용은 팀의 학습 곡선입니다. Solana로 옮기는 EVM 엔지니어라면 저희 Solana 개발 로드맵에서 시작하는 것이 좋습니다.

EVM과 SVM 중 어떻게 선택하는가?

구축하려는 것
권장 출발점
이유

고처리량 소비자 앱(결제, 트레이딩, 민팅)

SVM

병렬 실행과 계정별 수수료 격리 덕분에 자체 부하에서도 비용이 안정적으로 유지됨

기존 유동성과 조합되는 DeFi 프로토콜

EVM

기존 DeFi 프로토콜, 통합, 감사자가 EVM 생태계에 집중되어 있음

지연 시간에 민감하지 않은 제품을 만드는 Solidity 경험이 있는 팀

EVM 또는 병렬 EVM 체인

워크로드에 SVM 수준의 처리량이 필요하지 않다면 기존 스택을 유지해 재작성을 피할 수 있음

Alchemy는 EVM 및 SVM 개발을 어떻게 지원하는가?

저희는 단일 플랫폼에서 두 생태계를 모두 지원합니다. 저희 RPC 엔드포인트는 Ethereum, 주요 L2, 그리고 더 넓은 EVM 생태계를 지원합니다. 저희 Solana 인프라는 최대 20배 빠른 아카이브 호출과 99.99% 가동 시간을 제공하며, gRPC 스트리밍은 Solana 인덱싱에 필요한 실시간 데이터 피드를 제공합니다. Solana 인프라를 검토 중이라면 저희 Solana RPC 가이드에서 제공업체를 고를 때 확인할 사항을 다룹니다.

무료 API 키 하나로 하나의 대시보드에서 EVM 체인과 Solana용 엔드포인트를 사용할 수 있습니다. 계약이나 최소 약정이 없으며, 생태계마다 별도의 제공업체를 통합할 필요도 없습니다.

Background gradient

블록체인 매직을 만드세요

Alchemy는 가장 강력한 Web3 개발자 제품 및 도구를 리소스, 커뮤니티, 그리고 전설적인 지원과 결합합니다.