@samcoding

Solana Wallet Standard vs EIP-6963: 멀티 지갑 충돌을 해결하는 두 아키텍처 분석
브라우저 익스텐션 지갑이 늘어나면서 전역 객체를 서로 덮어쓰려 하는 충돌은 Web3 프론트엔드 개발자들을 오랫동안 괴롭혀 온 문제였습니다. 이를 해결하기 위해 EVM 진영은 표준 DOM 이벤트를 활용하는 EIP-6963을 채택했고, 솔라나는 독자적인 Wallet Standard 규격을 도입했습니다. 두 생태계가 멀티 지갑 탐색을 위해 선택한 아키텍처의 차이와, JSON-RPC 스트림 대 타입 안전한 피처 레지스트리가 가져다주는 개발자 경험의 차이를 명확히 비교해 봅니다.
EIP-6963: DOM 이벤트 기반의 EVM 멀티 지갑 탐색
EVM 생태계는 여러 지갑 브라우저 익스텐션이 전역 window.ethereum 객체를 서로 덮어쓰며 발생하는 충돌을 해결하기 위해 EIP-6963 표준을 도입했습니다. 이 표준은 전역 네임스페이스를 오염시키는 대신 브라우저의 표준 DOM 커스텀 이벤트를 활용하여 사용자의 지갑을 동적으로 탐색합니다.
dApp이 eip6963:requestProvider 이벤트를 발생시키면 브라우저에 설치된 개별 지갑들이 자신의 메타데이터를 담아 eip6963:announceProvider 이벤트로 응답합니다. 이때 반환되는 페이로드인 EIP6963ProviderDetail 객체 안에는 지갑의 고유 식별자, 이름, 아이콘 정보와 함께 기존 EIP-1193 사양의 프로바이더가 포함되어 있어 충돌 없이 여러 개의 지갑 인스턴스를 격리하여 관리할 수 있습니다.
// EIP-6963 지갑 탐색 및 프로바이더 등록 예시
window.addEventListener('eip6963:announceProvider', (event: any) => {
const { info, provider } = event.detail;
console.log(`Discovered: ${info.name} (${info.rdns})`);
// 여기서 각 지갑의 EIP-1193 프로바이더를 상태에 독립적으로 저장합니다.
});
// 지갑 탐색 요청 디스패치
window.dispatchEvent(new CustomEvent('eip6963:requestProvider'));다만 이 DOM 이벤트 기반 탐색은 데스크톱 익스텐션이나 모바일 지갑의 인앱 브라우저로 실행 환경이 제한됩니다. 일반 모바일 브라우저에서는 익스텐션 방식의 주입이 차단되므로, 모바일 대응을 위해서는 별도의 클라우드 릴레이나 디바이스 딥 링크 프로토콜을 함께 구현해야 합니다.
Solana Wallet Standard: 양방향 커스텀 이벤트와 글로벌 큐
솔라나는 @wallet-standard/core를 기반으로 dApp과 지갑이 서로를 유연하게 탐색할 수 있는 양방향 이벤트 핸드셰이크 구조를 사용합니다. 단방향 이벤트 전달 방식과 달리, 이 구조는 dApp과 지갑 중 어떤 쪽이 먼저 로드되더라도 초기화 시점의 레이스 컨디션 없이 안전하게 연결을 확보합니다.
지갑 익스텐션이 dApp보다 먼저 로드되면 wallet-standard:register-wallet 이벤트를 발생시켜 자신을 등록합니다. 반대로 dApp이 먼저 로드되어 대기 중일 때는 wallet-standard:app-ready 이벤트를 브로드캐스트하여 준비 상태임을 지갑에 알립니다. 이 핸드셰이크 과정에서 서로 등록 API 콜백을 실어 보내기 때문에 동기화 실패율이 극히 낮아집니다.
또한 지갑과 dApp의 로딩 시점 차이에서 오는 미세한 오차를 해결하기 위해 window.navigator.wallets 객체를 글로벌 폴백 큐로 활용합니다. 지갑들은 로딩 직후 이 객체의 커스텀 push 메서드를 호출해 등록 콜백을 안전하게 대기열에 집어넣을 수 있습니다. 브라우저의 비동기 스크립트 실행 순서에 영향을 받지 않는 탄탄한 초기화 아키텍처를 구현한 셈입니다.
JSON-RPC 스트림 vs 타입 안전한 피처 레지스트리
두 규격이 탐색을 마친 뒤 프론트엔드 개발자에게 제공하는 지갑 인터페이스의 형태는 완전히 다릅니다. EIP-6963은 표준화된 DOM 이벤트 핸드셰이크를 거친 후 결국 기존의 EIP-1193 프로바이더를 반환합니다. 이 프로바이더는 문자열 기반의 JSON-RPC 메서드인 provider.request 호출 스트림에 의존하므로, 특정 기능의 지원 여부를 확인하려면 런타임에 직접 요청을 보내고 예외를 처리하는 과정을 거쳐야 합니다.
반면 솔라나의 Wallet Standard는 문자열 기반의 RPC 메시징 대신 컴파일 타임에 타입 검증이 가능한 자바스크립트 객체를 반환합니다. 지갑 객체 내부의 features 맵에 지원하는 기능들이 명시되어 있어, 개발자는 타입 정의 파일만으로 해당 지갑이 어떤 표준 기능이나 커스텀 기능을 구현했는지 즉시 파악할 수 있습니다. 이 덕분에 런타임에 메서드 지원 여부를 추측하거나 에러를 내며 테스트할 필요가 없습니다.
코드 수준에서 이 차이는 매우 명확하게 나타납니다.
// EVM (EIP-1193): 문자열 기반 호출로 지원 여부를 사전에 검증하기 어려움
try {
await provider.request({
method: 'personal_sign',
params: [message, address],
});
} catch (error) {
console.error("지갑이 해당 메서드를 지원하지 않거나 오류가 발생했습니다.");
}
// 솔라나: 명시적인 피처 감지(Feature Detection)와 컴파일 타임 타입 검사
const signFeature = wallet.features['solana:signMessage'];
if (signFeature) {
await signFeature.signMessage({ message });
} else {
console.log("이 지갑은 메시지 서명을 지원하지 않습니다.");
}이러한 타입 안전한 피처 레지스트리 아키텍처 덕분에 멀티체인 dApp을 설계할 때 지갑 기능에 따른 UI 활성화나 조건부 분기 처리가 훨씬 간결해집니다.
멀티체인 환경을 위한 프론트엔드 설계 원칙
EVM의 EIP-6963과 솔라나의 Wallet Standard는 브라우저 전역 객체 충돌 문제를 우아하게 해결하고 다중 지갑 환경을 안정화하는 데 핵심적인 역할을 합니다. 두 표준 모두 저수준의 브라우저 이벤트를 통해 지갑을 탐색하지만, 탐색 이후 제공하는 인터페이스가 JSON-RPC 스트림과 타입 안전한 피처 레지스트리로 명확히 분리되는 아키텍처적 차이를 보여줍니다.
이러한 복잡한 이벤트 핸드셰이크와 프로바이더 매핑을 프론트엔드 개발자가 매번 수동으로 구현하는 것은 비효율적입니다. EVM 환경에서는 wagmi나 viem을 활용하고, 솔라나 환경에서는 @solana/wallet-adapter를 적극 도입하는 것이 안전합니다. 이 표준 라이브러리들은 내부적으로 지갑 탐색 규격을 완벽히 준수하면서도 개발자에게는 일관되고 고도로 추상화된 API를 제공합니다.
결과적으로 현대적인 멀티체인 dApp을 설계할 때는 지갑 연결부와 비즈니스 로직을 완전히 분리하는 클린 아키텍처를 지향해야 합니다. 각 체인의 표준 인터페이스를 통일성 있게 관리하는 범용 어댑터 레이어를 설계함으로써, 런타임 에러를 방지하고 유지보수가 용이한 Web3 프론트엔드를 구축할 수 있습니다.
참고 링크
- EIP-1193 Specification & Academic Research (SecureSign) — EIP-1193 Provider Injection Limitations on Mobile Browsers
- Wagmi React Hooks Documentation — EIP-712 Typed Data Signing in React Native using Wagmi & Viem
- wallet-standard Github Repository & Core Implementation — Solana Wallet Standard: Two-Way Event Handshake and Features Registry