React Native 지갑 연동 비교: Solana MWA의 로컬 소켓과 Reown AppKit의 링크 모

삼코딩

@samcoding

React Native 지갑 연동 비교: Solana MWA의 로컬 소켓과 Reown AppKit의 링크 모드

React Native 지갑 연동 비교: Solana MWA의 로컬 소켓과 Reown AppKit의 링크 모드

React Native로 모바일 디앱을 개발할 때 개발자를 가장 괴롭히는 요소는 모바일 지갑 연동과 세션 유지입니다. 데스크톱과 달리 모바일 OS 환경은 보안 샌드박스가 엄격하고 브라우저 확장 프로그램 방식의 프로바이더 주입이 불가능하기 때문입니다.

이 환경적 제약을 극복하기 위해 솔라나의 MWA와 EVM 진영의 Reown AppKit은 서로 완전히 다른 통신 구조를 채택했습니다. 이번 글에서는 두 기술의 통신 아키텍처 작동 원리를 깊이 비교하고, 멀티체인 모바일 앱 개발 시 마주하는 연속 서명 버그나 세션 유실 문제를 돌파하는 실무 설계 패턴을 다룹니다.

통신 아키텍처: 로컬 웹소켓과 클라우드 릴레이의 차이

모바일 환경에서는 브라우저 확장 프로그램처럼 전역 객체를 주입하는 방식이 불가능하므로, 지갑과 앱이 데이터를 주고받을 별도의 통신 통로가 필요합니다. 솔라나 MWA와 Reown AppKit은 이 통로를 확보하기 위해 완전히 대조적인 아키텍처를 선택했습니다.

솔라나 MWA는 안드로이드 환경에서 로컬 피어 투 피어 방식을 사용합니다. 디앱이 운영체제 인텐트를 던져 지갑 앱을 깨우면, 지갑 앱은 기기 내부의 로컬 호스트에서 무작위 포트로 임시 웹소켓 서버를 구동합니다. 디앱은 이 로컬 소켓에 클라이언트로 접속해 직접 보안 터널을 형성하고 서명을 요청합니다. 외부 중계 서버를 거치지 않아 서명 요청과 응답 속도가 물리적으로 매우 빠르다는 장점이 있습니다. 다만 iOS는 앱이 백그라운드로 전환되면 약 30초 내에 모든 네트워크 연결을 차단하는 운영체제 제약이 있어, 로컬 웹소켓 서버를 상시 유지할 수 없어 딥링크 리다이렉트 방식으로 우회하게 됩니다.

반면 Reown AppKit은 기본적으로 전용 클라우드 릴레이 서버를 거치는 암호화 웹소켓 터널을 형성합니다. 디앱과 지갑이 각각 외부 중계 노드에 접속해 세션을 동기화하는 방식입니다. 최근에는 이러한 중계 방식의 지연 속도를 줄이기 위해 '링크 모드'라는 직접 전송 프로토콜을 도입했습니다. 링크 모드를 활성화하면 클라우드 릴레이를 거치지 않고 iOS 유니버셜 링크와 안드로이드 앱 링크를 통해 지갑과 앱이 직접 트랜잭션과 세션 메시지를 교환할 수 있어, 네트워크 홉을 최소화하고 성능을 크게 개선할 수 있습니다.

연속 트랜잭션 서명과 무한 루프 버그 해결하기

EVM 기반 모바일 dApp에서 연속으로 트랜잭션을 서명할 때 고질적으로 발생하는 대표적인 UX 실패 사례가 있습니다. ERC-20 토큰의 approve 메서드를 호출한 직후 곧바로 transfer나 민팅 트랜잭션을 연속 실행할 때 나타나는 '루핑 오토 스위치(looping auto-switch)' 오류입니다. Reown AppKit의 알려진 이슈인 APKT-3503과 깃허브 Issue #4785에 해당하며, 짧은 간격으로 연속해서 트리거되는 딥링크 때문에 지갑 앱이 두 번째 서명 팝업을 띄우지 못하고 끊임없이 앱이 교차 전환되는 루프에 갇히게 됩니다.

이 문제를 보완하기 위해 Reown AppKit은 외부 클라우드 릴레이 서버를 거치지 않고 다이렉트로 통신하는 링크 모드(Link Mode)를 도입했습니다. 링크 모드는 iOS의 유니버셜 링크와 안드로이드의 앱 링크를 직접 전송 통로로 사용하여 연속 트랜잭션 요청을 지연 없이 지갑 앱으로 곧바로 전달합니다.

이를 적용하려면 dApp 설정 시 redirect 블록 내에 linkMode: true 속성을 명시적으로 활성화해야 합니다.

typescript
const metadata = {
  name: 'My Web3 App',
  description: 'React Native dApp',
  url: 'https://mydapp.com',
  icons: ['https://mydapp.com/icon.png'],
  redirect: {
    native: 'mydapp://',
    universal: 'https://mydapp.com/connect',
    linkMode: true, // 링크 모드 활성화
  },
};

반면 솔라나 Mobile Wallet Adapter는 아키텍처 수준에서 이 문제를 매끄럽게 해결합니다. 솔라나 MWA는 dApp과 지갑이 처음 연결을 맺을 때 로컬 웹소켓 세션을 수립하고 이를 원자적으로 보존합니다. 동일한 로컬 세션 컨텍스트 내부에서 여러 트랜잭션의 서명을 요청하기 때문에 dApp과 지갑이 매번 무거운 딥링크로 서로를 교차 호출할 필요가 없으며, 컨텍스트 전환 없이 직관적이고 안정적인 연속 서명이 가능합니다.

세션 유지 패턴: auth_token 재인증과 AsyncStorage 페어링

모바일 디앱 개발 시 앱을 종료했다가 다시 켤 때마다 지갑 연결을 매번 새로 승인해야 한다면 최악의 사용자 경험을 주게 됩니다. Solana MWA와 Reown AppKit은 이 문제를 해결하기 위해 완전히 다른 세션 보존 구조를 선택했습니다. MWA는 보안을 고려해 암호화된 토큰 기반의 무반응 재인증 방식을 직접 구현해야 하는 반면, Reown AppKit은 로컬 스토리지에 페어링 정보를 저장해 클라우드 중계 세션을 자동으로 재연결합니다.

Solana MWA는 보안상 지갑과의 소켓 세션을 기본적으로 일회성 휘발성 세션으로 처리합니다. 따라서 디앱은 지갑이 처음 연결될 때 반환하는 auth_token을 기기에 안전하게 보존해야 합니다. 이때 보안 위협을 방지하기 위해 일반 스토리지 대신 expo-secure-storereact-native-secure-key-store 같은 암호화된 보안 저장소를 활용해야 합니다. 이후 트랜잭션 요청을 보낼 때는 매번 새로 지갑 승인을 받는 대신, 저장해 둔 토큰을 사용해 wallet.reauthorize API를 백그라운드에서 실행하는 무반응 자동 재연동 상태를 구성합니다.

typescript
// Solana MWA의 백그라운드 자동 재인증 패턴 예시
import * as SecureStore from 'expo-secure-store';

async function connectOrReauthorize(wallet) {
  const storedToken = await SecureStore.getItemAsync('mwa_auth_token');
  
  if (storedToken) {
    try {
      // 저장된 토큰이 있다면 사용자 팝업 없이 백그라운드에서 재인증합니다.
      const authorization = await wallet.reauthorize({
        auth_token: storedToken,
        identity: APP_IDENTITY,
      });
      return authorization;
    } catch (error) {
      console.warn('재인증 실패, 신규 인증으로 전환합니다.', error);
    }
  }

  // 저장된 토큰이 없거나 만료된 경우 신규 인증을 요청합니다.
  const authorization = await wallet.authorize({ identity: APP_IDENTITY });
  await SecureStore.setItemAsync('mwa_auth_token', authorization.auth_token);
  return authorization;
}

이와 달리 Reown AppKit은 지갑과의 페어링 정보 및 세션 메타데이터를 @react-native-async-storage/async-storage를 통해 기기에 영구 저장합니다. 앱이 다시 켜질 때 AppKit 내부의 autoConnect 옵션이 이 로컬 저장소 상태를 읽고 클라우드 릴레이 노드와 실시간 웹소켓 터널을 자동으로 복구합니다. 개발자가 토큰 저장 및 갱신 비즈니스 로직을 직접 작성할 필요가 없어 연동이 간편하지만, 외부 릴레이 서버 네트워크의 가동 상태나 모바일 기기의 온라인 환경에 실시간으로 영향을 받는 구조적 한계를 감안해야 합니다.

프로덕션 배포 필수 조건: 도메인 및 앱 검증 자산 설정

모바일 Web3 앱을 프로덕션 환경에 배포할 때 가장 빈번하게 발생하는 실패 요인은 도메인 및 앱 검증 설정 누락입니다. 모바일 지갑은 악성 앱의 위장 공격으로부터 사용자를 보호하기 위해 앱과 웹 도메인의 소유권을 대조하는 엄격한 보안 검증을 수행합니다. 이 검증이 누락되면 지갑 화면에 '알 수 없는 앱' 경고가 표시되거나 서명 요청 자체가 차단됩니다.

솔라나 MWA 환경에서 안드로이드 지갑은 dApp의 신원을 검증하기 위해 메타데이터에 등록된 웹 도메인의 특정 경로를 조회합니다. 개발자는 서비스 도메인의 .well-known/assetlinks.json 경로에 안드로이드 패키지 이름과 SHA-256 서명 인증서 지문 정보가 매핑된 검증 파일을 반드시 호스팅해야 합니다.

json
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.mydapp",
      "sha256_cert_fingerprints": ["AB:CD:EF:..."]
    }
  }
]

Reown AppKit의 저지연 링크 모드 역시 양방향 도메인 연동을 전제로 작동합니다. 클라우드 릴레이 서버를 거치지 않고 유니버설 링크와 앱 링크를 통해 지갑과 직접 통신하려면 AppKit 메타데이터 설정에서 linkMode 옵션을 활성화해야 합니다.

typescript
const metadata = {
  name: 'My dApp',
  url: 'https://mydapp.com',
  redirect: {
    native: 'mydapp://',
    universal: 'https://mydapp.com'
  },
  linkMode: true
};

이 설정을 마치고 iOS를 위한 apple-app-site-association 파일과 안드로이드를 위한 assetlinks.json 파일을 자체 웹 서버의 .well-known 디렉터리에 올바르게 배치해야만 경고 창 없는 자연스러운 지갑 연동 UX를 완성할 수 있습니다.

모바일 Web3 앱의 크로스체인 설계를 위한 가이드라인

모바일 환경에서 최상의 디앱 사용자 경험을 구축하려면 체인별 아키텍처에 맞는 영리한 설계가 필요합니다. 솔라나 MWA는 로컬 웹소켓 기반의 강력한 로컬 피어 투 피어 통신을 통해 지연 없는 연속 서명을 제공하지만, iOS의 백그라운드 제한처럼 플랫폼 특유의 제약을 신중하게 고려해야 합니다. 반면 Reown AppKit은 멀티체인 대응력이 뛰어나며, 최근 링크 모드를 도입해 기존 클라우드 중계 방식의 지연 시간과 보안 문제를 효과적으로 보완하고 있습니다. 결국 프로덕션 환경의 성패는 도메인 검증 자산을 미리 선언하고 안전한 토큰 캐싱 패턴을 완벽히 준비하는 디테일에서 결정됩니다.

(Edited)

No comments yet.