@samcoding

Wagmi v3 useReconnect 도입: 모바일 AppKit 세션 끊김과 딥링크 무한 루프 해결하기
모바일 브라우저에서 디앱을 서비스할 때 개발자를 가장 괴롭히는 문제는 사용자가 모르는 사이에 자동 차단되는 지갑 연결 세션입니다. 특히 2026년 6월 22일 릴리스된 Wagmi v3의 useAccount에서 useConnection으로의 파괴적 API 변경과 모바일 운영체제의 엄격한 백그라운드 소켓 제한 정책이 맞물리면서, 화면에서는 연결된 것처럼 보이지만 정작 트랜잭션은 전송되지 않는 치명적인 상태 비동기화가 빈번히 발생합니다. 이 글에서는 useReconnect 훅을 활용해 죽어버린 소켓 세션을 감지하고 강제로 복구하는 자동화 패턴과 모바일 환경에서 딥링크 동작을 완벽히 보장하기 위한 필수 설정법을 정리합니다.
Wagmi v3 핵심 변경 사항: useAccount에서 useConnection으로
2026년 6월 22일 릴리스된 Wagmi v3는 기존 API 명세를 대거 개편하며 핵심 hook 이름을 파괴적으로 변경했습니다. 가장 대표적인 변화는 기존 useAccount가 useConnection으로, useAccountEffect가 useConnectionEffect로, useSwitchAccount가 useSwitchConnection으로 각각 바뀐 점입니다.
이러한 변경은 EIP-1193 프로바이더 연결 명세를 더욱 엄격하고 직관적으로 반영하기 위한 조치입니다. 기존 useAccount가 지갑 내부의 계정 주소를 단순히 조회하는 수준이었다면, 새로운 useConnection은 dApp과 블록체인 프로바이더 간의 실질적인 커넥션 생명주기를 직접 관측합니다. 이에 따라 연결 상태 정보 역시 connecting, reconnecting, connected, disconnected처럼 세분화되어 실시간으로 추적할 수 있습니다.
이 변화는 모바일 dApp 환경에서 매우 중요한 의미를 가집니다. 네트워크 상태가 유동적이고 백그라운드 전환이 빈번한 모바일 환경에서는 로컬 캐시에 저장된 계정 주소의 존재 여부만으로 연결을 보장할 수 없기 때문입니다. 개발자는 프로바이더 수준의 실제 연결 상태를 기민하게 파악하고, 일시적인 소켓 끊김 상황에 맞춰 유연한 복구 로직을 설계해야 합니다.
모바일 브라우저의 늪: 살아있는 것처럼 보이는 죽은 세션의 원인
AppKit 깃허브 이슈 4788번에 따르면, iOS 사파리나 안드로이드 크롬 브라우저에서 디앱 탭이 백그라운드로 전환되거나 기기가 잠금 상태가 될 때 웹소켓 페어링 채널이 강제로 일시 정지되거나 끊어집니다. 이는 모바일 운영체제의 엄격한 백그라운드 샌드박스 정책 때문입니다.
문제는 채널이 끊어져도 디앱 내부의 로컬 스토리지 캐시 데이터는 여전히 연결 상태가 유효하다고 기록하고 있다는 점입니다. 브라우저 상태 데이터와 실제 네트워크 세션의 상태가 일치하지 않는 비동기화가 일어납니다.
이 상태에서 사용자가 트랜잭션을 실행하면 지갑 앱으로 연결되는 딥링크가 정상적으로 호출되지 않고 무한 대기에 빠지는 세션 데드락 현상이 발생합니다. 결국 사용자는 화면이 멈춘 것으로 오해해 이탈하게 됩니다.
Page Visibility API와 useReconnect를 활용한 세션 자동 복구
모바일 브라우저의 가혹한 백그라운드 제약으로 인한 세션 끊김 현상을 방지하려면, 사용자가 dApp 화면으로 복귀하는 순간을 정확히 감지해 유실된 웹소켓 페어링 채널을 강제로 복구해야 합니다. Wagmi v3에서 제공하는 useReconnect 훅은 지갑과의 커넥션을 수동으로 다시 맺을 수 있는 mutate 메서드를 제공합니다. 이를 브라우저의 표준 visibilitychange 및 focus 이벤트 리스너와 결합하면 사용자가 다른 앱을 보거나 화면을 잠갔다가 돌아오는 즉시 통신 채널을 깨끗하게 복구할 수 있습니다.
다음은 React 환경에서 페이지 가시성 변화를 실시간으로 모니터링하여 끊어진 세션을 안전하게 복원하는 커스텀 훅의 완전한 TypeScript 구현 예시입니다.
import { useEffect } from 'react';
import { useReconnect, useConnection } from 'wagmi';
/**
* 모바일 브라우저 백그라운드 전환 시 끊어지는 지갑 세션을
* 사용자가 화면으로 돌아올 때 자동으로 복구하는 커스텀 훅입니다.
*/
export function useMobileSessionRecovery() {
const reconnect = useReconnect();
const { isConnected } = useConnection();
useEffect(() => {
// 활성화된 지갑 연결 상태가 없다면 재연결을 트리거하지 않습니다.
if (!isConnected) return;
const handleFocus = () => {
if (document.visibilityState === 'visible') {
reconnect.mutate();
}
};
window.addEventListener('focus', handleFocus);
window.addEventListener('visibilitychange', handleFocus);
return () => {
window.removeEventListener('focus', handleFocus);
window.removeEventListener('visibilitychange', handleFocus);
};
}, [isConnected, reconnect]);
}이 구현은 useConnection을 통해 로컬 세션 캐시가 살아있는 경우에만 재연결 요청을 보내므로, 불필요한 RPC 호출 오버헤드를 막아줍니다. 사용자가 화면을 여는 즉시 백그라운드에서 신속하게 웹소켓 재연결 핸드셰이크를 처리하기 때문에, 지갑에서 다시 돌아왔을 때 트랜잭션 전송이나 서명 요청이 지연 없이 정상적으로 지갑 앱에 전달됩니다.
iOS 및 안드로이드 11+ 필수 네이티브 딥링크 스킴 설정
PWA 환경이나 네이티브 하이브리드 앱으로 dApp을 패키징할 때는 모바일 OS 레벨의 권한 제한을 선제적으로 해결해야 합니다. iOS와 안드로이드는 보안상 외부 앱을 호출하거나 기기에 설치된 앱 목록을 조회하는 동작을 엄격하게 제한하기 때문입니다. 이 설정이 누락되면 AppKit과 Wagmi가 기기에 실제 지갑이 존재하는지 감지하지 못해 딥링크 프로세스가 정상적으로 완료되지 않습니다.
먼저 iOS 환경에서는 Info.plist 파일에 LSApplicationQueriesSchemes 키를 선언하고 메타마스크나 레인보우 등 대상 지갑 앱들의 커스텀 URL 스킴을 등록해야 합니다.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>metamask</string>
<string>trust</string>
<string>safe</string>
<string>rainbow</string>
</array>안드로이드 11 이상 버전 역시 패키지 가시성 제한 정책이 강화되어 상대 앱의 존재 여부를 쿼리하기 까다로워졌습니다. 해결하려면 AndroidManifest.xml 파일의 <queries> 블록 안에 감지할 지갑의 공식 패키지명을 명시해야 합니다.
<queries>
<package android:name="io.metamask" />
<package android:name="com.wallet.crypto.trustapp" />
<package android:name="io.gnosis.safe" />
<package android:name="me.rainbow" />
</queries>이렇게 네이티브 설정을 보완해 주면 AppKit이 사용자 기기의 지갑 설치 상태를 올바르게 판별하여, 미설치 시 스토어로 안내하거나 설치 시 원터치로 지갑을 호출하는 최적의 딥링크 연결성을 보장할 수 있습니다.
무한 스위칭 루프 예방과 트랜잭션 디바운스 대책
모바일 웹3 환경에서 토큰 승인 후 즉시 민팅을 요구하는 것과 같은 연속적인 멀티스텝 트랜잭션을 처리할 때는 딥링크 무한 루프를 방지하는 방어적 설계가 매우 중요합니다. 모바일 지갑 앱의 서명 창 렌더링 지연이 브라우저의 리다이렉트 로직과 충돌하면 사용자가 이탈하는 치명적인 버그가 발생하기 때문입니다.
Reown AppKit 깃허브 이슈 4785번에 따르면, 메타마스크를 비롯한 모바일 지갑이 트랜잭션 서명 팝업을 띄우는 데 기기 사양에 따라 5초 이상 소요될 수 있습니다. 이때 서명 창이 바로 뜨지 않아 답답해진 사용자가 브라우저로 수동 복귀하면, 디앱의 자동 세션 복구 리스너가 즉시 재트리거되어 사용자를 다시 지갑 앱으로 강제 리다이렉트하는 무한 루프가 발생합니다.
이러한 현상을 방지하려면 트랜잭션 실행 요청 단계에 디바운스를 엄격하게 적용해야 합니다. 첫 번째 트랜잭션의 상태가 온체인에서 완전히 확인되거나 사용자에 의해 취소될 때까지 다음 단계 트랜잭션의 실행을 막고, 트랜잭션이 활성화된 동안에는 백그라운드 세션 재연결 리스너가 간섭하지 않도록 상태 플래그를 제어해야 합니다.
모바일 웹3 사용자 경험을 견고하게 완성하는 법
Wagmi v3와 Reown AppKit을 활용한 멀티체인 환경 구축은 강력하지만, 모바일 브라우저의 엄격한 백그라운드 제약 앞에서는 개발자의 세심한 예외 처리가 필수적입니다. 자동 연결 복구 흐름을 보장하기 위해 useReconnect와 브라우저 API를 결합하고, 모바일 OS 플랫폼별 딥링크 대응 설정을 반드시 점검해야 합니다. 에뮬레이터에만 의존하지 말고, 실제 기기에서 장시간 화면이 꺼진 후 돌아와 트랜잭션을 전송해 보며 세션 복구 안정성을 직접 검증하는 것을 권장합니다.
참고 링크
- Reown AppKit GitHub Issues — Looping Auto-Switch and MetaMask Delayed Confirmations (AppKit Issue #4785)
- Community Web3 Mobile Integration Patterns — Custom Mobile Session Restoration Pattern with useReconnect
- Reown AppKit GitHub Issues — Mobile Reload Connection Desync & Deep Link Failures (AppKit Issue #4788)