Building a Simple NFT Explorer Using Web3 Data APIs

Web3 Data API๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ NFT ๊ฒ€์ƒ‰๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ด…๋‹ˆ๋‹ค.

๋นŒ๋” ์—ฌ๋Ÿฌ๋ถ„ ์•ˆ๋…•ํ•˜์„ธ์š”! NFT ํŠœํ† ๋ฆฌ์–ผ์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค ๐ŸŽ‰

์ด๋ฒˆ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ๊ฐ„๋‹จํ•œ NFT Explorer๋ฅผ ์„ค๊ณ„ํ•˜๊ณ  ์ง์ ‘ ๋งŒ๋“ค์–ด๊ฐ€๋Š” ๊ณผ์ •์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก

๋ณธ ํŠœํ† ๋ฆฌ์–ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

Step 1. ์š”๊ตฌ์‚ฌํ•ญ ์ˆ˜๋ฆฝ ๋ฐ ์—ฐ๋™ํ•  API ์‚ดํŽด๋ณด๊ธฐ

Step 2. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ

Step 3. API ์—ฐ๋™ํ•˜๊ธฐ

Step 4. ์˜ˆ์ œ ์ฝ”๋“œ๋กœ ๋™์ž‘ ํ™•์ธํ•˜๊ธฐ

๊ทธ๋Ÿผ ํ•˜๋‚˜์”ฉ ์ง„ํ–‰ํ•ด๋ณผ๊นŒ์š”?


Step 1. ์š”๊ตฌ์‚ฌํ•ญ ์ˆ˜๋ฆฝ ๋ฐ ์—ฐ๋™ํ•  API ์‚ดํŽด๋ณด๊ธฐ

๊ตฌํ˜„ํ•  NFT Explorer๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ„๋‹จํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๊ธฐ๋Šฅ 1. ๊ณ„์ • ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” NFT ๋ชฉ๋ก์„ ์กฐํšŒํ•˜์—ฌ ๋ณด์—ฌ์ค€๋‹ค.

  • ๊ธฐ๋Šฅ 2. NFT ๋ชฉ๋ก์—์„œ ํŠน์ • NFT๋ฅผ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น NFT์— ๋Œ€ํ•œ Metadata์™€ ํ•จ๊ป˜ ์•„๋ž˜ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
    • ๊ธฐ๋Šฅ 2-1. NFT Contract์˜ Metadata๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๊ธฐ๋Šฅ 2-2. ํ•ด๋‹น NFT๊ฐ€ ๊ฑฐ๋ž˜๋œ ์ด๋ ฅ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐ„๋‹จํ•˜์ง€๋งŒ NFT์™€ ๊ด€๋ จ๋œ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ๋ชจ๋‘ ๋‹ด๊ณ  ์žˆ๋„ค์š”! ์ด์ œ Nodit์˜ Web3 Data API ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๊ฐ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณผ๊นŒ์š”?


๊ธฐ๋Šฅ 1. ๊ณ„์ • ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” NFT ๋ชฉ๋ก์„ ์กฐํšŒํ•˜์—ฌ ๋ณด์—ฌ์ค€๋‹ค.

์ด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ Input์— ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  [๊ฒ€์ƒ‰] ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‘๋‹ต์œผ๋กœ๋ถ€ํ„ฐ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•œ NFT์˜ ๋ชฉ๋ก์„ ์กฐํšŒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ์—ฐ๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


์ด๋Ÿฌํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” API๋กœ Web3 Data API์˜ NFT API์ค‘ getNFTsOwnedByAccount API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”! ์•„๋ž˜์™€ ๊ฐ™์ด curl์„ ์ž‘์„ฑํ•˜์—ฌ API๋ฅผ ํ˜ธ์ถœํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

curl --request POST \
     --url https://web3.nodit.io/v1/ethereum/mainnet/nft/getNftsOwnedByAccount \
     --header 'X-API-KEY: Input_your_API_Key' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "accountAddress": "Input_your_account_address"
}
'

์œ„ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์‘๋‹ต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋กœ NFT์˜ ๋ชฉ๋ก์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด name๊ณผ symbol ๊ทธ๋ฆฌ๊ณ  tokenId, contractAddress๋ฅผ ์ด์šฉํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

{
  "rpp": 20,
  "cursor": "W3sia2V5IjoidmFsdWUiLCJvcmRlciI6IkRFU0MiLCJlcXVhbCI6dHJ1ZSwicG9pbnQiOiIxIn0seyJrZXkiOiJ0b2tlbklkIiwib3JkZXIiOiJBU0MiLCJlcXVhbCI6ZmFsc2UsInBvaW50IjoiMTkifV0=",
  "items": [
    {
      "tokenId": "0",
      "tokenUri": null,
      "tokenUriSyncedAt": null,
      "ownerAddress": "0x84bb73064300aB1f539310237B892Ca47C58778c",
      "balance": "1",
      "contract": {
        "address": "0xe6313d1776E4043D906D5B7221BE70CF470F5e87",
        "deployedTransactionHash": "0xe015aef229ebe54e6e0b788a0b3eeafac1f4a881dffef74d96e3695a63e0c261",
        "deployedAt": "2023-01-26T02:20:47.000Z",
        "deployerAddress": "0x84bb73064300aB1f539310237B892Ca47C58778c",
        "logoUrl": null,
        "type": "ERC721",
        "name": "OnChainShiba",
        "symbol": "OCS"
      }
    },
  ...
    }
  ]
}

๊ธฐ๋Šฅ 2-1. NFT Contract์˜ Metadata๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ๋ชฉ๋ก์˜ NFT ์ค‘ ํ•œ ๊ฐ€์ง€๋ฅผ ์„ ํƒํ•˜๋ฉด Contract์˜ Metadata์™€ ๊ฑฐ๋ž˜ ์ด๋ ฅ์„ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ๋‹ค์ด์–ด๊ทธ๋žจ๊ณผ ๊ฐ™์ด ์—ฐ๋™ํ•  ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”.

Nodit์˜ getNFTMetadataByTokenIds ๋ผ๋Š” API๋ฅผ ์ด์šฉํ•ด ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ contractAddress์™€ tokenId ๊ฐ’์„ ์ด์šฉํ•ด API๋ฅผ ํ˜ธ์ถœํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

curl --request POST \
     --url https://web3.nodit.io/v1/ethereum/mainnet/nft/getNftMetadataByTokenIds \
     --header 'X-API-KEY: Input_your_API_Key' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "tokens": [
    {
      "contractAddress": "Input_your_contract_account_address",
      "tokenId": "1"
    }
  ]
}
'

์œ„ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ค‘์—์„œ ํ•„์š”ํ•œ ๊ฐ’์„ ์„ ํƒํ•ด ํ™”๋ฉด์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

[
  {
    "tokenId": "1",
    "tokenUri": "ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/1",
    "tokenUriSyncedAt": "2024-04-17T14:38:49.279Z",
    "rawMetadata": "{\"image\":\"ipfs://QmPbxeGcXhYQQNgsC6a36dDyYUcHgMLnGKnF8pVFmGsvqi\",\"attributes\":[{\"trait_type\":\"Mouth\",\"value\":\"Grin\"},{\"trait_type\":\"Clothes\",\"value\":\"Vietnam Jacket\"},{\"trait_type\":\"Background\",\"value\":\"Orange\"},{\"trait_type\":\"Eyes\",\"value\":\"Blue Beams\"},{\"trait_type\":\"Fur\",\"value\":\"Robot\"}]}\n",
    "metadataSyncedAt": "2024-04-17T14:38:53.819Z",
    "media": {
      "originUrl": "ipfs://QmPbxeGcXhYQQNgsC6a36dDyYUcHgMLnGKnF8pVFmGsvqi",
      "cachedUrl": "https://cdn.luniverse.io/media/ethereum/mainnet/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d/1/raw.png",
      "thumbnailUrl": "https://cdn.luniverse.io/media/ethereum/mainnet/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d/1/medium.png",
      "updatedAt": "2023-08-29T09:23:45.000Z"
    },
    "contract": {
      "address": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
      "deployedTransactionHash": "0x22199329b0aa1aa68902a78e3b32ca327c872fab166c7a2838273de6ad383eba",
      "deployedAt": "2021-04-22T03:03:16.000Z",
      "deployerAddress": "0xaBA7161A7fb69c88e16ED9f455CE62B791EE4D03",
      "logoUrl": null,
      "type": "ERC721",
      "name": "BoredApeYachtClub",
      "symbol": "BAYC"
    }
  }
]

๊ธฐ๋Šฅ 2-2. ํ•ด๋‹น NFT๊ฐ€ ๊ฑฐ๋ž˜๋œ ์ด๋ ฅ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

ํŠน์ • NFT์˜ ๊ฑฐ๋ž˜ ์ด๋ ฅ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ฐจ๋ก€ ์ž…๋‹ˆ๋‹ค. 2-a์™€ ๋™์ผํ•œ ํ˜ธ์ถœ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ๊ฑฐ๋ž˜ ์ด๋ ฅ์— ๋Œ€ํ•œ ์ •๋ณด ๋ชฉ๋ก์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Nodit์—์„œ๋Š” getNftTransfersByTokenId API๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ํ•ด๋‹น API๋ฅผ ์ด์šฉํ•ด ํŠน์ • NFT์˜ ๊ฑฐ๋ž˜ ์ด๋ ฅ์„ ์กฐํšŒํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2024-08-30 แ„‹แ…ฉแ„’แ…ฎ 4.10.56.png
 curl --request POST \
     --url https://web3.nodit.io/v1/ethereum/mainnet/nft/getNftTransfersByTokenId \
     --header 'X-API-KEY: Input_your_API_Key' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "contractAddress": "Input_your_contract_account_address",
  "tokenId": "Input_your_token_id",
}
'

ํ˜ธ์ถœ์— ์„ฑ๊ณตํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์‘๋‹ต์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{
  "rpp": 20,
  "cursor": null,
  "items": [
    {
      "from": "0x0000000000000000000000000000000000000000",
      "to": "0x84bb73064300aB1f539310237B892Ca47C58778c",
      "value": "1",
      "timestamp": 1674701879,
      "blockNumber": 16488153,
      "transactionHash": "0x8d46fd387f991e7a3bc6c5eecc2c8f3737153ec689a8094f9631253d2544a857",
      "logIndex": 231,
      "contract": {
        "address": "0xe6313d1776E4043D906D5B7221BE70CF470F5e87",
        "deployedTransactionHash": "0xe015aef229ebe54e6e0b788a0b3eeafac1f4a881dffef74d96e3695a63e0c261",
        "deployedAt": "2023-01-26T02:20:47.000Z",
        "deployerAddress": "0x84bb73064300aB1f539310237B892Ca47C58778c",
        "logoUrl": null,
        "type": "ERC721",
        "name": "OnChainShiba",
        "symbol": "OCS"
      },
      "nft": {
        "tokenId": "1",
        "tokenUri": "data:application/json;base64,eyJuYW1lIjoiT25DaGFpblNoaWJhICMxIiwiZGVzY3JpcHRpb24iOiJUaGlzIGNvbGxlY3Rpb24gaXMgdGhlIGZpcnN0IG9mZmljaWFsIGNvbGxlY3Rpb24gb2YgTWV0YXZlcnNlICYgTkZUIFN0dWRpbywgJ0xhYmVsIE0nLiBEZWRpY2F0ZWQgdG8gYWxsIFNoaWJhIGFuZCBwaXhlbCBhcnQgZmFucy4iLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCM2FXUjBhRDBpTVRBd0pTSWdhR1ZwWjJoMFBTSXhNREFsSWlCMmFXVjNRbTk0UFNJd0lEQWdNVEl3TUNBeE1qQXdJaUIyWlhKemFXOXVQU0l4TGpJaUlIaHRiRzV6UFNKb2RIUndPaTh2ZDNkM0xuY3pMbTl5Wnk4eU1EQXdMM04yWnlJK1BHbHRZV2RsSUhkcFpIUm9QU0l4TWpBd0lpQm9aV2xuYUhROUlqRXlNREFpSUdoeVpXWTlJbVJoZEdFNmFXMWhaMlV2YzNabkszaHRiRHRpWVhObE5qUXNVRWhPTWxwNVFqTmhWMUl3WVVRd2FVMVVTWGROUTBsbllVZFdjRm95YURCUVUwbDRUV3BCZDBscFFqSmhWMVl6VVcwNU5GQlRTWGRKUkVGblRWUkpkMDFEUVhoTmFrRjNTV2xDTWxwWVNucGhWemwxVUZOSmVFeHFTV2xKU0doMFlrYzFlbEJUU205a1NGSjNUMms0ZG1RelpETk1ibU42VEcwNWVWcDVPSGxOUkVGM1RETk9NbHA1U1dkak0xSTFZa2RWT1VsdFNtaFpNblJ1WTIwNU1XSnRVWFJaTWpsellqTkpObVJJU21oaWJrNTNXVmhLYkdKdVVUZFpiVVpxWVRKa2VXSXpWblZhUXpGd1lsZEdibHBVY0RGamJYZHZXa2RHTUZsVWNIQmlWMFp1V2xNNWQySnRZemRaYlVaNldsUlpNRXhIYkZkUmF6bFRaSHBDVEZJeVpIWlJWVVpDVVZVMVZGWlhhRVpXVjJSQ1VWVkdRMW93UmtKUlZVWmFVVEJHV2xGVlJrSlNSMlJyWldwTk1GRlZSa0pSVlVaWlZHeE9VMDFGYkVKamJrMHdXWHBhVWxGVlJrSlNSbHBMVld0V1IxWldUa1prYmxGM1kydFdTMUZWUmtKVVZVWnlVMGRTYlUxdGVFbExNREY1WkZaVmQyUlZWak5MTVc4MFZVZFpjbFZGU2xSYU0wNXlWbFpHZGxvd1JrZFhSWEJEVWxWMFdsUlZSbGRWTVd4U1VUQmtUV0ZZV21oVFNFcEdVVlZLY2xKSVNURmlWVVpDVVZWR1FsRlZWbk5TYkZKeVZUTldVbUpWVGtSTFUzZ3hZMjEzYjFwSFJqQlpWSEJ3WWxkR2JscFRPWGRpYldNM1dXMUdlbHBVV1RCTVIyeFhVV3M1VTJSNlFreFNNbVIyVVZWR1FsRlZOVlJXVjJoR1ZsZGtRbEZWUmtOYU1FWkNVVlZHV2xFd1JscFJWVVpDVWtka2EyVnFUVEJSVlVaQ1VWVkdXVlJzVGxOTlJXeENZMjVOTUZsNldsSlJWVVpDVW14d1MxVnJWa2RXVms1R1pFZHdZVk13UmpSWFZYUlVaVU4wVW1WWGNFZGhSVVpPVGtkU1Jsb3daRzVOUjJ4dVlWVmFRbFpWTVVaa01WSnlWbTVHVTJGSFNYWlphbFYzVGpGR2FWSldRVFJWYlRsQ1l6STVVMW93YkVaVVZsa3dVMFZzVGxaV1RURlNXRkp1WVVjeFJsSkdiRFJaYTJzd1lVaHNibE50YkcxV1JWcDRVVlphY21GRk5WTmlNR2hEVTFVNVQwNVhkRVpTVlVaS1UydFdSbEZ0ZEhkV01GcFZaVlZHUWxGVlJrSlNWM2hIVmtkMFZHUldSblJSTUUxd1RFaFdlV0pEYUd0WldGSm9UMjFzZEZsWFpHeE1NMEoxV25wMGFWbFlUbXhPYWxGellWWmFRMVF4U2pOTlJYUklXakk1UWxGVlJrSlViRTVXWVVWV1Zsb3dSa0pSVlVwdVVWVkdRbEZXYkVSUlZteENVVlZHUlZveVVqWk5lbEpDVVZWR1FsRldhRTlWTVVsM1UxVkdlV042VW1wT2JFWkNVVlZHUjFwRmNGTlNWVnBXVlRCV01HRnNjRXhSV0doYVV6Rk9ORXN4UmpWaGExcHZVVlV3TUZwRlZtNVNNbU4zWVZka2NGSnJSbFpVVlZwMlV6Sm9kbFJyYkhaVFYyaFNVbXRTUTJWVlZYaFNhVGd5VW0xb1VtVnJSa1pSTUU1NVVWVldhbUZwT1V4TlJUQTFVakExZDFkRVpETlJhMjh3Wld4a1JWZEZSakJSVlVwdlZHMTRSRlV4VmxoU1ZXUldVMjVDVTFsVlJrMVpNRlpaVmtWR1JXUXhWa0pPUm5CNFVsZGFhVlpYWkVKUlZVWkNVV3R3VTFaVVZrWmpiWFJMV2pKa2JsQlVNSEJNU0ZaNVlrTm9hMWxZVW1oUGJXeDBXVmRrYkV3elFuVmFlblJwV1ZoT2JFNXFVWE5oVmxwRFZERktNMDFGZEVoYU1qbENVVlZHUWxSc1RsWmhSVlpXV2pCR1FsRlZTbTVSVlVaQ1VWWnNSRkZXYkVKUlZVWkZXakpTTmsxNlVrSlJWVVpDVVZab1QxVXhTWGRUVlVaNVkzcFNhazVzUmtKUlZVWlhaVVZ3VTFKVldsWlZNRll3WVd4d1RGRllhRnBSTVZKSFRERkJOVmRET1cxUFNIQm9ZbFJvY2xVeFJYWlZiV3d5VDBkYVlWVkRkRzFMTTFaV1QxaGplV1ZWV2xSUlZuQlVWRVV4U0dReFJrVlplbGx5VEhsMGMyUkZVbkpVVjNCUlYxTTRNRmRGZDNaVWJuQlJUMFp3TkZKR2FGcFNNRkp1Vm0weGExb3lWazFVTUZKdVZsVm9SbEZVYUZaVk1uUk9USHBCZVdKV1JscFVSMnR5V2tkc2RGSlhWbTlhTUZKS1kwWkdOVnB0V25GVFdHTTBWRVZvUlZSSFduTk1NVTR4VkZNNVRGRXliM3BoVlZvMFlsZE9UV0pyZEhCaFdFRjZUVmRaTkV3ell6VmFWMHBhVkZoV05Fc3hSalpXUlVaM1VXNUpjbHBIWkdGVGJUVkxTekZzZW1GcE9UWmFNbEY1VkVkT2JsRlhkRkJTV0VwNVRIcHNkRkV3Tld0aE1taFhZekJPZW1GR1RucGhiWEI2VkZSa1JWUnRkRmxQUmtKd1VtNUdlbFZHUmtoU1ZUVk9aRmR3ZUdKVVRUUlZWazQwV2pGc1NGTnViRzFrYTJSUlpERlZlVlp0YURGVlJYY3lWRVJvVDJNd2FISlNWV1JEWVhwV1IyRklVbFZYUkZZMFVraEdTRlZHUm1GU01EQjRaRlZXUTA5VlZuaFdXSEJ0VTFWR2FGRnJSa3haTW13MFUydDRhVmR1YUhWTmJrWnZZak53VGxOSWEzWmxibVJDVkVaS2RWSkZWbEpsVkVKeVpWZGtURlZXUm14UmEyc3hWakExU1ZSV1NqVmFWelZHVWpCa00yVllaelZhVmtaT1dUQm9OVkV3UmsxWFIzQkNUa2hTY1dWVlJsUmFNbmMxVmtaYVVWRlZWakZSYldSdVlUQmtVbFJWVmtWUlV6bHFTekprVUZSWFVrVlNhbFl5VTNwb1VGbFhaSFZSYlVadVZGUkdWRmt6VGxoVFdIQjBZVmR3UjJORlJtbFJWMlJWVFZkd2FsbHJTbWhSYTBaVFVrVkdTRkV3VG5KWGJGSkdXVEJXYmxGWFpFTk9NVnAyVkZad2RsbHNhM2RrUlVaQ1VWVkdRbEZyY0ZOV1ZGWkdZMjEwUzFveVpHNVFWREJ3VEVoV2VXSkRhR3RaV0ZKb1QyMXNkRmxYWkd4TU0wSjFXbnAwYVZsWVRteE9hbEZ6WVZaYVExUXhTak5OUlhSSVdqSTVRbEZWUmtKVWJFNVdZVVZXVmxvd1JrSlJWVXB1VVZWR1FsRldiRVJSVm14Q1VWVkdSVm95VWpaTmVsSkNVVlZHUWxGV2FFOVZNVWwzVTFWR2VXTjZVbXBPYkVaQ1VWVkdSbEpyY0ZOU1ZWcFdWVEJXTUdGc2NFeFJXR2hhVXpGT05Fc3hSalZoYTFwdlVWVXhXbGxYT1VkTlIxbHlWa2RhVVU5R2NEWldSR3h2WWxWV2FrMVZUWHBSYWxaaFdrWkdSRXN5ZEZsbFYwWkRaREZHUlZReldsTlRWVXAyVkd0c2VWTnJVbTVUTUZKSVRVZE9TMlJVVWtwVVZXeENVVzB3TkZGclNuUlpWR1JwVkd0S1FsRlZSa0pSVlZaelVteFNjbFV6VmxKaVZVNUVTMU40TVdOdGQyOWFSMFl3V1ZSd2NHSlhSbTVhVXpsM1ltMWpOMWx0Um5wYVZGa3dURWRzVjFGck9WTmtla0pNVWpKa2RsRlZSa0pSVlRWVVZsZG9SbFpYWkVKUlZVWkRXakJHUWxGVlJscFJNRVphVVZWR1FsSkhaR3RsYWswd1VWVkdRbEZWUmxsVWJFNVRUVVZzUW1OdVRUQlplbHBTVVZWR1FsVnVVa3RWYTFaSFZsWk9SbVJyV2xkV1NFNVFaREp3UWxSWFVsaFhXRll6V1RGR1UyRXdOVzlYYWtvMFVqQndkVlV3TkhkWGF6azJVMVZXYTJGV1NraFVibFpYWkd4S1MxTkdiRkJUVms0MlVsVm9VRlpFU25WWlZUbElUbFY0VTFNeGEzbFpNMUYyWlcxVmVrNVlRa05TTVVwc1UxZHdUa3Q1T1VwTlZVWkNZVE5rY0dGcWJHaFZSWFJoVWtoc01Gb3dXalJhYTNSSFRqQm9TbFp0ZEcxWGJFcENZakpHYTFOcldrVmhSbHBJWlZkYVdsZEdSa1JPYld4d1ZFWkpNRk5YY3pKV1YzUkpaREpPVkU5WVpGRmtiV2hXVVd4YWNXUnVRa2xVYld4VlVtMTBZVko2U2t0TmJXUjRVbFZ6TlZGdVFsbFdha3AxV201d1dWUllSa1JTYkVaT1lVaHNlVlJXUmxCTldFNVFVbGQ0Y1ZKWFNsUmFiV041WTFSU1dGUnFUbXRWTURrMFdsZFdhR05yYnpSUFJXeHdWVlZLTW1FelZUVlRSRXAwVWpGbmVtVkZlSGhaTVVwaFVXcHNOVlF4UVhKaFJGWnlUVmRzZDFscldYaFJNV3hJV201bmVsbFZVbmxXTVZaaFYxaGtjazFIVm0xUlZVVjRXa1ZqZGs1VlNrSlViVkp6V1ROdmVGSklUakJsYWtaeFlUQXhVMDFxVGpaT2JWWjZVVEpvUjJGVk9UVldRM1JDWTBoamVrNUZVbEZWTTBKSVpXcEtkMU13VlRSTE1XUkNZWHBvTkUxdGVEQk9NbmhHVFRCd00xUklSbEZoTW5CVlZqQnNhRXN6U20xVVZHUldWRVJhYkZSRmJIQlNNVVpLWVc1Vk5WcHVjRXRpVmxwSVZteFNWRTVGTlVKUlZVWkNVVlZHVkZaV1dsQlZhM014VVRGc1NsTlVNSEJNU0ZaNVlrTm9hMWxZVW1oUGJXeDBXVmRrYkV3elFuVmFlblJwV1ZoT2JFNXFVWE5oVmxwRFZERktNMDFGZEVoYU1qbENVVlZHUWxSc1RsWmhSVlpXV2pCR1FsRlZTbTVSVlVaQ1VWWnNSRkZXYkVKUlZVWkZXakpTTmsxNlVrSlJWVVpDVVZab1QxVXhTWGRUVlVaNVkzcFNhazVzUmtKUlZVWkZWbXR3VTFKVldsWlZNRll3WVdwR1NrMHdPSFpqZW14Q1ZWUm9RazVIUm01U01tZDNVV3BDVDFOWGRFcGhTR2hGVVZkR1UxTXdOVU5WYTFKQ1VsVk9SR0l4YkZWV1ZtUnhWVlpXVW1Rd1NrSmFNMFpJVWxSR1IxTlliRVJUVlVaRFZteENUV015ZUcxaGFrWjBZMVZHUWxGVlJrSlNWM2hIVmtkMFZHUldSblJSTUUxd1R6SkthRmt5ZEc1amJUa3hZbTFSZEdOdFZuZGFWMFl3VDIwMWRreFlTbXhqUjFab1pFUjBhVmxYVG5KYU0wcDJaRmMxYTB4WVRuQmxiVlUyV1RJNWRXUkhSbkJpYW5ScFdWZE9jbG96U25aa1Z6VnJURmhDZG1NeWJEQmhWemwxVDIxT2JHSnVVbXhqYW5Sd1lsZEdibHBUTVhsYVZ6VnJXbGhLY0dKdFl6Wk1XR1JzV1cxMGNHUkRNWFpqU0ZKd1lsZHNObHBUTVdwaU1qVXdZMjFHZW1SRWMzUmlXRTEwWVZjMU1GcFlTbmRpTW5ob1pFZHNkbUpwTVhSaU1sSnNUMjAxYkZsWVNteGpNMUYwWW0xV2NGb3lhR2xpTTBrM1lWY3hhRm95VlhSamJWWjFXa2RXZVdGWE5XNVBhVEYwWWpOdmRGa3pTbkJqTTBGMFdsZFNibHBZVFRkaFZ6Rm9XakpWZEdOdFZuVmFSMVo1WVZjMWJrOXVRbkJsUjFaeldWaFNiRnBFYzJsUWFuZDJZek5hYmxCblBUMGlQand2YVcxaFoyVStQQzl6ZG1jKyIsImF0dHJpYnV0ZXMiOlt7InRyYWl0X3R5cGUiOiJFeWVzIiwidmFsdWUiOiJDYWbDqSBDdXRpZXMifSx7InRyYWl0X3R5cGUiOiJNb3V0aCIsInZhbHVlIjoiVHdpZyJ9LHsidHJhaXRfdHlwZSI6IkhlYWQiLCJ2YWx1ZSI6IkNyb3duIn0seyJ0cmFpdF90eXBlIjoiRWFycmluZyIsInZhbHVlIjoiR29sZCJ9LHsidHJhaXRfdHlwZSI6IkZ1ciIsInZhbHVlIjoiT3JhbmdlIn1dfQ==",
        "tokenUriSyncedAt": "2024-02-21T08:26:18.754Z"
      }
    }
  ]
}

์ข‹์Šต๋‹ˆ๋‹ค! API๋ฅผ ํ†ตํ•ด NFT ํƒ์ƒ‰๊ธฐ ๊ตฌํ˜„์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋“ค์„ ๋ชจ๋‘ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Œ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ๊ฐ„๋‹จํ•œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•˜๊ณ  ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ด…์‹œ๋‹ค.


Step 2. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ

๊ฐ„๋‹จํ•œ DApp ๊ตฌํ˜„์„ ์œ„ํ•ด ์•„๋ž˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์„ค์น˜๋ฅผ ์œ„ํ•œ ๋„์›€์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•ด์ฃผ์„ธ์š”.

VS Code Required

์†Œ์Šค ์ฝ”๋“œ ํŽธ์ง‘๊ธฐ๋กœ ์ฝ”๋“œ๋ฅผ ์†์‰ฝ๊ฒŒ ์ž‘์„ฑํ•˜๊ณ  ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Node.js Required

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ์œผ๋กœ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰์„ ์œ„ํ•ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

git Optional

๋ถ„์‚ฐ ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์œผ๋กœ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋กœ์ปฌ ํ™˜๊ฒฝ์— ๋‹ค์šด๋กœ๋“œ ๋ฐ›๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์™ธ์— ์‚ฌ์šฉ๋œ ํ”„๋ ˆ์ž„์›Œํฌ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Step 4. ์˜ˆ์ œ์ฝ”๋“œ์˜ package.json ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.


Step 3. API ์—ฐ๋™ํ•˜๊ธฐ

์ด๋ฏธ ํ”„๋กœ์ ํŠธ ๊ตฌํ˜„์— ํ•„์š”ํ•œ API๋“ค์„ ์‚ดํŽด๋ณด๊ณ  ํ˜ธ์ถœ ์‹œ ์‘๋‹ต์ด ์–ด๋–ป๊ฒŒ ์˜ค๋Š”์ง€ ํ™•์ธํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ด API๋“ค์„ ์ด์šฉํ•ด ํ”„๋ก ํŠธ์—”๋“œ ์ฝ”๋“œ์— ์—ฐ๋™ํ•ด ๋ณผ๊นŒ์š”?

๐Ÿ“˜

Nodit API Key๋ฅผ ๋ฐœ๊ธ‰ ๋ฐ›์œผ์…จ๋‚˜์š”?

Nodit API๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Nodit API Key๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์•„์ง Nodit์„ ์‹œ์ž‘ํ•˜์ง€ ์•Š์œผ์…จ๋‹ค๋ฉด ์•„๋ž˜ ๋งํฌ๋œ ํŽ˜์ด์ง€์˜ ์•ˆ๋‚ด์— ๋”ฐ๋ผ Nodit ์ฝ˜์†”์— ๊ฐ€์ž…ํ•˜๊ณ , ๊ฐœ๋ฐœ ์—ฐ๋™์„ ์œ„ํ•œ API Key๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„๋ณด์„ธ์š”. ์‹œ์ž‘์€ ๋ฌด๋ฃŒ์ž…๋‹ˆ๋‹ค!

๐Ÿ‘‰ย Nodit Node Quickstart

์šฐ์„  Nodit์˜ API Key์™€ Protocol, Network๋ฅผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•˜์—ฌ ํ•˜๋‚˜์˜ ํŒŒ์ผ์—์„œ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

// .env

VITE_API_KEY=Your_Nodit_API_Key
VITE_PROTOCOL=ethereum
VITE_NETWORK=mainnet

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํŒŒ์ผ์— ์ž‘์„ฑํ•œ ๊ฐ’์„ ๋ถˆ๋Ÿฌ์™€ HTTP ํ†ต์‹ ์„ ์ด์šฉํ•ด API๋ฅผ ํ˜ธ์ถœํ•  ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

// instance.ts

import axios, { AxiosInstance } from "axios";

const apiKey = import.meta.env.VITE_API_KEY;
const protocol = import.meta.env.VITE_PROTOCOL;
const network = import.meta.env.VITE_NETWORK;

export function createWeb3ApiInstance(): AxiosInstance {
  const instance = axios.create({
    baseURL: `https://web3.nodit.io/v1/${protocol}/${network}`,
    headers: {
      "X-API-KEY": `${apiKey}`,
      "Content-Type": "application/json",
      Accept: "application/json",
    },
  });
  return instance;
}

์•ž์„œ ์ž‘์„ฑํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ด์šฉํ•ด API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“  ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

// useQueries.ts

import { useQuery } from "@tanstack/react-query";
import { createWeb3ApiInstance } from "../apis/instance";
const instance = createWeb3ApiInstance();

export const useGetNFTOwnedByAccount = (
  accountAddress: string,
  page: number
) => {
  return useQuery({
    queryKey: ["getNFTOwnedByAccount", accountAddress, page],
    queryFn: async () => {
      try {
        const result = await instance.post("nft/getNftsOwnedByAccount", {
          accountAddress,
          withCount: true,
          withMetadata: true,
          rpp: 20,
          page: page,
        });
        return result;
      } catch (error) {
        console.error(error);
      }
    },
    retry: false,
    staleTime: 1000 * 60 * 5,
  });
};

ํ™”๋ฉด์— ๋…ธ์ถœ๋  ํ…Œ์ด๋ธ” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ฒ˜๋ฆฌํ•œ ๋ฐ์ดํ„ฐ์ธ ownedNftsByAccountData๋ฅผ ๋ฐ›์•„ ํ…Œ์ด๋ธ” ์š”์†Œ์— ๋ฐ”์ธ๋”ฉ ํ•ฉ๋‹ˆ๋‹ค.

// NftTable.tsx

import {
  NftListTableProps,
  NftsOwnedByAccountResponse,
} from "../types/interface";

const NftTable = ({ ownedNftsByAccountData }: NftListTableProps) =>

  return (
    <div className="flex flex-col items-center justify-center">
      <div className="mt-10 text-2xl font-bold">NFT List</div>
      {ownedNftsByAccountData && ownedNftsByAccountData.items.length > 0 ? (
          <table className="w-full max-w-7xl table-fixed border-collapse mt-5 mb-10 shadow-xl shadow-black">
            <thead>
              <tr className="border-2 border-noditGreen bg-noditGreen text-white">
                <th className="pl-5">Number</th>
                <th className="p-5">Name</th>
                <th className="p-5">Symbol</th>
                <th className="pr-5">Token Id</th>
              </tr>
            </thead>
            <tbody>
              {ownedNftsByAccountData.items.map(
                (item: NftsOwnedByAccountResponse, index: number) => (
                  <tr
                    key={index + item.contract.deployedTransactionHash}
                    className="border border-noditGreen hover:scale-105 duration-100 cursor-pointer"
                  >
                    <th className="font-bold p-5">
                      {ownedNftsByAccountData.page === 1
                        ? index + 1
                        : ownedNftsByAccountData.page * 20 + index - 19}
                    </th>
                    <th className="font-light p-5">{item.contract.name}</th>
                    <th className="font-light p-5">{item.contract.symbol}</th>
                    <th className="font-light p-5">
                      {item.tokenId.length < 10
                        ? item.tokenId
                        : item.tokenId.slice(0, 10) + "..."}
                    </th>
                  </tr>
                )
              )}
            </tbody>
          </table>
      ) : (
        <div>This Account doesn't have any NFTs</div>
      )}
    </div>
  );
};

export default NftTable;

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” API์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ํ…Œ์ด๋ธ” ์ปดํฌ๋„ŒํŠธ์— API ํ˜ธ์ถœ ๊ฒฐ๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

// NftList.tsx

import React from "react";
import { useGetNFTOwnedByAccount } from "./useQueries";
import NftTable from "./NftTable";

const NftList = (): React.ReactElement => {
  const {
    isError,
    data: ownedNftData,
    isLoading,
  } = useGetNFTOwnedByAccount(accountAddress, currentPage);
  return (
    <div>
      <NftTable
        ownedNftByAccountData={ownedNftData?.data}
        accountAddress={accountAddress}
      />
    </div>
  );
};

export default NftList;

์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑ ํ›„ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์‚ฌ์ง„๊ณผ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฅผ ํ™”๋ฉด์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


Step 4. ์˜ˆ์ œ ์ฝ”๋“œ๋กœ ๋™์ž‘ ํ™•์ธํ•˜๊ธฐ

์œ„์—์„œ ๊ตฌํ˜„ํ•œ ๋‚ด์šฉ์„ ํฌํ•จํ•œ, ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด๋†“์€ ์˜ˆ์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ณต์œ ํ•ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

  • ์†Œ์Šค ์ฝ”๋“œ ๋‹ค์šด๋กœ๋“œ ๋ฐ ์ ‘๊ทผ
$ git clone https://github.com/Lambda256/Nodit-EVM-Tutorials
$ cd Nodit-EVM-Tutorials
$ cd nft_tutorial
  • ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™” ๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜
$ npm install

์‚ฌ์šฉ์ž๊ฐ€ ์ด์šฉํ•  ํ”„๋กœํ† ์ฝœ๊ณผ ๋„คํŠธ์›Œํฌ ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž์˜ Nodit API Key๋ฅผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. Nodit API Key์™€ ํ”„๋กœํ† ์ฝœ, ๋„คํŠธ์›Œํฌ์˜ ๊ฒฝ์šฐ Nodit Console์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ ์ž…๋ ฅ๋˜์–ด ์žˆ๋Š” ethereum๊ณผ mainnet ์™ธ์—๋„ ์‚ฌ์šฉ์ž๊ฐ€ ์—ฐ๊ฒฐํ•œ ๋…ธ๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// .env

VITE_API_KEY=Your_Nodit_API_Key
VITE_PROTOCOL=ethereum
VITE_NETWORK=mainnet

ํ™˜๊ฒฝ ์„ค์ •์„ ์™„๋ฃŒํ•œ ํ›„ ํ„ฐ๋ฏธ๋„์— ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ Nodit NFT Tutorial์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

$ npm run dev

์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋œจ๋ฉด ์ •์ƒ์ ์œผ๋กœ ๊ตฌ๋™๋œ๊ฒƒ์ž…๋‹ˆ๋‹ค! ๊ธฐ๋Šฅ์„ ํ™•์ธํ•ด๋ณด์„ธ์š”.


Congratulation! ๐ŸŽŠ

Nodit NFT Tutorial์„ ๋ชจ๋‘ ์™„๋ฃŒํ•˜์˜€์Šต๋‹ˆ๋‹ค!์ด์ œ ๋‹ค๋ฅธ Nodit API๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‚˜๋งŒ์˜ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด ๋ณด์„ธ์š”.

๋”์šฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ Nodit์˜ API๊ฐ€ ๊ถ๊ธˆํ•˜์‹ ๊ฐ€์š”? ์•„๋ž˜ ๋งํฌ๋ฅผ ๋ˆŒ๋Ÿฌ ํ™•์ธํ•ด ๋ณด์„ธ์š”!

Nodit API Reference ๋ฐ”๋กœ๊ฐ€๊ธฐ