React

@solana/react는 하나의 프로바이더를 통해 컴포넌트 트리에 Kit 클라이언트를 게시하고, 훅은 해당 프로바이더에서 RPC 데이터, 구독, 지갑 상태를 읽습니다. 브라우저 지갑 훅은 @solana/kit-plugin-wallet/react에 있으며 동일한 프로바이더를 읽습니다 — 별도의 지갑 프로바이더는 필요하지 않습니다.

설치

Terminal
$
npm install @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-wallet @solana/react

클라이언트 빌드

React 트리 외부의 모듈 스코프에서 클라이언트를 한 번 빌드하세요. 브라우저에서 walletSigner는 연결된 지갑을 수수료 납부자이자 신원으로 지정합니다.

client.ts
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>;

walletSignerclient.wallet(Wallet Standard 검색 및 연결)을 추가하고, solanaRpcclient.rpcclient.sendTransaction 헬퍼를 추가합니다. 이후에 비동기적으로 resolve되는 플러그인을 추가하더라도 타입이 올바르게 유지되도록 클라이언트 타입을 Awaited<>로 감싸서 내보내고, 모든 useClient 호출에 해당 AppClient를 전달하세요.

트리를 한 번만 감싸기

ClientProvider는 클라이언트를 게시합니다. 데이터 훅(useRequest, useSubscription, useTrackedData)은 useClient를 통해 클라이언트를 읽습니다. 아래의 지갑 훅은 첫 번째 인수로 동일한 클라이언트를 명시적으로 전달받아야 합니다.

providers.tsx
"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. 읽기 전용 지갑의 경우 signernull입니다.
  • 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-reconnect
if (connected) {
return (
<div>
<p>{connected.account.address}</p>
<button onClick={() => disconnect.dispatch()}>Disconnect</button>
</div>
);
}
return (
<div>
{wallets.map((wallet) => (
<button
key={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.rpcclient.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 서브패스는 SWRTanStack Query를 위해 동일한 훅을 래핑합니다.

Solana 개발자를 위한 일반적인 패턴

  • 단일 프로바이더: ClientProvider만 있으면 됩니다. useClient<AppClient>()로 클라이언트를 가져와 지갑 훅에 전달하세요.
  • 서버 컴포넌트 지원: 훅을 호출하는 리프 컴포넌트에만 "use client"를 표시하세요. 서버 읽기는 지갑 없이 일반 Kit RPC 클라이언트를 사용할 수 있습니다.
  • 테스트: 지갑과 RPC 응답을 시뮬레이션하기 위해 ClientProvider를 통해 모의 클라이언트를 게시하세요.

두 가지 훅 패밀리, 같은 이름

useSignInuseSignMessage@solana/react(UiWalletAccount 인수를 받음)와 @solana/kit-plugin-wallet/react(client를 받고 ActionResult를 반환) 양쪽에 모두 존재합니다. 위의 클라이언트 패턴에서는 @solana/kit-plugin-wallet/react 버전을 사용하세요. 기존 @solana/react 지갑 훅은 점차 대체되고 있습니다.

이 가이드를 Kit 클라이언트 개요와 함께 참고하여 각 훅이 읽는 클라이언트를 이해하세요.

Is this page helpful?

목차

페이지 편집