본문으로 건너뛰기

Sui TypeScript SDK로 클라이언트 앱 만들기

이 실습은 이 섹션의 이전 topic에서 빌드한 예제와는 다른 방향으로 진행된다.

실행 중인 예제에 frontend를 추가하는 대신, 이 가이드는 React App에서 dApp Kit를 설정하는 과정을 안내한다.

Wallet에 연결하고 Sui RPC node에서 데이터를 조회해 앱에 표시할 수 있다.

이것을 사용해 앞서 사용한 예제의 자체 frontend를 만들 수 있지만, 완전히 동작하는 앱을 빠르게 실행하고 싶다면 터미널 또는 콘솔에서 다음 명령을 실행해 이 실습의 모든 단계가 이미 구현된 새 앱을 scaffold한다:

정보

You must use the pnpm or yarn package managers to create Sui project scaffolds. Follow the pnpm install or yarn install instructions, if needed.

$ pnpm create @mysten/dapp --template react-client-dapp

또는

$ yarn create @mysten/dapp --template react-client-dapp

What is the Sui TypeScript SDK?

Sui TypeScript SDK (@mysten/sui)는 TypeScript에서 Sui 생태계와 상호작용하는 데 필요한 모든 low-level 기능을 제공한다.

TypeScript를 지원하는 React Native 같은 도구로 작성된 web app, Node.js app, mobile app을 포함해 어떤 TypeScript 또는 JavaScript project에서도 이를 사용할 수 있다.

Sui TypeScript SDK에 대한 자세한 내용은 Sui TypeScript SDK documentation을 본다.

What is dApp Kit?

dApp Kit (@mysten/dapp-kit-react)는 Sui에서 앱을 빌드하는 일을 단순하게 만드는 React hook, component, utility 모음이다.

dApp Kit에 대한 자세한 내용은 dApp Kit documentation을 본다.

Installing dependencies

시작하려면 React app이 필요하다.

다음 단계는 어떤 React app에도 적용되므로 기존 React app에 dApp Kit를 추가할 때도 같은 단계를 따르면 된다.

새 project를 시작한다면 Vite를 사용해 새 React app을 scaffold할 수 있다.

터미널 또는 콘솔에서 다음 명령을 실행하고 framework로 React를 선택한 다음 TypeScript template을 선택한다:

$ npm init vite

이제 React app이 있으므로 dApp Kit를 사용하는 데 필요한 dependency를 설치할 수 있다:

$ npm install @mysten/sui @mysten/dapp-kit-react @tanstack/react-query

Setting up Provider components

dApp Kit의 모든 기능을 사용하려면 앱을 DAppKitProvider component로 감싼다.

앱을 렌더링하는 루트 component를 열고(Vite template이 사용하는 기본 위치는 src/main.tsx이다) 현재 코드를 다음 내용으로 통합하거나 교체한다.

먼저 createDAppKit을 사용해 dApp Kit 인스턴스를 만든다.

이렇게 하면 앱이 지원하는 네트워크와 각 네트워크의 client를 만드는 방법이 구성된다.

import { createDAppKit, DAppKitProvider } from '@mysten/dapp-kit-react';
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

const dAppKit = createDAppKit({
networks: ['devnet', 'mainnet'],
defaultNetwork: 'devnet',
createClient(network) {
return new SuiGrpcClient({
network,
baseUrl:
network === 'mainnet'
? 'https://fullnode.mainnet.sui.io:443'
: 'https://fullnode.devnet.sui.io:443',
});
},
});

// TypeScript 지원을 위해 타입을 등록한다
declare module '@mysten/dapp-kit-react' {
interface Register {
dAppKit: typeof dAppKit;
}
}

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<DAppKitProvider dAppKit={dAppKit}>
<App />
</DAppKitProvider>
</QueryClientProvider>
</React.StrictMode>,
);

Connecting to a wallet

DAppKitProvider 설정이 완료되면 dApp Kit hook과 component를 사용할 수 있다.

사용자가 앱에 자신의 wallet을 연결할 수 있게 하려면 ConnectButton을 추가한다.

import { ConnectButton } from '@mysten/dapp-kit-react';

function App() {
return (
<div className="App">
<header className="App-header">
<ConnectButton />
</header>
</div>
);
}

ConnectButton component는 클릭하면 modal을 여는 버튼을 표시하며, 이를 통해 사용자가 자신의 wallet을 연결할 수 있다.

연결되면 component는 해당 address를 표시하고 연결 해제 옵션을 제공한다.

Getting the connected wallet address

이제 사용자가 자신의 wallet을 연결할 수 있는 방법이 있으므로 연결된 wallet account에 대한 세부 정보를 얻기 위해 useCurrentAccount hook 사용을 시작할 수 있다.

import { ConnectButton, useCurrentAccount } from '@mysten/dapp-kit-react';

function App() {
return (
<div className="App">
<header className="App-header">
<ConnectButton />
</header>

<ConnectedAccount />
</div>
);
}

function ConnectedAccount() {
const account = useCurrentAccount();

if (!account) {
return null;
}

return <div>Connected to {account.address}</div>;
}

Querying data from Sui RPC nodes

이제 연결할 account가 있으므로 연결된 account가 소유한 object를 조회할 수 있다.

Sui client를 얻기 위해 useCurrentClient hook을 사용하고, 이를 TanStack Query의 useQuery hook과 결합해 데이터를 가져온다:

import { useCurrentAccount, useCurrentClient } from '@mysten/dapp-kit-react';
import { useQuery } from '@tanstack/react-query';

function ConnectedAccount() {
const account = useCurrentAccount();

if (!account) {
return null;
}

return (
<div>
<div>Connected to {account.address}</div>;
<OwnedObjects address={account.address} />
</div>
);
}

function OwnedObjects({ address }: { address: string }) {
const client = useCurrentClient();
const { data } = useQuery({
queryKey: ['ownedObjects', address],
queryFn: () => client.core.listOwnedObjects({ owner: address }),
});

if (!data) {
return null;
}

return (
<ul>
{data.objects.map((object) => (
<li key={object.objectId}>
<a href={`https://example-explorer.com/object/${object.objectId}`}>
{object.objectId}
</a>
</li>
))}
</ul>
);

이제 wallet에 연결되고 RPC node에서 데이터를 조회할 수 있는 앱이 준비되었다.

여기서 다음 단계는 Move module과 상호작용하고, transaction block을 구성하고, Move call을 만드는 일을 시작하는 것이다.

이 실습은 Counter end-to-end example에서 이어진다.