@solana/react는 하나의 프로바이더를 통해 컴포넌트 트리에 Kit 클라이언트를 게시하고, 훅은 해당 프로바이더에서 RPC 데이터, 구독, 지갑 상태를 읽습니다. 브라우저 지갑 훅은 @solana/kit-plugin-wallet/react에 있으며 동일한 프로바이더를 읽습니다 — 별도의 지갑 프로바이더는 필요하지 않습니다.
설치
$npm install @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-wallet @solana/react
클라이언트 빌드
React 트리 외부의 모듈 스코프에서 클라이언트를 한 번 빌드하세요. 브라우저에서 walletSigner는 연결된 지갑을 수수료 납부자이자 신원으로 지정합니다.
import { createClient } from "@solana/kit";import { solanaRpc } from "@solana/kit-plugin-rpc";import { walletSigner } from "@solana/kit-plugin-wallet";export const client = createClient().use(walletSigner({ chain: "solana:devnet" })).use(solanaRpc({ rpcUrl: "https://api.devnet.solana.com" }));export type AppClient = Awaited<typeof client>;
walletSigner는 client.wallet(Wallet Standard 검색 및 연결)을 추가하고, solanaRpc는 client.rpc와 client.sendTransaction 헬퍼를 추가합니다. 이후에 비동기적으로 resolve되는 플러그인을 추가하더라도 타입이 올바르게 유지되도록 클라이언트 타입을 Awaited<>로 감싸서 내보내고, 모든 useClient 호출에 해당 AppClient를 전달하세요.
트리를 한 번만 감싸기
ClientProvider는 클라이언트를 게시합니다. 데이터 훅(useRequest, useSubscription, useTrackedData)은 useClient를 통해 클라이언트를 읽습니다. 아래의 지갑 훅은 첫 번째 인수로 동일한 클라이언트를 명시적으로 전달받아야 합니다.
"use client";import { ClientProvider } from "@solana/react";import { client } from "./client";export function Providers({ children }: { children: React.ReactNode }) {return <ClientProvider client={client}>{children}</ClientProvider>;}
지갑 훅
지갑 훅은 지갑 플러그인(예: walletSigner)이 설치된 클라이언트가 필요합니다. useClient<AppClient>()로 클라이언트를 가져와 각 훅의 첫 번째 인수로 전달하세요 — 이렇게 하면 컨텍스트 조회에 의존하지 않고 앱 전체에 걸쳐 완전한 타입 안정성이 유지됩니다.
useWallets(client)— 클라이언트의 체인에서 검색된 Wallet Standard 지갑 목록.useWalletStatus(client)—"pending" | "disconnected" | "connecting" | "connected" | "disconnecting" | "reconnecting".useConnectedWallet(client)—{ account, signer, wallet }또는null. 읽기 전용 지갑의 경우signer는null입니다.useConnect(client)/useDisconnect(client)—ActionResult를 반환하며,dispatch(및dispatchAsync)로 액션을 실행하고,isRunning,status,data,error,reset으로 결과를 추적합니다.
"use client";import {useConnect,useConnectedWallet,useDisconnect,useWallets,useWalletStatus} from "@solana/kit-plugin-wallet/react";import { useClient } from "@solana/react";import type { AppClient } from "./client";function WalletPanel() {const client = useClient<AppClient>();const status = useWalletStatus(client);const wallets = useWallets(client);const connected = useConnectedWallet(client);const connect = useConnect(client);const disconnect = useDisconnect(client);if (status === "pending") return null; // wait out auto-reconnectif (connected) {return (<div><p>{connected.account.address}</p><button onClick={() => disconnect.dispatch()}>Disconnect</button></div>);}return (<div>{wallets.map((wallet) => (<buttonkey={wallet.name}disabled={connect.isRunning}onClick={() => connect.dispatch(wallet)}>Connect {wallet.name}</button>))}</div>);}
트랜잭션 전송
useClient는 게시한 동일한 클라이언트를 반환합니다. 생성된 프로그램 클라이언트로 명령어를 빌드하고 client.sendTransaction을 통해 전송하세요.
"use client";import { address, lamports } from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";import { useClient } from "@solana/react";import { useConnectedWallet } from "@solana/kit-plugin-wallet/react";import type { AppClient } from "./client";function SendSol({ destination }: { destination: string }) {const client = useClient<AppClient>();const connected = useConnectedWallet(client);async function handleSend() {if (!connected?.signer) return;const transfer = getTransferSolInstruction({source: connected.signer,destination: address(destination),amount: lamports(10_000_000n) // 0.01 SOL});const result = await client.sendTransaction([transfer]);console.log("sent", result.context.signature);}return (<button disabled={!connected?.signer} onClick={handleSend}>Send 0.01 SOL</button>);}
useClient에 내보낸 AppClient 타입을 전달하면 훅이 플러그인이 확장된 전체 클라이언트를 가지게 되어 client.rpc와 client.sendTransaction이 올바르게 타입 지정됩니다.
데이터 읽기
@solana/react는 클라이언트를 통해 데이터를 읽는 훅을 제공합니다:
useRequest(source)— 단발성 RPC 읽기;{ data, error, status, refresh }를 반환합니다.useSubscription(source)— 실시간 구독;{ data, error, reconnect, status }를 반환합니다.useTrackedData(spec)— 구독으로 최신 상태를 유지하는 RPC 기반 값.
"use client";import { address } from "@solana/kit";import { useClient, useRequest } from "@solana/react";import { useMemo } from "react";import type { AppClient } from "./client";function Balance({ owner }: { owner: string }) {const client = useClient<AppClient>();const source = useMemo(() => client.rpc.getBalance(address(owner)),[client, owner]);const { data, status, refresh } = useRequest(source);if (status === "fetching") return <p>Loading…</p>;if (status === "error") return <p role="alert">RPC error</p>;return (<div><p>Lamports: {data?.value.toString()}</p><button onClick={() => refresh()}>Refresh</button></div>);}
소스를 메모이제이션하세요(useMemo 사용, 함수의 경우 useCallback 사용). 그러면 훅이 매 렌더링마다가 아닌 입력값이 변경될 때만 다시 가져옵니다.
캐싱과 재검증을 위해 @solana/react/swr 및 @solana/react/query 서브패스는 SWR과 TanStack Query를 위해 동일한 훅을 래핑합니다.
Solana 개발자를 위한 일반적인 패턴
- 단일 프로바이더:
ClientProvider만 있으면 됩니다.useClient<AppClient>()로 클라이언트를 가져와 지갑 훅에 전달하세요. - 서버 컴포넌트 지원: 훅을 호출하는 리프 컴포넌트에만
"use client"를 표시하세요. 서버 읽기는 지갑 없이 일반 Kit RPC 클라이언트를 사용할 수 있습니다. - 테스트: 지갑과 RPC 응답을 시뮬레이션하기 위해
ClientProvider를 통해 모의 클라이언트를 게시하세요.
두 가지 훅 패밀리, 같은 이름
useSignIn과 useSignMessage는 @solana/react(UiWalletAccount 인수를 받음)와 @solana/kit-plugin-wallet/react(client를 받고 ActionResult를 반환) 양쪽에 모두 존재합니다. 위의 클라이언트 패턴에서는 @solana/kit-plugin-wallet/react 버전을 사용하세요. 기존 @solana/react 지갑 훅은 점차 대체되고 있습니다.
이 가이드를 Kit 클라이언트 개요와 함께 참고하여 각 훅이 읽는 클라이언트를 이해하세요.
Is this page helpful?