요청 보내기
이 가이드를 완료하면 연결된 지갑이나 릴레이 세션에 JSON-RPC 요청을 전달하고, 응답 타입과 오류 처리를 일관되게 다룰 수 있습니다. 읽기 요청은 request<T>()로 처리하고, 서명은 서명에서 다룹니다.
사전 준비
AppKit인스턴스를 만들고init()을 호출합니다.connect()·selectWallet()·pair()중 하나로 연결을 먼저 수립합니다.- 아래 예제는 AppKit 설정에서 만든
appKit인스턴스를 사용합니다.
1. request() 호출 형식과 RequestArgs
request<T>()는 RequestArgs를 받아 지갑의 응답을 제네릭 타입 T로 반환합니다. 반환 형식을 알고 있으면 request<string>(...)처럼 T를 지정해 타입을 좁힙니다.
async request<T = unknown>(args: RequestArgs): Promise<T>
RequestArgs는 아래 세 필드를 받습니다. method는 필수이고, params와 chainId는 선택입니다.
interface RequestArgs {
method: string;
params?: unknown[];
chainId?: Caip2ChainId;
}
chainId는 릴레이 경로에서 요청을 어느 체인으로 보낼지 지정할 때 사용합니다. 주입형 EVM 연결에서는 이 값이 무시되고, 연결된 체인이 우선합니다.
제네릭 T는 TypeScript의 정적 타입만 좁히며 런타임 응답을 검증하지 않습니다. 사용자 정의 지갑이나 신뢰할 수 없는 응답은 dApp에서 별도로 검증합니다.
2. 읽기 요청 보내기
eth_chainId처럼 인자가 없는 요청은 method만 지정하면 됩니다.
const chainIdHex = await appKit.request<string>({
method: "eth_chainId",
});
console.log(chainIdHex); // 예: "0x1"
인자가 필요한 요청은 params 배열에 순서대로 넣습니다. 아래 예제는 계정의 nonce를 latest 블록 기준으로 조회합니다.
const nonceHex = await appKit.request<string>({
method: "eth_getTransactionCount",
params: ["0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", "latest"],
});
params에는 메서드가 요구하는 원본 인자만 전달합니다. request<T>()는 메서드 의미를 해석하지 않고 그대로 전달하므로, 응답의 형태는 호출한 메서드와 지갑이 결정합니다.
3. 릴레이 연결로 요청 보내기
릴레이 연결에서는 로컬 JSON-RPC 프로바이더 대신 세션 채널로 요청이 전달됩니다. 이때 chainId가 있으면 해당 체인으로 전달하고, 생략하면 연결 시점에 지정된 체인을 사용합니다.
체인 관리 계열 RPC인 wallet_switchEthereumChain과 wallet_addEthereumChain은 릴레이 세션에서 아무 동작도 하지 않고 undefined를 반환합니다. 릴레이 세션이 아직 성립하지 않았으면 NOT_CONNECTED가 발생합니다.
아무 작업도 하지 않고 반환하는 동작은 범용 request()에만 해당합니다. 전용 API인 addNetwork()은 릴레이·Klip A2A 연결에서 성공으로 처리하지 않고 UNSUPPORTED_METHOD를 던집니다 — 두 전송 방식 모두 연결 시점에 체인이 고정되기 때문입니다.
const chainIdHex = await appKit.request<string>({
method: "eth_chainId",
chainId: "eip155:11155111",
});
4. 오류 처리
request<T>()는 연결이 없을 때 NOT_CONNECTED를 던집니다. 기본 EVM 커넥터와 릴레이 경로는 사용자 거부를 AppError(USER_REJECTED)로 정규화합니다. Solana 커넥터는 범용 JSON-RPC 요청을 지원하지 않으므로 UNSUPPORTED_METHOD를 던지며, 트랜잭션 서명에는 signSolanaTransaction()을 사용합니다. 그 밖의 실패는 RPC_ERROR, SESSION_TIMEOUT, INVALID_RESPONSE가 될 수 있고, 사용자 정의 커넥터나 직접 호출한 provider가 자체 오류를 전달할 수도 있습니다.
import {
AppError,
AppResultCode,
isProviderRpcError,
USER_REJECTED_CODE,
} from "@scope-connect/appkit";
try {
const result = await appKit.request<string>({ method: "eth_chainId" });
} catch (error) {
if (error instanceof AppError) {
if (error.code === AppResultCode.NOT_CONNECTED) {
// 연결을 먼저 완료합니다.
} else if (error.code === AppResultCode.USER_REJECTED) {
// 기본 AppKit 경로에서 사용자가 요청을 거부했습니다.
}
} else if (isProviderRpcError(error) && error.code === USER_REJECTED_CODE) {
// 직접 프로바이더 또는 사용자 정의 커넥터가 원본 4001을 전달했습니다.
} else {
throw error;
}
}
AppResultCode와 프로바이더 오류의 전체 목록은 오류 처리에서 확인합니다.
5. 연결된 프로바이더 직접 호출
한 번의 요청에는 request<T>()로 충분하지만, 연결된 주입형 provider를 직접 호출해야 할 때는 getActiveProvider()를 사용합니다. 공개 타입이 보장하는 것은 request()뿐이므로, 이벤트 구독이 필요하면 별도의 런타임 검사를 두고 사용합니다.
const provider = appKit.getActiveProvider();
if (provider) {
const currentChain = await provider.request<string>({
method: "eth_chainId",
});
console.log(currentChain);
}
provider가 null이면 아직 연결되지 않았거나 EVM provider가 아닌 커넥터입니다. 이 경우는 request<T>()로 요청을 보내거나, 먼저 지갑 연결을 완료합니다.
다음 단계
- 서명 — EVM 메시지·타입 데이터와 Solana 트랜잭션 서명
- AppKit Connection — 연결 상태 조회와 네트워크 전환
- AppKit API —
request<T>()와RequestArgs의 전체 정의 - AppKit 오류 처리 —
NOT_CONNECTED와 프로바이더 오류 코드